You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils-tests.tsx 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { waitFor } from '@testing-library/react';
  21. import userEvent from '@testing-library/user-event';
  22. import { Profile } from '../../api/quality-profiles';
  23. import { renderAppRoutes } from '../../helpers/testReactTestingUtils';
  24. import {
  25. byLabelText,
  26. byPlaceholderText,
  27. byRole,
  28. byTestId,
  29. byText,
  30. } from '../../helpers/testSelector';
  31. import {
  32. CleanCodeAttribute,
  33. CleanCodeAttributeCategory,
  34. SoftwareQuality,
  35. } from '../../types/clean-code-taxonomy';
  36. import { CurrentUser } from '../../types/users';
  37. import routes from './routes';
  38. const selectors = {
  39. loading: byText('loading'),
  40. // List
  41. rulesList: byRole('list', { name: 'list_of_rules' }),
  42. ruleListItemLink: (name: string) => byRole('link', { name }),
  43. getAllRuleListItems: () =>
  44. byRole('list', { name: 'list_of_rules' })
  45. .byRole('listitem')
  46. .getAll()
  47. .filter((item) => !!item.getAttribute('data-rule')),
  48. currentListItem: byRole('listitem', { current: true }),
  49. // Filters
  50. searchInput: byRole('searchbox', { name: 'search.search_for_rules' }),
  51. clearAllFiltersButton: byRole('button', { name: 'clear_all_filters' }),
  52. // Facets
  53. cleanCodeCategoriesFacet: byRole('button', {
  54. name: 'coding_rules.facet.cleanCodeAttributeCategories',
  55. }),
  56. languagesFacet: byRole('button', { name: 'issues.facet.languages' }),
  57. typeFacet: byRole('button', { name: 'coding_rules.facet.types' }),
  58. tagsFacet: byRole('button', { name: 'coding_rules.facet.tags' }),
  59. repositoriesFacet: byRole('button', { name: 'coding_rules.facet.repositories' }),
  60. softwareQualitiesFacet: byRole('button', { name: 'coding_rules.facet.impactSoftwareQualities' }),
  61. severetiesFacet: byRole('button', { name: 'coding_rules.facet.impactSeverities' }),
  62. statusesFacet: byRole('button', { name: 'coding_rules.facet.statuses' }),
  63. standardsFacet: byRole('button', { name: 'issues.facet.standards' }),
  64. standardsOwasp2017Top10Facet: byRole('button', { name: 'issues.facet.owaspTop10' }),
  65. standardsOwasp2021Top10Facet: byRole('button', { name: 'issues.facet.owaspTop10_2021' }),
  66. standardsCweFacet: byRole('button', { name: 'issues.facet.cwe' }),
  67. availableSinceFacet: byRole('button', { name: 'coding_rules.facet.available_since' }),
  68. templateFacet: byRole('button', { name: 'coding_rules.facet.template' }),
  69. qpFacet: byRole('button', { name: 'coding_rules.facet.qprofile' }),
  70. facetClear: (name: string) => byTestId(name),
  71. facetSearchInput: (name: string) => byRole('searchbox', { name }),
  72. facetItem: (name: string | RegExp) => byRole('checkbox', { name }),
  73. availableSinceDateField: byPlaceholderText('date'),
  74. qpActiveRadio: byRole('radio', { name: `active` }),
  75. qpInactiveRadio: byRole('radio', { name: `inactive` }),
  76. dateInputMonthSelect: byTestId('month-select'),
  77. dateInputYearSelect: byTestId('year-select'),
  78. // Bulk change
  79. bulkChangeButton: byRole('button', { name: 'bulk_change' }),
  80. activateIn: byRole('menuitem', { name: 'coding_rules.activate_in' }),
  81. deactivateIn: byRole('menuitem', { name: 'coding_rules.deactivate_in' }),
  82. bulkChangeDialog: (count: number, activate = true) =>
  83. byRole('dialog', {
  84. name: `coding_rules.${
  85. activate ? 'ac' : 'deac'
  86. }tivate_in_quality_profile (${count} coding_rules._rules)`,
  87. }),
  88. activateInSelect: byRole('combobox', { name: 'coding_rules.activate_in' }),
  89. deactivateInSelect: byRole('combobox', { name: 'coding_rules.deactivate_in' }),
  90. noQualityProfiles: byText('coding_rules.bulk_change.no_quality_profile'),
  91. bulkApply: byRole('button', { name: 'apply' }),
  92. bulkSuccessMessage: (qpName: string, langName: string, count: number) =>
  93. byText(`coding_rules.bulk_change.success.${qpName}.${langName}.${count}`),
  94. bulkWarningMessage: (qpName: string, langName: string, count: number) =>
  95. byText(`coding_rules.bulk_change.warning.${qpName}.${langName}.${count}.1`),
  96. bulkClose: byRole('button', { name: 'close' }),
  97. // Rule details
  98. ruleTitle: (name: string) => byRole('heading', { level: 1, name }),
  99. externalDescription: (ruleName: string) => byText(`issue.external_issue_description.${ruleName}`),
  100. introTitle: byText(/Introduction to this rule/),
  101. rootCause: byText('Root cause'),
  102. howToFix: byText('This is how to fix'),
  103. resourceLink: byRole('link', { name: 'Awsome Reading' }),
  104. contextRadio: (name: string) => byRole('radio', { name }),
  105. contextSubtitle: (name: string) => byText(`coding_rules.description_context.subtitle.${name}`),
  106. otherContextTitle: byText('coding_rules.context.others.title'),
  107. caycNotificationButton: byRole('button', { name: 'coding_rules.more_info.scroll_message' }),
  108. extendDescriptionButton: byRole('button', { name: 'coding_rules.extend_description' }),
  109. extendDescriptionTextbox: byRole('textbox', { name: 'coding_rules.extend_description' }),
  110. saveButton: byRole('button', { name: 'save' }),
  111. cancelButton: byRole('button', { name: 'cancel' }),
  112. removeButton: byRole('button', { name: 'remove' }),
  113. ruleCleanCodeAttributeCategory: (category: CleanCodeAttributeCategory) =>
  114. byText(`rule.clean_code_attribute_category.${category}`),
  115. ruleCleanCodeAttribute: (attribute: CleanCodeAttribute) =>
  116. byText(new RegExp(`rule\\.clean_code_attribute\\.${attribute}$`)),
  117. ruleSoftwareQuality: (quality: SoftwareQuality) => byText(`software_quality.${quality}`),
  118. // Rule tags
  119. tagsDropdown: byLabelText(/tags_list_x/).byRole('button'),
  120. tagCheckbox: (tag: string) => byLabelText(tag),
  121. tagSearch: byRole('searchbox', { name: 'search.search_for_tags' }),
  122. // Rule tabs
  123. whyIssueTab: byRole('tab', {
  124. name: 'coding_rules.description_section.title.root_cause',
  125. }),
  126. whatRiskTab: byRole('tab', {
  127. name: 'coding_rules.description_section.title.root_cause.SECURITY_HOTSPOT',
  128. }),
  129. assessTab: byRole('tab', {
  130. name: 'coding_rules.description_section.title.assess_the_problem',
  131. }),
  132. moreInfoTab: byRole('tab', {
  133. name: /coding_rules.description_section.title.more_info/,
  134. }),
  135. howToFixTab: byRole('tab', {
  136. name: 'coding_rules.description_section.title.how_to_fix',
  137. }),
  138. // Rule Quality Profiles
  139. qpLink: (name: string) => byRole('link', { name }),
  140. activateButton: byRole('button', { name: 'coding_rules.activate' }),
  141. deactivateButton: byRole('button', { name: 'coding_rules.deactivate' }),
  142. oldSeveritySelect: byRole('combobox', { name: 'severity' }),
  143. qualityProfileSelect: byRole('combobox', { name: 'coding_rules.quality_profile' }),
  144. activateQPDialog: byRole('dialog', { name: 'coding_rules.activate_in_quality_profile' }),
  145. changeButton: (profile: string) =>
  146. byRole('button', { name: `coding_rules.change_details_x.${profile}` }),
  147. changeQPDialog: byRole('dialog', { name: 'coding_rules.change_details' }),
  148. deactivateInQPButton: (profile: string) =>
  149. byRole('button', { name: `coding_rules.deactivate_in_quality_profile_x.${profile}` }),
  150. revertToParentDefinitionButton: byRole('button', {
  151. name: 'coding_rules.revert_to_parent_definition',
  152. }),
  153. activaInAllQPs: byText('coding_rules.active_in_all_profiles'),
  154. yesButton: byRole('button', { name: 'yes' }),
  155. paramInput: (param: string) => byRole('textbox', { name: param }),
  156. // Custom rules
  157. customRuleSectionTitle: byRole('heading', { level: 2, name: 'coding_rules.custom_rules' }),
  158. createCustomRuleButton: byRole('button', { name: 'coding_rules.create' }),
  159. editCustomRuleButton: byRole('button', { name: 'edit' }),
  160. deleteCustomRuleButton: (rule: string) =>
  161. byRole('button', { name: `coding_rules.delete_rule_x.${rule}` }),
  162. customRuleItemLink: (name: string) => byRole('link', { name }),
  163. // Custom rule form
  164. createCustomRuleDialog: byRole('dialog', { name: 'coding_rules.create_custom_rule' }),
  165. updateCustomRuleDialog: byRole('dialog', { name: 'coding_rules.update_custom_rule' }),
  166. deleteCustomRuleDialog: byRole('dialog', { name: 'coding_rules.delete_rule' }),
  167. ruleNameTextbox: byRole('textbox', { name: 'name' }),
  168. keyTextbox: byRole('textbox', { name: 'key' }),
  169. cleanCodeCategorySelect: byRole('combobox', { name: 'category' }),
  170. cleanCodeAttributeSelect: byRole('combobox', { name: 'attribute' }),
  171. cleanCodeQualityCheckbox: (quality: SoftwareQuality) =>
  172. byRole('group', { name: `coding_rules.custom_rule.software_quality_x.${quality}` }).byRole(
  173. 'checkbox',
  174. ),
  175. cleanCodeSeveritySelect: (quality: SoftwareQuality) =>
  176. byRole('group', { name: `coding_rules.custom_rule.software_quality_x.${quality}` }).byRole(
  177. 'combobox',
  178. { name: 'severity' },
  179. ),
  180. statusSelect: byRole('combobox', { name: 'coding_rules.filters.status' }),
  181. descriptionTextbox: byRole('textbox', { name: 'description' }),
  182. createButton: byRole('button', { name: 'create' }),
  183. reactivateButton: byRole('button', { name: 'coding_rules.reactivate' }),
  184. deleteButton: byRole('button', { name: 'delete' }),
  185. };
  186. export function getPageObjects() {
  187. const user = userEvent.setup();
  188. const ui = {
  189. ...selectors,
  190. async appLoaded() {
  191. await waitFor(() => {
  192. expect(selectors.loading.query()).not.toBeInTheDocument();
  193. });
  194. },
  195. async detailsloaded() {
  196. expect(await byRole('heading', { level: 1 }).find()).toBeInTheDocument();
  197. await ui.appLoaded();
  198. },
  199. async bulkActivate(rulesCount: number, profile: Profile) {
  200. await user.click(ui.bulkChangeButton.get());
  201. await user.click(ui.activateIn.get());
  202. const dialog = ui.bulkChangeDialog(rulesCount).get();
  203. expect(dialog).toBeInTheDocument();
  204. await user.click(ui.activateInSelect.get());
  205. await user.click(byText(`${profile.name} - ${profile.languageName}`).get(dialog));
  206. await user.click(ui.bulkApply.get());
  207. },
  208. async bulkDeactivate(rulesCount: number, profile: Profile) {
  209. await user.click(ui.bulkChangeButton.get());
  210. await user.click(ui.deactivateIn.get());
  211. const dialog = ui.bulkChangeDialog(rulesCount, false).get();
  212. expect(dialog).toBeInTheDocument();
  213. await user.click(ui.deactivateInSelect.get());
  214. await user.click(byText(`${profile.name} - ${profile.languageName}`).get(dialog));
  215. await user.click(ui.bulkApply.get());
  216. },
  217. };
  218. return {
  219. ui,
  220. user,
  221. };
  222. }
  223. export function renderCodingRulesApp(currentUser?: CurrentUser, navigateTo?: string) {
  224. renderAppRoutes('coding_rules', routes, {
  225. navigateTo,
  226. currentUser,
  227. languages: {
  228. js: { key: 'js', name: 'JavaScript' },
  229. java: { key: 'java', name: 'Java' },
  230. c: { key: 'c', name: 'C' },
  231. py: { key: 'py', name: 'Python' },
  232. },
  233. });
  234. }