]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18424 RTL migration rules page
authorstanislavh <stanislav.honcharov@sonarsource.com>
Thu, 13 Jul 2023 08:17:22 +0000 (10:17 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 18 Jul 2023 20:03:22 +0000 (20:03 +0000)
48 files changed:
server/sonar-web/src/main/js/api/mocks/CodingRulesServiceMock.ts
server/sonar-web/src/main/js/api/rules.ts
server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
server/sonar-web/src/main/js/apps/coding-rules/components/ActivationButton.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleButton.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsDescription.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsParameters.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/TypeFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CodingRulesApp-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/FacetsList-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/LanguageFacet-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RepositoryFacet-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsTagsPopup-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/SimilarRulesFilter-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/TagFacet-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CodingRulesApp-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/FacetsList-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RepositoryFacet-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsIssues-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsTagsPopup-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/SimilarRulesFilter-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/TagFacet-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/utils-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/rules/RuleDescription.tsx
server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 0f6e2e397f502708ff69830d1b90572e010655ea..e76367f8005e5687e01c8879cffc1424bfa11af8 100644 (file)
@@ -23,29 +23,46 @@ import {
   mockCurrentUser,
   mockPaging,
   mockQualityProfile,
+  mockRuleActivation,
   mockRuleDetails,
   mockRuleRepository,
 } from '../../helpers/testMocks';
 import { RuleRepository, SearchRulesResponse } from '../../types/coding-rules';
 import { RawIssuesResponse } from '../../types/issues';
 import { SearchRulesQuery } from '../../types/rules';
-import { Rule, RuleActivation, RuleDetails, RulesUpdateRequest } from '../../types/types';
+import { Dict, Rule, RuleActivation, RuleDetails, RulesUpdateRequest } from '../../types/types';
 import { NoticeType } from '../../types/users';
 import { getFacet } from '../issues';
 import {
   Profile,
   SearchQualityProfilesParameters,
   SearchQualityProfilesResponse,
+  activateRule,
   bulkActivateRules,
   bulkDeactivateRules,
+  deactivateRule,
   searchQualityProfiles,
 } from '../quality-profiles';
-import { getRuleDetails, getRulesApp, searchRules, updateRule } from '../rules';
+import {
+  CreateRuleData,
+  createRule,
+  deleteRule,
+  getRuleDetails,
+  getRuleTags,
+  getRulesApp,
+  searchRules,
+  updateRule,
+} from '../rules';
 import { dismissNotice, getCurrentUser } from '../users';
 
 interface FacetFilter {
   languages?: string;
+  tags?: string;
   available_since?: string;
+  q?: string;
+  types?: string;
+  severities?: string;
+  is_template?: string | boolean;
 }
 
 const FACET_RULE_MAP: { [key: string]: keyof Rule } = {
@@ -53,8 +70,12 @@ const FACET_RULE_MAP: { [key: string]: keyof Rule } = {
   types: 'type',
 };
 
+export const RULE_TAGS_MOCK = ['awesome', 'cute', 'nice'];
+
 export default class CodingRulesServiceMock {
   defaultRules: RuleDetails[] = [];
+  defaultRulesActivations: Dict<RuleActivation[]> = {};
+  rulesActivations: Dict<RuleActivation[]> = {};
   rules: RuleDetails[] = [];
   qualityProfile: Profile[] = [];
   repositories: RuleRepository[] = [];
@@ -71,6 +92,12 @@ export default class CodingRulesServiceMock {
       mockQualityProfile({ key: 'p1', name: 'QP Foo', language: 'java', languageName: 'Java' }),
       mockQualityProfile({ key: 'p2', name: 'QP Bar', language: 'js' }),
       mockQualityProfile({ key: 'p3', name: 'QP FooBar', language: 'java', languageName: 'Java' }),
+      mockQualityProfile({
+        key: 'p4',
+        name: 'QP FooBarBaz',
+        language: 'java',
+        languageName: 'Java',
+      }),
     ];
 
     const resourceContent = 'Some link <a href="http://example.com">Awsome Reading</a>';
@@ -85,6 +112,10 @@ export default class CodingRulesServiceMock {
         lang: 'java',
         langName: 'Java',
         name: 'Awsome java rule',
+        params: [
+          { key: '1', type: 'TEXT', htmlDesc: 'html description for key 1' },
+          { key: '2', type: 'NUMBER', defaultValue: 'default value for key 2' },
+        ],
       }),
       mockRuleDetails({
         key: 'rule2',
@@ -128,7 +159,7 @@ export default class CodingRulesServiceMock {
       }),
       mockRuleDetails({
         key: 'rule6',
-        type: 'VULNERABILITY',
+        type: 'BUG',
         lang: 'py',
         langName: 'Python',
         name: 'Bad Python rule',
@@ -138,6 +169,7 @@ export default class CodingRulesServiceMock {
       mockRuleDetails({
         key: 'rule7',
         type: 'VULNERABILITY',
+        severity: 'MINOR',
         lang: 'py',
         langName: 'Python',
         name: 'Python rule with context',
@@ -163,11 +195,42 @@ export default class CodingRulesServiceMock {
         ],
       }),
       mockRuleDetails({
-        createdAt: '2022-12-16T17:26:54+0100',
         key: 'rule8',
+        type: 'BUG',
+        severity: 'MINOR',
+        lang: 'py',
+        langName: 'Python',
+        tags: ['awesome'],
+        name: 'Template rule',
+        params: [
+          { key: '1', type: 'TEXT', htmlDesc: 'html description for key 1' },
+          { key: '2', type: 'NUMBER', defaultValue: 'default value for key 2' },
+        ],
+        isTemplate: true,
+      }),
+      mockRuleDetails({
+        key: 'rule9',
+        type: 'BUG',
+        severity: 'MINOR',
+        lang: 'py',
+        langName: 'Python',
+        tags: ['awesome'],
+        name: 'Custom Rule based on rule8',
+        params: [
+          { key: '1', type: 'TEXT', htmlDesc: 'html description for key 1' },
+          { key: '2', type: 'NUMBER', defaultValue: 'default value for key 2' },
+        ],
+        templateKey: 'rule8',
+      }),
+      // Keep this last
+      mockRuleDetails({
+        createdAt: '2022-12-16T17:26:54+0100',
+        key: 'rule10',
         type: 'VULNERABILITY',
+        severity: 'MINOR',
         lang: 'py',
         langName: 'Python',
+        tags: ['awesome'],
         name: 'Awesome Python rule with education principles',
         descriptionSections: [
           { key: RuleDescriptionSections.INTRODUCTION, content: introTitle },
@@ -181,17 +244,27 @@ export default class CodingRulesServiceMock {
       }),
     ];
 
-    (updateRule as jest.Mock).mockImplementation(this.handleUpdateRule);
-    (searchRules as jest.Mock).mockImplementation(this.handleSearchRules);
-    (getRuleDetails as jest.Mock).mockImplementation(this.handleGetRuleDetails);
-    (searchQualityProfiles as jest.Mock).mockImplementation(this.handleSearchQualityProfiles);
-    (getRulesApp as jest.Mock).mockImplementation(this.handleGetRulesApp);
-    (bulkActivateRules as jest.Mock).mockImplementation(this.handleBulkActivateRules);
-    (bulkDeactivateRules as jest.Mock).mockImplementation(this.handleBulkDeactivateRules);
-    (getFacet as jest.Mock).mockImplementation(this.handleGetGacet);
-    (getCurrentUser as jest.Mock).mockImplementation(this.handleGetCurrentUser);
-    (dismissNotice as jest.Mock).mockImplementation(this.handleDismissNotification);
+    this.defaultRulesActivations = {
+      [this.defaultRules[0].key]: [mockRuleActivation({ qProfile: 'p1' })],
+    };
+
+    jest.mocked(updateRule).mockImplementation(this.handleUpdateRule);
+    jest.mocked(createRule).mockImplementation(this.handleCreateRule);
+    jest.mocked(deleteRule).mockImplementation(this.handleDeleteRule);
+    jest.mocked(searchRules).mockImplementation(this.handleSearchRules);
+    jest.mocked(getRuleDetails).mockImplementation(this.handleGetRuleDetails);
+    jest.mocked(searchQualityProfiles).mockImplementation(this.handleSearchQualityProfiles);
+    jest.mocked(getRulesApp).mockImplementation(this.handleGetRulesApp);
+    jest.mocked(bulkActivateRules).mockImplementation(this.handleBulkActivateRules);
+    jest.mocked(bulkDeactivateRules).mockImplementation(this.handleBulkDeactivateRules);
+    jest.mocked(activateRule).mockImplementation(this.handleActivateRule);
+    jest.mocked(deactivateRule).mockImplementation(this.handleDeactivateRule);
+    jest.mocked(getFacet).mockImplementation(this.handleGetGacet);
+    jest.mocked(getRuleTags).mockImplementation(this.handleGetRuleTags);
+    jest.mocked(getCurrentUser).mockImplementation(this.handleGetCurrentUser);
+    jest.mocked(dismissNotice).mockImplementation(this.handleDismissNotification);
     this.rules = cloneDeep(this.defaultRules);
+    this.rulesActivations = cloneDeep(this.defaultRulesActivations);
   }
 
   getRulesWithoutDetails(rules: RuleDetails[]) {
@@ -212,16 +285,40 @@ export default class CodingRulesServiceMock {
     );
   }
 
-  filterFacet({ languages, available_since }: FacetFilter) {
+  filterFacet({
+    languages,
+    available_since,
+    q,
+    severities,
+    types,
+    tags,
+    is_template,
+  }: FacetFilter) {
     let filteredRules = this.rules;
+    if (types) {
+      filteredRules = filteredRules.filter((r) => types.includes(r.type));
+    }
     if (languages) {
       filteredRules = filteredRules.filter((r) => r.lang && languages.includes(r.lang));
     }
+    if (severities) {
+      filteredRules = filteredRules.filter((r) => r.severity && severities.includes(r.severity));
+    }
     if (available_since) {
       filteredRules = filteredRules.filter(
         (r) => r.createdAt && new Date(r.createdAt) > new Date(available_since)
       );
     }
+    if (is_template !== undefined) {
+      filteredRules = filteredRules.filter((r) => (is_template ? r.isTemplate : !r.isTemplate));
+    }
+    if (q && q.length > 2) {
+      filteredRules = filteredRules.filter((r) => r.name.includes(q));
+    }
+
+    if (tags) {
+      filteredRules = filteredRules.filter((r) => r.tags && r.tags.some((t) => tags.includes(t)));
+    }
     return this.getRulesWithoutDetails(filteredRules);
   }
 
@@ -238,6 +335,7 @@ export default class CodingRulesServiceMock {
     this.applyWithWarning = false;
     this.dismissedNoticesEP = false;
     this.rules = cloneDeep(this.defaultRules);
+    this.rulesActivations = cloneDeep(this.defaultRulesActivations);
   }
 
   allRulesCount() {
@@ -279,7 +377,10 @@ export default class CodingRulesServiceMock {
         errors: [{ msg: `No rule has been found for id ${parameters.key}` }],
       });
     }
-    return this.reply({ actives: parameters.actives ? [] : undefined, rule });
+    return this.reply({
+      actives: parameters.actives ? this.rulesActivations[rule.key] ?? [] : undefined,
+      rule,
+    });
   };
 
   handleUpdateRule = (data: RulesUpdateRequest): Promise<RuleDetails> => {
@@ -325,13 +426,42 @@ export default class CodingRulesServiceMock {
     return this.reply(rule);
   };
 
+  handleCreateRule = (data: CreateRuleData) => {
+    const newRule = mockRuleDetails({
+      descriptionSections: [
+        { key: RuleDescriptionSections.DEFAULT, content: data.markdownDescription },
+      ],
+      ...pick(data, ['templateKey', 'severity', 'type', 'name', 'status']),
+      key: data.customKey,
+      params:
+        data.params?.split(';').map((param: string) => {
+          const [key, value] = param.split('=');
+          return { key, defaultValue: value, type: 'TEXT' };
+        }) ?? [],
+    });
+
+    this.rules.push(newRule);
+
+    return this.reply(newRule);
+  };
+
+  handleDeleteRule = (data: { key: string }) => {
+    this.rules = this.rules.filter((r) => r.key !== data.key);
+    return this.reply(undefined);
+  };
+
   handleSearchRules = ({
     facets,
+    types,
     languages,
     p,
     ps,
     available_since,
+    severities,
+    tags,
+    q,
     rule_key,
+    is_template,
   }: SearchRulesQuery): Promise<SearchRulesResponse> => {
     const countFacet = (facets || '').split(',').map((facet: keyof Rule) => {
       const facetCount = countBy(
@@ -348,7 +478,15 @@ export default class CodingRulesServiceMock {
     if (rule_key) {
       filteredRules = this.getRulesWithoutDetails(this.rules).filter((r) => r.key === rule_key);
     } else {
-      filteredRules = this.filterFacet({ languages, available_since });
+      filteredRules = this.filterFacet({
+        languages,
+        available_since,
+        q,
+        severities,
+        types,
+        tags,
+        is_template,
+      });
     }
     const responseRules = filteredRules.slice((currentP - 1) * currentPs, currentP * currentPs);
     return this.reply({
@@ -384,6 +522,32 @@ export default class CodingRulesServiceMock {
     });
   };
 
+  handleActivateRule = (data: {
+    key: string;
+    params?: Dict<string>;
+    reset?: boolean;
+    rule: string;
+    severity?: string;
+  }) => {
+    const nextActivation = mockRuleActivation({ qProfile: data.key, severity: data.severity });
+    const activationIndex = this.rulesActivations[data.rule]?.findIndex((activation) => {
+      return activation.qProfile === data.key;
+    });
+    if (activationIndex !== -1) {
+      this.rulesActivations[data.rule][activationIndex] = nextActivation;
+    } else {
+      this.rulesActivations[data.rule] = [...this.rulesActivations[data.rule], nextActivation];
+    }
+    return this.reply(undefined);
+  };
+
+  handleDeactivateRule = (data: { key: string; rule: string }) => {
+    this.rulesActivations[data.rule] = this.rulesActivations[data.rule]?.filter(
+      (activation) => activation.qProfile !== data.key
+    );
+    return this.reply(undefined);
+  };
+
   handleSearchQualityProfiles = ({
     language,
   }: SearchQualityProfilesParameters = {}): Promise<SearchQualityProfilesResponse> => {
@@ -396,6 +560,10 @@ export default class CodingRulesServiceMock {
     return this.reply({ profiles });
   };
 
+  handleGetRuleTags = (data: { ps?: number; q: string }) => {
+    return this.reply(RULE_TAGS_MOCK.filter((tag) => tag.includes(data.q)));
+  };
+
   handleGetRulesApp = () => {
     return this.reply({ canWrite: this.isAdmin, repositories: this.repositories });
   };
index beb06283727b40fc9eae9b0d7e753c8bc3a9e5e3..666f798ac5083ba570a610dcc2cfc40d358f8417 100644 (file)
@@ -21,7 +21,19 @@ import { throwGlobalError } from '../helpers/error';
 import { getJSON, post, postJSON } from '../helpers/request';
 import { GetRulesAppResponse, SearchRulesResponse } from '../types/coding-rules';
 import { SearchRulesQuery } from '../types/rules';
-import { RuleActivation, RuleDetails, RulesUpdateRequest } from '../types/types';
+import { RuleActivation, RuleDetails, RuleType, RulesUpdateRequest } from '../types/types';
+
+export interface CreateRuleData {
+  customKey: string;
+  markdownDescription: string;
+  name: string;
+  params?: string;
+  preventReactivation?: boolean;
+  severity?: string;
+  status?: string;
+  templateKey: string;
+  type?: RuleType;
+}
 
 export function getRulesApp(): Promise<GetRulesAppResponse> {
   return getJSON('/api/rules/app').catch(throwGlobalError);
@@ -51,17 +63,7 @@ export function getRuleTags(parameters: { ps?: number; q: string }): Promise<str
   return getJSON('/api/rules/tags', parameters).then((r) => r.tags, throwGlobalError);
 }
 
-export function createRule(data: {
-  customKey: string;
-  markdownDescription: string;
-  name: string;
-  params?: string;
-  preventReactivation?: boolean;
-  severity?: string;
-  status?: string;
-  templateKey: string;
-  type?: string;
-}): Promise<RuleDetails> {
+export function createRule(data: CreateRuleData): Promise<RuleDetails> {
   return postJSON('/api/rules/create', data).then(
     (r) => r.rule,
     (response) => {
index 098dc46d64c064df8d8d56d8b450c4c9e5c75567..9dbe08d6be55a91b5b54d7a6dd5082b33c2e7b2e 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { fireEvent, screen, waitFor, within } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import CodingRulesServiceMock from '../../../api/mocks/CodingRulesServiceMock';
+import { act, fireEvent, screen } from '@testing-library/react';
+import { last } from 'lodash';
+import selectEvent from 'react-select-event';
+import CodingRulesServiceMock, { RULE_TAGS_MOCK } from '../../../api/mocks/CodingRulesServiceMock';
+import { RULE_TYPES } from '../../../helpers/constants';
+import { parseDate } from '../../../helpers/dates';
 import { mockCurrentUser, mockLoggedInUser } from '../../../helpers/testMocks';
-import { renderAppRoutes } from '../../../helpers/testReactTestingUtils';
-import { byPlaceholderText, byRole } from '../../../helpers/testSelector';
+import { dateInputEvent, renderAppRoutes } from '../../../helpers/testReactTestingUtils';
 import { CurrentUser } from '../../../types/users';
 import routes from '../routes';
+import { getPageObjects } from '../utils-test';
 
 jest.mock('../../../api/rules');
 jest.mock('../../../api/issues');
 jest.mock('../../../api/users');
 jest.mock('../../../api/quality-profiles');
 
-const ui = {
-  rulesList: byRole('list', { name: 'list_of_rules' }),
-  activateInSelectOption: byRole('combobox', { name: 'coding_rules.activate_in' }),
-  deactivateInSelectOption: byRole('combobox', { name: 'coding_rules.deactivate_in' }),
-  availableSinceFacet: byRole('button', { name: 'coding_rules.facet.available_since' }),
-  availableSinceDateField: byPlaceholderText('date'),
-};
-
 let handler: CodingRulesServiceMock;
 
 beforeAll(() => {
@@ -49,482 +44,525 @@ beforeAll(() => {
 
 afterEach(() => handler.reset());
 
-it('should select rules with keyboard navigation', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp();
-  let listitem = await screen.findByRole('listitem', { current: true });
-  expect(within(listitem).getByRole('link', { name: 'Awsome java rule' })).toBeInTheDocument();
-  await user.keyboard('{ArrowDown}');
-  listitem = await screen.findByRole('listitem', { current: true });
-  expect(within(listitem).getByRole('link', { name: 'Hot hotspot' })).toBeInTheDocument();
-  await user.keyboard('{ArrowUp}');
-  listitem = await screen.findByRole('listitem', { current: true });
-  expect(within(listitem).getByRole('link', { name: 'Awsome java rule' })).toBeInTheDocument();
-  await user.keyboard('{ArrowRight}');
-  expect(screen.getByRole('heading', { level: 1, name: 'Awsome java rule' })).toBeInTheDocument();
-  await user.keyboard('{ArrowLeft}');
-  listitem = await screen.findByRole('listitem', { current: true });
-  expect(within(listitem).getByRole('link', { name: 'Awsome java rule' })).toBeInTheDocument();
-});
-
-it('should open with permalink', async () => {
-  renderCodingRulesApp(undefined, 'coding_rules?rule_key=rule1');
-  expect(await screen.findByRole('link', { name: 'Awsome java rule' })).toBeInTheDocument();
-  expect(screen.queryByRole('link', { name: 'Hot hotspot' })).not.toBeInTheDocument();
-});
+describe('Rules app', () => {
+  it('renders correctly', async () => {
+    const { ui } = getPageObjects();
+    renderCodingRulesApp();
 
-it('should show open rule with default description section', async () => {
-  renderCodingRulesApp(undefined, 'coding_rules?open=rule1');
-  expect(
-    await screen.findByRole('heading', { level: 1, name: 'Awsome java rule' })
-  ).toBeInTheDocument();
-  expect(document.title).toEqual('page_title.template.with_category.coding_rules.page');
-  expect(screen.getByText('Why')).toBeInTheDocument();
-  expect(screen.getByText('Because')).toBeInTheDocument();
-});
+    await ui.appLoaded();
 
-it('should show open rule with no description', async () => {
-  renderCodingRulesApp(undefined, 'coding_rules?open=rule6');
-  expect(
-    await screen.findByRole('heading', { level: 1, name: 'Bad Python rule' })
-  ).toBeInTheDocument();
-  expect(screen.getByText('issue.external_issue_description.Bad Python rule')).toBeInTheDocument();
-});
+    // Renders list
+    handler
+      .allRulesName()
+      .forEach((name) => expect(ui.ruleListItemLink(name).get()).toBeInTheDocument());
 
-it('should show hotspot rule section', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(undefined, 'coding_rules?open=rule2');
-  expect(await screen.findByRole('heading', { level: 1, name: 'Hot hotspot' })).toBeInTheDocument();
-  expect(screen.getByText('Introduction to this rule')).toBeInTheDocument();
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.root_cause.SECURITY_HOTSPOT',
-    })
-  ).toBeInTheDocument();
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.assess_the_problem',
-    })
-  ).toBeInTheDocument();
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  ).toBeInTheDocument();
-  // Check that we render plain html
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-  expect(screen.getByRole('link', { name: 'Awsome Reading' })).toBeInTheDocument();
-});
+    // Renders type facets
+    RULE_TYPES.map((type) => `issue.type.${type}`).forEach((name) =>
+      expect(ui.facetItem(name).get()).toBeInTheDocument()
+    );
 
-it('should show rule advanced section', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(undefined, 'coding_rules?open=rule5');
-  expect(
-    await screen.findByRole('heading', { level: 1, name: 'Awsome Python rule' })
-  ).toBeInTheDocument();
-  expect(screen.getByText('Introduction to this rule')).toBeInTheDocument();
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.how_to_fix',
-    })
-  ).toBeInTheDocument();
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  ).toBeInTheDocument();
-  // Check that we render plain html
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-  expect(screen.getByRole('link', { name: 'Awsome Reading' })).toBeInTheDocument();
-});
+    // Renders language facets
+    ['JavaScript', 'Java', 'C'].forEach((name) =>
+      expect(ui.facetItem(name).get()).toBeInTheDocument()
+    );
 
-it('should show rule advanced section with context', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(undefined, 'coding_rules?open=rule7');
-  expect(
-    await screen.findByRole('heading', { level: 1, name: 'Python rule with context' })
-  ).toBeInTheDocument();
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.how_to_fix',
-    })
-  ).toBeInTheDocument();
-
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.how_to_fix',
-    })
-  );
-  expect(screen.getByRole('radio', { name: 'Spring' })).toBeInTheDocument();
-  expect(screen.getByRole('radio', { name: 'Spring boot' })).toBeInTheDocument();
-  expect(
-    screen.getByRole('radio', { name: 'coding_rules.description_context.other' })
-  ).toBeInTheDocument();
-  expect(screen.getByText('coding_rules.description_context.sub_title.Spring')).toBeInTheDocument();
-  expect(screen.getByText('This is how to fix for spring')).toBeInTheDocument();
-
-  await user.click(screen.getByRole('radio', { name: 'Spring boot' }));
-  expect(
-    screen.getByText('coding_rules.description_context.sub_title.Spring boot')
-  ).toBeInTheDocument();
-  expect(screen.getByText('This is how to fix for spring boot')).toBeInTheDocument();
-
-  await user.click(screen.getByRole('radio', { name: 'coding_rules.description_context.other' }));
-  expect(
-    screen.queryByText(
-      'coding_rules.description_context.sub_title.coding_rules.description_context.other'
-    )
-  ).not.toBeInTheDocument();
-  expect(screen.getByText('coding_rules.context.others.title')).toBeInTheDocument();
-  expect(screen.getByText('coding_rules.context.others.description.first')).toBeInTheDocument();
-
-  const productBoardLink = screen.getByRole('link', {
-    name: 'coding_rules.context.others.feedback_description.link',
+    // Other facets are collapsed
+    [
+      ui.tagsFacet,
+      ui.repositoriesFacet,
+      ui.severetiesFacet,
+      ui.statusesFacet,
+      ui.standardsFacet,
+      ui.availableSinceFacet,
+      ui.templateFacet,
+      ui.qpFacet,
+    ].forEach((facet) => {
+      expect(facet.get()).toHaveAttribute('aria-expanded', 'false');
+    });
   });
-  expect(productBoardLink).toBeInTheDocument();
-  expect(productBoardLink).toHaveAttribute('target', '_blank');
-});
 
-it('should be able to extend the rule description', async () => {
-  const user = userEvent.setup();
-  handler.setIsAdmin();
-  renderCodingRulesApp(undefined, 'coding_rules?open=rule5');
-  expect(
-    await screen.findByRole('heading', { level: 1, name: 'Awsome Python rule' })
-  ).toBeInTheDocument();
-
-  // Add
-  await user.click(screen.getByRole('button', { name: 'coding_rules.extend_description' }));
-  expect(screen.getByRole('textbox')).toBeInTheDocument();
-  await user.click(screen.getByRole('textbox'));
-  await user.keyboard('TEST DESC');
-  await user.click(screen.getByRole('button', { name: 'save' }));
-  expect(await screen.findByText('TEST DESC')).toBeInTheDocument();
-
-  // Edit
-  await user.click(screen.getByRole('button', { name: 'coding_rules.extend_description' }));
-  await user.click(screen.getByRole('textbox'));
-  await user.keyboard('{Control>}A{/Control}NEW DESC');
-  await user.click(screen.getByRole('button', { name: 'save' }));
-  expect(await screen.findByText('NEW DESC')).toBeInTheDocument();
-
-  //Cancel
-  await user.click(screen.getByRole('button', { name: 'coding_rules.extend_description' }));
-  await user.dblClick(screen.getByRole('textbox'));
-  await user.keyboard('DIFFERENCE');
-  await user.click(screen.getByRole('button', { name: 'cancel' }));
-  expect(await screen.findByText('NEW DESC')).toBeInTheDocument();
-
-  //Remove
-  await user.click(screen.getByRole('button', { name: 'coding_rules.extend_description' }));
-  await user.click(screen.getByRole('button', { name: 'remove' }));
-  await user.click(within(screen.getByRole('dialog')).getByRole('button', { name: 'remove' }));
-  await waitFor(() => expect(screen.queryByText('NEW DESC')).not.toBeInTheDocument());
-});
+  describe('filtering', () => {
+    it('filters by facets', async () => {
+      const { ui, user } = getPageObjects();
+      const { pickDate } = dateInputEvent(user);
+      renderCodingRulesApp(mockCurrentUser());
+      await ui.appLoaded();
 
-it('should list all rules', async () => {
-  renderCodingRulesApp();
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(10);
 
-  await waitFor(() => {
-    handler
-      .allRulesName()
-      .forEach((name) => expect(screen.getByRole('link', { name })).toBeInTheDocument());
-  });
-});
+      // Filter by language facet
+      await user.click(ui.facetItem('py').get());
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(6);
 
-it('should have all type facet', async () => {
-  renderCodingRulesApp();
+      // Filter by date facet
+      await user.click(await ui.availableSinceFacet.find());
+      await pickDate(ui.availableSinceDateField.get(), parseDate('Nov 1, 2022'));
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(1);
 
-  await waitFor(() => {
-    [
-      'issue.type.BUG',
-      'issue.type.VULNERABILITY',
-      'issue.type.CODE_SMELL',
-      'issue.type.SECURITY_HOTSPOT',
-    ].forEach((name) => expect(screen.getByRole('checkbox', { name })).toBeInTheDocument());
-  });
-});
+      // Clear filters
+      await user.click(ui.clearAllFiltersButton.get());
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(10);
+    });
 
-it('select the correct quality profile for bulk change base on language search', async () => {
-  const user = userEvent.setup();
-  handler.setIsAdmin();
-  renderCodingRulesApp(mockLoggedInUser());
-  const selectQP = handler.allQualityProfile('js')[0];
-
-  await user.click(await screen.findByRole('checkbox', { name: 'JavaScript' }));
-  await user.click(await screen.findByRole('button', { name: 'bulk_change' }));
-  await user.click(await screen.findByRole('link', { name: 'coding_rules.activate_in…' }));
-  const dialog = screen.getByRole('dialog', {
-    name: 'coding_rules.activate_in_quality_profile (2 coding_rules._rules)',
-  });
+    it('filters by similar rules', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(mockCurrentUser());
+      await ui.appLoaded();
 
-  expect(dialog).toBeInTheDocument();
-  const dialogScreen = within(dialog);
-  expect(dialogScreen.getByText(`${selectQP.name} - ${selectQP.languageName}`)).toBeInTheDocument();
-});
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(10);
+
+      const lastRule = last(ui.ruleListItem.getAll());
+
+      await user.click(ui.similarIssuesButton.get(lastRule));
+      await user.click(ui.similarIssuesFilterByLang('Python').get(lastRule));
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(6);
 
-it('no quality profile for bulk cahnge base on language search', async () => {
-  const user = userEvent.setup();
-  handler.setIsAdmin();
-  renderCodingRulesApp(mockLoggedInUser());
+      await user.click(ui.similarIssuesButton.get(lastRule));
+      await user.click(ui.similarIssuesFilterByType('issue.type.VULNERABILITY').get(lastRule));
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(3);
+      expect(ui.facetItem('issue.type.VULNERABILITY').get()).toBeChecked();
 
-  await user.click(await screen.findByRole('checkbox', { name: 'C' }));
-  await user.click(await screen.findByRole('button', { name: 'bulk_change' }));
-  await user.click(await screen.findByRole('link', { name: 'coding_rules.activate_in…' }));
-  const dialog = screen.getByRole('dialog', {
-    name: 'coding_rules.activate_in_quality_profile (1 coding_rules._rules)',
+      await user.click(ui.similarIssuesButton.get(lastRule));
+      await user.click(ui.similarIssuesFilterBySeverity('MINOR').get(lastRule));
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(2);
+
+      await user.click(ui.similarIssuesButton.get(lastRule));
+      await user.click(ui.similarIssuesFilterByTag('awesome').get(lastRule));
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(1);
+    });
+
+    it('filters by search', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(mockCurrentUser());
+      await ui.appLoaded();
+
+      await user.type(ui.searchInput.get(), 'Python');
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(4);
+
+      await user.clear(ui.searchInput.get());
+      await user.type(ui.searchInput.get(), 'Hot hotspot');
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(1);
+    });
   });
 
-  expect(dialog).toBeInTheDocument();
-  const dialogScreen = within(dialog);
-  await user.click(ui.activateInSelectOption.get());
-  expect(dialogScreen.getByText('coding_rules.bulk_change.no_quality_profile')).toBeInTheDocument();
-});
+  describe('bulk change', () => {
+    it('no quality profile for bulk change based on language search', async () => {
+      const { ui, user } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser());
+      await ui.appLoaded();
+
+      await user.click(ui.facetItem('C').get());
+      await user.click(ui.bulkChangeButton.get());
+      await user.click(ui.activateIn.get());
+
+      const dialog = ui.bulkChangeDialog(1).get();
+      expect(dialog).toBeInTheDocument();
+
+      selectEvent.openMenu(ui.activateInSelect.get());
+      expect(ui.noQualityProfiles.get(dialog)).toBeInTheDocument();
+    });
 
-it('should be able to bulk activate quality profile', async () => {
-  const user = userEvent.setup();
-  handler.setIsAdmin();
-  renderCodingRulesApp(mockLoggedInUser());
+    it('should be able to bulk activate quality profile', async () => {
+      const { ui, user } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser());
+      await ui.appLoaded();
 
-  const selectQPSuccess = handler.allQualityProfile('java')[0];
-  const selectQPWarning = handler.allQualityProfile('java')[1];
+      const [selectQPSuccess, selectQPWarning] = handler.allQualityProfile('java');
 
-  await user.click(await screen.findByRole('button', { name: 'bulk_change' }));
-  await user.click(await screen.findByRole('link', { name: 'coding_rules.activate_in…' }));
+      const rulesCount = handler.allRulesCount();
 
-  const dialog = screen.getByRole('dialog', {
-    name: `coding_rules.activate_in_quality_profile (${handler.allRulesCount()} coding_rules._rules)`,
+      await ui.bulkActivate(rulesCount, selectQPSuccess);
+
+      expect(
+        ui.bulkSuccessMessage(selectQPSuccess.name, selectQPSuccess.languageName, rulesCount).get()
+      ).toBeInTheDocument();
+
+      await user.click(ui.bulkClose.get());
+
+      // Try bulk change when quality profile has warnning.
+      handler.activateWithWarning();
+
+      await ui.bulkActivate(rulesCount, selectQPWarning);
+      expect(
+        ui
+          .bulkWarningMessage(selectQPWarning.name, selectQPWarning.languageName, rulesCount - 1)
+          .get()
+      ).toBeInTheDocument();
+    });
+
+    it('should be able to bulk deactivate quality profile', async () => {
+      const { ui } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser());
+      await ui.appLoaded();
+
+      const [selectQP] = handler.allQualityProfile('java');
+      const rulesCount = handler.allRulesCount();
+
+      await ui.bulkDeactivate(rulesCount, selectQP);
+
+      expect(
+        ui.bulkSuccessMessage(selectQP.name, selectQP.languageName, rulesCount).get()
+      ).toBeInTheDocument();
+    });
+
+    it('should be able to activate for specific quality profile', async () => {});
+
+    it('should be able to deactivate for specific quality profile', async () => {});
   });
-  expect(dialog).toBeInTheDocument();
-
-  let dialogScreen = within(dialog);
-  await user.click(ui.activateInSelectOption.get());
-  await user.click(
-    dialogScreen.getByText(`${selectQPSuccess.name} - ${selectQPSuccess.languageName}`)
-  );
-  expect(
-    dialogScreen.getByText(`${selectQPSuccess.name} - ${selectQPSuccess.languageName}`)
-  ).toBeInTheDocument();
-
-  await user.click(dialogScreen.getByRole('button', { name: 'apply' }));
-  expect(
-    dialogScreen.getByText(
-      `coding_rules.bulk_change.success.${selectQPSuccess.name}.${selectQPSuccess.languageName}.${
-        handler.allRulesName().length
-      }`
-    )
-  ).toBeInTheDocument();
-
-  await user.click(dialogScreen.getByRole('button', { name: 'close' }));
-
-  // Try bulk change when quality profile has warnning.
-  handler.activateWithWarning();
-
-  await user.click(await screen.findByRole('button', { name: 'bulk_change' }));
-  await user.click(await screen.findByRole('link', { name: 'coding_rules.activate_in…' }));
-  dialogScreen = within(
-    screen.getByRole('dialog', {
-      name: `coding_rules.activate_in_quality_profile (${handler.allRulesCount()} coding_rules._rules)`,
-    })
-  );
-  await user.click(ui.activateInSelectOption.get());
-  await user.click(
-    dialogScreen.getByText(`${selectQPWarning.name} - ${selectQPWarning.languageName}`)
-  );
-  await user.click(dialogScreen.getByRole('button', { name: 'apply' }));
-  expect(
-    dialogScreen.getByText(
-      `coding_rules.bulk_change.warning.${selectQPWarning.name}.${selectQPWarning.languageName}.${
-        handler.allRulesName().length - 1
-      }.1`
-    )
-  ).toBeInTheDocument();
-});
 
-it('should be able to bulk deactivate quality profile', async () => {
-  const user = userEvent.setup();
-  handler.setIsAdmin();
-  renderCodingRulesApp(mockLoggedInUser());
-
-  const selectQP = handler.allQualityProfile('java')[0];
-
-  await user.click(await screen.findByRole('button', { name: 'bulk_change' }));
-  await user.click(await screen.findByRole('link', { name: 'coding_rules.deactivate_in…' }));
-  const dialogScreen = within(
-    screen.getByRole('dialog', {
-      name: `coding_rules.deactivate_in_quality_profile (${handler.allRulesCount()} coding_rules._rules)`,
-    })
-  );
-  await user.click(ui.deactivateInSelectOption.get());
-
-  await user.click(dialogScreen.getByText(`${selectQP.name} - ${selectQP.languageName}`));
-  await user.click(dialogScreen.getByRole('button', { name: 'apply' }));
-  expect(
-    dialogScreen.getByText(
-      `coding_rules.bulk_change.success.${selectQP.name}.${selectQP.languageName}.${
-        handler.allRulesName().length
-      }`
-    )
-  ).toBeInTheDocument();
-});
+  it('can activate/deactivate specific rule for quality profile', async () => {});
 
-it('should handle hash parameters', async () => {
-  renderCodingRulesApp(mockLoggedInUser(), 'coding_rules#languages=c,js|types=BUG');
+  it('navigates by keyboard', async () => {
+    const { user, ui } = getPageObjects();
+    renderCodingRulesApp();
+    await ui.appLoaded();
 
-  // 2 languages
-  expect(await screen.findByText('x_selected.2')).toBeInTheDocument();
-  expect(screen.getAllByTitle('issue.type.BUG')).toHaveLength(2);
-  // Only 3 rules shown
-  expect(screen.getByText('x_of_y_shown.3.3')).toBeInTheDocument();
-});
+    expect(
+      ui.ruleListItemLink('Awsome java rule').get(ui.currentListItem.get())
+    ).toBeInTheDocument();
+
+    await user.keyboard('{ArrowDown}');
+    expect(ui.ruleListItemLink('Hot hotspot').get(ui.currentListItem.get())).toBeInTheDocument();
+
+    await user.keyboard('{ArrowUp}');
+    expect(
+      ui.ruleListItemLink('Awsome java rule').get(ui.currentListItem.get())
+    ).toBeInTheDocument();
+
+    await user.keyboard('{ArrowRight}');
+    expect(screen.getByRole('heading', { level: 1, name: 'Awsome java rule' })).toBeInTheDocument();
 
-it('should show notification for rule advanced section and remove it after user visits', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule8');
-  await screen.findByRole('heading', {
-    level: 1,
-    name: 'Awesome Python rule with education principles',
+    await user.keyboard('{ArrowLeft}');
+    expect(
+      ui.ruleListItemLink('Awsome java rule').get(ui.currentListItem.get())
+    ).toBeInTheDocument();
   });
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  ).toBeInTheDocument();
-
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-
-  expect(screen.getByText('coding_rules.more_info.notification_message')).toBeInTheDocument();
-  expect(
-    screen.getByRole('button', {
-      name: 'coding_rules.more_info.scroll_message',
-    })
-  ).toBeInTheDocument();
-  await user.click(
-    screen.getByRole('button', {
-      name: 'coding_rules.more_info.scroll_message',
-    })
-  );
-  // navigate away and come back
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.how_to_fix',
-    })
-  );
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-  expect(screen.queryByText('coding_rules.more_info.notification_message')).not.toBeInTheDocument();
 });
 
