aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts74
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx58
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx24
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonForm-test.tsx86
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResultActivation-test.tsx45
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx104
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonForm-test.tsx.snap21
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonResultActivation-test.tsx.snap60
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/constants.ts (renamed from server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileHeader-test.tsx)19
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx127
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileHeader-test.tsx.snap214
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesList-test.tsx45
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListRow-test.tsx46
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesList-test.tsx.snap288
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListRow-test.tsx.snap411
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/utils.ts9
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts31
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
26 files changed, 298 insertions, 1433 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts
index e3f0f2bf79c..26d473f3f27 100644
--- a/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts
+++ b/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts
@@ -19,11 +19,14 @@
*/
import { cloneDeep } from 'lodash';
import { RequestData } from '../../helpers/request';
-import { mockQualityProfile } from '../../helpers/testMocks';
+import { mockCompareResult, mockQualityProfile, mockRuleDetails } from '../../helpers/testMocks';
import { SearchRulesResponse } from '../../types/coding-rules';
-import { Dict, Paging, ProfileInheritanceDetails } from '../../types/types';
+import { Dict, Paging, ProfileInheritanceDetails, RuleDetails } from '../../types/types';
import {
+ activateRule,
changeProfileParent,
+ compareProfiles,
+ CompareResponse,
copyProfile,
createQualityProfile,
getImporters,
@@ -35,18 +38,20 @@ import {
SearchQualityProfilesParameters,
SearchQualityProfilesResponse,
} from '../quality-profiles';
-import { searchRules } from '../rules';
+import { getRuleDetails, searchRules } from '../rules';
export default class QualityProfilesServiceMock {
isAdmin = false;
listQualityProfile: Profile[] = [];
-
languageMapping: Dict<Partial<Profile>> = {
c: { language: 'c', languageName: 'C' },
};
+ comparisonResult: CompareResponse = mockCompareResult();
+
constructor() {
this.resetQualityProfile();
+ this.resetComparisonResult();
(searchQualityProfiles as jest.Mock).mockImplementation(this.handleSearchQualityProfiles);
(createQualityProfile as jest.Mock).mockImplementation(this.handleCreateQualityProfile);
@@ -56,6 +61,9 @@ export default class QualityProfilesServiceMock {
(copyProfile as jest.Mock).mockImplementation(this.handleCopyProfile);
(getImporters as jest.Mock).mockImplementation(this.handleGetImporters);
(searchRules as jest.Mock).mockImplementation(this.handleSearchRules);
+ (compareProfiles as jest.Mock).mockImplementation(this.handleCompareQualityProfiles);
+ (activateRule as jest.Mock).mockImplementation(this.handleActivateRule);
+ (getRuleDetails as jest.Mock).mockImplementation(this.handleGetRuleDetails);
}
resetQualityProfile() {
@@ -70,12 +78,27 @@ export default class QualityProfilesServiceMock {
mockQualityProfile({
key: 'java-qp',
language: 'java',
+ languageName: 'Java',
name: 'java quality profile',
activeDeprecatedRuleCount: 0,
}),
+ mockQualityProfile({
+ key: 'java-qp-1',
+ language: 'java',
+ languageName: 'Java',
+ name: 'java quality profile #2',
+ activeDeprecatedRuleCount: 1,
+ actions: {
+ edit: true,
+ },
+ }),
];
}
+ resetComparisonResult() {
+ this.comparisonResult = mockCompareResult();
+ }
+
handleGetImporters = () => {
return this.reply([]);
};
@@ -201,7 +224,7 @@ export default class QualityProfilesServiceMock {
profiles = profiles.filter((p) => p.language === language);
}
if (this.isAdmin) {
- profiles = profiles.map((p) => ({ ...p, actions: { copy: true } }));
+ profiles = profiles.map((p) => ({ ...p, actions: { ...p.actions, copy: true } }));
}
return this.reply({
@@ -210,6 +233,46 @@ export default class QualityProfilesServiceMock {
});
};
+ handleActivateRule = (data: {
+ key: string;
+ params?: Dict<string>;
+ reset?: boolean;
+ rule: string;
+ severity?: string;
+ }): Promise<undefined> => {
+ const profile = this.listQualityProfile.find((profile) => profile.key === data.key) as Profile;
+ const keyFilter = profile.name === this.comparisonResult.left.name ? 'inRight' : 'inLeft';
+
+ this.comparisonResult[keyFilter] = this.comparisonResult[keyFilter].filter(
+ ({ key }) => key !== data.rule
+ );
+
+ return this.reply(undefined);
+ };
+
+ handleCompareQualityProfiles = (leftKey: string, rightKey: string): Promise<CompareResponse> => {
+ const comparedProfiles = this.listQualityProfile.reduce((profiles, profile) => {
+ if (profile.key === leftKey || profile.key === rightKey) {
+ profiles.push(profile);
+ }
+ return profiles;
+ }, [] as Profile[]);
+ const [leftName, rightName] = comparedProfiles.map((profile) => profile.name);
+
+ this.comparisonResult.left = { name: leftName };
+ this.comparisonResult.right = { name: rightName };
+
+ return this.reply(this.comparisonResult);
+ };
+
+ handleGetRuleDetails = (params: { key: string }): Promise<{ rule: RuleDetails }> => {
+ return this.reply({
+ rule: mockRuleDetails({
+ key: params.key,
+ }),
+ });
+ };
+
setAdmin() {
this.isAdmin = true;
}
@@ -217,6 +280,7 @@ export default class QualityProfilesServiceMock {
reset() {
this.isAdmin = false;
this.resetQualityProfile();
+ this.resetComparisonResult();
}
reply<T>(response: T): Promise<T> {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
index 2e795272fda..f24cb1dd8ef 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
@@ -39,10 +39,10 @@ const ui = {
cQualityProfileName: 'c quality profile',
newCQualityProfileName: 'New c quality profile',
newCQualityProfileNameFromCreateButton: 'New c quality profile from create',
-
- actionOnCQualityProfile: byRole('button', {
- name: 'quality_profiles.actions.c quality profile.C',
- }),
+ profileActions: (name: string, language: string) =>
+ byRole('button', {
+ name: `quality_profiles.actions.${name}.${language}`,
+ }),
extendButton: byRole('button', {
name: 'extend',
}),
@@ -50,6 +50,9 @@ const ui = {
name: 'copy',
}),
createButton: byRole('button', { name: 'create' }),
+ compareButton: byRole('link', { name: 'compare' }),
+ compareDropdown: byRole('textbox', { name: 'quality_profiles.compare_with' }),
+ changelogLink: byRole('link', { name: 'changelog' }),
popup: byRole('dialog'),
copyRadio: byRole('radio', {
name: 'quality_profiles.creation_from_copy quality_profiles.creation_from_copy_description_1 quality_profiles.creation_from_copy_description_2',
@@ -57,6 +60,11 @@ const ui = {
blankRadio: byRole('radio', {
name: 'quality_profiles.creation_from_blank quality_profiles.creation_from_blank_description',
}),
+ activeRuleButton: (profileName: string) =>
+ byRole('button', {
+ name: `quality_profiles.comparison.activate_rule.${profileName}`,
+ }),
+ activateConfirmButton: byRole('button', { name: 'coding_rules.activate' }),
namePropupInput: byRole('textbox', { name: 'quality_profiles.new_name field_required' }),
filterByLang: byRole('textbox', { name: 'quality_profiles.filter_by:' }),
listLinkCQualityProfile: byRole('link', { name: 'c quality profile' }),
@@ -74,6 +82,12 @@ const ui = {
name: 'quality_profiles.creation.choose_copy_quality_profile field_required',
}),
nameCreatePopupInput: byRole('textbox', { name: 'name field_required' }),
+ comparisonDiffTableHeading: (rulesQuantity: number, profileName: string) =>
+ byRole('heading', { name: `quality_profiles.x_rules_only_in.${rulesQuantity} ${profileName}` }),
+ comparisonModifiedTableHeading: (rulesQuantity: number) =>
+ byRole('heading', {
+ name: `quality_profiles.x_rules_have_different_configuration.${rulesQuantity}`,
+ }),
};
it('should list Quality Profiles and filter by language', async () => {
@@ -103,7 +117,7 @@ it('should be able to extend Quality Profile', async () => {
serviceMock.setAdmin();
renderQualityProfiles();
- await user.click(await ui.actionOnCQualityProfile.find());
+ await user.click(await ui.profileActions('c quality profile', 'C').find());
await user.click(ui.extendButton.get());
await user.clear(ui.namePropupInput.get());
@@ -129,7 +143,7 @@ it('should be able to copy Quality Profile', async () => {
serviceMock.setAdmin();
renderQualityProfiles();
- await user.click(await ui.actionOnCQualityProfile.find());
+ await user.click(await ui.profileActions('c quality profile', 'C').find());
await user.click(ui.copyButton.get());
await user.clear(ui.namePropupInput.get());
@@ -166,6 +180,38 @@ it('should be able to create blank Quality Profile', async () => {
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument();
});
+it('should be able to compare profiles', async () => {
+ // From the list page
+ const user = userEvent.setup();
+ serviceMock.setAdmin();
+ renderQualityProfiles();
+
+ // For language with 1 profle we should not see compare action
+ await user.click(await ui.profileActions('c quality profile', 'C').find());
+ expect(ui.compareButton.query()).not.toBeInTheDocument();
+
+ await user.click(ui.profileActions('java quality profile', 'Java').get());
+ expect(ui.compareButton.get()).toBeInTheDocument();
+ await user.click(ui.compareButton.get());
+ expect(ui.compareDropdown.get()).toBeInTheDocument();
+ expect(ui.profileActions('java quality profile', 'Java').query()).not.toBeInTheDocument();
+ expect(ui.changelogLink.query()).not.toBeInTheDocument();
+
+ await selectEvent.select(ui.compareDropdown.get(), 'java quality profile #2');
+ expect(ui.comparisonDiffTableHeading(1, 'java quality profile').get()).toBeInTheDocument();
+ expect(ui.comparisonDiffTableHeading(1, 'java quality profile #2').get()).toBeInTheDocument();
+ expect(ui.comparisonModifiedTableHeading(1).query()).toBeInTheDocument();
+
+ // java quality profile is not editable
+ expect(ui.activeRuleButton('java quality profile').query()).not.toBeInTheDocument();
+
+ await user.click(ui.activeRuleButton('java quality profile #2').get());
+ expect(ui.popup.get()).toBeInTheDocument();
+
+ await user.click(ui.activateConfirmButton.get());
+ expect(ui.comparisonDiffTableHeading(1, 'java quality profile').query()).not.toBeInTheDocument();
+});
+
function renderQualityProfiles() {
renderAppRoutes('profiles', routes, {
languages: {
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
index 7b400b19a60..e82c0d872b6 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResultActivation.tsx
@@ -21,8 +21,9 @@ import * as React from 'react';
import { Profile } from '../../../api/quality-profiles';
import { getRuleDetails } from '../../../api/rules';
import { Button } from '../../../components/controls/buttons';
+import Tooltip from '../../../components/controls/Tooltip';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
-import { translate } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
import { RuleDetails } from '../../../types/types';
import ActivationFormModal from '../../coding-rules/components/ActivationFormModal';
@@ -83,9 +84,24 @@ export default class ComparisonResultActivation extends React.PureComponent<Prop
return (
<DeferredSpinner loading={this.state.state === 'opening'}>
- <Button disabled={this.state.state !== 'closed'} onClick={this.handleButtonClick}>
- {this.props.children}
- </Button>
+ <Tooltip
+ placement="bottom"
+ overlay={translateWithParameters(
+ 'quality_profiles.comparison.activate_rule',
+ profile.name
+ )}
+ >
+ <Button
+ disabled={this.state.state !== 'closed'}
+ aria-label={translateWithParameters(
+ 'quality_profiles.comparison.activate_rule',
+ profile.name
+ )}
+ onClick={this.handleButtonClick}
+ >
+ {this.props.children}
+ </Button>
+ </Tooltip>
{this.isOpen(this.state) && (
<ActivationFormModal
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonForm-test.tsx
deleted file mode 100644
index 3694ed2ed33..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonForm-test.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { mockReactSelectOptionProps } from '../../../../helpers/mocks/react-select';
-import Select from '../../../../components/controls/Select';
-import { mockQualityProfile } from '../../../../helpers/testMocks';
-import ComparisonForm from '../ComparisonForm';
-
-it('should render Select with right options', () => {
- const output = shallowRender().find(Select);
-
- expect(output.length).toBe(1);
- expect(output.prop('value')).toEqual([
- { isDefault: true, value: 'another', label: 'another name' },
- ]);
- expect(output.prop('options')).toEqual([
- { isDefault: true, value: 'another', label: 'another name' },
- ]);
-});
-
-it('should render option correctly', () => {
- const wrapper = shallowRender();
- const mockOptions = [
- {
- value: 'val',
- label: 'label',
- isDefault: undefined,
- },
- ];
- const OptionRenderer = wrapper.instance().optionRenderer.bind(null, mockOptions);
- expect(
- shallow(<OptionRenderer {...mockReactSelectOptionProps({ value: 'test' })} />)
- ).toMatchSnapshot('option render');
-});
-
-it('should render value correctly', () => {
- const wrapper = shallowRender();
- const mockOptions = [
- {
- value: 'val',
- label: 'label',
- isDefault: true,
- },
- ];
- const ValueRenderer = wrapper.instance().singleValueRenderer.bind(null, mockOptions);
- expect(
- shallow(<ValueRenderer {...mockReactSelectOptionProps({ value: 'test' })} />)
- ).toMatchSnapshot('value render');
-});
-
-function shallowRender(overrides: Partial<ComparisonForm['props']> = {}) {
- const profile = mockQualityProfile();
- const profiles = [
- profile,
- mockQualityProfile({ key: 'another', name: 'another name', isDefault: true }),
- mockQualityProfile({ key: 'java', name: 'java', language: 'java' }),
- ];
-
- return shallow<ComparisonForm>(
- <ComparisonForm
- onCompare={() => true}
- profile={profile}
- profiles={profiles}
- withKey="another"
- {...overrides}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResultActivation-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResultActivation-test.tsx
deleted file mode 100644
index faa6e4101e8..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResultActivation-test.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { Profile } from '../../../../api/quality-profiles';
-import { click, waitAndUpdate } from '../../../../helpers/testUtils';
-import ComparisonResultActivation from '../ComparisonResultActivation';
-
-jest.mock('../../../../api/rules', () => ({
- getRuleDetails: jest.fn().mockResolvedValue({ key: 'foo' }),
-}));
-
-it('should activate', async () => {
- const profile = { actions: { edit: true }, key: 'profile-key' } as Profile;
- const wrapper = shallow(
- <ComparisonResultActivation onDone={jest.fn()} profile={profile} ruleKey="foo" />
- );
- expect(wrapper).toMatchSnapshot();
-
- click(wrapper.find('Button'));
- expect(wrapper).toMatchSnapshot();
-
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-
- wrapper.find('ActivationFormModal').prop<Function>('onClose')();
- expect(wrapper).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx
deleted file mode 100644
index 3c4922760e1..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonResults-test.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { Profile } from '../../../../api/quality-profiles';
-import Link from '../../../../components/common/Link';
-import ComparisonEmpty from '../ComparisonEmpty';
-import ComparisonResults from '../ComparisonResults';
-
-it('should render ComparisonEmpty', () => {
- const output = shallow(
- <ComparisonResults
- inLeft={[]}
- inRight={[]}
- left={{ name: 'left' }}
- leftProfile={{} as Profile}
- modified={[]}
- refresh={jest.fn()}
- right={{ name: 'right' }}
- />
- );
- expect(output.is(ComparisonEmpty)).toBe(true);
-});
-
-it('should compare', () => {
- const inLeft = [{ key: 'rule1', name: 'rule1', severity: 'BLOCKER' }];
- const inRight = [
- { key: 'rule2', name: 'rule2', severity: 'CRITICAL' },
- { key: 'rule3', name: 'rule3', severity: 'MAJOR' },
- ];
- const modified = [
- {
- key: 'rule4',
- name: 'rule4',
- left: {
- severity: 'BLOCKER',
- params: { foo: 'bar' },
- },
- right: {
- severity: 'INFO',
- params: { foo: 'qwe' },
- },
- },
- ];
-
- const output = shallow(
- <ComparisonResults
- inLeft={inLeft}
- inRight={inRight}
- left={{ name: 'left' }}
- leftProfile={{} as Profile}
- modified={modified}
- refresh={jest.fn()}
- right={{ name: 'right' }}
- />
- );
-
- const leftDiffs = output.find('.js-comparison-in-left');
- expect(leftDiffs.length).toBe(1);
- expect(leftDiffs.find(Link).length).toBe(1);
- expect(leftDiffs.find(Link).prop('to')).toHaveProperty('search', '?rule_key=rule1&open=rule1');
- expect(leftDiffs.find(Link).prop('children')).toContain('rule1');
- expect(leftDiffs.find('SeverityIcon').length).toBe(1);
- expect(leftDiffs.find('SeverityIcon').prop('severity')).toBe('BLOCKER');
-
- const rightDiffs = output.find('.js-comparison-in-right');
- expect(rightDiffs.length).toBe(2);
- expect(rightDiffs.at(0).find(Link).length).toBe(1);
- expect(rightDiffs.at(0).find(Link).prop('to')).toHaveProperty(
- 'search',
- '?rule_key=rule2&open=rule2'
- );
- expect(rightDiffs.at(0).find(Link).prop('children')).toContain('rule2');
- expect(rightDiffs.at(0).find('SeverityIcon').length).toBe(1);
- expect(rightDiffs.at(0).find('SeverityIcon').prop('severity')).toBe('CRITICAL');
-
- const modifiedDiffs = output.find('.js-comparison-modified');
- expect(modifiedDiffs.length).toBe(1);
- expect(modifiedDiffs.find(Link).at(0).prop('to')).toHaveProperty(
- 'search',
- '?rule_key=rule4&open=rule4'
- );
- expect(modifiedDiffs.find(Link).at(0).prop('children')).toContain('rule4');
- expect(modifiedDiffs.find('SeverityIcon').length).toBe(2);
- expect(modifiedDiffs.text()).toContain('bar');
- expect(modifiedDiffs.text()).toContain('qwe');
-});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonForm-test.tsx.snap
deleted file mode 100644
index 70cd9a811d3..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonForm-test.tsx.snap
+++ /dev/null
@@ -1,21 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render option correctly: option render 1`] = `
-<Option
- data={
- Object {
- "value": "test",
- }
- }
-/>
-`;
-
-exports[`should render value correctly: value render 1`] = `
-<SingleValue
- data={
- Object {
- "value": "test",
- }
- }
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonResultActivation-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonResultActivation-test.tsx.snap
deleted file mode 100644
index ffb0851a94a..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/__snapshots__/ComparisonResultActivation-test.tsx.snap
+++ /dev/null
@@ -1,60 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should activate 1`] = `
-<DeferredSpinner
- loading={false}
->
- <Button
- disabled={false}
- onClick={[Function]}
- />
-</DeferredSpinner>
-`;
-
-exports[`should activate 2`] = `
-<DeferredSpinner
- loading={true}
->
- <Button
- disabled={true}
- onClick={[Function]}
- />
-</DeferredSpinner>
-`;
-
-exports[`should activate 3`] = `
-<DeferredSpinner
- loading={false}
->
- <Button
- disabled={true}
- onClick={[Function]}
- />
- <ActivationFormModal
- modalHeader="coding_rules.activate_in_quality_profile"
- onClose={[Function]}
- onDone={[MockFunction]}
- profiles={
- Array [
- Object {
- "actions": Object {
- "edit": true,
- },
- "key": "profile-key",
- },
- ]
- }
- />
-</DeferredSpinner>
-`;
-
-exports[`should activate 4`] = `
-<DeferredSpinner
- loading={false}
->
- <Button
- disabled={false}
- onClick={[Function]}
- />
-</DeferredSpinner>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx
index a9418b29e80..3022d193a24 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx
@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import classNames from 'classnames';
+import { some } from 'lodash';
import * as React from 'react';
import {
changeProfileParent,
@@ -36,8 +38,9 @@ import { Router, withRouter } from '../../../components/hoc/withRouter';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/system';
import { getRulesUrl } from '../../../helpers/urls';
+import { PROFILE_PATH } from '../constants';
import { Profile, ProfileActionModals } from '../types';
-import { getProfileComparePath, getProfilePath, PROFILE_PATH } from '../utils';
+import { getProfileComparePath, getProfilePath } from '../utils';
import DeleteProfileForm from './DeleteProfileForm';
import ProfileModalForm from './ProfileModalForm';
@@ -45,6 +48,7 @@ interface Props {
className?: string;
profile: Profile;
router: Router;
+ isComparable: boolean;
updateProfiles: () => Promise<void>;
}
@@ -175,7 +179,7 @@ export class ProfileActions extends React.PureComponent<Props, State> {
};
render() {
- const { profile } = this.props;
+ const { profile, isComparable } = this.props;
const { loading, openModal } = this.state;
const { actions = {} } = profile;
@@ -187,11 +191,12 @@ export class ProfileActions extends React.PureComponent<Props, State> {
});
const hasNoActiveRules = profile.activeRuleCount === 0;
+ const hasAnyAction = some([...Object.values(actions), !profile.isBuiltIn, isComparable]);
return (
<>
<ActionsDropdown
- className={this.props.className}
+ className={classNames(this.props.className, { invisible: !hasAnyAction })}
label={translateWithParameters(
'quality_profiles.actions',
profile.name,
@@ -217,12 +222,14 @@ export class ProfileActions extends React.PureComponent<Props, State> {
</ActionsDropdownItem>
)}
- <ActionsDropdownItem
- className="it__quality-profiles__compare"
- to={getProfileComparePath(profile.name, profile.language)}
- >
- {translate('compare')}
- </ActionsDropdownItem>
+ {isComparable && (
+ <ActionsDropdownItem
+ className="it__quality-profiles__compare"
+ to={getProfileComparePath(profile.name, profile.language)}
+ >
+ {translate('compare')}
+ </ActionsDropdownItem>
+ )}
{actions.copy && (
<>
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
index 5e18a430888..9f9f042ba00 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
@@ -48,7 +48,8 @@ export function ProfileContainer(props: QualityProfilesContextProps) {
return profileForKey ? null : <ProfileNotFound />;
}
- const profile = profiles.find((p) => p.language === language && p.name === name);
+ const filteredProfiles = profiles.filter((p) => p.language === language);
+ const profile = filteredProfiles.find((p) => p.name === name);
if (!profile) {
return <ProfileNotFound />;
@@ -62,7 +63,11 @@ export function ProfileContainer(props: QualityProfilesContextProps) {
return (
<div id="quality-profile">
<Helmet defer={false} title={profile.name} />
- <ProfileHeader profile={profile} updateProfiles={props.updateProfiles} />
+ <ProfileHeader
+ profile={profile}
+ isComparable={filteredProfiles.length > 1}
+ updateProfiles={props.updateProfiles}
+ />
<Outlet context={context} />
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.tsx
index 02665f130c1..28a49f85dd7 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.tsx
@@ -20,7 +20,7 @@
import * as React from 'react';
import { NavLink } from 'react-router-dom';
import { translate } from '../../../helpers/l10n';
-import { PROFILE_PATH } from '../utils';
+import { PROFILE_PATH } from '../constants';
export default function ProfileNotFound() {
return (
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx
index 1b74b4165c0..b7aba04b96a 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx
@@ -30,8 +30,8 @@ import {
import { mockQualityProfile, mockRouter } from '../../../../helpers/testMocks';
import { click, waitAndUpdate } from '../../../../helpers/testUtils';
import { queryToSearch } from '../../../../helpers/urls';
+import { PROFILE_PATH } from '../../constants';
import { ProfileActionModals } from '../../types';
-import { PROFILE_PATH } from '../../utils';
import DeleteProfileForm from '../DeleteProfileForm';
import { ProfileActions } from '../ProfileActions';
import ProfileModalForm from '../ProfileModalForm';
@@ -331,6 +331,12 @@ it('should not allow to set a profile as the default if the profile has no activ
function shallowRender(props: Partial<ProfileActions['props']> = {}) {
const router = mockRouter();
return shallow<ProfileActions>(
- <ProfileActions profile={PROFILE} router={router} updateProfiles={jest.fn()} {...props} />
+ <ProfileActions
+ isComparable={true}
+ profile={PROFILE}
+ router={router}
+ updateProfiles={jest.fn()}
+ {...props}
+ />
);
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap
index 4b5c3a0a4ae..098839406ec 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap
@@ -3,6 +3,7 @@
exports[`renders correctly: all permissions 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -77,6 +78,7 @@ exports[`renders correctly: all permissions 1`] = `
exports[`renders correctly: copy modal 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -127,6 +129,7 @@ exports[`renders correctly: copy modal 1`] = `
exports[`renders correctly: delete modal 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -176,6 +179,7 @@ exports[`renders correctly: delete modal 1`] = `
exports[`renders correctly: edit only 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -220,6 +224,7 @@ exports[`renders correctly: edit only 1`] = `
exports[`renders correctly: extend modal 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -270,6 +275,7 @@ exports[`renders correctly: extend modal 1`] = `
exports[`renders correctly: no permissions 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -297,6 +303,7 @@ exports[`renders correctly: no permissions 1`] = `
exports[`renders correctly: rename modal 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
@@ -347,6 +354,7 @@ exports[`renders correctly: rename modal 1`] = `
exports[`should not allow to set a profile as the default if the profile has no active rules 1`] = `
<Fragment>
<ActionsDropdown
+ className=""
label="quality_profiles.actions.name.JavaScript"
>
<ActionsDropdownItem
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileHeader-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/constants.ts
index dd88c2f4502..ed4e1f71118 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileHeader-test.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/constants.ts
@@ -17,20 +17,5 @@
* 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 ProfileHeader from '../ProfileHeader';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
- expect(shallowRender({ profile: mockQualityProfile({ isDefault: true }) })).toMatchSnapshot(
- 'for default profile'
- );
-});
-
-function shallowRender(props: Partial<ProfileHeader['props']> = {}) {
- return shallow(
- <ProfileHeader profile={mockQualityProfile()} updateProfiles={jest.fn()} {...props} />
- );
-}
+export const PROFILE_PATH = '/profiles';
+export const PROFILE_COMPARE_PATH = `${PROFILE_PATH}/compare`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx
index 972f92892c0..8fddc6e6e3e 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx
@@ -23,48 +23,55 @@ import { NavLink } from 'react-router-dom';
import Link from '../../../components/common/Link';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import Tooltip from '../../../components/controls/Tooltip';
+import { useLocation } from '../../../components/hoc/withRouter';
import DateFromNow from '../../../components/intl/DateFromNow';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getQualityProfileUrl } from '../../../helpers/urls';
import BuiltInQualityProfileBadge from '../components/BuiltInQualityProfileBadge';
import ProfileActions from '../components/ProfileActions';
import ProfileLink from '../components/ProfileLink';
+import { PROFILE_PATH } from '../constants';
import { Profile } from '../types';
-import { getProfileChangelogPath, getProfilesForLanguagePath, PROFILE_PATH } from '../utils';
+import {
+ getProfileChangelogPath,
+ getProfilesForLanguagePath,
+ isProfileComparePath,
+} from '../utils';
interface Props {
profile: Profile;
+ isComparable: boolean;
updateProfiles: () => Promise<void>;
}
-export default class ProfileHeader extends React.PureComponent<Props> {
- render() {
- const { profile } = this.props;
+export default function ProfileHeader(props: Props) {
+ const { profile, isComparable, updateProfiles } = props;
+ const location = useLocation();
- return (
- <div className="page-header quality-profile-header">
- <div className="note spacer-bottom">
- <NavLink end={true} to={PROFILE_PATH}>
- {translate('quality_profiles.page')}
- </NavLink>
- {' / '}
- <Link to={getProfilesForLanguagePath(profile.language)}>{profile.languageName}</Link>
- </div>
-
- <h1 className="page-title">
- <ProfileLink language={profile.language} name={profile.name}>
- <span>{profile.name}</span>
- </ProfileLink>
- {profile.isDefault && (
- <Tooltip overlay={translate('quality_profiles.list.default.help')}>
- <span className=" spacer-left badge">{translate('default')}</span>
- </Tooltip>
- )}
- {profile.isBuiltIn && (
- <BuiltInQualityProfileBadge className="spacer-left" tooltip={false} />
- )}
- </h1>
+ return (
+ <div className="page-header quality-profile-header">
+ <div className="note spacer-bottom">
+ <NavLink end={true} to={PROFILE_PATH}>
+ {translate('quality_profiles.page')}
+ </NavLink>
+ {' / '}
+ <Link to={getProfilesForLanguagePath(profile.language)}>{profile.languageName}</Link>
+ </div>
+ <h1 className="page-title">
+ <ProfileLink language={profile.language} name={profile.name}>
+ <span>{profile.name}</span>
+ </ProfileLink>
+ {profile.isDefault && (
+ <Tooltip overlay={translate('quality_profiles.list.default.help')}>
+ <span className=" spacer-left badge">{translate('default')}</span>
+ </Tooltip>
+ )}
+ {profile.isBuiltIn && (
+ <BuiltInQualityProfileBadge className="spacer-left" tooltip={false} />
+ )}
+ </h1>
+ {!isProfileComparePath(location.pathname) && (
<div className="pull-right">
<ul className="list-inline" style={{ lineHeight: '24px' }}>
<li className="small spacer-right">
@@ -78,47 +85,47 @@ export default class ProfileHeader extends React.PureComponent<Props> {
{translate('changelog')}
</Link>
</li>
+
<li>
<ProfileActions
className="pull-left"
profile={profile}
- updateProfiles={this.props.updateProfiles}
+ isComparable={isComparable}
+ updateProfiles={updateProfiles}
/>
</li>
</ul>
</div>
+ )}
- {profile.isBuiltIn && (
- <div className="page-description">
- {translate('quality_profiles.built_in.description')}
- </div>
- )}
+ {profile.isBuiltIn && (
+ <div className="page-description">{translate('quality_profiles.built_in.description')}</div>
+ )}
- {profile.parentKey && profile.parentName && (
- <div className="page-description">
- <FormattedMessage
- defaultMessage={translate('quality_profiles.extend_description')}
- id="quality_profiles.extend_description"
- values={{
- link: (
- <>
- <Link to={getQualityProfileUrl(profile.parentName, profile.language)}>
- {profile.parentName}
- </Link>
- <HelpTooltip
- className="little-spacer-left"
- overlay={translateWithParameters(
- 'quality_profiles.extend_description_help',
- profile.parentName
- )}
- />
- </>
- ),
- }}
- />
- </div>
- )}
- </div>
- );
- }
+ {profile.parentKey && profile.parentName && (
+ <div className="page-description">
+ <FormattedMessage
+ defaultMessage={translate('quality_profiles.extend_description')}
+ id="quality_profiles.extend_description"
+ values={{
+ link: (
+ <>
+ <Link to={getQualityProfileUrl(profile.parentName, profile.language)}>
+ {profile.parentName}
+ </Link>
+ <HelpTooltip
+ className="little-spacer-left"
+ overlay={translateWithParameters(
+ 'quality_profiles.extend_description_help',
+ profile.parentName
+ )}
+ />
+ </>
+ ),
+ }}
+ />
+ </div>
+ )}
+ </div>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileHeader-test.tsx.snap
deleted file mode 100644
index f181062d432..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileHeader-test.tsx.snap
+++ /dev/null
@@ -1,214 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="page-header quality-profile-header"
->
- <div
- className="note spacer-bottom"
- >
- <NavLink
- end={true}
- to="/profiles"
- >
- quality_profiles.page
- </NavLink>
- /
- <ForwardRef(Link)
- to={
- Object {
- "pathname": "/profiles",
- "search": "?language=js",
- }
- }
- >
- JavaScript
- </ForwardRef(Link)>
- </div>
- <h1
- className="page-title"
- >
- <ProfileLink
- language="js"
- name="name"
- >
- <span>
- name
- </span>
- </ProfileLink>
- </h1>
- <div
- className="pull-right"
- >
- <ul
- className="list-inline"
- style={
- Object {
- "lineHeight": "24px",
- }
- }
- >
- <li
- className="small spacer-right"
- >
- quality_profiles.updated_
-
- <DateFromNow />
- </li>
- <li
- className="small big-spacer-right"
- >
- quality_profiles.used_
-
- <DateFromNow />
- </li>
- <li>
- <ForwardRef(Link)
- className="button"
- to={
- Object {
- "pathname": "/profiles/changelog",
- "search": "?language=js&name=name",
- }
- }
- >
- changelog
- </ForwardRef(Link)>
- </li>
- <li>
- <withRouter(ProfileActions)
- className="pull-left"
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </li>
- </ul>
- </div>
-</div>
-`;
-
-exports[`should render correctly: for default profile 1`] = `
-<div
- className="page-header quality-profile-header"
->
- <div
- className="note spacer-bottom"
- >
- <NavLink
- end={true}
- to="/profiles"
- >
- quality_profiles.page
- </NavLink>
- /
- <ForwardRef(Link)
- to={
- Object {
- "pathname": "/profiles",
- "search": "?language=js",
- }
- }
- >
- JavaScript
- </ForwardRef(Link)>
- </div>
- <h1
- className="page-title"
- >
- <ProfileLink
- language="js"
- name="name"
- >
- <span>
- name
- </span>
- </ProfileLink>
- <Tooltip
- overlay="quality_profiles.list.default.help"
- >
- <span
- className=" spacer-left badge"
- >
- default
- </span>
- </Tooltip>
- </h1>
- <div
- className="pull-right"
- >
- <ul
- className="list-inline"
- style={
- Object {
- "lineHeight": "24px",
- }
- }
- >
- <li
- className="small spacer-right"
- >
- quality_profiles.updated_
-
- <DateFromNow />
- </li>
- <li
- className="small big-spacer-right"
- >
- quality_profiles.used_
-
- <DateFromNow />
- </li>
- <li>
- <ForwardRef(Link)
- className="button"
- to={
- Object {
- "pathname": "/profiles/changelog",
- "search": "?language=js&name=name",
- }
- }
- >
- changelog
- </ForwardRef(Link)>
- </li>
- <li>
- <withRouter(ProfileActions)
- className="pull-left"
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": true,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </li>
- </ul>
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx
index c54cc79976c..83cad70d3f0 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx
@@ -42,6 +42,7 @@ export default class ProfilesList extends React.PureComponent<Props> {
key={profile.key}
profile={profile}
updateProfiles={this.props.updateProfiles}
+ isComparable={profiles.length > 1}
/>
));
}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx
index ab015cac395..6d70b65f9d4 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.tsx
@@ -21,7 +21,8 @@ import * as React from 'react';
import Select from '../../../components/controls/Select';
import { Router, withRouter } from '../../../components/hoc/withRouter';
import { translate } from '../../../helpers/l10n';
-import { getProfilesForLanguagePath, PROFILE_PATH } from '../utils';
+import { PROFILE_PATH } from '../constants';
+import { getProfilesForLanguagePath } from '../utils';
interface Props {
currentFilter?: string;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx
index 274e6d96ec1..2ef64f2f3a1 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx
@@ -31,10 +31,11 @@ import { Profile } from '../types';
export interface ProfilesListRowProps {
profile: Profile;
updateProfiles: () => Promise<void>;
+ isComparable: boolean;
}
export function ProfilesListRow(props: ProfilesListRowProps) {
- const { profile } = props;
+ const { profile, isComparable } = props;
const offset = 25 * (profile.depth - 1);
const activeRulesUrl = getRulesUrl({
@@ -99,7 +100,11 @@ export function ProfilesListRow(props: ProfilesListRowProps) {
</td>
<td className="quality-profiles-table-actions thin nowrap text-middle text-right">
- <ProfileActions profile={profile} updateProfiles={props.updateProfiles} />
+ <ProfileActions
+ isComparable={isComparable}
+ profile={profile}
+ updateProfiles={props.updateProfiles}
+ />
</td>
</tr>
);
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesList-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesList-test.tsx
deleted file mode 100644
index deda1b4c523..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesList-test.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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, mockQualityProfile } from '../../../../helpers/testMocks';
-import ProfilesList from '../ProfilesList';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-
- expect(shallowRender({ language: 'css' })).toMatchSnapshot();
-
- expect(shallowRender({ language: 'unknown' })).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ProfilesList['props']> = {}) {
- return shallow(
- <ProfilesList
- languages={[mockLanguage(), mockLanguage({ key: 'js', name: 'JS' })]}
- profiles={[
- mockQualityProfile(),
- mockQualityProfile({ language: 'css', languageName: 'CSS' }),
- ]}
- updateProfiles={jest.fn()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListRow-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListRow-test.tsx
deleted file mode 100644
index b8415e754ec..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListRow-test.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 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 { ProfilesListRow, ProfilesListRowProps } from '../ProfilesListRow';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ profile: mockQualityProfile({ isBuiltIn: true }) })).toMatchSnapshot(
- 'built-in profile'
- );
- expect(shallowRender({ profile: mockQualityProfile({ isDefault: true }) })).toMatchSnapshot(
- 'default profile'
- );
- expect(
- shallowRender({ profile: mockQualityProfile({ activeDeprecatedRuleCount: 10 }) })
- ).toMatchSnapshot('with deprecated rules');
-});
-
-function shallowRender(props: Partial<ProfilesListRowProps> = {}) {
- return shallow(
- <ProfilesListRow
- profile={mockQualityProfile({ activeDeprecatedRuleCount: 0 })}
- updateProfiles={jest.fn()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesList-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesList-test.tsx.snap
deleted file mode 100644
index e9091091c0f..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesList-test.tsx.snap
+++ /dev/null
@@ -1,288 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div>
- <withRouter(ProfilesListHeader)
- languages={
- Array [
- Object {
- "key": "css",
- "name": "CSS",
- },
- Object {
- "key": "js",
- "name": "JS",
- },
- ]
- }
- />
- <div
- className="boxed-group boxed-group-inner quality-profiles-table"
- key="css"
- >
- <table
- className="data zebra zebra-hover"
- data-language="css"
- >
- <thead>
- <tr>
- <th>
- CSS
- ,
- quality_profiles.x_profiles.1
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.projects
- <HelpTooltip
- className="table-cell-doc"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_profiles.list.projects.help
- </div>
- }
- />
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.rules
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.updated
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.used
- </th>
- <th>
-  
- </th>
- </tr>
- </thead>
- <tbody>
- <Memo(ProfilesListRow)
- key="key"
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "css",
- "languageName": "CSS",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </tbody>
- </table>
- </div>
- <div
- className="boxed-group boxed-group-inner quality-profiles-table"
- key="js"
- >
- <table
- className="data zebra zebra-hover"
- data-language="js"
- >
- <thead>
- <tr>
- <th>
- JS
- ,
- quality_profiles.x_profiles.1
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.projects
- <HelpTooltip
- className="table-cell-doc"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_profiles.list.projects.help
- </div>
- }
- />
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.rules
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.updated
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.used
- </th>
- <th>
-  
- </th>
- </tr>
- </thead>
- <tbody>
- <Memo(ProfilesListRow)
- key="key"
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </tbody>
- </table>
- </div>
-</div>
-`;
-
-exports[`should render correctly 2`] = `
-<div>
- <withRouter(ProfilesListHeader)
- currentFilter="css"
- languages={
- Array [
- Object {
- "key": "css",
- "name": "CSS",
- },
- Object {
- "key": "js",
- "name": "JS",
- },
- ]
- }
- />
- <div
- className="boxed-group boxed-group-inner quality-profiles-table"
- key="css"
- >
- <table
- className="data zebra zebra-hover"
- data-language="css"
- >
- <thead>
- <tr>
- <th>
- CSS
- ,
- quality_profiles.x_profiles.1
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.projects
- <HelpTooltip
- className="table-cell-doc"
- overlay={
- <div
- className="big-padded-top big-padded-bottom"
- >
- quality_profiles.list.projects.help
- </div>
- }
- />
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.rules
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.updated
- </th>
- <th
- className="text-right nowrap"
- >
- quality_profiles.list.used
- </th>
- <th>
-  
- </th>
- </tr>
- </thead>
- <tbody>
- <Memo(ProfilesListRow)
- key="key"
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "css",
- "languageName": "CSS",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </tbody>
- </table>
- </div>
-</div>
-`;
-
-exports[`should render correctly 3`] = `
-<div>
- <withRouter(ProfilesListHeader)
- currentFilter="unknown"
- languages={
- Array [
- Object {
- "key": "css",
- "name": "CSS",
- },
- Object {
- "key": "js",
- "name": "JS",
- },
- ]
- }
- />
- <Alert
- className="spacer-top"
- variant="warning"
- >
- no_results
- </Alert>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListRow-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListRow-test.tsx.snap
deleted file mode 100644
index 90c5fc4af4e..00000000000
--- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListRow-test.tsx.snap
+++ /dev/null
@@ -1,411 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: built-in profile 1`] = `
-<tr
- className="quality-profiles-table-row text-middle"
- data-key="key"
- data-name="name"
->
- <td
- className="quality-profiles-table-name text-middle"
- >
- <div
- className="display-flex-center"
- style={
- Object {
- "paddingLeft": 0,
- }
- }
- >
- <div>
- <ProfileLink
- language="js"
- name="name"
- >
- name
- </ProfileLink>
- </div>
- <BuiltInQualityProfileBadge
- className="spacer-left"
- />
- </div>
- </td>
- <td
- className="quality-profiles-table-projects thin nowrap text-middle text-right"
- >
- <span>
- 3
- </span>
- </td>
- <td
- className="quality-profiles-table-rules thin nowrap text-middle text-right"
- >
- <div>
- <span
- className="spacer-right"
- >
- <Tooltip
- overlay="quality_profiles.deprecated_rules"
- >
- <ForwardRef(Link)
- className="badge badge-error"
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true&statuses=DEPRECATED",
- }
- }
- >
- 2
- </ForwardRef(Link)>
- </Tooltip>
- </span>
- <ForwardRef(Link)
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true",
- }
- }
- >
- 10
- </ForwardRef(Link)>
- </div>
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-actions thin nowrap text-middle text-right"
- >
- <withRouter(ProfileActions)
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": true,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </td>
-</tr>
-`;
-
-exports[`should render correctly: default 1`] = `
-<tr
- className="quality-profiles-table-row text-middle"
- data-key="key"
- data-name="name"
->
- <td
- className="quality-profiles-table-name text-middle"
- >
- <div
- className="display-flex-center"
- style={
- Object {
- "paddingLeft": 0,
- }
- }
- >
- <div>
- <ProfileLink
- language="js"
- name="name"
- >
- name
- </ProfileLink>
- </div>
- </div>
- </td>
- <td
- className="quality-profiles-table-projects thin nowrap text-middle text-right"
- >
- <span>
- 3
- </span>
- </td>
- <td
- className="quality-profiles-table-rules thin nowrap text-middle text-right"
- >
- <div>
- <ForwardRef(Link)
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true",
- }
- }
- >
- 10
- </ForwardRef(Link)>
- </div>
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-actions thin nowrap text-middle text-right"
- >
- <withRouter(ProfileActions)
- profile={
- Object {
- "activeDeprecatedRuleCount": 0,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </td>
-</tr>
-`;
-
-exports[`should render correctly: default profile 1`] = `
-<tr
- className="quality-profiles-table-row text-middle"
- data-key="key"
- data-name="name"
->
- <td
- className="quality-profiles-table-name text-middle"
- >
- <div
- className="display-flex-center"
- style={
- Object {
- "paddingLeft": 0,
- }
- }
- >
- <div>
- <ProfileLink
- language="js"
- name="name"
- >
- name
- </ProfileLink>
- </div>
- </div>
- </td>
- <td
- className="quality-profiles-table-projects thin nowrap text-middle text-right"
- >
- <Tooltip
- overlay="quality_profiles.list.default.help"
- >
- <span
- className="badge"
- >
- default
- </span>
- </Tooltip>
- </td>
- <td
- className="quality-profiles-table-rules thin nowrap text-middle text-right"
- >
- <div>
- <span
- className="spacer-right"
- >
- <Tooltip
- overlay="quality_profiles.deprecated_rules"
- >
- <ForwardRef(Link)
- className="badge badge-error"
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true&statuses=DEPRECATED",
- }
- }
- >
- 2
- </ForwardRef(Link)>
- </Tooltip>
- </span>
- <ForwardRef(Link)
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true",
- }
- }
- >
- 10
- </ForwardRef(Link)>
- </div>
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-actions thin nowrap text-middle text-right"
- >
- <withRouter(ProfileActions)
- profile={
- Object {
- "activeDeprecatedRuleCount": 2,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": true,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </td>
-</tr>
-`;
-
-exports[`should render correctly: with deprecated rules 1`] = `
-<tr
- className="quality-profiles-table-row text-middle"
- data-key="key"
- data-name="name"
->
- <td
- className="quality-profiles-table-name text-middle"
- >
- <div
- className="display-flex-center"
- style={
- Object {
- "paddingLeft": 0,
- }
- }
- >
- <div>
- <ProfileLink
- language="js"
- name="name"
- >
- name
- </ProfileLink>
- </div>
- </div>
- </td>
- <td
- className="quality-profiles-table-projects thin nowrap text-middle text-right"
- >
- <span>
- 3
- </span>
- </td>
- <td
- className="quality-profiles-table-rules thin nowrap text-middle text-right"
- >
- <div>
- <span
- className="spacer-right"
- >
- <Tooltip
- overlay="quality_profiles.deprecated_rules"
- >
- <ForwardRef(Link)
- className="badge badge-error"
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true&statuses=DEPRECATED",
- }
- }
- >
- 10
- </ForwardRef(Link)>
- </Tooltip>
- </span>
- <ForwardRef(Link)
- to={
- Object {
- "pathname": "/coding_rules",
- "search": "?qprofile=key&activation=true",
- }
- }
- >
- 10
- </ForwardRef(Link)>
- </div>
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-date thin nowrap text-middle text-right"
- >
- <DateFromNow />
- </td>
- <td
- className="quality-profiles-table-actions thin nowrap text-middle text-right"
- >
- <withRouter(ProfileActions)
- profile={
- Object {
- "activeDeprecatedRuleCount": 10,
- "activeRuleCount": 10,
- "childrenCount": 0,
- "depth": 1,
- "isBuiltIn": false,
- "isDefault": false,
- "isInherited": false,
- "key": "key",
- "language": "js",
- "languageName": "JavaScript",
- "name": "name",
- "projectCount": 3,
- }
- }
- updateProfiles={[MockFunction]}
- />
- </td>
-</tr>
-`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/utils.ts b/server/sonar-web/src/main/js/apps/quality-profiles/utils.ts
index 326de09028f..2db3e7fcb78 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/utils.ts
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/utils.ts
@@ -22,6 +22,7 @@ import { sortBy } from 'lodash';
import { Profile as BaseProfile } from '../../api/quality-profiles';
import { isValidDate, parseDate } from '../../helpers/dates';
import { queryToSearch } from '../../helpers/urls';
+import { PROFILE_COMPARE_PATH, PROFILE_PATH } from './constants';
import { Profile } from './types';
export function sortProfiles(profiles: BaseProfile[]): Profile[] {
@@ -65,8 +66,6 @@ export function isStagnant(profile: Profile): boolean {
return false;
}
-export const PROFILE_PATH = '/profiles';
-
export const getProfilesForLanguagePath = (language: string) => ({
pathname: PROFILE_PATH,
search: queryToSearch({ language }),
@@ -83,7 +82,7 @@ export const getProfileComparePath = (name: string, language: string, withKey?:
Object.assign(query, { withKey });
}
return {
- pathname: `${PROFILE_PATH}/compare`,
+ pathname: PROFILE_COMPARE_PATH,
search: queryToSearch(query),
};
};
@@ -107,3 +106,7 @@ export const getProfileChangelogPath = (
search: queryToSearch(query),
};
};
+
+export const isProfileComparePath = (pathname: string): boolean => {
+ return pathname === PROFILE_COMPARE_PATH;
+};
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index a38b03e8d5c..7e24416d250 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { To } from 'react-router-dom';
+import { CompareResponse } from '../api/quality-profiles';
import { RuleDescriptionSections } from '../apps/coding-rules/rule';
import { Exporter, Profile } from '../apps/quality-profiles/types';
import { Location, Router } from '../components/hoc/withRouter';
@@ -437,6 +438,36 @@ export function mockQualityProfile(overrides: Partial<Profile> = {}): Profile {
};
}
+export function mockCompareResult(overrides: Partial<CompareResponse> = {}): CompareResponse {
+ return {
+ left: { name: 'Profile A' },
+ right: { name: 'Profile B' },
+ inLeft: [
+ {
+ key: 'java:S4604',
+ name: 'Rule in left',
+ severity: 'MINOR',
+ },
+ ],
+ inRight: [
+ {
+ key: 'java:S5128',
+ name: 'Rule in right',
+ severity: 'MAJOR',
+ },
+ ],
+ modified: [
+ {
+ key: 'java:S1698',
+ name: '== and != should not be used when equals is overridden',
+ left: { params: {}, severity: 'MINOR' },
+ right: { params: {}, severity: 'CRITICAL' },
+ },
+ ],
+ ...overrides,
+ };
+}
+
export function mockQualityProfileInheritance(
overrides: Partial<ProfileInheritanceDetails> = {}
): ProfileInheritanceDetails {
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 7704dea71d0..253cc61493c 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1701,6 +1701,7 @@ quality_profile.empty_comparison=The quality profiles are equal.
quality_profiles.activate_more=Activate More
quality_profiles.activate_more.help.built_in=This quality profile is built in, and cannot be updated manually. If you want to activate more rules, create a new profile that inherits from this one and add rules there.
quality_profiles.activate_more_rules=Activate More Rules
+quality_profiles.comparison.activate_rule=Activate rule for profile "{0}"
quality_profiles.intro1=Quality profiles are collections of rules to apply during an analysis.
quality_profiles.intro2=For each language there is a default profile. All projects not explicitly assigned to some other profile will be analyzed with the default. Ideally, all projects will use the same profile for a language.
quality_profiles.list.projects=Projects
@@ -1758,7 +1759,6 @@ quality_profiles.actions=Open {0} {1} quality profile actions
-
#------------------------------------------------------------------------------
#
# QUALITY GATES