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 } = {
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[] = [];
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>';
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',
}),
mockRuleDetails({
key: 'rule6',
- type: 'VULNERABILITY',
+ type: 'BUG',
lang: 'py',
langName: 'Python',
name: 'Bad Python rule',
mockRuleDetails({
key: 'rule7',
type: 'VULNERABILITY',
+ severity: 'MINOR',
lang: 'py',
langName: 'Python',
name: 'Python rule with context',
],
}),
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 },
}),
];
- (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[]) {
);
}
- 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);
}
this.applyWithWarning = false;
this.dismissedNoticesEP = false;
this.rules = cloneDeep(this.defaultRules);
+ this.rulesActivations = cloneDeep(this.defaultRulesActivations);
}
allRulesCount() {
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> => {
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(
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({
});
};
+ 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> => {
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 });
};
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);
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) => {
* 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(() => {
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) {
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}
+ />
+ )}
+ </>
+ );
}
) : (
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}
/>
) : (
<input
+ id={param.key}
disabled={submitting}
name={param.key}
onChange={this.handleParameterChange}
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}
+ />
+ )}
+ </>
+ );
}
severity: string;
status: string;
submitting: boolean;
- type: string;
+ type: RuleType;
}
export default class CustomRuleFormModal extends React.PureComponent<Props, State> {
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');
}
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';
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';
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>
)}
<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}
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>
- );
- }
}
{!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')}
{({ 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')}
*/
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';
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}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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} />);
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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} />);
-}
+++ /dev/null
-/*
- * 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} />
- );
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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}
- />
- );
-}
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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"
-/>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render tags 1`] = `
-<TagsSelector
- listSize={10}
- onSearch={[Function]}
- onSelect={[Function]}
- onUnselect={[Function]}
- selectedTags={
- [
- "foo",
- ]
- }
- tags={[]}
-/>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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>
-`;
+++ /dev/null
-// 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={[]}
-/>
-`;
--- /dev/null
+/*
+ * 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,
+ };
+}
{selectedContext.key !== OTHERS_KEY && (
<h2>
{translateWithParameters(
- 'coding_rules.description_context.sub_title',
+ 'coding_rules.description_context.subtitle',
selectedContext.displayName
)}
</h2>
* 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';
);
}
-/* 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()) }));
},
};
}
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.
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
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