-it('should show notification for rule advanced section and removes it when user scroll to the principles', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule8');
+describe('Rule app', () => {
+  describe('rendering', () => {
+    it('shows rule with default description section and params', async () => {
+      const { ui } = getPageObjects();
+      renderCodingRulesApp(undefined, 'coding_rules?open=rule1');
+      await ui.appLoaded();
+      expect(ui.ruleTitle('Awsome java rule').get()).toBeInTheDocument();
+      expect(document.title).toEqual('page_title.template.with_category.coding_rules.page');
+      expect(screen.getByText('Why')).toBeInTheDocument();
+      expect(screen.getByText('Because')).toBeInTheDocument();
+
+      // Check params data
+      expect(screen.getByText('html description for key 1')).toBeInTheDocument();
+      expect(screen.getByText('default value for key 2')).toBeInTheDocument();
+    });
+
+    it('shows external rule', async () => {
+      const { ui } = getPageObjects();
+      renderCodingRulesApp(undefined, 'coding_rules?open=rule6');
+      await ui.appLoaded();
+      expect(ui.ruleTitle('Bad Python rule').get()).toBeInTheDocument();
+      expect(ui.externalDescription('Bad Python rule').get()).toBeInTheDocument();
+    });
+
+    it('shows hotspot rule', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(undefined, 'coding_rules?open=rule2');
+      await ui.appLoaded();
+      expect(ui.ruleTitle('Hot hotspot').get()).toBeInTheDocument();
+      expect(ui.introTitle.get()).toBeInTheDocument();
+
+      // Shows correct tabs
+      [ui.whatRiskTab, ui.assessTab, ui.moreInfoTab].forEach((tab) => {
+        expect(tab.get()).toBeInTheDocument();
+      });
+
+      await user.click(ui.moreInfoTab.get());
+      expect(ui.resourceLink.get()).toBeInTheDocument();
+    });
+
+    it('shows rule advanced section', async () => {
+      const { ui } = getPageObjects();
+      renderCodingRulesApp(undefined, 'coding_rules?open=rule5');
+      await ui.appLoaded();
+      expect(ui.ruleTitle('Awsome Python rule').get()).toBeInTheDocument();
+      expect(ui.introTitle.get()).toBeInTheDocument();
+      // Shows correct tabs
+      [ui.howToFixTab, ui.moreInfoTab].forEach((tab) => {
+        expect(tab.get()).toBeInTheDocument();
+      });
+    });
+
+    it('shows rule advanced section with context', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(undefined, 'coding_rules?open=rule7');
+      await ui.appLoaded();
+      expect(ui.ruleTitle('Python rule with context').get()).toBeInTheDocument();
+
+      await user.click(ui.howToFixTab.get());
+
+      expect(ui.contextSubtitle('Spring').get()).toBeInTheDocument();
+      expect(screen.getByText('This is how to fix for spring')).toBeInTheDocument();
+
+      await user.click(ui.contextRadio('Spring boot').get());
+      expect(ui.contextSubtitle('Spring boot').get()).toBeInTheDocument();
+      expect(screen.getByText('This is how to fix for spring boot')).toBeInTheDocument();
+
+      await user.click(ui.contextRadio('coding_rules.description_context.other').get());
+      expect(ui.otherContextTitle.get()).toBeInTheDocument();
+    });
+
+    it('should show CYAC notification for rule advanced section and removes it after user`s visit', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule10');
+      await ui.appLoaded();
+      await user.click(ui.moreInfoTab.get());
+
+      expect(ui.caycNotificationButton.get()).toBeInTheDocument();
+
+      // navigate away and come back
+      await user.click(ui.howToFixTab.get());
+      await user.click(ui.moreInfoTab.get());
+
+      expect(ui.caycNotificationButton.query()).not.toBeInTheDocument();
+    });
+
+    it('should show CAYC notification for rule advanced section and removes it when user scrolls to the principles', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule10');
+      await ui.appLoaded();
+      await user.click(ui.moreInfoTab.get());
+      expect(ui.caycNotificationButton.get()).toBeInTheDocument();
+
+      fireEvent.scroll(screen.getByText('coding_rules.more_info.education_principles.title'));
+
+      // navigate away and come back
+      await user.click(ui.howToFixTab.get());
+      await user.click(ui.moreInfoTab.get());
+
+      expect(ui.caycNotificationButton.query()).not.toBeInTheDocument();
+    });
+
+    it('should not show notification for anonymous users', async () => {
+      const { ui, user } = getPageObjects();
+      renderCodingRulesApp(mockCurrentUser(), 'coding_rules?open=rule10');
+
+      await ui.appLoaded();
+      await user.click(ui.moreInfoTab.get());
+
+      expect(ui.caycNotificationButton.query()).not.toBeInTheDocument();
+    });
+  });
 
-  await screen.findByRole('heading', {
-    level: 1,
-    name: 'Awesome Python rule with education principles',
+  it('can activate/change/deactivate rule in quality profile', async () => {
+    const { ui, user } = getPageObjects();
+    handler.setIsAdmin();
+    renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule1');
+    await ui.appLoaded();
+    expect(ui.qpLink('QP Foo').get()).toBeInTheDocument();
+
+    // Activate rule in quality profile
+    await user.click(ui.activateButton.get());
+    await selectEvent.select(ui.qualityProfileSelect.get(), 'QP FooBar');
+    await selectEvent.select(ui.severitySelect.get(), 'severity.MINOR');
+    await user.type(ui.paramInput('1').get(), 'OK');
+
+    await act(() => user.click(ui.activateButton.get(ui.activateQPDialog.get())));
+    expect(ui.qpLink('QP FooBar').get()).toBeInTheDocument();
+
+    // Change rule details in quality profile
+    await user.click(ui.changeButton('QP FooBar').get());
+    await selectEvent.select(ui.severitySelect.get(), 'severity.BLOCKER');
+    await act(() => user.click(ui.saveButton.get(ui.changeQPDialog.get())));
+    expect(screen.getByText('severity.BLOCKER')).toBeInTheDocument();
+
+    // activate last java rule
+    await user.click(ui.activateButton.get());
+    await act(() => user.click(ui.activateButton.get(ui.activateQPDialog.get())));
+    expect(ui.qpLink('QP FooBarBaz').get()).toBeInTheDocument();
+
+    // Rule is activated in all quality profiles - show notification in dialog
+    await user.click(ui.activateButton.get());
+    expect(ui.activaInAllQPs.get()).toBeInTheDocument();
+    expect(ui.activateButton.get(ui.activateQPDialog.get())).toBeDisabled();
+    await user.click(ui.cancelButton.get());
+
+    // Deactivate rule in quality profile
+    await user.click(ui.deactivateButton('QP FooBar').get());
+    await act(() => user.click(ui.yesButton.get()));
+    expect(ui.qpLink('QP FooBar').query()).not.toBeInTheDocument();
   });
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  ).toBeInTheDocument();
-
-  // navigate away and come back
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.how_to_fix',
-    })
-  );
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-
-  expect(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  ).toBeInTheDocument();
-
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-
-  expect(screen.getByText('coding_rules.more_info.notification_message')).toBeInTheDocument();
-  expect(
-    screen.getByRole('button', {
-      name: 'coding_rules.more_info.scroll_message',
-    })
-  ).toBeInTheDocument();
-
-  fireEvent.scroll(screen.getByText('coding_rules.more_info.education_principles.title'));
-
-  // navigate away and come back
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.how_to_fix',
-    })
-  );
-  await user.click(
-    screen.getByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-  expect(screen.queryByText('coding_rules.more_info.notification_message')).not.toBeInTheDocument();
-});
 
-it('should not show notification for anonymous users', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(mockCurrentUser(), 'coding_rules?open=rule8');
-
-  await user.click(
-    await screen.findByRole('tab', {
-      name: 'coding_rules.description_section.title.more_info',
-    })
-  );
-
-  expect(screen.queryByText('coding_rules.more_info.notification_message')).not.toBeInTheDocument();
-  expect(
-    screen.queryByRole('button', {
-      name: 'coding_rules.more_info.scroll_message',
-    })
-  ).not.toBeInTheDocument();
+  it('can extend the rule description', async () => {
+    const { ui, user } = getPageObjects();
+    handler.setIsAdmin();
+    renderCodingRulesApp(undefined, 'coding_rules?open=rule5');
+    await ui.appLoaded();
+    expect(ui.ruleTitle('Awsome Python rule').get()).toBeInTheDocument();
+
+    // Add
+    await user.click(ui.extendDescriptionButton.get());
+    await user.type(ui.extendDescriptionTextbox.get(), 'TEST DESC');
+    await user.click(ui.saveButton.get());
+    expect(await screen.findByText('TEST DESC')).toBeInTheDocument();
+
+    // Edit
+    await user.click(ui.extendDescriptionButton.get());
+    await user.clear(ui.extendDescriptionTextbox.get());
+    await user.type(ui.extendDescriptionTextbox.get(), 'NEW DESC');
+    await user.click(ui.saveButton.get());
+    expect(await screen.findByText('NEW DESC')).toBeInTheDocument();
+
+    // Cancel
+    await user.click(ui.extendDescriptionButton.get());
+    await user.type(ui.extendDescriptionTextbox.get(), 'Difference');
+    await user.click(ui.cancelButton.get());
+    expect(await screen.findByText('NEW DESC')).toBeInTheDocument();
+
+    // Remove
+    await user.click(ui.extendDescriptionButton.get());
+    await user.click(ui.removeButton.get());
+    await user.click(ui.removeButton.get(screen.getByRole('dialog')));
+    expect(screen.queryByText('NEW DESC')).not.toBeInTheDocument();
+  });
+
+  it('can set tags', async () => {
+    const { ui, user } = getPageObjects();
+    handler.setIsAdmin();
+    renderCodingRulesApp(undefined, 'coding_rules?open=rule10');
+    await ui.appLoaded();
+
+    await user.click(ui.tagsDropdown.get());
+    expect(ui.tagsMenu.get()).toBeInTheDocument();
+
+    RULE_TAGS_MOCK.forEach((tag) => {
+      expect(ui.tagCheckbox(tag).get()).toBeInTheDocument();
+    });
+
+    // Rule already has this tag
+    expect(ui.tagCheckbox(RULE_TAGS_MOCK[0]).get()).toBeChecked();
+
+    // Set tag
+    await user.click(ui.tagCheckbox(RULE_TAGS_MOCK[1]).get());
+    expect(ui.tagCheckbox(RULE_TAGS_MOCK[1]).get()).toBeChecked();
+    await user.click(ui.tagCheckbox(RULE_TAGS_MOCK[1]).get());
+
+    // Search for specific tag
+    await user.type(ui.tagSearch.get(), RULE_TAGS_MOCK[2]);
+    expect(ui.tagCheckbox(RULE_TAGS_MOCK[2]).get()).toBeInTheDocument();
+    expect(ui.tagCheckbox(RULE_TAGS_MOCK[1]).query()).not.toBeInTheDocument();
+  });
+
+  describe('custom rule', () => {
+    it('can create custom rule', async () => {
+      const { ui, user } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser());
+      await ui.appLoaded();
+
+      await user.click(ui.templateFacet.get());
+      await user.click(ui.facetItem('coding_rules.filters.template.is_template').get());
+
+      // Shows only one template rule
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(1);
+
+      // Show template rule details
+      await user.click(ui.ruleListItemLink('Template rule').get());
+      expect(ui.ruleTitle('Template rule').get()).toBeInTheDocument();
+      expect(ui.customRuleSectionTitle.get()).toBeInTheDocument();
+
+      // Create custom rule
+      await user.click(ui.createCustomRuleButton.get());
+      await user.type(ui.ruleNameTextbox.get(), 'New Custom Rule');
+      expect(ui.keyTextbox.get()).toHaveValue('New_Custom_Rule');
+      await user.clear(ui.keyTextbox.get());
+      await user.type(ui.keyTextbox.get(), 'new_custom_rule');
+
+      await selectEvent.select(ui.typeSelect.get(), 'issue.type.BUG');
+      await selectEvent.select(ui.severitySelect.get(), 'severity.MINOR');
+      await selectEvent.select(ui.statusSelect.get(), 'rules.status.BETA');
+
+      await user.type(ui.descriptionTextbox.get(), 'Some description for custom rule');
+      await user.type(ui.paramInput('1').get(), 'Default value');
+
+      await user.click(ui.createButton.get());
+
+      // Verify the rule is created
+      expect(ui.customRuleItemLink('New Custom Rule').get()).toBeInTheDocument();
+    });
+
+    it('can edit custom rule', async () => {
+      const { ui, user } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule9');
+      await ui.appLoaded();
+
+      await user.click(ui.editCustomRuleButton.get());
+
+      // Change name and description of custom rule
+      await user.clear(ui.ruleNameTextbox.get());
+      await user.type(ui.ruleNameTextbox.get(), 'Updated custom rule name');
+      await user.type(ui.descriptionTextbox.get(), 'Some description for custom rule');
+
+      await user.click(ui.saveButton.get(ui.updateCustomRuleDialog.get()));
+
+      expect(ui.ruleTitle('Updated custom rule name').get()).toBeInTheDocument();
+    });
+
+    it('can delete custom rule', async () => {
+      const { ui, user } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule9');
+      await ui.appLoaded();
+
+      await user.click(ui.deleteButton.get());
+      await user.click(ui.deleteButton.get(ui.deleteCustomRuleDialog.get()));
+
+      // Shows the list of rules, custom rule should not be included
+      expect(ui.ruleListItem.getAll(ui.rulesList.get())).toHaveLength(9);
+      expect(ui.ruleListItemLink('Custom Rule based on rule8').query()).not.toBeInTheDocument();
+    });
+
+    it('can delete custom rule from template page', async () => {
+      const { ui, user } = getPageObjects();
+      handler.setIsAdmin();
+      renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule8');
+      await ui.appLoaded();
+
+      await user.click(ui.deleteCustomRuleButton('Custom Rule based on rule8').get());
+      await user.click(ui.deleteButton.get(ui.deleteCustomRuleDialog.get()));
+      expect(ui.customRuleItemLink('Custom Rule based on rule8').query()).not.toBeInTheDocument();
+    });
+
+    it('anonymous user cannot modify custom rule', async () => {
+      const { ui } = getPageObjects();
+      renderCodingRulesApp(undefined, 'coding_rules?open=rule9');
+      await ui.appLoaded();
+
+      expect(ui.editCustomRuleButton.query()).not.toBeInTheDocument();
+      expect(ui.deleteButton.query()).not.toBeInTheDocument();
+    });
+  });
 });
 
-it('should filter correctly', async () => {
-  const user = userEvent.setup();
-  renderCodingRulesApp(mockCurrentUser());
-
-  expect(await within(await ui.rulesList.find()).findAllByRole('listitem')).toHaveLength(8);
-  await user.click(await ui.availableSinceFacet.find());
-  await user.click(await ui.availableSinceDateField.find());
-  await userEvent.selectOptions(
-    await screen.findByRole('combobox', { name: 'Month:' }),
-    'November'
-  );
-  await userEvent.selectOptions(screen.getByRole('combobox', { name: 'Year:' }), '2022');
-  await user.click(screen.getByRole('gridcell', { name: '1' }));
-  expect(ui.availableSinceDateField.get()).toHaveDisplayValue('Nov 1, 2022');
-  // eslint-disable-next-line jest-dom/prefer-in-document
-  expect(within(ui.rulesList.get()).getAllByRole('listitem')).toHaveLength(1);
+describe('redirects', () => {
+  it('should open with permalink', async () => {
+    const { ui } = getPageObjects();
+    renderCodingRulesApp(undefined, 'coding_rules?rule_key=rule1');
+    await ui.appLoaded();
+    expect(ui.ruleListItemLink('Awsome java rule').get()).toBeInTheDocument();
+    expect(ui.ruleListItemLink('Hot hotspot').query()).not.toBeInTheDocument();
+  });
+
+  it('should handle hash parameters', async () => {
+    renderCodingRulesApp(mockLoggedInUser(), 'coding_rules#languages=c,js|types=BUG');
+    // 2 languages
+    expect(await screen.findByText('x_selected.2')).toBeInTheDocument();
+    expect(screen.getAllByTitle('issue.type.BUG')).toHaveLength(2);
+    // Only 3 rules shown
+    expect(screen.getByText('x_of_y_shown.2.2')).toBeInTheDocument();
+  });
 });
 
 function renderCodingRulesApp(currentUser?: CurrentUser, navigateTo?: string) {
index f2f634a1710660513e39c2a3f22162bf1b7ab1ba..9a4713acf355e2f2f58980fdfaaf473bb70aca2b 100644 (file)
@@ -31,45 +31,34 @@ interface Props {
   onDone: (severity: string) => Promise<void>;
   profiles: BaseProfile[];
   rule: Rule | RuleDetails;
+  ariaLabel?: string;
 }
 
-interface State {
-  modal: boolean;
-}
-
-export default class ActivationButton extends React.PureComponent<Props, State> {
-  state: State = { modal: false };
-
-  handleButtonClick = () => {
-    this.setState({ modal: true });
-  };
-
-  handleCloseModal = () => {
-    this.setState({ modal: false });
-  };
+export default function ActivationButton(props: Props) {
+  const { className, ariaLabel, buttonText, activation, modalHeader, profiles, rule } = props;
+  const [openModal, setOpenModal] = React.useState(false);
 
-  render() {
-    return (
-      <>
-        <Button
-          className={this.props.className}
-          id="coding-rules-quality-profile-activate"
-          onClick={this.handleButtonClick}
-        >
-          {this.props.buttonText}
-        </Button>
+  return (
+    <>
+      <Button
+        aria-label={ariaLabel}
+        className={className}
+        id="coding-rules-quality-profile-activate"
+        onClick={() => setOpenModal(true)}
+      >
+        {buttonText}
+      </Button>
 
-        {this.state.modal && (
-          <ActivationFormModal
-            activation={this.props.activation}
-            modalHeader={this.props.modalHeader}
-            onClose={this.handleCloseModal}
-            onDone={this.props.onDone}
-            profiles={this.props.profiles}
-            rule={this.props.rule}
-          />
-        )}
-      </>
-    );
-  }
+      {openModal && (
+        <ActivationFormModal
+          activation={activation}
+          modalHeader={modalHeader}
+          onClose={() => setOpenModal(false)}
+          onDone={props.onDone}
+          profiles={profiles}
+          rule={rule}
+        />
+      )}
+    </>
+  );
 }
index 4568687ddb6be14baaed568622b6f4872d17c244..60cae4ab9cc4abf06a27af6e96574c9b0f86a2f6 100644 (file)
@@ -196,9 +196,12 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
             ) : (
               params.map((param) => (
                 <div className="modal-field" key={param.key}>
-                  <label title={param.key}>{param.key}</label>
+                  <label title={param.key} htmlFor={param.key}>
+                    {param.key}
+                  </label>
                   {param.type === 'TEXT' ? (
                     <textarea
+                      id={param.key}
                       disabled={submitting}
                       name={param.key}
                       onChange={this.handleParameterChange}
@@ -208,6 +211,7 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
                     />
                   ) : (
                     <input
+                      id={param.key}
                       disabled={submitting}
                       name={param.key}
                       onChange={this.handleParameterChange}
index dbba81fbb3191594095c546461e68faf298fd843..229d8dfb3be5665629189cc415824db464d44393 100644 (file)
@@ -28,50 +28,26 @@ interface Props {
   templateRule: RuleDetails;
 }
 
-interface State {
-  modal: boolean;
-}
-
-export default class CustomRuleButton extends React.PureComponent<Props, State> {
-  mounted = false;
-  state: State = { modal: false };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  handleClick = () => {
-    this.setState({ modal: true });
-  };
-
-  handleModalClose = () => {
-    if (this.mounted) {
-      this.setState({ modal: false });
-    }
-  };
+export default function CustomRuleButton(props: Props) {
+  const { customRule, templateRule } = props;
+  const [modalOpen, setModalOpen] = React.useState(false);
 
-  handleDone = (newRuleDetails: RuleDetails) => {
-    this.handleModalClose();
-    this.props.onDone(newRuleDetails);
+  const handleDone = (newRuleDetails: RuleDetails) => {
+    setModalOpen(false);
+    props.onDone(newRuleDetails);
   };
 
-  render() {
-    return (
-      <>
-        {this.props.children({ onClick: this.handleClick })}
-        {this.state.modal && (
-          <CustomRuleFormModal
-            customRule={this.props.customRule}
-            onClose={this.handleModalClose}
-            onDone={this.handleDone}
-            templateRule={this.props.templateRule}
-          />
-        )}
-      </>
-    );
-  }
+  return (
+    <>
+      {props.children({ onClick: () => setModalOpen(true) })}
+      {modalOpen && (
+        <CustomRuleFormModal
+          customRule={customRule}
+          onClose={() => setModalOpen(false)}
+          onDone={handleDone}
+          templateRule={templateRule}
+        />
+      )}
+    </>
+  );
 }
index d9c46f62d78e2c059807c16634e039b8fd979da4..08fef2f612c09fa5529c255199b623baedc26f84 100644 (file)
@@ -53,7 +53,7 @@ interface State {
   severity: string;
   status: string;
   submitting: boolean;
-  type: string;
+  type: RuleType;
 }
 
 export default class CustomRuleFormModal extends React.PureComponent<Props, State> {
@@ -334,7 +334,7 @@ export default class CustomRuleFormModal extends React.PureComponent<Props, Stat
     const header = customRule
       ? translate('coding_rules.update_custom_rule')
       : translate('coding_rules.create_custom_rule');
-    let submit = this.props.customRule ? translate('save') : translate('create');
+    let submit = translate(customRule ? 'save' : 'create');
     if (this.state.reactivating) {
       submit = translate('coding_rules.reactivate');
     }
index 6eac07780bfbd17113f638ca936da7d693bfa0f9..4b59c2c0b60f6a69714c512ed381fd8857e3609e 100644 (file)
@@ -20,9 +20,9 @@
 import * as React from 'react';
 import { Profile } from '../../../api/quality-profiles';
 import { deleteRule, getRuleDetails, updateRule } from '../../../api/rules';
-import { Button } from '../../../components/controls/buttons';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
 import HelpTooltip from '../../../components/controls/HelpTooltip';
+import { Button } from '../../../components/controls/buttons';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Dict, RuleActivation, RuleDetails as TypeRuleDetails } from '../../../types/types';
index 6af3d26a6f16c531f2390ab69aa680b86073fdbb..cd1d53047d510a56fcb75c99ec73168de87249a5 100644 (file)
@@ -21,8 +21,8 @@ import { sortBy } from 'lodash';
 import * as React from 'react';
 import { deleteRule, searchRules } from '../../../api/rules';
 import Link from '../../../components/common/Link';
-import { Button } from '../../../components/controls/buttons';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
+import { Button } from '../../../components/controls/buttons';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
@@ -132,7 +132,11 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
             onConfirm={this.handleRuleDelete}
           >
             {({ onClick }) => (
-              <Button className="button-red js-delete-custom-rule" onClick={onClick}>
+              <Button
+                className="button-red js-delete-custom-rule"
+                aria-label={translateWithParameters('coding_rules.delete_rule_x', rule.name)}
+                onClick={onClick}
+              >
                 {translate('delete')}
               </Button>
             )}
index 3a66d28122c274286255b01386266c2a64758551..f7e8184a9308991ffa46973e0c58c1db6ffbd588 100644 (file)
@@ -141,6 +141,7 @@ export default class RuleDetailsDescription extends React.PureComponent<Props, S
             <td colSpan={2}>
               <textarea
                 autoFocus
+                aria-label={translate('coding_rules.extend_description')}
                 className="width-100 little-spacer-bottom"
                 id="coding-rules-detail-extend-description-text"
                 onChange={this.handleDescriptionChange}
index 90eb03411f61343f3d31a47b52425fa5ebdffb47..ba62978ab67352e1e3bd538b7dd4ca20fb298e97 100644 (file)
@@ -26,36 +26,36 @@ interface Props {
   params: RuleParameter[];
 }
 
-export default class RuleDetailsParameters extends React.PureComponent<Props> {
-  renderParameter = (param: RuleParameter) => (
-    <tr className="coding-rules-detail-parameter" key={param.key}>
-      <td className="coding-rules-detail-parameter-name">{param.key}</td>
-      <td className="coding-rules-detail-parameter-description">
-        {param.htmlDesc !== undefined && (
-          <p
-            // eslint-disable-next-line react/no-danger
-            dangerouslySetInnerHTML={{ __html: sanitizeString(param.htmlDesc) }}
-          />
-        )}
-        {param.defaultValue !== undefined && (
-          <div className="note spacer-top">
-            {translate('coding_rules.parameters.default_value')}
-            <br />
-            <span className="coding-rules-detail-parameter-value">{param.defaultValue}</span>
-          </div>
-        )}
-      </td>
-    </tr>
+export default function RuleDetailsParameters({ params }: Props) {
+  return (
+    <div className="js-rule-parameters">
+      <h3 className="coding-rules-detail-title">{translate('coding_rules.parameters')}</h3>
+      <table className="coding-rules-detail-parameters">
+        <tbody>
+          {params.map((param) => (
+            <tr className="coding-rules-detail-parameter" key={param.key}>
+              <td className="coding-rules-detail-parameter-name">{param.key}</td>
+              <td className="coding-rules-detail-parameter-description">
+                {param.htmlDesc !== undefined && (
+                  <p
+                    // eslint-disable-next-line react/no-danger
+                    dangerouslySetInnerHTML={{ __html: sanitizeString(param.htmlDesc) }}
+                  />
+                )}
+                {param.defaultValue !== undefined && (
+                  <div className="note spacer-top">
+                    {translate('coding_rules.parameters.default_value')}
+                    <br />
+                    <span className="coding-rules-detail-parameter-value">
+                      {param.defaultValue}
+                    </span>
+                  </div>
+                )}
+              </td>
+            </tr>
+          ))}
+        </tbody>
+      </table>
+    </div>
   );
-
-  render() {
-    return (
-      <div className="js-rule-parameters">
-        <h3 className="coding-rules-detail-title">{translate('coding_rules.parameters')}</h3>
-        <table className="coding-rules-detail-parameters">
-          <tbody>{this.props.params.map(this.renderParameter)}</tbody>
-        </table>
-      </div>
-    );
-  }
 }
index 5dffc23758c74ac4accaa6f331f3205cde8a3c15..a424a638e357957b6127d27f3e6e3237912b4ff7 100644 (file)
@@ -135,6 +135,7 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props> {
             {!ruleDetails.isTemplate && (
               <ActivationButton
                 activation={activation}
+                ariaLabel={translateWithParameters('coding_rules.change_details_x', profile.name)}
                 buttonText={translate('change_verb')}
                 className="coding-rules-detail-quality-profile-change"
                 modalHeader={translate('coding_rules.change_details')}
@@ -177,6 +178,10 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props> {
                 {({ onClick }) => (
                   <Button
                     className="coding-rules-detail-quality-profile-deactivate button-red spacer-left"
+                    aria-label={translateWithParameters(
+                      'coding_rules.deactivate_in_quality_profile_x',
+                      profile.name
+                    )}
                     onClick={onClick}
                   >
                     {translate('coding_rules.deactivate')}
index 684affb408223430312a636f73576a9391423edf..c79723f39754e6331c32df52c3d3b736f0cf8037 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import IssueTypeIcon from '../../../components/icons/IssueTypeIcon';
+import { RULE_TYPES } from '../../../helpers/constants';
 import { translate } from '../../../helpers/l10n';
 import Facet, { BasicProps } from './Facet';
 
@@ -33,12 +34,10 @@ export default class TypeFacet extends React.PureComponent<BasicProps> {
   renderTextName = (type: string) => translate('issue.type', type);
 
   render() {
-    const options = ['BUG', 'VULNERABILITY', 'CODE_SMELL', 'SECURITY_HOTSPOT'];
-
     return (
       <Facet
         {...this.props}
-        options={options}
+        options={RULE_TYPES}
         property="types"
         renderName={this.renderName}
         renderTextName={this.renderTextName}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/ActivationFormModal-test.tsx
deleted file mode 100644 (file)
index 933273f..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { activateRule } from '../../../../api/quality-profiles';
-import {
-  mockQualityProfile,
-  mockRule,
-  mockRuleActivation,
-  mockRuleDetails,
-  mockRuleDetailsParameter,
-} from '../../../../helpers/testMocks';
-import ActivationFormModal from '../ActivationFormModal';
-
-jest.mock('../../../../api/quality-profiles', () => ({
-  activateRule: jest.fn().mockResolvedValueOnce({}),
-}));
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-  expect(
-    shallowRender({
-      profiles: [
-        mockQualityProfile(),
-        mockQualityProfile({ depth: 2, actions: { edit: true }, language: 'js' }),
-      ],
-    })
-  ).toMatchSnapshot('with deep profiles');
-  expect(shallowRender({ rule: mockRuleDetails({ templateKey: 'foobar' }) })).toMatchSnapshot(
-    'custom rule'
-  );
-  expect(shallowRender({ activation: mockRuleActivation() })).toMatchSnapshot('update mode');
-  const wrapper = shallowRender();
-  wrapper.setState({ submitting: true });
-  expect(wrapper).toMatchSnapshot('submitting');
-});
-
-it('should activate rule on quality profile when submit', () => {
-  const wrapper = shallowRender();
-  wrapper.instance().handleFormSubmit({
-    preventDefault: jest.fn(),
-  } as any as React.SyntheticEvent<HTMLFormElement>);
-  expect(activateRule).toHaveBeenCalledWith({
-    key: '',
-    params: {
-      '1': '1',
-      '2': '1',
-    },
-    rule: 'javascript:S1067',
-    severity: 'MAJOR',
-  });
-});
-
-it('should handle profile change correctly', () => {
-  const wrapper = shallowRender();
-  const qualityProfile = mockQualityProfile();
-  wrapper.instance().handleProfileChange(qualityProfile);
-  expect(wrapper.state().profile).toBe(qualityProfile);
-});
-
-function shallowRender(props: Partial<ActivationFormModal['props']> = {}) {
-  return shallow<ActivationFormModal>(
-    <ActivationFormModal
-      modalHeader="title"
-      onClose={jest.fn()}
-      onDone={jest.fn()}
-      profiles={[mockQualityProfile()]}
-      rule={mockRule({
-        params: [
-          mockRuleDetailsParameter(),
-          mockRuleDetailsParameter({ key: '2', type: 'TEXT', htmlDesc: undefined }),
-        ],
-      })}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx
deleted file mode 100644 (file)
index 62dbc22..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockQualityProfile } from '../../../../helpers/testMocks';
-import { mockEvent } from '../../../../helpers/testUtils';
-import BulkChange from '../BulkChange';
-
-const profile = mockQualityProfile({
-  actions: {
-    edit: true,
-    setAsDefault: true,
-    copy: true,
-    associateProjects: true,
-    delete: false,
-  },
-});
-
-it('should render correctly', () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should not a disabled button when edition is not possible', () => {
-  const wrapper = shallowRender({
-    referencedProfiles: { key: { ...profile, actions: { ...profile.actions, edit: false } } },
-  });
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should display BulkChangeModal', () => {
-  const wrapper = shallowRender();
-  wrapper.instance().handleActivateClick(mockEvent());
-  expect(wrapper.find('withLanguagesContext(BulkChangeModal)').exists()).toBe(true);
-});
-
-function shallowRender(props: Partial<BulkChange['props']> = {}) {
-  return shallow<BulkChange>(
-    <BulkChange
-      query={{ activation: false, profile: 'key' } as BulkChange['props']['query']}
-      referencedProfiles={{ key: profile }}
-      total={2}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CodingRulesApp-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CodingRulesApp-test.tsx
deleted file mode 100644 (file)
index 85eb368..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { searchQualityProfiles } from '../../../../api/quality-profiles';
-import { getRulesApp } from '../../../../api/rules';
-import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper';
-import {
-  mockCurrentUser,
-  mockLocation,
-  mockQualityProfile,
-  mockRouter,
-} from '../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { CodingRulesApp } from '../CodingRulesApp';
-
-jest.mock('../../../../components/common/ScreenPositionHelper');
-
-jest.mock('../../../../api/rules', () => {
-  const { mockRule, mockPaging } = jest.requireActual('../../../../helpers/testMocks');
-  return {
-    getRulesApp: jest.fn().mockResolvedValue({ canWrite: true, repositories: [] }),
-    searchRules: jest.fn().mockResolvedValue({
-      actives: [],
-      rawActives: [],
-      facets: [],
-      rawFacets: [],
-      rules: [mockRule(), mockRule()],
-      paging: mockPaging({
-        total: 0,
-        pageIndex: 1,
-        pageSize: 100,
-      }),
-    }),
-  };
-});
-
-jest.mock('../../../../api/quality-profiles', () => ({
-  searchQualityProfiles: jest.fn().mockResolvedValue({ profiles: [] }),
-}));
-
-jest.mock('../../../../helpers/system', () => ({
-  getReactDomContainerSelector: () => '#content',
-}));
-
-it('should render correctly', async () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot('loading');
-
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot('loaded');
-  expect(wrapper.find(ScreenPositionHelper).dive()).toMatchSnapshot(
-    'loaded (ScreenPositionHelper)'
-  );
-});
-
-describe('renderBulkButton', () => {
-  it('should be null when the user is not logged in', () => {
-    const wrapper = shallowRender({
-      currentUser: mockCurrentUser(),
-    });
-    expect(wrapper.instance().renderBulkButton()).toMatchSnapshot();
-  });
-
-  it('should be null when the user does not have the sufficient permission', () => {
-    (getRulesApp as jest.Mock).mockReturnValueOnce({ canWrite: false, repositories: [] });
-
-    const wrapper = shallowRender();
-    expect(wrapper.instance().renderBulkButton()).toMatchSnapshot();
-  });
-
-  it('should show bulk change button when user has global admin rights on quality profiles', async () => {
-    (getRulesApp as jest.Mock).mockReturnValueOnce({ canWrite: true, repositories: [] });
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-
-    expect(wrapper.instance().renderBulkButton()).toMatchSnapshot();
-  });
-
-  it('should show bulk change button when user has edit rights on specific quality profile', async () => {
-    (getRulesApp as jest.Mock).mockReturnValueOnce({ canWrite: false, repositories: [] });
-    (searchQualityProfiles as jest.Mock).mockReturnValueOnce({
-      profiles: [mockQualityProfile({ key: 'foo', actions: { edit: true } }), mockQualityProfile()],
-    });
-
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-
-    expect(wrapper.instance().renderBulkButton()).toMatchSnapshot();
-  });
-});
-
-function shallowRender(props: Partial<CodingRulesApp['props']> = {}) {
-  return shallow<CodingRulesApp>(
-    <CodingRulesApp
-      currentUser={mockCurrentUser({
-        isLoggedIn: true,
-      })}
-      location={mockLocation()}
-      router={mockRouter()}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/CustomRuleFormModal-test.tsx
deleted file mode 100644 (file)
index 2cd65df..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { createRule } from '../../../../api/rules';
-import { mockRuleDetails, mockRuleDetailsParameter } from '../../../../helpers/testMocks';
-import { submit, waitAndUpdate } from '../../../../helpers/testUtils';
-import CustomRuleFormModal from '../CustomRuleFormModal';
-
-jest.mock('../../../../api/rules', () => ({ createRule: jest.fn() }));
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-});
-
-it('should handle re-activation', async () => {
-  (createRule as jest.Mock).mockRejectedValue({ status: 409 });
-  const wrapper = shallowRender();
-  submit(wrapper.find('form'));
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<CustomRuleFormModal['props']> = {}) {
-  return shallow(
-    <CustomRuleFormModal
-      onClose={jest.fn()}
-      onDone={jest.fn()}
-      templateRule={{
-        ...mockRuleDetails({
-          params: [
-            mockRuleDetailsParameter(),
-            mockRuleDetailsParameter({ key: '2', type: 'TEXT', htmlDesc: undefined }),
-          ],
-        }),
-        createdAt: 'date',
-        repo: 'squid',
-      }}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/FacetsList-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/FacetsList-test.tsx
deleted file mode 100644 (file)
index 694565e..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { Query } from '../../query';
-import FacetsList, { FacetsListProps } from '../FacetsList';
-
-it('should render correctly', () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should correctly hide profile facets', () => {
-  const wrapper = shallowRender({ hideProfileFacet: true });
-  expect(wrapper.find('ProfileFacet').length).toEqual(0);
-  expect(wrapper.find('InheritanceFacet').length).toEqual(0);
-  expect(wrapper.find('ActivationSeverityFacet').length).toEqual(0);
-});
-
-it('should correctly enable/disable the language facet', () => {
-  const wrapper = shallowRender({ query: { profile: 'foo' } as Query });
-  expect(wrapper.find('withLanguagesContext(LanguageFacet)').prop('disabled')).toBe(true);
-
-  wrapper.setProps({ query: {} }).update();
-  expect(wrapper.find('withLanguagesContext(LanguageFacet)').prop('disabled')).toBe(false);
-});
-
-it('should correctly enable/disable the activation severity facet', () => {
-  const wrapper = shallowRender();
-  expect(wrapper.find('ActivationSeverityFacet').prop('disabled')).toBe(true);
-
-  wrapper.setProps({ query: { activation: 'foo' }, selectedProfile: { key: 'foo' } }).update();
-  expect(wrapper.find('ActivationSeverityFacet').prop('disabled')).toBe(false);
-});
-
-it('should correctly enable/disable the inheritcance facet', () => {
-  const wrapper = shallowRender();
-  expect(wrapper.find('InheritanceFacet').prop('disabled')).toBe(true);
-
-  wrapper.setProps({ selectedProfile: { isInherited: true } }).update();
-  expect(wrapper.find('InheritanceFacet').prop('disabled')).toBe(false);
-});
-
-function shallowRender(props: Partial<FacetsListProps> = {}) {
-  return shallow(
-    <FacetsList
-      onFacetToggle={jest.fn()}
-      onFilterChange={jest.fn()}
-      openFacets={{}}
-      query={{} as Query}
-      referencedProfiles={{}}
-      referencedRepositories={{}}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/LanguageFacet-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/LanguageFacet-test.tsx
deleted file mode 100644 (file)
index f83bf56..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockLanguage } from '../../../../helpers/testMocks';
-import { LanguageFacet } from '../LanguageFacet';
-
-it('should handle search correctly', async () => {
-  const wrapper = shallowRender({ stats: { java: 12 } });
-  const result = await wrapper.instance().handleSearch('ja');
-
-  expect(result).toStrictEqual({
-    paging: {
-      pageIndex: 1,
-      pageSize: 2,
-      total: 2,
-    },
-    results: [
-      { key: 'js', name: 'javascript' },
-      { key: 'java', name: 'java' },
-    ],
-  });
-});
-
-it('should render name correctly', () => {
-  const wrapper = shallowRender();
-
-  expect(wrapper.instance().getLanguageName('js')).toBe('javascript');
-  expect(wrapper.instance().getLanguageName('unknownKey')).toBe('unknownKey');
-});
-
-it('should render search results correctly', () => {
-  const wrapper = shallowRender();
-
-  expect(wrapper.instance().renderSearchResult({ key: 'hello', name: 'Hello' }, 'llo'))
-    .toMatchInlineSnapshot(`
-    <React.Fragment>
-      He
-      <mark>
-        llo
-      </mark>
-    </React.Fragment>
-  `);
-});
-
-function shallowRender(props: Partial<LanguageFacet['props']> = {}) {
-  return shallow<LanguageFacet>(
-    <LanguageFacet
-      languages={{
-        js: mockLanguage({ key: 'js', name: 'javascript' }),
-        c: mockLanguage({ key: 'c', name: 'c' }),
-      }}
-      onChange={jest.fn()}
-      onToggle={jest.fn()}
-      open={false}
-      stats={{}}
-      values={[]}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/PageActions-test.tsx
deleted file mode 100644 (file)
index 3ea5a08..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { Paging } from '../../../../types/types';
-import PageActions, { PageActionsProps } from '../PageActions';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-  expect(shallowRender({ paging: { total: 100 } as Paging })).toMatchSnapshot('with paging');
-});
-
-function shallowRender(props: Partial<PageActionsProps> = {}) {
-  return shallow<PageActionsProps>(<PageActions {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RepositoryFacet-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RepositoryFacet-test.tsx
deleted file mode 100644 (file)
index f042ca9..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockLanguage, mockRuleRepository } from '../../../../helpers/testMocks';
-import { RepositoryFacet } from '../RepositoryFacet';
-
-jest.mock('../../../../api/rules', () => ({
-  getRuleRepositories: jest
-    .fn()
-    .mockResolvedValue([{ key: 'clirr', name: 'Clirr', language: 'java' }]),
-}));
-
-it('should handle search correctly', async () => {
-  const wrapper = shallowRender();
-  const result = await wrapper.instance().handleSearch('foo');
-
-  expect(result).toStrictEqual({
-    paging: {
-      pageIndex: 1,
-      pageSize: 1,
-      total: 1,
-    },
-    results: ['clirr'],
-  });
-});
-
-it('should return text repository correctly', () => {
-  const wrapper = shallowRender();
-  let text = wrapper.instance().renderTextName('l');
-  expect(text).toBe('SonarQube');
-
-  text = wrapper.instance().renderTextName('notFound');
-  expect(text).toBe('notFound');
-
-  text = wrapper.instance().renderTextName('noName');
-  expect(text).toBe('noName');
-});
-
-it('should render repository correctly', () => {
-  const wrapper = shallowRender();
-  let text = wrapper.instance().renderName('l');
-  expect(shallow(<div>{text}</div>)).toMatchSnapshot();
-
-  text = wrapper.instance().renderName('notFound');
-  expect(text).toBe('notFound');
-});
-
-it('should render search repository correctly', () => {
-  const wrapper = shallowRender();
-  let text = wrapper.instance().renderSearchTextName('l', 'Son');
-  expect(shallow(<div>{text}</div>)).toMatchSnapshot();
-
-  text = wrapper.instance().renderSearchTextName('notFound', '');
-  expect(text).toBe('notFound');
-});
-
-function shallowRender(props: Partial<RepositoryFacet['props']> = {}) {
-  return shallow<RepositoryFacet>(
-    <RepositoryFacet
-      languages={{ l: mockLanguage() }}
-      referencedRepositories={{
-        l: mockRuleRepository(),
-        noName: mockRuleRepository({ name: undefined }),
-      }}
-      onChange={jest.fn()}
-      onToggle={jest.fn()}
-      open={false}
-      stats={{}}
-      values={[]}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx
deleted file mode 100644 (file)
index 1f53299..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { deleteRule, getRuleDetails, updateRule } from '../../../../api/rules';
-import { mockQualityProfile } from '../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { RuleType } from '../../../../types/types';
-import RuleDetails from '../RuleDetails';
-
-jest.mock('../../../../api/rules', () => {
-  const { mockRuleDetails } = jest.requireActual('../../../../helpers/testMocks');
-  return {
-    deleteRule: jest.fn().mockResolvedValue(null),
-    getRuleDetails: jest.fn().mockResolvedValue({
-      rule: mockRuleDetails(),
-      actives: [
-        {
-          qProfile: 'foo',
-          inherit: 'NONE',
-          severity: 'MAJOR',
-          params: [],
-          createdAt: '2017-06-16T16:13:38+0200',
-          updatedAt: '2017-06-16T16:13:38+0200',
-        },
-      ],
-    }),
-    updateRule: jest.fn().mockResolvedValue(null),
-  };
-});
-
-beforeEach(() => {
-  jest.clearAllMocks();
-});
-
-it('should render correctly', async () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot('loading');
-
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot('loaded');
-
-  expect(getRuleDetails).toHaveBeenCalledWith(
-    expect.objectContaining({
-      actives: true,
-      key: 'squid:S1337',
-    })
-  );
-});
-
-it('should correctly handle prop changes', async () => {
-  const ruleKey = 'foo:bar';
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-  jest.clearAllMocks();
-
-  wrapper.setProps({ ruleKey });
-  expect(getRuleDetails).toHaveBeenCalledWith(
-    expect.objectContaining({
-      actives: true,
-      key: ruleKey,
-    })
-  );
-});
-
-it('should correctly handle tag changes', async () => {
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-
-  wrapper.instance().handleTagsChange(['foo', 'bar']);
-  const ruleDetails = wrapper.state('ruleDetails');
-  expect(ruleDetails && ruleDetails.tags).toEqual(['foo', 'bar']);
-  await waitAndUpdate(wrapper);
-
-  expect(updateRule).toHaveBeenCalledWith({
-    key: 'squid:S1337',
-    tags: 'foo,bar',
-  });
-});
-
-it('should correctly handle rule changes', () => {
-  const wrapper = shallowRender();
-  const ruleChange = {
-    createdAt: '2019-02-01',
-    descriptionSections: [],
-    key: 'foo',
-    name: 'Foo',
-    repo: 'bar',
-    severity: 'MAJOR',
-    status: 'READY',
-    type: 'BUG' as RuleType,
-  };
-
-  wrapper.instance().handleRuleChange(ruleChange);
-  expect(wrapper.state().ruleDetails).toBe(ruleChange);
-});
-
-it('should correctly handle activation', async () => {
-  const onActivate = jest.fn();
-  const wrapper = shallowRender({ onActivate });
-  await waitAndUpdate(wrapper);
-
-  wrapper.instance().handleActivate();
-  await waitAndUpdate(wrapper);
-  expect(onActivate).toHaveBeenCalledWith(
-    'foo',
-    'squid:S1337',
-    expect.objectContaining({
-      inherit: 'NONE',
-      severity: 'MAJOR',
-    })
-  );
-});
-
-it('should correctly handle deactivation', async () => {
-  const onDeactivate = jest.fn();
-  const selectedProfile = mockQualityProfile({ key: 'bar' });
-  const wrapper = shallowRender({ onDeactivate, selectedProfile });
-  await waitAndUpdate(wrapper);
-
-  wrapper.instance().handleDeactivate();
-  await waitAndUpdate(wrapper);
-  expect(onDeactivate).toHaveBeenCalledWith(selectedProfile.key, 'squid:S1337');
-});
-
-it('should correctly handle deletion', async () => {
-  const onDelete = jest.fn();
-  const wrapper = shallowRender({ onDelete });
-  await waitAndUpdate(wrapper);
-
-  wrapper.instance().handleDelete();
-  await waitAndUpdate(wrapper);
-  expect(deleteRule).toHaveBeenCalledWith(expect.objectContaining({ key: 'squid:S1337' }));
-  expect(onDelete).toHaveBeenCalledWith('squid:S1337');
-});
-
-function shallowRender(props: Partial<RuleDetails['props']> = {}) {
-  const profile = mockQualityProfile({ key: 'foo' });
-
-  return shallow<RuleDetails>(
-    <RuleDetails
-      onActivate={jest.fn()}
-      onDeactivate={jest.fn()}
-      onDelete={jest.fn()}
-      onFilterChange={jest.fn()}
-      referencedProfiles={{ key: profile }}
-      referencedRepositories={{
-        javascript: { key: 'javascript', language: 'js', name: 'SonarAnalyzer' },
-      }}
-      ruleKey="squid:S1337"
-      selectedProfile={profile}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsIssues-test.tsx
deleted file mode 100644 (file)
index 2f26f04..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { getFacet } from '../../../../api/issues';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { RuleDetailsIssues } from '../RuleDetailsIssues';
-
-jest.mock('../../../../api/issues', () => ({
-  getFacet: jest.fn().mockResolvedValue({
-    facet: [
-      { count: 13, val: 'sample-key' },
-      { count: 5, val: 'example-key' },
-    ],
-    response: {
-      components: [
-        { key: 'sample-key', name: 'Sample' },
-        { key: 'example-key', name: 'Example' },
-      ],
-      paging: { total: 18 },
-    },
-  }),
-}));
-
-beforeEach(() => {
-  (getFacet as jest.Mock).mockClear();
-});
-
-it('should fetch issues and render', async () => {
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
-  expect(getFacet).toHaveBeenCalledWith(
-    {
-      resolved: 'false',
-      rules: 'foo',
-    },
-    'projects'
-  );
-});
-
-function shallowRender(props: Partial<RuleDetailsIssues['props']> = {}) {
-  return shallow(
-    <RuleDetailsIssues
-      hasFeature={jest.fn().mockReturnValue(false)}
-      ruleDetails={{ key: 'foo', type: 'BUG' }}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsMeta-test.tsx
deleted file mode 100644 (file)
index b49aba3..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { RuleDetails } from '../../../../types/types';
-import RuleDetailsMeta from '../RuleDetailsMeta';
-import RuleDetailsTagsPopup from '../RuleDetailsTagsPopup';
-
-const RULE: RuleDetails = {
-  key: 'squid:S1133',
-  repo: 'squid',
-  name: 'Deprecated code should be removed',
-  createdAt: '2013-07-26T09:40:51+0200',
-  descriptionSections: [],
-  severity: 'INFO',
-  status: 'READY',
-  lang: 'java',
-  langName: 'Java',
-  scope: 'MAIN',
-  type: 'CODE_SMELL',
-  remFnType: 'LINEAR_OFFSET',
-  remFnBaseEffort: '1min',
-  remFnGapMultiplier: '2min',
-  gapDescription: 'per test',
-};
-
-const EXTERNAL_RULE: RuleDetails = {
-  key: 'external_xoo:OneExternalIssuePerLine',
-  repo: 'external_xoo',
-  name: 'xoo:OneExternalIssuePerLine',
-  createdAt: '2018-05-31T11:22:13+0200',
-  descriptionSections: [],
-  severity: 'MAJOR',
-  status: 'READY',
-  scope: 'ALL',
-  isExternal: true,
-  type: 'UNKNOWN',
-};
-
-const EXTERNAL_RULE_WITH_DATA: RuleDetails = {
-  key: 'external_xoo:OneExternalIssueWithDetailsPerLine',
-  repo: 'external_xoo',
-  name: 'One external issue per line',
-  createdAt: '2018-05-31T11:19:51+0200',
-  descriptionSections: [],
-  severity: 'MAJOR',
-  status: 'READY',
-  tags: ['tag'],
-  lang: 'xoo',
-  langName: 'Xoo',
-  scope: 'ALL',
-  isExternal: true,
-  type: 'BUG',
-};
-
-it('should display right meta info', () => {
-  expect(shallowRender()).toMatchSnapshot();
-  expect(
-    shallowRender({ hideSimilarRulesFilter: true, ruleDetails: EXTERNAL_RULE })
-  ).toMatchSnapshot();
-  expect(
-    shallowRender({ hideSimilarRulesFilter: true, ruleDetails: EXTERNAL_RULE_WITH_DATA })
-  ).toMatchSnapshot();
-});
-
-it('should edit tags', () => {
-  const onTagsChange = jest.fn();
-  const wrapper = shallowRender({ onTagsChange });
-  expect(wrapper.find('[data-meta="tags"]')).toMatchSnapshot();
-  const overlay = wrapper
-    .find('[data-meta="tags"]')
-    .find('Dropdown')
-    .prop('overlay') as RuleDetailsTagsPopup;
-
-  overlay.props.setTags(['foo', 'bar']);
-  expect(onTagsChange).toHaveBeenCalledWith(['foo', 'bar']);
-});
-
-function shallowRender(props: Partial<RuleDetailsMeta['props']> = {}) {
-  return shallow(
-    <RuleDetailsMeta
-      canWrite
-      onFilterChange={jest.fn()}
-      onTagsChange={jest.fn()}
-      referencedRepositories={{}}
-      ruleDetails={RULE}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsParameters-test.tsx
deleted file mode 100644 (file)
index 1cc6ebb..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockRuleDetailsParameter } from '../../../../helpers/testMocks';
-import RuleDetailsParameters from '../RuleDetailsParameters';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<RuleDetailsParameters['props']> = {}) {
-  const params = [mockRuleDetailsParameter(), mockRuleDetailsParameter()];
-
-  return shallow(<RuleDetailsParameters params={params} {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsTagsPopup-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetailsTagsPopup-test.tsx
deleted file mode 100644 (file)
index 139b049..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import RuleDetailsTagsPopup, { Props } from '../RuleDetailsTagsPopup';
-
-jest.mock('../../../../api/rules', () => ({
-  getRuleTags: jest.fn(() => Promise.resolve(['system', 'foo', 'bar', 'not-system'])),
-}));
-
-const getRuleTags = require('../../../../api/rules').getRuleTags as jest.Mock<any>;
-
-it('should render tags', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should search tags', async () => {
-  const wrapper = shallowRender();
-  wrapper.prop<Function>('onSearch')('sys');
-  expect(getRuleTags).toHaveBeenCalledWith({ ps: 11, q: 'sys' });
-  await new Promise(setImmediate);
-  wrapper.update();
-  // should not contain system tags
-  expect(wrapper.prop('tags')).toEqual(['bar', 'not-system']);
-});
-
-it('should select & unselect tags', () => {
-  const setTags = jest.fn();
-  const wrapper = shallowRender({ setTags });
-
-  wrapper.prop<Function>('onSelect')('another');
-  expect(setTags).toHaveBeenLastCalledWith(['foo', 'another']);
-
-  wrapper.prop<Function>('onUnselect')('foo');
-  expect(setTags).toHaveBeenLastCalledWith([]);
-});
-
-function shallowRender(props?: Partial<Props>) {
-  return shallow(
-    <RuleDetailsTagsPopup setTags={jest.fn()} sysTags={['system']} tags={['foo']} {...props} />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleListItem-test.tsx
deleted file mode 100644 (file)
index 5f886a7..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { deactivateRule } from '../../../../api/quality-profiles';
-import Link from '../../../../components/common/Link';
-import { mockQualityProfile, mockRule } from '../../../../helpers/testMocks';
-import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils';
-import RuleListItem from '../RuleListItem';
-
-jest.mock('../../../../api/quality-profiles', () => ({
-  deactivateRule: jest.fn().mockResolvedValue(null),
-}));
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-  expect(shallowRender({})).toMatchSnapshot('with activation');
-});
-
-it('should open rule', () => {
-  const onOpen = jest.fn();
-  const wrapper = shallowRender({ onOpen });
-  wrapper.find(Link).simulate('click', mockEvent({ button: 0 }));
-  expect(onOpen).toHaveBeenCalledWith('javascript:S1067');
-});
-
-it('handle activation', () => {
-  const profile = mockQualityProfile();
-  const rule = mockRule();
-  const onActivate = jest.fn();
-  const wrapper = shallowRender({ onActivate, rule, selectedProfile: profile });
-
-  wrapper.instance().handleActivate('MAJOR');
-  expect(onActivate).toHaveBeenCalledWith(profile.key, rule.key, {
-    severity: 'MAJOR',
-    inherit: 'NONE',
-  });
-});
-
-it('handle deactivation', async () => {
-  const profile = mockQualityProfile();
-  const rule = mockRule();
-  const onDeactivate = jest.fn();
-  const wrapper = shallowRender({ onDeactivate, rule, selectedProfile: profile });
-
-  wrapper.instance().handleDeactivate();
-  expect(deactivateRule).toHaveBeenCalledWith(
-    expect.objectContaining({
-      key: profile.key,
-      rule: rule.key,
-    })
-  );
-  await waitAndUpdate(wrapper);
-  expect(onDeactivate).toHaveBeenCalledWith(profile.key, rule.key);
-});
-
-describe('renderActions', () => {
-  it('should be null when there is no selected profile', () => {
-    const wrapper = shallowRender({
-      isLoggedIn: true,
-    });
-
-    expect(wrapper.instance().renderActions()).toBeNull();
-  });
-
-  it('should be null when I am not logged in', () => {
-    const wrapper = shallowRender({
-      isLoggedIn: false,
-      selectedProfile: mockQualityProfile(),
-    });
-
-    expect(wrapper.instance().renderActions()).toBeNull();
-  });
-
-  it('should be null when the user does not have the sufficient permissions', () => {
-    const wrapper = shallowRender({
-      isLoggedIn: true,
-      selectedProfile: mockQualityProfile(),
-    });
-
-    expect(wrapper.instance().renderActions()).toBeNull();
-  });
-
-  it('should disable the button when I am on a built-in profile', () => {
-    const wrapper = shallowRender({
-      selectedProfile: mockQualityProfile({
-        actions: {
-          copy: true,
-        },
-        isBuiltIn: true,
-      }),
-    });
-
-    expect(wrapper.instance().renderActions()).toMatchSnapshot('activate');
-    wrapper.setProps({ activation: { inherit: 'INHERITED', severity: 'CRITICAL' } });
-    expect(wrapper.instance().renderActions()).toMatchSnapshot('deactivate');
-  });
-
-  it('should render the deactivate button', () => {
-    const wrapper = shallowRender({
-      activation: {
-        inherit: 'NONE',
-        severity: 'warning',
-      },
-      selectedProfile: mockQualityProfile({
-        actions: {
-          edit: true,
-        },
-        isBuiltIn: false,
-      }),
-    });
-
-    expect(wrapper.instance().renderActions()).toMatchSnapshot();
-  });
-
-  it('should render the activate button', () => {
-    const wrapper = shallowRender({
-      rule: mockRule({
-        isTemplate: false,
-      }),
-      selectedProfile: mockQualityProfile({
-        actions: {
-          edit: true,
-        },
-        isBuiltIn: false,
-      }),
-    });
-
-    expect(wrapper.instance().renderActions()).toMatchSnapshot();
-  });
-});
-
-function shallowRender(props?: Partial<RuleListItem['props']>) {
-  return shallow<RuleListItem>(
-    <RuleListItem
-      isLoggedIn
-      onActivate={jest.fn()}
-      onDeactivate={jest.fn()}
-      onFilterChange={jest.fn()}
-      onOpen={jest.fn()}
-      rule={mockRule({ key: 'javascript:S1067' })}
-      selected={false}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/SimilarRulesFilter-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/SimilarRulesFilter-test.tsx
deleted file mode 100644 (file)
index 4162980..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { mount, shallow } from 'enzyme';
-import * as React from 'react';
-import { mockRule } from '../../../../helpers/testMocks';
-import { click } from '../../../../helpers/testUtils';
-import SimilarRulesFilter from '../SimilarRulesFilter';
-
-it('should render correctly', () => {
-  expect(
-    shallow(<SimilarRulesFilter onFilterChange={jest.fn()} rule={mockRule()} />)
-  ).toMatchSnapshot();
-});
-
-it('should filter by similar language', () => {
-  const onFilterChange = jest.fn();
-  const wrapper = mountRenderAction('language', { onFilterChange });
-  click(wrapper);
-  expect(onFilterChange).toHaveBeenCalledWith({ languages: ['js'] });
-});
-
-it('should filter by similar type', () => {
-  const onFilterChange = jest.fn();
-  const wrapper = mountRenderAction('type', { onFilterChange });
-  click(wrapper);
-  expect(onFilterChange).toHaveBeenCalledWith({ types: ['CODE_SMELL'] });
-});
-
-it('should filter by similar severity', () => {
-  const onFilterChange = jest.fn();
-  const wrapper = mountRenderAction('severity', { onFilterChange });
-  click(wrapper);
-  expect(onFilterChange).toHaveBeenCalledWith({ severities: ['MAJOR'] });
-});
-
-it('should filter by similar tag', () => {
-  const onFilterChange = jest.fn();
-  const wrapper = mountRenderAction('tag', { onFilterChange });
-  click(wrapper);
-  expect(onFilterChange).toHaveBeenCalledWith({ tags: ['x'] });
-});
-
-function mountRenderAction(actionName: string, props: Partial<SimilarRulesFilter['props']> = {}) {
-  const wrapper = mount(
-    <SimilarRulesFilter onFilterChange={jest.fn()} rule={mockRule()} {...props} />
-  );
-  return mount(wrapper.find('Dropdown').prop<React.ReactElement>('overlay'))
-    .find(`button[data-test="coding-rules__similar-${actionName}"]`)
-    .first();
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/TagFacet-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/TagFacet-test.tsx
deleted file mode 100644 (file)
index eb91c0c..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { getRuleTags } from '../../../../api/rules';
-import TagFacet from '../TagFacet';
-
-jest.mock('../../../../api/rules', () => ({
-  getRuleTags: jest.fn().mockResolvedValue([]),
-}));
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle search', async () => {
-  const wrapper = shallowRender();
-
-  const query = 'query';
-
-  await wrapper.instance().handleSearch(query);
-
-  expect(getRuleTags).toHaveBeenCalledWith({ ps: 50, q: query });
-});
-
-describe('ListStyleFacet Renderers', () => {
-  const instance = shallowRender().instance();
-
-  it('should include renderFacetItem', () => {
-    expect(instance.renderTag('tag')).toMatchSnapshot();
-  });
-
-  it('should include renderSearchResult', () => {
-    expect(instance.renderSearchResult('my-tag', 'tag')).toMatchSnapshot();
-  });
-});
-
-function shallowRender(props: Partial<TagFacet['props']> = {}) {
-  return shallow<TagFacet>(
-    <TagFacet
-      onChange={jest.fn()}
-      onToggle={jest.fn()}
-      open={false}
-      stats={{}}
-      values={[]}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/ActivationFormModal-test.tsx.snap
deleted file mode 100644 (file)
index 44237c4..0000000
+++ /dev/null
@@ -1,603 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: custom rule 1`] = `
-<Modal
-  contentLabel="title"
-  onRequestClose={[MockFunction]}
-  size="small"
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        title
-      </h2>
-    </div>
-    <div
-      className="modal-body"
-    >
-      <Alert
-        variant="info"
-      >
-        coding_rules.active_in_all_profiles
-      </Alert>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-quality-profile-select-label"
-        >
-          coding_rules.quality_profile
-        </label>
-        <Select
-          aria-labelledby="coding-rules-quality-profile-select-label"
-          getOptionLabel={[Function]}
-          id="coding-rules-quality-profile-select"
-          isClearable={false}
-          isDisabled={false}
-          onChange={[Function]}
-          options={[]}
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-severity-select-label"
-        >
-          severity
-        </label>
-        <SeveritySelect
-          ariaLabelledby="coding-rules-severity-select-label"
-          isDisabled={false}
-          onChange={[Function]}
-          severity="MAJOR"
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <p
-          className="note"
-        >
-          coding_rules.custom_rule.activation_notice
-        </p>
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={true}
-      >
-        coding_rules.activate
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
-
-exports[`should render correctly: default 1`] = `
-<Modal
-  contentLabel="title"
-  onRequestClose={[MockFunction]}
-  size="small"
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        title
-      </h2>
-    </div>
-    <div
-      className="modal-body modal-container"
-    >
-      <Alert
-        variant="info"
-      >
-        coding_rules.active_in_all_profiles
-      </Alert>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-quality-profile-select-label"
-        >
-          coding_rules.quality_profile
-        </label>
-        <Select
-          aria-labelledby="coding-rules-quality-profile-select-label"
-          getOptionLabel={[Function]}
-          id="coding-rules-quality-profile-select"
-          isClearable={false}
-          isDisabled={false}
-          onChange={[Function]}
-          options={[]}
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-severity-select-label"
-        >
-          severity
-        </label>
-        <SeveritySelect
-          ariaLabelledby="coding-rules-severity-select-label"
-          isDisabled={false}
-          onChange={[Function]}
-          severity="MAJOR"
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="1"
-      >
-        <label
-          title="1"
-        >
-          1
-        </label>
-        <input
-          disabled={false}
-          name="1"
-          onChange={[Function]}
-          placeholder="1"
-          type="text"
-          value="1"
-        />
-        <div
-          className="note"
-          dangerouslySetInnerHTML={
-            {
-              "__html": "description",
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="2"
-      >
-        <label
-          title="2"
-        >
-          2
-        </label>
-        <textarea
-          disabled={false}
-          name="2"
-          onChange={[Function]}
-          placeholder="1"
-          rows={3}
-          value="1"
-        />
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={true}
-      >
-        coding_rules.activate
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
-
-exports[`should render correctly: submitting 1`] = `
-<Modal
-  contentLabel="title"
-  onRequestClose={[MockFunction]}
-  size="small"
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        title
-      </h2>
-    </div>
-    <div
-      className="modal-body modal-container"
-    >
-      <Alert
-        variant="info"
-      >
-        coding_rules.active_in_all_profiles
-      </Alert>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-quality-profile-select-label"
-        >
-          coding_rules.quality_profile
-        </label>
-        <Select
-          aria-labelledby="coding-rules-quality-profile-select-label"
-          getOptionLabel={[Function]}
-          id="coding-rules-quality-profile-select"
-          isClearable={false}
-          isDisabled={true}
-          onChange={[Function]}
-          options={[]}
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-severity-select-label"
-        >
-          severity
-        </label>
-        <SeveritySelect
-          ariaLabelledby="coding-rules-severity-select-label"
-          isDisabled={true}
-          onChange={[Function]}
-          severity="MAJOR"
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="1"
-      >
-        <label
-          title="1"
-        >
-          1
-        </label>
-        <input
-          disabled={true}
-          name="1"
-          onChange={[Function]}
-          placeholder="1"
-          type="text"
-          value="1"
-        />
-        <div
-          className="note"
-          dangerouslySetInnerHTML={
-            {
-              "__html": "description",
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="2"
-      >
-        <label
-          title="2"
-        >
-          2
-        </label>
-        <textarea
-          disabled={true}
-          name="2"
-          onChange={[Function]}
-          placeholder="1"
-          rows={3}
-          value="1"
-        />
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <i
-        className="spinner spacer-right"
-      />
-      <SubmitButton
-        disabled={true}
-      >
-        coding_rules.activate
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={true}
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
-
-exports[`should render correctly: update mode 1`] = `
-<Modal
-  contentLabel="title"
-  onRequestClose={[MockFunction]}
-  size="small"
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        title
-      </h2>
-    </div>
-    <div
-      className="modal-body modal-container"
-    >
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-quality-profile-select-label"
-        >
-          coding_rules.quality_profile
-        </label>
-        <Select
-          aria-labelledby="coding-rules-quality-profile-select-label"
-          getOptionLabel={[Function]}
-          id="coding-rules-quality-profile-select"
-          isClearable={false}
-          isDisabled={false}
-          onChange={[Function]}
-          options={[]}
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-severity-select-label"
-        >
-          severity
-        </label>
-        <SeveritySelect
-          ariaLabelledby="coding-rules-severity-select-label"
-          isDisabled={false}
-          onChange={[Function]}
-          severity="MAJOR"
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="1"
-      >
-        <label
-          title="1"
-        >
-          1
-        </label>
-        <input
-          disabled={false}
-          name="1"
-          onChange={[Function]}
-          placeholder="1"
-          type="text"
-          value="1"
-        />
-        <div
-          className="note"
-          dangerouslySetInnerHTML={
-            {
-              "__html": "description",
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="2"
-      >
-        <label
-          title="2"
-        >
-          2
-        </label>
-        <textarea
-          disabled={false}
-          name="2"
-          onChange={[Function]}
-          placeholder="1"
-          rows={3}
-          value="1"
-        />
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={true}
-      >
-        save
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
-
-exports[`should render correctly: with deep profiles 1`] = `
-<Modal
-  contentLabel="title"
-  onRequestClose={[MockFunction]}
-  size="small"
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        title
-      </h2>
-    </div>
-    <div
-      className="modal-body modal-container"
-    >
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-quality-profile-select-label"
-        >
-          coding_rules.quality_profile
-        </label>
-        <Select
-          aria-labelledby="coding-rules-quality-profile-select-label"
-          getOptionLabel={[Function]}
-          id="coding-rules-quality-profile-select"
-          isClearable={false}
-          isDisabled={true}
-          onChange={[Function]}
-          options={
-            [
-              {
-                "actions": {
-                  "edit": true,
-                },
-                "activeDeprecatedRuleCount": 2,
-                "activeRuleCount": 10,
-                "childrenCount": 0,
-                "depth": 0,
-                "isBuiltIn": false,
-                "isDefault": false,
-                "isInherited": false,
-                "key": "key",
-                "language": "js",
-                "languageName": "JavaScript",
-                "name": "name",
-                "projectCount": 3,
-              },
-            ]
-          }
-          value={
-            {
-              "actions": {
-                "edit": true,
-              },
-              "activeDeprecatedRuleCount": 2,
-              "activeRuleCount": 10,
-              "childrenCount": 0,
-              "depth": 0,
-              "isBuiltIn": false,
-              "isDefault": false,
-              "isInherited": false,
-              "key": "key",
-              "language": "js",
-              "languageName": "JavaScript",
-              "name": "name",
-              "projectCount": 3,
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          id="coding-rules-severity-select-label"
-        >
-          severity
-        </label>
-        <SeveritySelect
-          ariaLabelledby="coding-rules-severity-select-label"
-          isDisabled={false}
-          onChange={[Function]}
-          severity="MAJOR"
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="1"
-      >
-        <label
-          title="1"
-        >
-          1
-        </label>
-        <input
-          disabled={false}
-          name="1"
-          onChange={[Function]}
-          placeholder="1"
-          type="text"
-          value="1"
-        />
-        <div
-          className="note"
-          dangerouslySetInnerHTML={
-            {
-              "__html": "description",
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="2"
-      >
-        <label
-          title="2"
-        >
-          2
-        </label>
-        <textarea
-          disabled={false}
-          name="2"
-          onChange={[Function]}
-          placeholder="1"
-          rows={3}
-          value="1"
-        />
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={false}
-      >
-        coding_rules.activate
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </footer>
-  </form>
-</Modal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap
deleted file mode 100644 (file)
index 3ed9569..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should not a disabled button when edition is not possible 1`] = `
-<Tooltip
-  overlay="coding_rules.can_not_bulk_change"
->
-  <Button
-    className="js-bulk-change"
-    disabled={true}
-  >
-    bulk_change
-  </Button>
-</Tooltip>
-`;
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <Dropdown
-    overlay={
-      <ul
-        className="menu"
-      >
-        <li>
-          <a
-            href="#"
-            onClick={[Function]}
-          >
-            coding_rules.activate_in
-            …
-          </a>
-        </li>
-        <li>
-          <a
-            href="#"
-            onClick={[Function]}
-          >
-            coding_rules.activate_in
-             
-            <strong>
-              name
-            </strong>
-          </a>
-        </li>
-        <li>
-          <a
-            href="#"
-            onClick={[Function]}
-          >
-            coding_rules.deactivate_in
-            …
-          </a>
-        </li>
-      </ul>
-    }
-    overlayPlacement="bottom-left"
-  >
-    <Button
-      className="js-bulk-change"
-    >
-      bulk_change
-    </Button>
-  </Dropdown>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CodingRulesApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CodingRulesApp-test.tsx.snap
deleted file mode 100644 (file)
index 4521ea5..0000000
+++ /dev/null
@@ -1,395 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renderBulkButton should be null when the user does not have the sufficient permission 1`] = `<div />`;
-
-exports[`renderBulkButton should be null when the user is not logged in 1`] = `<div />`;
-
-exports[`renderBulkButton should show bulk change button when user has edit rights on specific quality profile 1`] = `
-<BulkChange
-  query={
-    {
-      "activation": undefined,
-      "activationSeverities": [],
-      "availableSince": undefined,
-      "compareToProfile": undefined,
-      "cwe": [],
-      "inheritance": undefined,
-      "languages": [],
-      "owaspTop10": [],
-      "owaspTop10-2021": [],
-      "profile": undefined,
-      "repositories": [],
-      "ruleKey": undefined,
-      "searchQuery": undefined,
-      "severities": [],
-      "sonarsourceSecurity": [],
-      "statuses": [],
-      "tags": [],
-      "template": undefined,
-      "types": [],
-    }
-  }
-  referencedProfiles={
-    {
-      "foo": {
-        "actions": {
-          "edit": true,
-        },
-        "activeDeprecatedRuleCount": 2,
-        "activeRuleCount": 10,
-        "childrenCount": 0,
-        "depth": 1,
-        "isBuiltIn": false,
-        "isDefault": false,
-        "isInherited": false,
-        "key": "foo",
-        "language": "js",
-        "languageName": "JavaScript",
-        "name": "name",
-        "projectCount": 3,
-      },
-      "key": {
-        "activeDeprecatedRuleCount": 2,
-        "activeRuleCount": 10,
-        "childrenCount": 0,
-        "depth": 1,
-        "isBuiltIn": false,
-        "isDefault": false,
-        "isInherited": false,
-        "key": "key",
-        "language": "js",
-        "languageName": "JavaScript",
-        "name": "name",
-        "projectCount": 3,
-      },
-    }
-  }
-  total={0}
-/>
-`;
-
-exports[`renderBulkButton should show bulk change button when user has global admin rights on quality profiles 1`] = `
-<BulkChange
-  query={
-    {
-      "activation": undefined,
-      "activationSeverities": [],
-      "availableSince": undefined,
-      "compareToProfile": undefined,
-      "cwe": [],
-      "inheritance": undefined,
-      "languages": [],
-      "owaspTop10": [],
-      "owaspTop10-2021": [],
-      "profile": undefined,
-      "repositories": [],
-      "ruleKey": undefined,
-      "searchQuery": undefined,
-      "severities": [],
-      "sonarsourceSecurity": [],
-      "statuses": [],
-      "tags": [],
-      "template": undefined,
-      "types": [],
-    }
-  }
-  referencedProfiles={{}}
-  total={0}
-/>
-`;
-
-exports[`should render correctly: loaded (ScreenPositionHelper) 1`] = `
-<nav
-  aria-label="filters"
-  className="layout-page-side"
-  style={
-    {
-      "top": 0,
-    }
-  }
->
-  <div
-    className="layout-page-side-inner"
-  >
-    <div
-      className="layout-page-filters"
-    >
-      <A11ySkipTarget
-        anchor="rules_filters"
-        label="coding_rules.skip_to_filters"
-        weight={10}
-      />
-      <FiltersHeader
-        displayReset={false}
-        onReset={[Function]}
-      />
-      <SearchBox
-        className="spacer-bottom"
-        id="coding-rules-search"
-        maxLength={200}
-        minLength={2}
-        onChange={[Function]}
-        placeholder="search.search_for_rules"
-        value=""
-      />
-      <FacetsList
-        facets={{}}
-        onFacetToggle={[Function]}
-        onFilterChange={[Function]}
-        openFacets={
-          {
-            "languages": true,
-            "owaspTop10": false,
-            "owaspTop10-2021": false,
-            "sonarsourceSecurity": false,
-            "standards": false,
-            "types": true,
-          }
-        }
-        query={
-          {
-            "activation": undefined,
-            "activationSeverities": [],
-            "availableSince": undefined,
-            "compareToProfile": undefined,
-            "cwe": [],
-            "inheritance": undefined,
-            "languages": [],
-            "owaspTop10": [],
-            "owaspTop10-2021": [],
-            "profile": undefined,
-            "repositories": [],
-            "ruleKey": undefined,
-            "searchQuery": undefined,
-            "severities": [],
-            "sonarsourceSecurity": [],
-            "statuses": [],
-            "tags": [],
-            "template": undefined,
-            "types": [],
-          }
-        }
-        referencedProfiles={{}}
-        referencedRepositories={{}}
-      />
-    </div>
-  </div>
-</nav>
-`;
-
-exports[`should render correctly: loaded 1`] = `
-<Fragment>
-  <Suggestions
-    suggestions="coding_rules"
-  />
-  <Helmet
-    defer={false}
-    encodeSpecialCharacters={true}
-    prioritizeSeoTags={false}
-    title="coding_rules.page"
-  >
-    <meta
-      content="noindex"
-      name="robots"
-    />
-  </Helmet>
-  <div
-    className="layout-page"
-    id="coding-rules-page"
-  >
-    <ScreenPositionHelper
-      className="layout-page-side-outer"
-    >
-      <Component />
-    </ScreenPositionHelper>
-    <main
-      className="layout-page-main"
-    >
-      <div
-        className="layout-page-header-panel layout-page-main-header"
-      >
-        <div
-          className="layout-page-header-panel-inner layout-page-main-header-inner"
-        >
-          <div
-            className="layout-page-main-inner"
-          >
-            <A11ySkipTarget
-              anchor="rules_main"
-            />
-            <div
-              className="display-flex-space-between"
-            >
-              <BulkChange
-                query={
-                  {
-                    "activation": undefined,
-                    "activationSeverities": [],
-                    "availableSince": undefined,
-                    "compareToProfile": undefined,
-                    "cwe": [],
-                    "inheritance": undefined,
-                    "languages": [],
-                    "owaspTop10": [],
-                    "owaspTop10-2021": [],
-                    "profile": undefined,
-                    "repositories": [],
-                    "ruleKey": undefined,
-                    "searchQuery": undefined,
-                    "severities": [],
-                    "sonarsourceSecurity": [],
-                    "statuses": [],
-                    "tags": [],
-                    "template": undefined,
-                    "types": [],
-                  }
-                }
-                referencedProfiles={{}}
-                total={0}
-              />
-              <PageActions
-                paging={
-                  {
-                    "pageIndex": 1,
-                    "pageSize": 100,
-                    "total": 0,
-                  }
-                }
-              />
-            </div>
-          </div>
-        </div>
-      </div>
-      <div
-        className="layout-page-main-inner"
-      >
-        <ul
-          aria-label="list_of_rules"
-        >
-          <RuleListItem
-            isLoggedIn={true}
-            key="javascript:S1067"
-            onActivate={[Function]}
-            onDeactivate={[Function]}
-            onFilterChange={[Function]}
-            onOpen={[Function]}
-            rule={
-              {
-                "key": "javascript:S1067",
-                "lang": "js",
-                "langName": "JavaScript",
-                "name": "Use foo",
-                "severity": "MAJOR",
-                "status": "READY",
-                "sysTags": [
-                  "a",
-                  "b",
-                ],
-                "tags": [
-                  "x",
-                ],
-                "type": "CODE_SMELL",
-              }
-            }
-            selected={false}
-          />
-          <RuleListItem
-            isLoggedIn={true}
-            key="javascript:S1067"
-            onActivate={[Function]}
-            onDeactivate={[Function]}
-            onFilterChange={[Function]}
-            onOpen={[Function]}
-            rule={
-              {
-                "key": "javascript:S1067",
-                "lang": "js",
-                "langName": "JavaScript",
-                "name": "Use foo",
-                "severity": "MAJOR",
-                "status": "READY",
-                "sysTags": [
-                  "a",
-                  "b",
-                ],
-                "tags": [
-                  "x",
-                ],
-                "type": "CODE_SMELL",
-              }
-            }
-            selected={false}
-          />
-        </ul>
-        <ListFooter
-          count={2}
-          loadMore={[Function]}
-          ready={true}
-          total={0}
-        />
-      </div>
-    </main>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly: loading 1`] = `
-<Fragment>
-  <Suggestions
-    suggestions="coding_rules"
-  />
-  <Helmet
-    defer={false}
-    encodeSpecialCharacters={true}
-    prioritizeSeoTags={false}
-    title="coding_rules.page"
-  >
-    <meta
-      content="noindex"
-      name="robots"
-    />
-  </Helmet>
-  <div
-    className="layout-page"
-    id="coding-rules-page"
-  >
-    <ScreenPositionHelper
-      className="layout-page-side-outer"
-    >
-      <Component />
-    </ScreenPositionHelper>
-    <main
-      className="layout-page-main"
-    >
-      <div
-        className="layout-page-header-panel layout-page-main-header"
-      >
-        <div
-          className="layout-page-header-panel-inner layout-page-main-header-inner"
-        >
-          <div
-            className="layout-page-main-inner"
-          >
-            <A11ySkipTarget
-              anchor="rules_main"
-            />
-            <div
-              className="display-flex-space-between"
-            >
-              <div />
-              <PageActions />
-            </div>
-          </div>
-        </div>
-      </div>
-      <div
-        className="layout-page-main-inner"
-      >
-        <ul
-          aria-label="list_of_rules"
-        />
-      </div>
-    </main>
-  </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/CustomRuleFormModal-test.tsx.snap
deleted file mode 100644 (file)
index 77ad50e..0000000
+++ /dev/null
@@ -1,516 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should handle re-activation 1`] = `
-<Modal
-  contentLabel="coding_rules.create_custom_rule"
-  onRequestClose={[MockFunction]}
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        coding_rules.create_custom_rule
-      </h2>
-    </div>
-    <div
-      className="modal-body modal-container"
-    >
-      <Alert
-        variant="warning"
-      >
-        coding_rules.reactivate.help
-      </Alert>
-      <MandatoryFieldsExplanation
-        className="spacer-bottom"
-      />
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="coding-rules-custom-rule-creation-name"
-        >
-          name
-           
-          <MandatoryFieldMarker />
-        </label>
-        <input
-          autoFocus={true}
-          disabled={false}
-          id="coding-rules-custom-rule-creation-name"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="coding-rules-custom-rule-creation-key"
-        >
-          key
-           
-          <MandatoryFieldMarker />
-        </label>
-        <input
-          disabled={false}
-          id="coding-rules-custom-rule-creation-key"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="display-flex-space-between"
-      >
-        <div
-          className="modal-field flex-1 spacer-right"
-        >
-          <label
-            id="coding-rules-custom-rule-type"
-          >
-            type
-          </label>
-          <Select
-            aria-labelledby="coding-rules-custom-rule-type"
-            components={
-              {
-                "Option": [Function],
-                "SingleValue": [Function],
-              }
-            }
-            isClearable={false}
-            isDisabled={false}
-            isSearchable={false}
-            onChange={[Function]}
-            options={
-              [
-                {
-                  "label": "issue.type.BUG",
-                  "value": "BUG",
-                },
-                {
-                  "label": "issue.type.VULNERABILITY",
-                  "value": "VULNERABILITY",
-                },
-                {
-                  "label": "issue.type.CODE_SMELL",
-                  "value": "CODE_SMELL",
-                },
-                {
-                  "label": "issue.type.SECURITY_HOTSPOT",
-                  "value": "SECURITY_HOTSPOT",
-                },
-              ]
-            }
-            value={
-              {
-                "label": "issue.type.BUG",
-                "value": "BUG",
-              }
-            }
-          />
-        </div>
-        <div
-          className="modal-field flex-1 spacer-right"
-        >
-          <label
-            id="coding-rules-custom-rule-severity"
-          >
-            severity
-          </label>
-          <SeveritySelect
-            ariaLabelledby="coding-rules-custom-rule-severity"
-            isDisabled={false}
-            onChange={[Function]}
-            severity="MAJOR"
-          />
-        </div>
-        <div
-          className="modal-field flex-1"
-        >
-          <label
-            id="coding-rules-custom-rule-status"
-          >
-            coding_rules.filters.status
-          </label>
-          <Select
-            aria-labelledby="coding-rules-custom-rule-status"
-            isClearable={false}
-            isDisabled={false}
-            isSearchable={false}
-            onChange={[Function]}
-            options={
-              [
-                {
-                  "label": "rules.status.READY",
-                  "value": "READY",
-                },
-                {
-                  "label": "rules.status.BETA",
-                  "value": "BETA",
-                },
-                {
-                  "label": "rules.status.DEPRECATED",
-                  "value": "DEPRECATED",
-                },
-              ]
-            }
-            value={
-              {
-                "label": "rules.status.READY",
-                "value": "READY",
-              }
-            }
-          />
-        </div>
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="coding-rules-custom-rule-creation-html-description"
-        >
-          description
-          <MandatoryFieldMarker />
-        </label>
-        <textarea
-          disabled={false}
-          id="coding-rules-custom-rule-creation-html-description"
-          onChange={[Function]}
-          required={true}
-          rows={5}
-          value=""
-        />
-        <FormattingTips
-          className="modal-field-descriptor text-right"
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="1"
-      >
-        <label
-          className="capitalize"
-          htmlFor="1"
-        >
-          1
-        </label>
-        <input
-          disabled={false}
-          id="1"
-          name="1"
-          onChange={[Function]}
-          placeholder="1"
-          type="text"
-          value=""
-        />
-        <div
-          className="modal-field-description"
-          dangerouslySetInnerHTML={
-            {
-              "__html": "description",
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="2"
-      >
-        <label
-          className="capitalize"
-          htmlFor="2"
-        >
-          2
-        </label>
-        <textarea
-          disabled={false}
-          id="2"
-          name="2"
-          onChange={[Function]}
-          placeholder="1"
-          rows={3}
-          value=""
-        />
-      </div>
-    </div>
-    <div
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={false}
-      >
-        coding_rules.reactivate
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={false}
-        id="coding-rules-custom-rule-creation-cancel"
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </div>
-  </form>
-</Modal>
-`;
-
-exports[`should render correctly: default 1`] = `
-<Modal
-  contentLabel="coding_rules.create_custom_rule"
-  onRequestClose={[MockFunction]}
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-head"
-    >
-      <h2>
-        coding_rules.create_custom_rule
-      </h2>
-    </div>
-    <div
-      className="modal-body modal-container"
-    >
-      <MandatoryFieldsExplanation
-        className="spacer-bottom"
-      />
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="coding-rules-custom-rule-creation-name"
-        >
-          name
-           
-          <MandatoryFieldMarker />
-        </label>
-        <input
-          autoFocus={true}
-          disabled={false}
-          id="coding-rules-custom-rule-creation-name"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="coding-rules-custom-rule-creation-key"
-        >
-          key
-           
-          <MandatoryFieldMarker />
-        </label>
-        <input
-          disabled={false}
-          id="coding-rules-custom-rule-creation-key"
-          onChange={[Function]}
-          required={true}
-          type="text"
-          value=""
-        />
-      </div>
-      <div
-        className="display-flex-space-between"
-      >
-        <div
-          className="modal-field flex-1 spacer-right"
-        >
-          <label
-            id="coding-rules-custom-rule-type"
-          >
-            type
-          </label>
-          <Select
-            aria-labelledby="coding-rules-custom-rule-type"
-            components={
-              {
-                "Option": [Function],
-                "SingleValue": [Function],
-              }
-            }
-            isClearable={false}
-            isDisabled={false}
-            isSearchable={false}
-            onChange={[Function]}
-            options={
-              [
-                {
-                  "label": "issue.type.BUG",
-                  "value": "BUG",
-                },
-                {
-                  "label": "issue.type.VULNERABILITY",
-                  "value": "VULNERABILITY",
-                },
-                {
-                  "label": "issue.type.CODE_SMELL",
-                  "value": "CODE_SMELL",
-                },
-                {
-                  "label": "issue.type.SECURITY_HOTSPOT",
-                  "value": "SECURITY_HOTSPOT",
-                },
-              ]
-            }
-            value={
-              {
-                "label": "issue.type.BUG",
-                "value": "BUG",
-              }
-            }
-          />
-        </div>
-        <div
-          className="modal-field flex-1 spacer-right"
-        >
-          <label
-            id="coding-rules-custom-rule-severity"
-          >
-            severity
-          </label>
-          <SeveritySelect
-            ariaLabelledby="coding-rules-custom-rule-severity"
-            isDisabled={false}
-            onChange={[Function]}
-            severity="MAJOR"
-          />
-        </div>
-        <div
-          className="modal-field flex-1"
-        >
-          <label
-            id="coding-rules-custom-rule-status"
-          >
-            coding_rules.filters.status
-          </label>
-          <Select
-            aria-labelledby="coding-rules-custom-rule-status"
-            isClearable={false}
-            isDisabled={false}
-            isSearchable={false}
-            onChange={[Function]}
-            options={
-              [
-                {
-                  "label": "rules.status.READY",
-                  "value": "READY",
-                },
-                {
-                  "label": "rules.status.BETA",
-                  "value": "BETA",
-                },
-                {
-                  "label": "rules.status.DEPRECATED",
-                  "value": "DEPRECATED",
-                },
-              ]
-            }
-            value={
-              {
-                "label": "rules.status.READY",
-                "value": "READY",
-              }
-            }
-          />
-        </div>
-      </div>
-      <div
-        className="modal-field"
-      >
-        <label
-          htmlFor="coding-rules-custom-rule-creation-html-description"
-        >
-          description
-          <MandatoryFieldMarker />
-        </label>
-        <textarea
-          disabled={false}
-          id="coding-rules-custom-rule-creation-html-description"
-          onChange={[Function]}
-          required={true}
-          rows={5}
-          value=""
-        />
-        <FormattingTips
-          className="modal-field-descriptor text-right"
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="1"
-      >
-        <label
-          className="capitalize"
-          htmlFor="1"
-        >
-          1
-        </label>
-        <input
-          disabled={false}
-          id="1"
-          name="1"
-          onChange={[Function]}
-          placeholder="1"
-          type="text"
-          value=""
-        />
-        <div
-          className="modal-field-description"
-          dangerouslySetInnerHTML={
-            {
-              "__html": "description",
-            }
-          }
-        />
-      </div>
-      <div
-        className="modal-field"
-        key="2"
-      >
-        <label
-          className="capitalize"
-          htmlFor="2"
-        >
-          2
-        </label>
-        <textarea
-          disabled={false}
-          id="2"
-          name="2"
-          onChange={[Function]}
-          placeholder="1"
-          rows={3}
-          value=""
-        />
-      </div>
-    </div>
-    <div
-      className="modal-foot"
-    >
-      <SubmitButton
-        disabled={false}
-      >
-        create
-      </SubmitButton>
-      <ResetButtonLink
-        disabled={false}
-        id="coding-rules-custom-rule-creation-cancel"
-        onClick={[MockFunction]}
-      >
-        cancel
-      </ResetButtonLink>
-    </div>
-  </form>
-</Modal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/FacetsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/FacetsList-test.tsx.snap
deleted file mode 100644 (file)
index ec82fc3..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <withLanguagesContext(LanguageFacet)
-    disabled={false}
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <TypeFacet
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <TagFacet
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <withLanguagesContext(RepositoryFacet)
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-    referencedRepositories={{}}
-  />
-  <DefaultSeverityFacet
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <StatusFacet
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <StandardFacet
-    cweOpen={false}
-    fetchingCwe={false}
-    fetchingOwaspTop10={false}
-    fetchingOwaspTop10-2021={false}
-    fetchingSonarSourceSecurity={false}
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-    owaspTop10-2021Open={false}
-    owaspTop10Open={false}
-    query={{}}
-    sonarsourceSecurityOpen={false}
-  />
-  <injectIntl(AvailableSinceFacet)
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <TemplateFacet
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <ProfileFacet
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-    referencedProfiles={{}}
-  />
-  <InheritanceFacet
-    disabled={true}
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-  <ActivationSeverityFacet
-    disabled={true}
-    onChange={[MockFunction]}
-    onToggle={[MockFunction]}
-    open={false}
-  />
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/PageActions-test.tsx.snap
deleted file mode 100644 (file)
index c8c2a74..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<div
-  className="display-flex-center"
->
-  <PageShortcutsTooltip
-    className="big-spacer-right"
-    leftAndRightLabel="issues.to_navigate"
-    upAndDownLabel="coding_rules.to_select_rules"
-  />
-</div>
-`;
-
-exports[`should render correctly: with paging 1`] = `
-<div
-  className="display-flex-center"
->
-  <PageShortcutsTooltip
-    className="big-spacer-right"
-    leftAndRightLabel="issues.to_navigate"
-    upAndDownLabel="coding_rules.to_select_rules"
-  />
-  <PageCounter
-    className="spacer-left"
-    label="coding_rules._rules"
-    total={100}
-  />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RepositoryFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RepositoryFacet-test.tsx.snap
deleted file mode 100644 (file)
index d0be822..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render repository correctly 1`] = `
-<div>
-  SonarQube
-  <span
-    className="note little-spacer-left"
-  >
-    css
-  </span>
-</div>
-`;
-
-exports[`should render search repository correctly 1`] = `
-<div>
-  <mark>
-    Son
-  </mark>
-  arQube
-  <span
-    className="note little-spacer-left"
-  >
-    css
-  </span>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
deleted file mode 100644 (file)
index 7b2ac32..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: loaded 1`] = `
-<div
-  className="coding-rule-details"
->
-  <DeferredSpinner
-    loading={false}
-  >
-    <RuleDetailsMeta
-      onFilterChange={[MockFunction]}
-      onTagsChange={[Function]}
-      referencedRepositories={
-        {
-          "javascript": {
-            "key": "javascript",
-            "language": "js",
-            "name": "SonarAnalyzer",
-          },
-        }
-      }
-      ruleDetails={
-        {
-          "createdAt": "2014-12-16T17:26:54+0100",
-          "defaultRemFnBaseEffort": "5min",
-          "defaultRemFnType": "CONSTANT_ISSUE",
-          "descriptionSections": [
-            {
-              "content": "<b>Why</b> Because",
-              "key": "default",
-            },
-          ],
-          "htmlDesc": "",
-          "isExternal": false,
-          "isTemplate": false,
-          "key": "squid:S1337",
-          "lang": "java",
-          "langName": "Java",
-          "mdDesc": "",
-          "name": "".equals()" should not be used to test the values of "Atomic" classes",
-          "params": [],
-          "remFnBaseEffort": "5min",
-          "remFnOverloaded": false,
-          "remFnType": "CONSTANT_ISSUE",
-          "repo": "squid",
-          "scope": "MAIN",
-          "severity": "MAJOR",
-          "status": "READY",
-          "sysTags": [
-            "multi-threading",
-          ],
-          "tags": [],
-          "type": "BUG",
-        }
-      }
-    />
-    <RuleDetailsDescription
-      onChange={[Function]}
-      ruleDetails={
-        {
-          "createdAt": "2014-12-16T17:26:54+0100",
-          "defaultRemFnBaseEffort": "5min",
-          "defaultRemFnType": "CONSTANT_ISSUE",
-          "descriptionSections": [
-            {
-              "content": "<b>Why</b> Because",
-              "key": "default",
-            },
-          ],
-          "htmlDesc": "",
-          "isExternal": false,
-          "isTemplate": false,
-          "key": "squid:S1337",
-          "lang": "java",
-          "langName": "Java",
-          "mdDesc": "",
-          "name": "".equals()" should not be used to test the values of "Atomic" classes",
-          "params": [],
-          "remFnBaseEffort": "5min",
-          "remFnOverloaded": false,
-          "remFnType": "CONSTANT_ISSUE",
-          "repo": "squid",
-          "scope": "MAIN",
-          "severity": "MAJOR",
-          "status": "READY",
-          "sysTags": [
-            "multi-threading",
-          ],
-          "tags": [],
-          "type": "BUG",
-        }
-      }
-    />
-    <RuleDetailsProfiles
-      activations={
-        [
-          {
-            "createdAt": "2017-06-16T16:13:38+0200",
-            "inherit": "NONE",
-            "params": [],
-            "qProfile": "foo",
-            "severity": "MAJOR",
-            "updatedAt": "2017-06-16T16:13:38+0200",
-          },
-        ]
-      }
-      onActivate={[Function]}
-      onDeactivate={[Function]}
-      referencedProfiles={
-        {
-          "key": {
-            "activeDeprecatedRuleCount": 2,
-            "activeRuleCount": 10,
-            "childrenCount": 0,
-            "depth": 1,
-            "isBuiltIn": false,
-            "isDefault": false,
-            "isInherited": false,
-            "key": "foo",
-            "language": "js",
-            "languageName": "JavaScript",
-            "name": "name",
-            "projectCount": 3,
-          },
-        }
-      }
-      ruleDetails={
-        {
-          "createdAt": "2014-12-16T17:26:54+0100",
-          "defaultRemFnBaseEffort": "5min",
-          "defaultRemFnType": "CONSTANT_ISSUE",
-          "descriptionSections": [
-            {
-              "content": "<b>Why</b> Because",
-              "key": "default",
-            },
-          ],
-          "htmlDesc": "",
-          "isExternal": false,
-          "isTemplate": false,
-          "key": "squid:S1337",
-          "lang": "java",
-          "langName": "Java",
-          "mdDesc": "",
-          "name": "".equals()" should not be used to test the values of "Atomic" classes",
-          "params": [],
-          "remFnBaseEffort": "5min",
-          "remFnOverloaded": false,
-          "remFnType": "CONSTANT_ISSUE",
-          "repo": "squid",
-          "scope": "MAIN",
-          "severity": "MAJOR",
-          "status": "READY",
-          "sysTags": [
-            "multi-threading",
-          ],
-          "tags": [],
-          "type": "BUG",
-        }
-      }
-    />
-    <withAvailableFeaturesContext(RuleDetailsIssues)
-      ruleDetails={
-        {
-          "createdAt": "2014-12-16T17:26:54+0100",
-          "defaultRemFnBaseEffort": "5min",
-          "defaultRemFnType": "CONSTANT_ISSUE",
-          "descriptionSections": [
-            {
-              "content": "<b>Why</b> Because",
-              "key": "default",
-            },
-          ],
-          "htmlDesc": "",
-          "isExternal": false,
-          "isTemplate": false,
-          "key": "squid:S1337",
-          "lang": "java",
-          "langName": "Java",
-          "mdDesc": "",
-          "name": "".equals()" should not be used to test the values of "Atomic" classes",
-          "params": [],
-          "remFnBaseEffort": "5min",
-          "remFnOverloaded": false,
-          "remFnType": "CONSTANT_ISSUE",
-          "repo": "squid",
-          "scope": "MAIN",
-          "severity": "MAJOR",
-          "status": "READY",
-          "sysTags": [
-            "multi-threading",
-          ],
-          "tags": [],
-          "type": "BUG",
-        }
-      }
-    />
-  </DeferredSpinner>
-</div>
-`;
-
-exports[`should render correctly: loading 1`] = `
-<div
-  className="coding-rule-details"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsIssues-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsIssues-test.tsx.snap
deleted file mode 100644 (file)
index 17a6780..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should fetch issues and render 1`] = `
-<div
-  className="js-rule-issues coding-rule-section"
->
-  <DeferredSpinner
-    loading={false}
-  >
-    <h2
-      className="coding-rules-detail-title"
-    >
-      coding_rules.issues
-      <span
-        className="little-spacer-left"
-      >
-        (
-        <ForwardRef(Link)
-          to={
-            {
-              "pathname": "/issues",
-              "search": "?resolved=false&rules=foo",
-            }
-          }
-        >
-          18
-        </ForwardRef(Link)>
-        )
-      </span>
-    </h2>
-    <table
-      className="coding-rules-detail-list coding-rules-most-violated-projects"
-    >
-      <tbody>
-        <tr>
-          <td
-            className="coding-rules-detail-list-name"
-            colSpan={2}
-          >
-            coding_rules.most_violating_projects
-          </td>
-        </tr>
-        <tr
-          key="sample-key"
-        >
-          <td
-            className="coding-rules-detail-list-name"
-          >
-            Sample
-          </td>
-          <td
-            className="coding-rules-detail-list-parameters"
-          >
-            <ForwardRef(Link)
-              to={
-                {
-                  "pathname": "/issues",
-                  "search": "?resolved=false&rules=foo&projects=sample-key",
-                }
-              }
-            >
-              13
-            </ForwardRef(Link)>
-          </td>
-        </tr>
-        <tr
-          key="example-key"
-        >
-          <td
-            className="coding-rules-detail-list-name"
-          >
-            Example
-          </td>
-          <td
-            className="coding-rules-detail-list-parameters"
-          >
-            <ForwardRef(Link)
-              to={
-                {
-                  "pathname": "/issues",
-                  "search": "?resolved=false&rules=foo&projects=example-key",
-                }
-              }
-            >
-              5
-            </ForwardRef(Link)>
-          </td>
-        </tr>
-      </tbody>
-    </table>
-  </DeferredSpinner>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap
deleted file mode 100644 (file)
index cb2bfa9..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display right meta info 1`] = `
-<div
-  className="js-rule-meta"
->
-  <div
-    className="page-header"
-  >
-    <div
-      className="pull-right"
-    >
-      <span
-        className="note text-middle"
-      >
-        squid:S1133
-      </span>
-      <ForwardRef(Link)
-        className="coding-rules-detail-permalink link-no-underline spacer-left text-middle"
-        title="permalink"
-        to={
-          {
-            "pathname": "/coding_rules",
-            "search": "?open=squid%3AS1133&rule_key=squid%3AS1133",
-          }
-        }
-      >
-        <LinkIcon />
-      </ForwardRef(Link)>
-      <SimilarRulesFilter
-        onFilterChange={[MockFunction]}
-        rule={
-          {
-            "createdAt": "2013-07-26T09:40:51+0200",
-            "descriptionSections": [],
-            "gapDescription": "per test",
-            "key": "squid:S1133",
-            "lang": "java",
-            "langName": "Java",
-            "name": "Deprecated code should be removed",
-            "remFnBaseEffort": "1min",
-            "remFnGapMultiplier": "2min",
-            "remFnType": "LINEAR_OFFSET",
-            "repo": "squid",
-            "scope": "MAIN",
-            "severity": "INFO",
-            "status": "READY",
-            "type": "CODE_SMELL",
-          }
-        }
-      />
-    </div>
-    <h1
-      className="page-title coding-rules-detail-header"
-    >
-      Deprecated code should be removed
-    </h1>
-  </div>
-  <ul
-    className="coding-rules-detail-properties"
-  >
-    <Tooltip
-      overlay="coding_rules.type.tooltip.CODE_SMELL"
-    >
-      <li
-        className="coding-rules-detail-property"
-        data-meta="type"
-      >
-        <IssueTypeIcon
-          className="little-spacer-right"
-          query="CODE_SMELL"
-        />
-        issue.type.CODE_SMELL
-      </li>
-    </Tooltip>
-    <Tooltip
-      overlay="default_severity"
-    >
-      <li
-        className="coding-rules-detail-property"
-        data-meta="severity"
-      >
-        <SeverityHelper
-          className="display-inline-flex-center"
-          severity="INFO"
-        />
-      </li>
-    </Tooltip>
-    <li
-      className="coding-rules-detail-property"
-      data-meta="tags"
-    >
-      <Dropdown
-        closeOnClick={false}
-        closeOnClickOutside={true}
-        overlay={
-          <RuleDetailsTagsPopup
-            setTags={[MockFunction]}
-            sysTags={[]}
-            tags={[]}
-          />
-        }
-        overlayPlacement="bottom-left"
-      >
-        <ButtonLink>
-          <TagsList
-            allowUpdate={true}
-            tags={
-              [
-                "coding_rules.no_tags",
-              ]
-            }
-          />
-        </ButtonLink>
-      </Dropdown>
-    </li>
-    <li
-      className="coding-rules-detail-property"
-      data-meta="available-since"
-    >
-      <span
-        className="little-spacer-right"
-      >
-        coding_rules.available_since
-      </span>
-      <DateFormatter
-        date="2013-07-26T09:40:51+0200"
-      />
-    </li>
-    <Tooltip
-      overlay="coding_rules.remediation_function"
-    >
-      <li
-        className="coding-rules-detail-property"
-        data-meta="remediation-function"
-      >
-        coding_rules.remediation_function.LINEAR_OFFSET
-        :
-         1min
-         +2min
-         per test
-      </li>
-    </Tooltip>
-  </ul>
-</div>
-`;
-
-exports[`should display right meta info 2`] = `
-<div
-  className="js-rule-meta"
->
-  <div
-    className="page-header"
-  >
-    <div
-      className="pull-right"
-    >
-      <span
-        className="note text-middle"
-      >
-        xoo:OneExternalIssuePerLine
-      </span>
-    </div>
-    <h1
-      className="page-title coding-rules-detail-header"
-    >
-      xoo:OneExternalIssuePerLine
-    </h1>
-  </div>
-</div>
-`;
-
-exports[`should display right meta info 3`] = `
-<div
-  className="js-rule-meta"
->
-  <div
-    className="page-header"
-  >
-    <div
-      className="pull-right"
-    >
-      <span
-        className="note text-middle"
-      >
-        xoo:OneExternalIssueWithDetailsPerLine
-      </span>
-    </div>
-    <h1
-      className="page-title coding-rules-detail-header"
-    >
-      One external issue per line
-    </h1>
-  </div>
-  <ul
-    className="coding-rules-detail-properties"
-  >
-    <Tooltip
-      overlay="coding_rules.type.tooltip.BUG"
-    >
-      <li
-        className="coding-rules-detail-property"
-        data-meta="type"
-      >
-        <IssueTypeIcon
-          className="little-spacer-right"
-          query="BUG"
-        />
-        issue.type.BUG
-      </li>
-    </Tooltip>
-    <Tooltip
-      overlay="default_severity"
-    >
-      <li
-        className="coding-rules-detail-property"
-        data-meta="severity"
-      >
-        <SeverityHelper
-          className="display-inline-flex-center"
-          severity="MAJOR"
-        />
-      </li>
-    </Tooltip>
-    <li
-      className="coding-rules-detail-property"
-      data-meta="tags"
-    >
-      <Dropdown
-        closeOnClick={false}
-        closeOnClickOutside={true}
-        overlay={
-          <RuleDetailsTagsPopup
-            setTags={[MockFunction]}
-            sysTags={[]}
-            tags={
-              [
-                "tag",
-              ]
-            }
-          />
-        }
-        overlayPlacement="bottom-left"
-      >
-        <ButtonLink>
-          <TagsList
-            allowUpdate={true}
-            tags={
-              [
-                "tag",
-              ]
-            }
-          />
-        </ButtonLink>
-      </Dropdown>
-    </li>
-    <Tooltip
-      overlay="coding_rules.external_rule.engine.xoo"
-    >
-      <li
-        className="coding-rules-detail-property"
-      >
-        <div
-          className="badge spacer-left text-text-top"
-        >
-          xoo
-        </div>
-      </li>
-    </Tooltip>
-  </ul>
-</div>
-`;
-
-exports[`should edit tags 1`] = `
-<li
-  className="coding-rules-detail-property"
-  data-meta="tags"
->
-  <Dropdown
-    closeOnClick={false}
-    closeOnClickOutside={true}
-    overlay={
-      <RuleDetailsTagsPopup
-        setTags={[MockFunction]}
-        sysTags={[]}
-        tags={[]}
-      />
-    }
-    overlayPlacement="bottom-left"
-  >
-    <ButtonLink>
-      <TagsList
-        allowUpdate={true}
-        tags={
-          [
-            "coding_rules.no_tags",
-          ]
-        }
-      />
-    </ButtonLink>
-  </Dropdown>
-</li>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsParameters-test.tsx.snap
deleted file mode 100644 (file)
index 1c441e4..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
-  className="js-rule-parameters"
->
-  <h3
-    className="coding-rules-detail-title"
-  >
-    coding_rules.parameters
-  </h3>
-  <table
-    className="coding-rules-detail-parameters"
-  >
-    <tbody>
-      <tr
-        className="coding-rules-detail-parameter"
-        key="1"
-      >
-        <td
-          className="coding-rules-detail-parameter-name"
-        >
-          1
-        </td>
-        <td
-          className="coding-rules-detail-parameter-description"
-        >
-          <p
-            dangerouslySetInnerHTML={
-              {
-                "__html": "description",
-              }
-            }
-          />
-          <div
-            className="note spacer-top"
-          >
-            coding_rules.parameters.default_value
-            <br />
-            <span
-              className="coding-rules-detail-parameter-value"
-            >
-              1
-            </span>
-          </div>
-        </td>
-      </tr>
-      <tr
-        className="coding-rules-detail-parameter"
-        key="1"
-      >
-        <td
-          className="coding-rules-detail-parameter-name"
-        >
-          1
-        </td>
-        <td
-          className="coding-rules-detail-parameter-description"
-        >
-          <p
-            dangerouslySetInnerHTML={
-              {
-                "__html": "description",
-              }
-            }
-          />
-          <div
-            className="note spacer-top"
-          >
-            coding_rules.parameters.default_value
-            <br />
-            <span
-              className="coding-rules-detail-parameter-value"
-            >
-              1
-            </span>
-          </div>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsTagsPopup-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsTagsPopup-test.tsx.snap
deleted file mode 100644 (file)
index 908a893..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render tags 1`] = `
-<TagsSelector
-  listSize={10}
-  onSearch={[Function]}
-  onSelect={[Function]}
-  onUnselect={[Function]}
-  selectedTags={
-    [
-      "foo",
-    ]
-  }
-  tags={[]}
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleListItem-test.tsx.snap
deleted file mode 100644 (file)
index 7b78699..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renderActions should disable the button when I am on a built-in profile: activate 1`] = `
-<td
-  className="coding-rule-table-meta-cell coding-rule-activation-actions"
->
-  <Tooltip
-    overlay="coding_rules.need_extend_or_copy"
-  >
-    <Button
-      className="coding-rules-detail-quality-profile-deactivate button-red disabled"
-    >
-      coding_rules.activate
-    </Button>
-  </Tooltip>
-</td>
-`;
-
-exports[`renderActions should disable the button when I am on a built-in profile: deactivate 1`] = `
-<td
-  className="coding-rule-table-meta-cell coding-rule-activation-actions"
->
-  <Tooltip
-    overlay="coding_rules.need_extend_or_copy"
-  >
-    <Button
-      className="coding-rules-detail-quality-profile-deactivate button-red disabled"
-    >
-      coding_rules.deactivate
-    </Button>
-  </Tooltip>
-</td>
-`;
-
-exports[`renderActions should render the activate button 1`] = `
-<td
-  className="coding-rule-table-meta-cell coding-rule-activation-actions"
->
-  <ActivationButton
-    buttonText="coding_rules.activate"
-    className="coding-rules-detail-quality-profile-activate"
-    modalHeader="coding_rules.activate_in_quality_profile"
-    onDone={[Function]}
-    profiles={
-      [
-        {
-          "actions": {
-            "edit": true,
-          },
-          "activeDeprecatedRuleCount": 2,
-          "activeRuleCount": 10,
-          "childrenCount": 0,
-          "depth": 1,
-          "isBuiltIn": false,
-          "isDefault": false,
-          "isInherited": false,
-          "key": "key",
-          "language": "js",
-          "languageName": "JavaScript",
-          "name": "name",
-          "projectCount": 3,
-        },
-      ]
-    }
-    rule={
-      {
-        "isTemplate": false,
-        "key": "javascript:S1067",
-        "lang": "js",
-        "langName": "JavaScript",
-        "name": "Use foo",
-        "severity": "MAJOR",
-        "status": "READY",
-        "sysTags": [
-          "a",
-          "b",
-        ],
-        "tags": [
-          "x",
-        ],
-        "type": "CODE_SMELL",
-      }
-    }
-  />
-</td>
-`;
-
-exports[`renderActions should render the deactivate button 1`] = `
-<td
-  className="coding-rule-table-meta-cell coding-rule-activation-actions"
->
-  <ConfirmButton
-    confirmButtonText="yes"
-    modalBody="coding_rules.deactivate.confirm"
-    modalHeader="coding_rules.deactivate"
-    onConfirm={[Function]}
-  >
-    [Function]
-  </ConfirmButton>
-</td>
-`;
-
-exports[`should render correctly: default 1`] = `
-<li
-  aria-current={false}
-  className="coding-rule"
-  data-rule="javascript:S1067"
->
-  <table
-    className="coding-rule-table"
-  >
-    <tbody>
-      <tr>
-        <td>
-          <div
-            className="coding-rule-title"
-          >
-            <ForwardRef(Link)
-              className="link-no-underline"
-              onClick={[Function]}
-              to={
-                {
-                  "pathname": "/coding_rules",
-                  "search": "?open=javascript%3AS1067&rule_key=javascript%3AS1067",
-                }
-              }
-            >
-              Use foo
-            </ForwardRef(Link)>
-          </div>
-        </td>
-        <td
-          className="coding-rule-table-meta-cell"
-        >
-          <div
-            className="display-flex-center coding-rule-meta"
-          >
-            <span
-              className="display-inline-flex-center spacer-left note"
-            >
-              JavaScript
-            </span>
-            <Tooltip
-              overlay="coding_rules.type.tooltip.CODE_SMELL"
-            >
-              <span
-                className="display-inline-flex-center spacer-left note"
-              >
-                <IssueTypeIcon
-                  query="CODE_SMELL"
-                />
-                <span
-                  className="little-spacer-left text-middle"
-                >
-                  issue.type.CODE_SMELL
-                </span>
-              </span>
-            </Tooltip>
-            <TagsList
-              allowUpdate={false}
-              className="display-inline-flex-center note spacer-left"
-              tags={
-                [
-                  "x",
-                  "a",
-                  "b",
-                ]
-              }
-            />
-            <SimilarRulesFilter
-              onFilterChange={[MockFunction]}
-              rule={
-                {
-                  "key": "javascript:S1067",
-                  "lang": "js",
-                  "langName": "JavaScript",
-                  "name": "Use foo",
-                  "severity": "MAJOR",
-                  "status": "READY",
-                  "sysTags": [
-                    "a",
-                    "b",
-                  ],
-                  "tags": [
-                    "x",
-                  ],
-                  "type": "CODE_SMELL",
-                }
-              }
-            />
-          </div>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-</li>
-`;
-
-exports[`should render correctly: with activation 1`] = `
-<li
-  aria-current={false}
-  className="coding-rule"
-  data-rule="javascript:S1067"
->
-  <table
-    className="coding-rule-table"
-  >
-    <tbody>
-      <tr>
-        <td>
-          <div
-            className="coding-rule-title"
-          >
-            <ForwardRef(Link)
-              className="link-no-underline"
-              onClick={[Function]}
-              to={
-                {
-                  "pathname": "/coding_rules",
-                  "search": "?open=javascript%3AS1067&rule_key=javascript%3AS1067",
-                }
-              }
-            >
-              Use foo
-            </ForwardRef(Link)>
-          </div>
-        </td>
-        <td
-          className="coding-rule-table-meta-cell"
-        >
-          <div
-            className="display-flex-center coding-rule-meta"
-          >
-            <span
-              className="display-inline-flex-center spacer-left note"
-            >
-              JavaScript
-            </span>
-            <Tooltip
-              overlay="coding_rules.type.tooltip.CODE_SMELL"
-            >
-              <span
-                className="display-inline-flex-center spacer-left note"
-              >
-                <IssueTypeIcon
-                  query="CODE_SMELL"
-                />
-                <span
-                  className="little-spacer-left text-middle"
-                >
-                  issue.type.CODE_SMELL
-                </span>
-              </span>
-            </Tooltip>
-            <TagsList
-              allowUpdate={false}
-              className="display-inline-flex-center note spacer-left"
-              tags={
-                [
-                  "x",
-                  "a",
-                  "b",
-                ]
-              }
-            />
-            <SimilarRulesFilter
-              onFilterChange={[MockFunction]}
-              rule={
-                {
-                  "key": "javascript:S1067",
-                  "lang": "js",
-                  "langName": "JavaScript",
-                  "name": "Use foo",
-                  "severity": "MAJOR",
-                  "status": "READY",
-                  "sysTags": [
-                    "a",
-                    "b",
-                  ],
-                  "tags": [
-                    "x",
-                  ],
-                  "type": "CODE_SMELL",
-                }
-              }
-            />
-          </div>
-        </td>
-      </tr>
-    </tbody>
-  </table>
-</li>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/SimilarRulesFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/SimilarRulesFilter-test.tsx.snap
deleted file mode 100644 (file)
index 63f445a..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Dropdown
-  className="display-inline-block"
-  overlay={
-    <React.Fragment>
-      <h3
-        className="coding-rules-filter-title"
-      >
-        coding_rules.filter_similar_rules
-      </h3>
-      <ul
-        className="menu"
-      >
-        <li>
-          <ButtonPlain
-            aria-label="coding_rules.filter_by_language.JavaScript"
-            data-test="coding-rules__similar-language"
-            onClick={[Function]}
-          >
-            JavaScript
-          </ButtonPlain>
-        </li>
-        <li>
-          <ButtonPlain
-            aria-label="coding_rules.filter_by_type.issue.type.CODE_SMELL"
-            data-test="coding-rules__similar-type"
-            onClick={[Function]}
-          >
-            <IssueTypeIcon
-              query="CODE_SMELL"
-            />
-            <span
-              className="little-spacer-left"
-            >
-              issue.type.CODE_SMELL
-            </span>
-          </ButtonPlain>
-        </li>
-        <li>
-          <ButtonPlain
-            aria-label="coding_rules.filter_by_severity.MAJOR"
-            data-test="coding-rules__similar-severity"
-            onClick={[Function]}
-          >
-            <SeverityHelper
-              className="display-flex-center"
-              severity="MAJOR"
-            />
-          </ButtonPlain>
-        </li>
-        <li
-          className="coding-rules-similar-tag coding-rules-similar-tag-divider"
-        >
-          <ButtonPlain
-            aria-label="coding_rules.filter_by_tag.x"
-            data-tag="x"
-            data-test="coding-rules__similar-tag"
-            onClick={[Function]}
-          >
-            <TagsIcon
-              className="little-spacer-right text-middle"
-            />
-            <span
-              className="text-middle"
-            >
-              x
-            </span>
-          </ButtonPlain>
-        </li>
-        <li
-          className="coding-rules-similar-tag"
-        >
-          <ButtonPlain
-            aria-label="coding_rules.filter_by_tag.a"
-            data-tag="a"
-            data-test="coding-rules__similar-tag"
-            onClick={[Function]}
-          >
-            <TagsIcon
-              className="little-spacer-right text-middle"
-            />
-            <span
-              className="text-middle"
-            >
-              a
-            </span>
-          </ButtonPlain>
-        </li>
-        <li
-          className="coding-rules-similar-tag"
-        >
-          <ButtonPlain
-            aria-label="coding_rules.filter_by_tag.b"
-            data-tag="b"
-            data-test="coding-rules__similar-tag"
-            onClick={[Function]}
-          >
-            <TagsIcon
-              className="little-spacer-right text-middle"
-            />
-            <span
-              className="text-middle"
-            >
-              b
-            </span>
-          </ButtonPlain>
-        </li>
-      </ul>
-    </React.Fragment>
-  }
->
-  <Button
-    className="js-rule-filter spacer-left"
-    title="coding_rules.filter_similar_rules"
-  >
-    <FilterIcon />
-    <DropdownIcon />
-  </Button>
-</Dropdown>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/TagFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/TagFacet-test.tsx.snap
deleted file mode 100644 (file)
index 51eabc3..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`ListStyleFacet Renderers should include renderFacetItem 1`] = `
-<React.Fragment>
-  <TagsIcon
-    className="little-spacer-right"
-    fill="#888"
-  />
-  tag
-</React.Fragment>
-`;
-
-exports[`ListStyleFacet Renderers should include renderSearchResult 1`] = `
-<React.Fragment>
-  <TagsIcon
-    className="little-spacer-right"
-    fill="#888"
-  />
-  <React.Fragment>
-    my-
-    <mark>
-      tag
-    </mark>
-  </React.Fragment>
-</React.Fragment>
-`;
-
-exports[`should render correctly 1`] = `
-<ListStyleFacet
-  facetHeader="coding_rules.facet.tags"
-  fetching={false}
-  getFacetItemText={[Function]}
-  getSearchResultKey={[Function]}
-  getSearchResultText={[Function]}
-  maxInitialItems={15}
-  maxItems={100}
-  minSearchLength={2}
-  onChange={[MockFunction]}
-  onSearch={[Function]}
-  onToggle={[MockFunction]}
-  open={false}
-  property="tags"
-  renderFacetItem={[Function]}
-  renderSearchResult={[Function]}
-  searchPlaceholder="search.search_for_tags"
-  showLessAriaLabel="coding_rules.facet.tag.show_less"
-  showMoreAriaLabel="coding_rules.facet.tag.show_more"
-  stats={{}}
-  values={[]}
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/utils-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/utils-test.tsx
new file mode 100644 (file)
index 0000000..54d92c3
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { Profile } from '../../api/quality-profiles';
+import { byLabelText, byPlaceholderText, byRole, byText } from '../../helpers/testSelector';
+
+const selectors = {
+  loading: byLabelText('loading'),
+
+  // List
+  rulesList: byRole('list', { name: 'list_of_rules' }),
+  ruleListItemLink: (name: string) => byRole('link', { name }),
+  ruleListItem: byRole('listitem'),
+  currentListItem: byRole('listitem', { current: true }),
+  similarIssuesButton: byRole('button', { name: 'coding_rules.filter_similar_rules' }),
+  similarIssuesFilterByLang: (lang: string) =>
+    byRole('button', { name: `coding_rules.filter_by_language.${lang}` }),
+  similarIssuesFilterByType: (type: string) =>
+    byRole('button', { name: `coding_rules.filter_by_type.${type}` }),
+  similarIssuesFilterBySeverity: (severity: string) =>
+    byRole('button', { name: `coding_rules.filter_by_severity.${severity}` }),
+  similarIssuesFilterByTag: (tag: string) =>
+    byRole('button', { name: `coding_rules.filter_by_tag.${tag}` }),
+
+  // Filters
+  searchInput: byRole('searchbox', { name: 'search.search_for_rules' }),
+  clearAllFiltersButton: byRole('button', { name: 'clear_all_filters' }),
+
+  // Facets
+  languagesFacet: byRole('button', { name: 'coding_rules.facet.languages' }),
+  typeFacet: byRole('button', { name: 'coding_rules.facet.types' }),
+  tagsFacet: byRole('button', { name: 'coding_rules.facet.tags' }),
+  repositoriesFacet: byRole('button', { name: 'coding_rules.facet.repositories' }),
+  severetiesFacet: byRole('button', { name: 'coding_rules.facet.severities' }),
+  statusesFacet: byRole('button', { name: 'coding_rules.facet.statuses' }),
+  standardsFacet: byRole('button', { name: 'issues.facet.standards' }),
+  availableSinceFacet: byRole('button', { name: 'coding_rules.facet.available_since' }),
+  templateFacet: byRole('button', { name: 'coding_rules.facet.template' }),
+  qpFacet: byRole('button', { name: 'coding_rules.facet.qprofile' }),
+  facetItem: (name: string) => byRole('checkbox', { name }),
+  availableSinceDateField: byPlaceholderText('date'),
+
+  // Bulk change
+  bulkChangeButton: byRole('button', { name: 'bulk_change' }),
+  activateIn: byRole('link', { name: 'coding_rules.activate_in…' }),
+  deactivateIn: byRole('link', { name: 'coding_rules.deactivate_in…' }),
+  bulkChangeDialog: (count: number, activate = true) =>
+    byRole('dialog', {
+      name: `coding_rules.${
+        activate ? 'ac' : 'deac'
+      }tivate_in_quality_profile (${count} coding_rules._rules)`,
+    }),
+  activateInSelect: byRole('combobox', { name: 'coding_rules.activate_in' }),
+  deactivateInSelect: byRole('combobox', { name: 'coding_rules.deactivate_in' }),
+  noQualityProfiles: byText('coding_rules.bulk_change.no_quality_profile'),
+  bulkApply: byRole('button', { name: 'apply' }),
+  bulkSuccessMessage: (qpName: string, langName: string, count: number) =>
+    byText(`coding_rules.bulk_change.success.${qpName}.${langName}.${count}`),
+  bulkWarningMessage: (qpName: string, langName: string, count: number) =>
+    byText(`coding_rules.bulk_change.warning.${qpName}.${langName}.${count}.1`),
+  bulkClose: byRole('button', { name: 'close' }),
+
+  // Rule details
+  ruleTitle: (name: string) => byRole('heading', { level: 1, name }),
+  externalDescription: (ruleName: string) => byText(`issue.external_issue_description.${ruleName}`),
+  introTitle: byText(/Introduction to this rule/),
+  rootCause: byText('Root cause'),
+  howToFix: byText('This is how to fix'),
+  resourceLink: byRole('link', { name: 'Awsome Reading' }),
+  contextRadio: (name: string) => byRole('radio', { name }),
+  contextSubtitle: (name: string) => byText(`coding_rules.description_context.subtitle.${name}`),
+  otherContextTitle: byText('coding_rules.context.others.title'),
+  caycNotificationButton: byRole('button', { name: 'coding_rules.more_info.scroll_message' }),
+  extendDescriptionButton: byRole('button', { name: 'coding_rules.extend_description' }),
+  extendDescriptionTextbox: byRole('textbox', { name: 'coding_rules.extend_description' }),
+  saveButton: byRole('button', { name: 'save' }),
+  cancelButton: byRole('button', { name: 'cancel' }),
+  removeButton: byRole('button', { name: 'remove' }),
+
+  // Rule tags
+  tagsDropdown: byRole('button', { name: /tags_list_x/ }),
+  tagsMenu: byRole('group', { name: 'select_tags' }),
+  tagCheckbox: (tag: string) => byRole('checkbox', { name: tag }),
+  tagSearch: byRole('searchbox', { name: 'search.search_for_tags' }),
+
+  // Rule tabs
+  whyIssueTab: byRole('tab', {
+    name: 'coding_rules.description_section.title.root_cause',
+  }),
+  whatRiskTab: byRole('tab', {
+    name: 'coding_rules.description_section.title.root_cause.SECURITY_HOTSPOT',
+  }),
+  assessTab: byRole('tab', {
+    name: 'coding_rules.description_section.title.assess_the_problem',
+  }),
+  moreInfoTab: byRole('tab', {
+    name: 'coding_rules.description_section.title.more_info',
+  }),
+  howToFixTab: byRole('tab', {
+    name: 'coding_rules.description_section.title.how_to_fix',
+  }),
+
+  // Rule Quality Profiles
+  qpLink: (name: string) => byRole('link', { name }),
+  activateButton: byRole('button', { name: 'coding_rules.activate' }),
+  severitySelect: byRole('combobox', { name: 'severity' }),
+  qualityProfileSelect: byRole('combobox', { name: 'coding_rules.quality_profile' }),
+  activateQPDialog: byRole('dialog', { name: 'coding_rules.activate_in_quality_profile' }),
+  changeButton: (profile: string) =>
+    byRole('button', { name: `coding_rules.change_details_x.${profile}` }),
+  changeQPDialog: byRole('dialog', { name: 'coding_rules.change_details' }),
+  deactivateButton: (profile: string) =>
+    byRole('button', { name: `coding_rules.deactivate_in_quality_profile_x.${profile}` }),
+  activaInAllQPs: byText('coding_rules.active_in_all_profiles'),
+  yesButton: byRole('button', { name: 'yes' }),
+  paramInput: (param: string) => byRole('textbox', { name: param }),
+
+  // Custom rules
+  customRuleSectionTitle: byRole('heading', { level: 2, name: 'coding_rules.custom_rules' }),
+  createCustomRuleButton: byRole('button', { name: 'coding_rules.create' }),
+  editCustomRuleButton: byRole('button', { name: 'edit' }),
+  deleteCustomRuleButton: (rule: string) =>
+    byRole('button', { name: `coding_rules.delete_rule_x.${rule}` }),
+  customRuleItemLink: (name: string) => byRole('link', { name }),
+
+  // Custom rule form
+  createCustomRuleDialog: byRole('dialog', { name: 'coding_rules.create_custom_rule' }),
+  updateCustomRuleDialog: byRole('dialog', { name: 'coding_rules.update_custom_rule' }),
+  deleteCustomRuleDialog: byRole('dialog', { name: 'coding_rules.delete_rule' }),
+  ruleNameTextbox: byRole('textbox', { name: 'name field_required' }),
+  keyTextbox: byRole('textbox', { name: 'key field_required' }),
+  typeSelect: byRole('combobox', { name: 'type' }),
+  statusSelect: byRole('combobox', { name: 'coding_rules.filters.status' }),
+  descriptionTextbox: byRole('textbox', { name: 'description field_required' }),
+  createButton: byRole('button', { name: 'create' }),
+  deleteButton: byRole('button', { name: 'delete' }),
+};
+
+export function getPageObjects() {
+  const user = userEvent.setup();
+
+  const ui = {
+    ...selectors,
+    async appLoaded() {
+      await waitFor(() => {
+        expect(selectors.loading.query()).not.toBeInTheDocument();
+      });
+    },
+
+    async bulkActivate(rulesCount: number, profile: Profile) {
+      await user.click(ui.bulkChangeButton.get());
+      await user.click(ui.activateIn.get());
+
+      const dialog = ui.bulkChangeDialog(rulesCount).get();
+      expect(dialog).toBeInTheDocument();
+
+      await user.click(ui.activateInSelect.get());
+      await user.click(byText(`${profile.name} - ${profile.languageName}`).get(dialog));
+
+      await user.click(ui.bulkApply.get());
+    },
+
+    async bulkDeactivate(rulesCount: number, profile: Profile) {
+      await user.click(ui.bulkChangeButton.get());
+      await user.click(ui.deactivateIn.get());
+
+      const dialog = ui.bulkChangeDialog(rulesCount, false).get();
+      expect(dialog).toBeInTheDocument();
+
+      await user.click(ui.deactivateInSelect.get());
+      await user.click(byText(`${profile.name} - ${profile.languageName}`).get(dialog));
+
+      await user.click(ui.bulkApply.get());
+    },
+  };
+
+  return {
+    ui,
+    user,
+  };
+}
index 7cd177c3fa1017986d200d3540cb0b6740f368e8..ce3f33ab3082adfe3b9588576d5d2635440fea64 100644 (file)
@@ -158,7 +158,7 @@ export default class RuleDescription extends React.PureComponent<Props, State> {
             {selectedContext.key !== OTHERS_KEY && (
               <h2>
                 {translateWithParameters(
-                  'coding_rules.description_context.sub_title',
+                  'coding_rules.description_context.subtitle',
                   selectedContext.displayName
                 )}
               </h2>
index 3b8fcced33e8e61a7cbd3d72d2b9da2197231d12..ab12c10b69d6a04bf68a022438a48c2a75ffefcf 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
-import { Matcher, RenderResult, fireEvent, render, screen, within } from '@testing-library/react';
+import { Matcher, RenderResult, render, screen, within } from '@testing-library/react';
 import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
 import { omit } from 'lodash';
 import * as React from 'react';
@@ -220,43 +220,23 @@ function renderRoutedApp(
   );
 }
 
-/* eslint-disable testing-library/no-node-access */
 export function dateInputEvent(user: UserEvent) {
   return {
-    async openDatePicker(element: HTMLElement) {
-      await user.click(element);
-    },
     async pickDate(element: HTMLElement, date: Date) {
       await user.click(element);
 
-      const monthSelect =
-        element.parentNode?.querySelector<HTMLSelectElement>('select[name="months"]');
-      if (!monthSelect) {
-        throw new Error('Could not find the month selector of the date picker element');
-      }
-
-      const yearSelect =
-        element.parentNode?.querySelector<HTMLSelectElement>('select[name="years"]');
-      if (!yearSelect) {
-        throw new Error('Could not find the year selector of the date picker element');
-      }
+      const formatter = new Intl.DateTimeFormat('en', { month: 'long' });
 
-      fireEvent.change(monthSelect, { target: { value: date.getMonth() } });
-      fireEvent.change(yearSelect, { target: { value: date.getFullYear() } });
-
-      const dayButtons =
-        element.parentNode?.querySelectorAll<HTMLSelectElement>('button[name="day"]');
-      if (!dayButtons) {
-        throw new Error('Could not find the day buttons of the date picker element');
-      }
-      const dayButton = Array.from(dayButtons).find(
-        (button) => Number(button.textContent) === date.getDate()
+      await user.selectOptions(
+        await screen.findByRole('combobox', { name: 'Month:' }),
+        formatter.format(date)
+      );
+      await user.selectOptions(
+        screen.getByRole('combobox', { name: 'Year:' }),
+        String(date.getFullYear())
       );
-      if (!dayButton) {
-        throw new Error(`Could not find the button for day ${date.getDate()}`);
-      }
 
-      await user.click(dayButton);
+      await user.click(screen.getByRole('gridcell', { name: String(date.getDate()) }));
     },
   };
 }
index 41b5a82ee18ba77d68e8783a73ab5fc90ef2d82f..45a4748e51e68ac15df89aff7506b62b1c075594 100644 (file)
@@ -2109,6 +2109,7 @@ coding_rules.bulk_change.no_quality_profile=No quality profile.
 coding_rules.can_not_bulk_change=Bulk change is only available when you have a custom Quality Profile to target. You can create a customizable Quality Profile based on a built-in one by Copying or Extending it in the Quality Profiles list.
 coding_rules.can_not_deactivate=This rule is inherited and cannot be deactivated.
 coding_rules.change_details=Change Details of Quality Profile
+coding_rules.change_details_x=Change Details of Quality Profile {0}
 coding_rules.context.others.title=How can I fix it in another component or framework?
 coding_rules.context.others.description.first=Although the main framework or component you use in your project is not listed above, you may find helpful content in the instructions we provide.
 coding_rules.context.others.description.second=Caution: The libraries mentioned in these instructions may not be appropriate for your code.
@@ -2126,7 +2127,9 @@ coding_rules.custom_rule.activation_notice=Note: parameters of a custom rule are
 coding_rules.custom_rule.removal=Only custom rules may be deleted. When a custom rule is deleted, it is not removed from the SonarQube instance. Instead its status is set to "REMOVED", allowing relevant issues to continue to be displayed properly.
 coding_rules.custom_rules=Custom Rules
 coding_rules.deactivate_in_quality_profile=Deactivate In Quality Profile
+coding_rules.deactivate_in_quality_profile_x=Deactivate In Quality Profile {0}
 coding_rules.delete_rule=Delete Rule
+condig_rules.delete_rule_x=Delete Rule {0}
 coding_rules.delete.custom.confirm=Are you sure you want to delete custom rule "{0}"?
 coding_rules.extend_description=Extend Description
 coding_rules.deactivate_in=Deactivate In
@@ -2252,7 +2255,7 @@ coding_rules.description_section.title.more_info=More Info
 coding_rules.description_section.title.activity=Activity
 
 coding_rules.description_context.title=Which component or framework contains the issue?
-coding_rules.description_context.sub_title=How can I fix it in {0}?
+coding_rules.description_context.subtitle=How can I fix it in {0}?
 coding_rules.description_context.default_information={0} was detected as the most relevant component or framework for this issue.
 coding_rules.description_context.other=Other