diff options
author | guillaume-peoch-sonarsource <guillaume.peoch@sonarsource.com> | 2023-07-04 17:53:54 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-07-18 20:03:22 +0000 |
commit | 5ebbed2a0f508d5bef63de4ed1e89a57e67e25df (patch) | |
tree | 9685cdca497f78821ca199c71931ec6bf1a9e0d4 | |
parent | ee95bb3b3db83bbadf265c850138134bd740ecf3 (diff) | |
download | sonarqube-5ebbed2a0f508d5bef63de4ed1e89a57e67e25df.tar.gz sonarqube-5ebbed2a0f508d5bef63de4ed1e89a57e67e25df.zip |
SONAR-18441 RTL migration for Quality Profiles
87 files changed, 1288 insertions, 5845 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 372d7afe666..38102654660 100644 --- a/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts @@ -18,14 +18,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { cloneDeep } from 'lodash'; +import { ProfileChangelogEvent } from '../../apps/quality-profiles/types'; import { RequestData } from '../../helpers/request'; import { mockCompareResult, + mockGroup, mockPaging, mockQualityProfile, + mockQualityProfileChangelogEvent, mockRuleDetails, + mockUserSelected, } from '../../helpers/testMocks'; import { SearchRulesResponse } from '../../types/coding-rules'; +import { SearchRulesQuery } from '../../types/rules'; import { Dict, Paging, ProfileInheritanceDetails, RuleDetails } from '../../types/types'; import { CompareResponse, @@ -34,22 +39,41 @@ import { SearchQualityProfilesParameters, SearchQualityProfilesResponse, activateRule, + addGroup, + addUser, + associateProject, changeProfileParent, compareProfiles, copyProfile, createQualityProfile, + deleteProfile, + dissociateProject, + getExporters, getImporters, + getProfileChangelog, getProfileInheritance, getProfileProjects, + getQualityProfile, + removeGroup, + removeUser, + renameProfile, + restoreQualityProfile, + searchGroups, searchQualityProfiles, + searchUsers, + setDefaultProfile, } from '../quality-profiles'; import { getRuleDetails, searchRules } from '../rules'; +jest.mock('../../api/rules'); + export default class QualityProfilesServiceMock { isAdmin = false; listQualityProfile: Profile[] = []; languageMapping: Dict<Partial<Profile>> = { c: { language: 'c', languageName: 'C' }, + php: { language: 'php', languageName: 'PHP' }, + java: { language: 'java', languageName: 'Java' }, }; comparisonResult: CompareResponse = mockCompareResult(); @@ -58,21 +82,43 @@ export default class QualityProfilesServiceMock { paging: mockPaging(), }; + profileProjects: { + [profileKey: string]: ProfileProject[]; + } = {}; + + changelogEvents: ProfileChangelogEvent[] = []; + constructor() { this.resetQualityProfile(); this.resetComparisonResult(); - - (searchQualityProfiles as jest.Mock).mockImplementation(this.handleSearchQualityProfiles); - (createQualityProfile as jest.Mock).mockImplementation(this.handleCreateQualityProfile); - (changeProfileParent as jest.Mock).mockImplementation(this.handleChangeProfileParent); - (getProfileInheritance as jest.Mock).mockImplementation(this.handleGetProfileInheritance); - (getProfileProjects as jest.Mock).mockImplementation(this.handleGetProfileProjects); - (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); + this.resetChangelogEvents(); + + jest.mocked(searchQualityProfiles).mockImplementation(this.handleSearchQualityProfiles); + jest.mocked(createQualityProfile).mockImplementation(this.handleCreateQualityProfile); + jest.mocked(changeProfileParent).mockImplementation(this.handleChangeProfileParent); + jest.mocked(getProfileInheritance).mockImplementation(this.handleGetProfileInheritance); + jest.mocked(getProfileProjects).mockImplementation(this.handleGetProfileProjects); + jest.mocked(copyProfile).mockImplementation(this.handleCopyProfile); + jest.mocked(getImporters).mockImplementation(this.handleGetImporters); + jest.mocked(searchRules).mockImplementation(this.handleSearchRules); + jest.mocked(compareProfiles).mockImplementation(this.handleCompareQualityProfiles); + jest.mocked(activateRule).mockImplementation(this.handleActivateRule); + jest.mocked(getRuleDetails).mockImplementation(this.handleGetRuleDetails); + jest.mocked(restoreQualityProfile).mockImplementation(this.handleRestoreQualityProfile); + jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers); + jest.mocked(addUser).mockImplementation(this.handleAddUser); + jest.mocked(searchGroups).mockImplementation(this.handleSearchGroups); + jest.mocked(addGroup).mockImplementation(this.handleAddGroup); + jest.mocked(removeGroup).mockImplementation(this.handleRemoveGroup); + jest.mocked(removeUser).mockImplementation(this.handleRemoveUser); + jest.mocked(associateProject).mockImplementation(this.handleAssociateProject); + jest.mocked(getProfileChangelog).mockImplementation(this.handleGetProfileChangelog); + jest.mocked(dissociateProject).mockImplementation(this.handleDissociateProject); + jest.mocked(getQualityProfile).mockImplementation(this.handleGetQualityProfile); + jest.mocked(getExporters).mockImplementation(this.handleGetExporters); + jest.mocked(deleteProfile).mockImplementation(this.handleDeleteProfile); + jest.mocked(renameProfile).mockImplementation(this.handleRenameProfile); + jest.mocked(setDefaultProfile).mockImplementation(this.handleSetDefaultProfile); } resetQualityProfile() { @@ -98,9 +144,66 @@ export default class QualityProfilesServiceMock { name: 'java quality profile #2', activeDeprecatedRuleCount: 1, actions: { - edit: true, + edit: this.isAdmin, + }, + }), + mockQualityProfile({ + key: 'sonar', + language: 'java', + languageName: 'Java', + name: 'Sonar way', + isBuiltIn: true, + isDefault: true, + }), + mockQualityProfile({ + key: 'old-php-qp', + language: 'php', + languageName: 'PHP', + name: 'Good old PHP quality profile', + activeDeprecatedRuleCount: 8, + rulesUpdatedAt: '2019-09-16T21:10:36+0000', + parentKey: 'php-sonar-way-1', + actions: { + edit: this.isAdmin, + associateProjects: this.isAdmin, + delete: this.isAdmin, + setAsDefault: this.isAdmin, + copy: this.isAdmin, + }, + }), + mockQualityProfile({ + key: 'no-rule-qp', + activeRuleCount: 0, + actions: { + associateProjects: true, + setAsDefault: this.isAdmin, }, }), + mockQualityProfile({ + activeRuleCount: 6, + isBuiltIn: true, + key: 'php-sonar-way-1', + name: 'PHP Sonar way 1', + language: 'php', + languageName: 'PHP', + }), + mockQualityProfile({ + activeRuleCount: 6, + isBuiltIn: false, + key: 'php-sonar-way-2', + name: 'PHP Sonar way 2', + language: 'php', + languageName: 'PHP', + }), + mockQualityProfile({ + activeRuleCount: 3, + isBuiltIn: false, + key: 'php-sonar-way', + parentKey: 'old-php-qp', + name: 'PHP way', + language: 'php', + languageName: 'PHP', + }), ]; } @@ -108,17 +211,95 @@ export default class QualityProfilesServiceMock { this.comparisonResult = mockCompareResult(); } + resetChangelogEvents() { + this.changelogEvents = [ + mockQualityProfileChangelogEvent({ + date: '2019-05-23T04:12:32+0100', + }), + mockQualityProfileChangelogEvent({ + date: '2019-05-23T03:12:32+0100', + action: 'DEACTIVATED', + ruleKey: 'php:rule1', + ruleName: 'PHP Rule', + }), + mockQualityProfileChangelogEvent({ + date: '2019-05-23T03:12:32+0100', + action: 'ACTIVATED', + ruleKey: 'c:rule0', + ruleName: 'Rule 0', + params: {}, + }), + mockQualityProfileChangelogEvent({ + date: '2019-04-23T03:12:32+0100', + action: 'DEACTIVATED', + ruleKey: 'c:rule0', + ruleName: 'Rule 0', + }), + mockQualityProfileChangelogEvent({ + date: '2019-04-23T03:12:32+0100', + action: 'DEACTIVATED', + ruleKey: 'c:rule1', + ruleName: 'Rule 1', + }), + mockQualityProfileChangelogEvent({ + date: '2019-04-23T02:12:32+0100', + action: 'DEACTIVATED', + ruleKey: 'c:rule2', + ruleName: 'Rule 2', + authorName: 'John Doe', + }), + mockQualityProfileChangelogEvent({ + date: '2019-03-23T02:12:32+0100', + ruleKey: 'c:rule2', + ruleName: 'Rule 2', + authorName: 'John Doe', + params: { + severity: 'CRITICAL', + credentialWords: 'foo,bar', + }, + }), + ]; + } + resetSearchRulesResponse() { this.searchRulesResponse = { + facets: [ + { + property: 'types', + values: [ + { val: 'CODE_SMELL', count: 250 }, + { val: 'BUG', count: 60 }, + { val: 'VULNERABILITY', count: 40 }, + { val: 'SECURITY_HOTSPOT', count: 50 }, + ], + }, + ], rules: [], paging: { pageIndex: 1, - pageSize: 500, - total: 0, + pageSize: 400, + total: 400, }, }; } + resetProfileProjects() { + this.profileProjects = { + 'old-php-qp': [ + { + key: 'Benflix', + name: 'Benflix', + selected: true, + }, + { + key: 'Twitter', + name: 'Twitter', + selected: false, + }, + ], + }; + } + handleGetImporters = () => { return this.reply([]); }; @@ -142,11 +323,19 @@ export default class QualityProfilesServiceMock { return this.reply(copiedQualityProfile); }; - handleGetProfileProjects = (): Promise<{ + handleGetProfileProjects = ( + data: RequestData + ): Promise<{ more: boolean; paging: Paging; results: ProfileProject[]; }> => { + const results = (this.profileProjects[data.key] ?? []).filter( + (project) => + project.selected === + (data.selected !== undefined ? Boolean(data.selected === 'selected') : true) + ); + return this.reply({ more: false, paging: { @@ -154,7 +343,7 @@ export default class QualityProfilesServiceMock { pageSize: 10, total: 0, }, - results: [], + results, }); }; @@ -173,10 +362,16 @@ export default class QualityProfilesServiceMock { }); } - // Lets fake it for now + const ancestors = this.listQualityProfile.filter( + (p) => p.key === profile.parentKey + ) as ProfileInheritanceDetails[]; + const children = this.listQualityProfile.filter( + (p) => p.parentKey === profile.key + ) as ProfileInheritanceDetails[]; + return this.reply({ - ancestors: [], - children: [], + ancestors, + children, profile: { name: profile.name, activeRuleCount: 0, @@ -226,10 +421,54 @@ export default class QualityProfilesServiceMock { return this.reply({ profile: newQualityProfile }); }; - handleSearchRules = (): Promise<SearchRulesResponse> => { + handleSearchRules = (data: SearchRulesQuery): Promise<SearchRulesResponse> => { + if (data.activation) { + this.setRulesSearchResponse({ + facets: [ + { + property: 'types', + values: [ + { + val: 'CODE_SMELL', + count: 200, + }, + { + val: 'BUG', + count: 50, + }, + { + val: 'VULNERABILITY', + count: 30, + }, + { + val: 'SECURITY_HOTSPOT', + count: 20, + }, + ], + }, + ], + rules: [], + paging: { + pageIndex: 1, + pageSize: 1, + total: 300, + }, + }); + } return this.reply(this.searchRulesResponse); }; + handleGetQualityProfile = () => { + return this.reply({ + profile: mockQualityProfile(), + compareToSonarWay: { + profile: '', + profileName: 'Sonar way', + missingRuleCount: 2, + }, + }); + }; + handleSearchQualityProfiles = ( parameters: SearchQualityProfilesParameters = {} ): Promise<SearchQualityProfilesResponse> => { @@ -288,8 +527,169 @@ export default class QualityProfilesServiceMock { }); }; + handleRestoreQualityProfile = () => { + return this.reply({ + profile: { + key: 'c-sonarsource-06756', + name: 'SonarSource', + language: 'c', + isDefault: false, + isInherited: false, + languageName: 'C', + }, + ruleSuccesses: 231, + ruleFailures: 0, + }); + }; + + handleSearchUsers = () => { + return this.reply({ + users: [ + mockUserSelected({ + login: 'buzz', + name: 'Buzz', + }), + ], + paging: mockPaging(), + }); + }; + + handleAddUser = () => { + return this.reply(undefined); + }; + + handleRemoveUser = () => { + return this.reply(undefined); + }; + + handleSearchGroups = () => { + return this.reply({ + groups: [mockGroup({ name: 'ACDC' })], + paging: mockPaging(), + }); + }; + + handleAddGroup = () => { + return this.reply(undefined); + }; + + handleRemoveGroup = () => { + return this.reply(undefined); + }; + + handleAssociateProject = ({ key }: Profile, project: string) => { + const projects = this.profileProjects[key].map((profileProject) => { + if (profileProject.key === project) { + return { + ...profileProject, + selected: true, + }; + } + return profileProject; + }); + this.profileProjects[key] = projects; + + return this.reply({}); + }; + + handleDissociateProject = ({ key }: Profile, project: string) => { + const projects = this.profileProjects[key].map((profileProject) => { + if (profileProject.key === project) { + return { + ...profileProject, + selected: false, + }; + } + return profileProject; + }); + this.profileProjects[key] = projects; + + return this.reply({}); + }; + + handleGetProfileChangelog: typeof getProfileChangelog = (since, to, { language }, page) => { + const PAGE_SIZE = 50; + const p = page || 1; + const events = this.changelogEvents.filter((event) => { + if (event.ruleKey.split(':')[0] !== language) { + return false; + } + if (since && new Date(since) >= new Date(event.date)) { + return false; + } + if (to && new Date(to) <= new Date(event.date)) { + return false; + } + return true; + }); + + return this.reply({ + events: events.slice((p - 1) * PAGE_SIZE, (p - 1) * PAGE_SIZE + PAGE_SIZE), + paging: mockPaging({ + total: events.length, + pageSize: PAGE_SIZE, + pageIndex: p, + }), + }); + }; + + handleGetExporters = () => { + return this.reply([ + { + key: 'sonarlint-vs', + name: 'SonarLint for Visual Studio', + languages: ['php'], + }, + { + key: 'sonarlint-eclipse', + name: 'SonarLint for Eclipse', + languages: ['php'], + }, + ]); + }; + + handleDeleteProfile = ({ name }: Profile) => { + // delete Children + const qualityProfileToDelete = this.listQualityProfile.find((profile) => profile.name === name); + this.listQualityProfile = this.listQualityProfile.filter( + (profile) => profile.parentKey !== qualityProfileToDelete?.key + ); + + // delete profile + this.listQualityProfile = this.listQualityProfile.filter((profile) => profile.name !== name); + + return this.reply({}); + }; + + handleRenameProfile = (key: string, newName: string) => { + this.listQualityProfile = this.listQualityProfile.map((profile) => { + if (profile.key === key) { + return { + ...profile, + name: newName, + }; + } + return profile; + }); + return this.reply({}); + }; + + handleSetDefaultProfile = ({ name }: Profile) => { + this.listQualityProfile = this.listQualityProfile.map((profile) => { + if (profile.name === name) { + return { + ...profile, + isDefault: true, + }; + } + return profile; + }); + return Promise.resolve(); + }; + setAdmin() { this.isAdmin = true; + this.resetQualityProfile(); } setRulesSearchResponse(overrides: Partial<SearchRulesResponse>) { @@ -304,6 +704,8 @@ export default class QualityProfilesServiceMock { this.resetQualityProfile(); this.resetComparisonResult(); this.resetSearchRulesResponse(); + this.resetProfileProjects(); + this.resetChangelogEvents(); } reply<T>(response: T): Promise<T> { diff --git a/server/sonar-web/src/main/js/api/quality-profiles.ts b/server/sonar-web/src/main/js/api/quality-profiles.ts index 5f1d0090082..8e87cbe5107 100644 --- a/server/sonar-web/src/main/js/api/quality-profiles.ts +++ b/server/sonar-web/src/main/js/api/quality-profiles.ts @@ -144,13 +144,6 @@ export function changeProfileParent( }).catch(throwGlobalError); } -export function getQualityProfileBackupUrl({ language, name: qualityProfile }: Profile) { - const queryParams = Object.entries({ language, qualityProfile }) - .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) - .join('&'); - return `/api/qualityprofiles/backup?${queryParams}`; -} - export function getQualityProfileExporterUrl( { key: exporterKey }: Exporter, { language, name: qualityProfile }: Profile diff --git a/server/sonar-web/src/main/js/api/rules.ts b/server/sonar-web/src/main/js/api/rules.ts index 4ae50959e67..beb06283727 100644 --- a/server/sonar-web/src/main/js/api/rules.ts +++ b/server/sonar-web/src/main/js/api/rules.ts @@ -31,11 +31,6 @@ export function searchRules(data: SearchRulesQuery): Promise<SearchRulesResponse return getJSON('/api/rules/search', data).catch(throwGlobalError); } -export function takeFacet(response: SearchRulesResponse, property: string) { - const facet = response.facets?.find((f) => f.property === property); - return facet ? facet.values : []; -} - export function getRuleRepositories(parameters: { q: string; }): Promise<Array<{ key: string; language: string; name: string }>> { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfileApp-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfileApp-it.tsx new file mode 100644 index 00000000000..7f8209579f8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfileApp-it.tsx @@ -0,0 +1,513 @@ +/* + * 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 { act, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import selectEvent from 'react-select-event'; +import QualityProfilesServiceMock from '../../../api/mocks/QualityProfilesServiceMock'; +import { renderAppRoutes } from '../../../helpers/testReactTestingUtils'; +import { byRole, byText } from '../../../helpers/testSelector'; +import routes from '../routes'; + +jest.mock('../../../api/quality-profiles'); +jest.mock('../../../api/rules'); + +const serviceMock = new QualityProfilesServiceMock(); +const ui = { + permissionSection: byRole('region', { name: 'permissions.page' }), + projectSection: byRole('region', { name: 'projects' }), + rulesSection: byRole('region', { name: 'rules' }), + exportersSection: byRole('region', { name: 'quality_profiles.exporters' }), + inheritanceSection: byRole('region', { name: 'quality_profiles.profile_inheritance' }), + grantPermissionButton: byRole('button', { + name: 'quality_profiles.grant_permissions_to_more_users', + }), + grantPermissionDialog: byRole('dialog', { + name: 'quality_profiles.grant_permissions_to_user_or_group', + }), + removeGroupDialog: byRole('dialog', { + name: 'quality_profiles.permissions.remove.group', + }), + removeUserDialog: byRole('dialog', { + name: 'quality_profiles.permissions.remove.user', + }), + deleteProfileDialog: byRole('dialog', { name: 'quality_profiles.delete_confirm_title' }), + changeProjectsDialog: byRole('dialog', { name: 'projects' }), + changeParentDialog: byRole('dialog', { name: 'quality_profiles.change_parent' }), + extendDialog: byRole('dialog', { name: /quality_profiles.extend_x_title/ }), + copyDialog: byRole('dialog', { name: /quality_profiles.copy_x_title/ }), + renameDialog: byRole('dialog', { name: /quality_profiles.rename_x_title/ }), + selectparent: byRole('combobox', { name: /quality_profiles.parent/ }), + selectUserOrGroup: byRole('combobox', { name: 'quality_profiles.search_description' }), + twitterCheckbox: byRole('checkbox', { name: 'Twitter Twitter' }), + benflixCheckbox: byRole('checkbox', { name: 'Benflix Benflix' }), + addButton: byRole('button', { name: 'add_verb' }), + removeButton: byRole('button', { name: 'remove' }), + closeButton: byRole('button', { name: 'close' }), + changeProjectsButton: byRole('button', { name: 'quality_profiles.change_projects' }), + changeButton: byRole('button', { name: 'change_verb' }), + withoutFilterButton: byRole('button', { name: 'quality_gates.projects.without' }), + changeParentButton: byRole('button', { name: 'quality_profiles.change_parent' }), + qualityProfileActions: byRole('button', { + name: /quality_profiles.actions/, + }), + qualityProfilesHeader: byRole('heading', { name: 'quality_profiles.page' }), + deleteQualityProfileButton: byRole('button', { name: 'delete' }), + activateMoreRulesButton: byRole('button', { name: 'quality_profiles.activate_more' }), + activateMoreLink: byRole('link', { name: 'quality_profiles.activate_more' }), + activateMoreRulesLink: byRole('link', { name: 'quality_profiles.activate_more_rules' }), + backUpLink: byRole('link', { name: 'backup_verb' }), + compareLink: byRole('link', { name: 'compare' }), + extendButton: byRole('button', { name: 'extend' }), + copyButton: byRole('button', { name: 'copy' }), + renameButton: byRole('button', { name: 'rename' }), + setAsDefaultButton: byRole('button', { name: 'set_as_default' }), + newNameInput: byRole('textbox', { name: /quality_profiles.new_name/ }), + qualityProfilePageLink: byRole('link', { name: 'quality_profiles.page' }), + rulesTotalRow: byRole('row', { name: /total/ }), + rulesBugsRow: byRole('row', { name: /issue.type.BUG.plural/ }), + rulesVulnerabilitiesRow: byRole('row', { name: /issue.type.VULNERABILITY/ }), + rulesCodeSmellsRow: byRole('row', { name: /issue.type.CODE_SMELL/ }), + rulesSecurityHotspotsRow: byRole('row', { name: /issue.type.SECURITY_HOTSPOT/ }), + rulesMissingSonarWayWarning: byText('quality_profiles.sonarway_missing_rules_description'), + rulesMissingSonarWayLink: byRole('link', { name: '2' }), + rulesDeprecatedWarning: byText('quality_profiles.deprecated_rules_description'), + rulesDeprecatedLink: byRole('link', { name: '8' }), +}; + +beforeEach(() => { + serviceMock.reset(); +}); + +describe('Admin or user with permission', () => { + beforeEach(() => { + serviceMock.setAdmin(); + }); + + describe('Permissions', () => { + it('should be able to grant permission to a user and remove it', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + expect(await ui.permissionSection.find()).toBeInTheDocument(); + + // Add user + await act(async () => { + await user.click(ui.grantPermissionButton.get()); + }); + expect(ui.grantPermissionDialog.get()).toBeInTheDocument(); + await selectEvent.select(ui.selectUserOrGroup.get(), 'Buzz'); + await user.click(ui.addButton.get()); + expect(ui.permissionSection.byText('Buzz').get()).toBeInTheDocument(); + + // Remove User + await user.click( + ui.permissionSection + .byRole('button', { name: 'quality_profiles.permissions.remove.user_x.Buzz' }) + .get() + ); + expect(ui.removeUserDialog.get()).toBeInTheDocument(); + await user.click(ui.removeButton.get()); + expect(ui.permissionSection.byText('buzz').query()).not.toBeInTheDocument(); + }); + + it('should be able to grant permission to a group and remove it', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + expect(await ui.permissionSection.find()).toBeInTheDocument(); + + // Add Group + await act(async () => { + await user.click(ui.grantPermissionButton.get()); + }); + expect(ui.grantPermissionDialog.get()).toBeInTheDocument(); + await selectEvent.select(ui.selectUserOrGroup.get(), 'ACDC'); + await user.click(ui.addButton.get()); + expect(ui.permissionSection.byText('ACDC').get()).toBeInTheDocument(); + + // Remove group + await user.click( + ui.permissionSection + .byRole('button', { name: 'quality_profiles.permissions.remove.group_x.ACDC' }) + .get() + ); + expect(ui.removeGroupDialog.get()).toBeInTheDocument(); + await user.click(ui.removeButton.get()); + expect(ui.permissionSection.byText('ACDC').query()).not.toBeInTheDocument(); + }); + + it('should not be able to grant permission if the profile is built-in', async () => { + renderQualityProfile('sonar'); + expect(await screen.findByText('Sonar way')).toBeInTheDocument(); + expect(ui.permissionSection.query()).not.toBeInTheDocument(); + }); + }); + + describe('Projects', () => { + it('should be able to add a project to Quality Profile with active rules', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + expect(await ui.projectSection.find()).toBeInTheDocument(); + expect(ui.projectSection.byText('Twitter').query()).not.toBeInTheDocument(); + await user.click(ui.changeProjectsButton.get()); + expect(ui.changeProjectsDialog.get()).toBeInTheDocument(); + + await user.click(ui.withoutFilterButton.get()); + await user.click(ui.twitterCheckbox.get()); + await user.click(ui.closeButton.get()); + expect(ui.projectSection.byText('Twitter').get()).toBeInTheDocument(); + }); + + it('should be able to remove a project from a Quality Profile with active rules', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + expect(await ui.projectSection.find()).toBeInTheDocument(); + expect(ui.projectSection.byText('Benflix').get()).toBeInTheDocument(); + await user.click(ui.changeProjectsButton.get()); + expect(ui.changeProjectsDialog.get()).toBeInTheDocument(); + + await user.click(ui.benflixCheckbox.get()); + await user.click(ui.closeButton.get()); + expect(ui.projectSection.byText('Benflix').query()).not.toBeInTheDocument(); + }); + + it('should not be able to change project for Quality Profile with no active rules', async () => { + renderQualityProfile('no-rule-qp'); + + expect(await ui.projectSection.find()).toBeInTheDocument(); + expect(ui.changeProjectsButton.get()).toHaveAttribute('disabled'); + }); + + it('should not be able to change projects for default profiles', async () => { + renderQualityProfile('sonar'); + expect(await ui.projectSection.find()).toBeInTheDocument(); + expect( + await ui.projectSection.byText('quality_profiles.projects_for_default').get() + ).toBeInTheDocument(); + }); + }); + + describe('Rules', () => { + it('should be able to activate more rules', async () => { + renderQualityProfile(); + + expect(await ui.rulesSection.find()).toBeInTheDocument(); + + expect(ui.activateMoreLink.get()).toBeInTheDocument(); + expect(ui.activateMoreLink.get()).toHaveAttribute( + 'href', + '/coding_rules?qprofile=old-php-qp&activation=false' + ); + }); + + it("shouldn't be able to activate more rules for built in Quality Profile", async () => { + renderQualityProfile('sonar'); + expect(await ui.rulesSection.find()).toBeInTheDocument(); + expect(ui.activateMoreRulesButton.get()).toBeInTheDocument(); + expect(ui.activateMoreRulesButton.get()).toHaveClass('disabled'); + }); + }); + + describe('Inheritance', () => { + it("should be able to change a quality profile's parents", async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + expect(await ui.inheritanceSection.find()).toBeInTheDocument(); + // Parents + expect(ui.inheritanceSection.byText('PHP Sonar way 1').get()).toBeInTheDocument(); + expect(ui.inheritanceSection.byText('PHP Sonar way 2').query()).not.toBeInTheDocument(); + // Children + expect(ui.inheritanceSection.byText('PHP way').get()).toBeInTheDocument(); + + await user.click(ui.changeParentButton.get()); + expect(await ui.changeParentDialog.find()).toBeInTheDocument(); + expect(ui.changeButton.get()).toBeDisabled(); + await selectEvent.select(ui.selectparent.get(), 'PHP Sonar way 2'); + await user.click(ui.changeButton.get()); + expect(ui.changeParentDialog.query()).not.toBeInTheDocument(); + + // Parents + expect(ui.inheritanceSection.byText('PHP Sonar way 2').get()).toBeInTheDocument(); + expect(ui.inheritanceSection.byText('PHP Sonar way 1').query()).not.toBeInTheDocument(); + // Children + expect(ui.inheritanceSection.byText('PHP way').get()).toBeInTheDocument(); + }); + + it("should not be able to change a Built-in quality profile's parents", async () => { + renderQualityProfile('php-sonar-way-1'); + + expect(await ui.inheritanceSection.find()).toBeInTheDocument(); + expect(ui.changeParentButton.query()).not.toBeInTheDocument(); + }); + }); + + describe('Actions', () => { + it('should be able to activate more rules', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + expect(ui.activateMoreRulesLink.get()).toBeInTheDocument(); + expect(ui.activateMoreRulesLink.get()).toHaveAttribute( + 'href', + '/coding_rules?qprofile=old-php-qp&activation=false' + ); + }); + + it('should be able to extend a quality profile', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + expect(await screen.findByText('Good old PHP quality profile')).toBeInTheDocument(); + + await user.click(ui.qualityProfileActions.get()); + await user.click(ui.extendButton.get()); + + expect(ui.extendDialog.get()).toBeInTheDocument(); + expect(ui.extendDialog.byRole('button', { name: 'extend' }).get()).toBeDisabled(); + + await user.clear(ui.newNameInput.get()); + await user.type(ui.newNameInput.get(), 'Bad new PHP quality profile'); + await act(async () => { + await user.click(ui.extendDialog.byRole('button', { name: 'extend' }).get()); + }); + + expect(ui.extendDialog.query()).not.toBeInTheDocument(); + + expect(screen.getAllByText('Bad new PHP quality profile')).toHaveLength(2); + expect(screen.getAllByText('Good old PHP quality profile')).toHaveLength(2); + }); + + it('should be able to copy a quality profile', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + await user.click(ui.copyButton.get()); + + expect(ui.copyDialog.get()).toBeInTheDocument(); + expect(ui.copyDialog.byRole('button', { name: 'copy' }).get()).toBeDisabled(); + + await user.clear(ui.newNameInput.get()); + await user.type(ui.newNameInput.get(), 'Good old PHP quality profile copy'); + await act(async () => { + await user.click(ui.copyDialog.byRole('button', { name: 'copy' }).get()); + }); + + expect(ui.copyDialog.query()).not.toBeInTheDocument(); + + expect(screen.getAllByText('Good old PHP quality profile copy')).toHaveLength(2); + }); + + it('should be able to rename a quality profile', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + await user.click(ui.renameButton.get()); + + expect(ui.renameDialog.get()).toBeInTheDocument(); + expect(ui.renameDialog.byRole('button', { name: 'rename' }).get()).toBeDisabled(); + + await user.clear(ui.newNameInput.get()); + await user.type(ui.newNameInput.get(), 'Fossil PHP quality profile'); + await act(async () => { + await user.click(ui.renameDialog.byRole('button', { name: 'rename' }).get()); + }); + + expect(ui.renameDialog.query()).not.toBeInTheDocument(); + + expect(screen.getAllByText('Fossil PHP quality profile')).toHaveLength(2); + }); + + it('should be able to set a quality profile as default', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + await user.click(ui.setAsDefaultButton.get()); + + expect(screen.getAllByText('default')).toHaveLength(2); + }); + + it('should NOT be able to set a quality profile as default if it has no active rules', async () => { + const user = userEvent.setup(); + renderQualityProfile('no-rule-qp'); + + await user.click(await ui.qualityProfileActions.find()); + expect(ui.setAsDefaultButton.query()).not.toBeInTheDocument(); + }); + + it("should be able to delete a Quality Profile and it's children", async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + await user.click(ui.deleteQualityProfileButton.get()); + + expect(ui.deleteProfileDialog.get()).toBeInTheDocument(); + expect( + ui.deleteProfileDialog + .byText(/quality_profiles.are_you_sure_want_delete_profile_x_and_descendants/) + .get() + ).toBeInTheDocument(); + await act(async () => { + await user.click(ui.deleteProfileDialog.byRole('button', { name: 'delete' }).get()); + }); + + expect(ui.qualityProfilesHeader.get()).toBeInTheDocument(); + // children + expect(screen.queryByText('PHP way')).not.toBeInTheDocument(); + expect(screen.queryByText('Good old PHP quality profile')).not.toBeInTheDocument(); + }); + }); +}); + +describe('Users with no permission', () => { + it('should not be able to activate more rules', async () => { + renderQualityProfile(); + + expect(await ui.rulesSection.find()).toBeInTheDocument(); + expect(ui.activateMoreLink.query()).not.toBeInTheDocument(); + }); + + it('should not be able to grant permission to a user', async () => { + renderQualityProfile(); + expect(await screen.findByText('Good old PHP quality profile')).toBeInTheDocument(); + expect(ui.permissionSection.query()).not.toBeInTheDocument(); + }); + + it("should not be able to change a quality profile's parents", async () => { + renderQualityProfile(); + + expect(await ui.inheritanceSection.find()).toBeInTheDocument(); + expect(ui.inheritanceSection.byText('PHP Sonar way 1').get()).toBeInTheDocument(); + expect(ui.inheritanceSection.byText('PHP way').get()).toBeInTheDocument(); + + expect(ui.changeParentButton.query()).not.toBeInTheDocument(); + }); + + it('should not be able to change projects for Quality Profile', async () => { + renderQualityProfile(); + + expect(await ui.projectSection.find()).toBeInTheDocument(); + expect(ui.changeProjectsButton.query()).not.toBeInTheDocument(); + }); +}); + +describe('Every Users', () => { + it('should be able to see active/inactive rules for a Quality Profile', async () => { + renderQualityProfile(); + + expect(await ui.rulesSection.find()).toBeInTheDocument(); + + // Active rules + expect(ui.rulesTotalRow.byText('300').get()).toBeInTheDocument(); + expect(ui.rulesBugsRow.byText('50').get()).toBeInTheDocument(); + expect(ui.rulesVulnerabilitiesRow.byText('30').get()).toBeInTheDocument(); + expect(ui.rulesCodeSmellsRow.byText('200').get()).toBeInTheDocument(); + expect(ui.rulesSecurityHotspotsRow.byText('20').get()).toBeInTheDocument(); + + // Inactive rules + expect(ui.rulesTotalRow.byText('100').get()).toBeInTheDocument(); + expect(ui.rulesBugsRow.byText('10').get()).toBeInTheDocument(); + expect(ui.rulesVulnerabilitiesRow.byText('10').get()).toBeInTheDocument(); + expect(ui.rulesCodeSmellsRow.byText('50').get()).toBeInTheDocument(); + expect(ui.rulesSecurityHotspotsRow.byText('30').get()).toBeInTheDocument(); + }); + + it('should be able to see a warning when some rules are missing compare to Sonar way', async () => { + renderQualityProfile(); + + expect(await ui.rulesMissingSonarWayWarning.findAll()).toHaveLength(2); + expect(ui.rulesMissingSonarWayLink.get()).toBeInTheDocument(); + expect(ui.rulesMissingSonarWayLink.get()).toHaveAttribute( + 'href', + '/coding_rules?qprofile=old-php-qp&activation=false&languages=php' + ); + }); + + it('should be able to see a warning when some rules are deprecated', async () => { + renderQualityProfile(); + + expect(await ui.rulesDeprecatedWarning.findAll()).toHaveLength(2); + expect(ui.rulesDeprecatedLink.get()).toBeInTheDocument(); + expect(ui.rulesDeprecatedLink.get()).toHaveAttribute( + 'href', + '/coding_rules?qprofile=old-php-qp&activation=true&statuses=DEPRECATED' + ); + }); + + it('should be able to see exporters links when there are exporters for the language', async () => { + renderQualityProfile(); + + expect(await ui.exportersSection.find()).toBeInTheDocument(); + expect(ui.exportersSection.byText('SonarLint for Visual Studio').get()).toBeInTheDocument(); + expect(ui.exportersSection.byText('SonarLint for Eclipse').get()).toBeInTheDocument(); + }); + + it('should be informed when the quality profile has not been found', async () => { + renderQualityProfile('i-dont-exist'); + + expect(await screen.findByText('quality_profiles.not_found')).toBeInTheDocument(); + expect(ui.qualityProfilePageLink.get()).toBeInTheDocument(); + }); + + it('should be able to backup quality profile', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + expect(ui.backUpLink.get()).toHaveAttribute( + 'href', + '/api/qualityprofiles/backup?language=php&qualityProfile=Good%20old%20PHP%20quality%20profile' + ); + expect(ui.backUpLink.get()).toHaveAttribute('download', 'old-php-qp.xml'); + }); + + it('should not be able to backup a built-in quality profile', async () => { + const user = userEvent.setup(); + renderQualityProfile('sonar'); + + await user.click(await ui.qualityProfileActions.find()); + expect(ui.backUpLink.query()).not.toBeInTheDocument(); + }); + + it('should be able to compare quality profile', async () => { + const user = userEvent.setup(); + renderQualityProfile(); + + await user.click(await ui.qualityProfileActions.find()); + + expect(ui.compareLink.get()).toBeInTheDocument(); + expect(ui.compareLink.get()).toHaveAttribute( + 'href', + '/profiles/compare?language=php&name=Good+old+PHP+quality+profile' + ); + }); +}); + +function renderQualityProfile(key = 'old-php-qp') { + renderAppRoutes(`profiles/show?key=${key}`, routes, {}); +} 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 a3fa9b46259..057dff8439e 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 @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { act, getByText } from '@testing-library/react'; +import { act, getByText, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import selectEvent from 'react-select-event'; import QualityProfilesServiceMock from '../../../api/mocks/QualityProfilesServiceMock'; @@ -50,10 +50,13 @@ const ui = { name: 'copy', }), createButton: byRole('button', { name: 'create' }), + restoreButton: byRole('button', { name: 'restore' }), compareButton: byRole('link', { name: 'compare' }), + cancelButton: byRole('button', { name: 'cancel' }), compareDropdown: byRole('combobox', { name: 'quality_profiles.compare_with' }), changelogLink: byRole('link', { name: 'changelog' }), popup: byRole('dialog'), + restoreProfileDialog: byRole('dialog', { name: 'quality_profiles.restore_profile' }), copyRadio: byRole('radio', { name: 'quality_profiles.creation_from_copy quality_profiles.creation_from_copy_description_1 quality_profiles.creation_from_copy_description_2', }), @@ -90,6 +93,9 @@ const ui = { byRole('table', { name: `quality_profiles.x_rules_have_different_configuration.${rulesQuantity}`, }), + deprecatedRulesRegion: byRole('region', { name: 'quality_profiles.deprecated_rules' }), + stagnantProfilesRegion: byRole('region', { name: 'quality_profiles.stagnant_profiles' }), + recentlyAddedRulesRegion: byRole('region', { name: 'quality_profiles.latest_new_rules' }), newRuleLink: byRole('link', { name: 'Recently Added Rule' }), seeAllNewRulesLink: byRole('link', { name: 'see_all 20 quality_profiles.latest_new_rules' }), }; @@ -115,97 +121,139 @@ it('should list Quality Profiles and filter by language', async () => { expect(ui.createButton.get(ui.popup.get())).toBeEnabled(); }); -it('should list recently added rules', async () => { - serviceMock.setAdmin(); - serviceMock.setRulesSearchResponse({ - rules: [mockRule({ name: 'Recently Added Rule' })], - paging: mockPaging({ - total: 20, - }), +describe('Evolution', () => { + it('should list deprecated rules', async () => { + serviceMock.setAdmin(); + renderQualityProfiles(); + + expect(await ui.deprecatedRulesRegion.find()).toBeInTheDocument(); + expect( + ui.deprecatedRulesRegion.byRole('link', { name: 'Good old PHP quality profile' }).get() + ).toBeInTheDocument(); + expect( + ui.deprecatedRulesRegion.byRole('link', { name: 'java quality profile #2' }).get() + ).toBeInTheDocument(); }); - renderQualityProfiles(); - expect(await ui.newRuleLink.find()).toBeInTheDocument(); - expect(ui.seeAllNewRulesLink.get()).toBeInTheDocument(); -}); - -it('should be able to extend Quality Profile', async () => { - // From the list page - const user = userEvent.setup(); - serviceMock.setAdmin(); - renderQualityProfiles(); - - await user.click(await ui.profileActions('c quality profile', 'C').find()); - await user.click(ui.extendButton.get()); + it('should list stagnant profiles', async () => { + serviceMock.setAdmin(); + renderQualityProfiles(); - await user.clear(ui.namePropupInput.get()); - await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); - await act(async () => { - await user.click(ui.extendButton.get()); + expect(await ui.stagnantProfilesRegion.find()).toBeInTheDocument(); + expect( + ui.stagnantProfilesRegion.byRole('link', { name: 'Good old PHP quality profile' }).get() + ).toBeInTheDocument(); }); - expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); - - // From the create form - await user.click(ui.returnToList.get()); - await user.click(ui.createButton.get()); - await selectEvent.select(ui.languageSelect.get(), 'C'); - await selectEvent.select(ui.profileExtendSelect.get(), ui.newCQualityProfileName); - await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); - await act(async () => { - await user.click(ui.createButton.get(ui.popup.get())); + it('should list recently added rules', async () => { + serviceMock.setAdmin(); + serviceMock.setRulesSearchResponse({ + rules: [mockRule({ name: 'Recently Added Rule' })], + paging: mockPaging({ + total: 20, + }), + }); + + renderQualityProfiles(); + expect(await ui.recentlyAddedRulesRegion.find()).toBeInTheDocument(); + expect(ui.newRuleLink.get()).toBeInTheDocument(); + expect(ui.seeAllNewRulesLink.get()).toBeInTheDocument(); }); - - expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); }); -it('should be able to copy Quality Profile', async () => { - // From the list page - const user = userEvent.setup(); - serviceMock.setAdmin(); - renderQualityProfiles(); +describe('Create', () => { + it('should be able to extend an existing Quality Profile', async () => { + const user = userEvent.setup(); + serviceMock.setAdmin(); + renderQualityProfiles(); - await user.click(await ui.profileActions('c quality profile', 'C').find()); - await user.click(ui.copyButton.get()); - - await user.clear(ui.namePropupInput.get()); - await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); - await act(async () => { - await user.click(ui.copyButton.get(ui.popup.get())); + await user.click(await ui.profileActions('c quality profile', 'C').find()); + await user.click(ui.extendButton.get()); + await user.clear(ui.namePropupInput.get()); + await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); + await act(async () => { + await user.click(ui.extendButton.get()); + }); + + expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); + + await user.click(ui.returnToList.get()); + await user.click(ui.createButton.get()); + await selectEvent.select(ui.languageSelect.get(), 'C'); + await selectEvent.select(ui.profileExtendSelect.get(), ui.newCQualityProfileName); + await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); + await act(async () => { + await user.click(ui.createButton.get(ui.popup.get())); + }); + + expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); }); - expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); - - // From the create form - await user.click(ui.returnToList.get()); - await user.click(ui.createButton.get()); - - await user.click(ui.copyRadio.get()); - await selectEvent.select(ui.languageSelect.get(), 'C'); - await selectEvent.select(ui.profileCopySelect.get(), ui.newCQualityProfileName); - await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); - await act(async () => { - await user.click(ui.createButton.get(ui.popup.get())); + + it('should be able to copy an existing Quality Profile', async () => { + const user = userEvent.setup(); + serviceMock.setAdmin(); + renderQualityProfiles(); + + await user.click(await ui.profileActions('c quality profile', 'C').find()); + await user.click(ui.copyButton.get()); + await user.clear(ui.namePropupInput.get()); + await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); + await act(async () => { + await user.click(ui.copyButton.get(ui.popup.get())); + }); + + expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); + + await user.click(ui.returnToList.get()); + await user.click(ui.createButton.get()); + await user.click(ui.copyRadio.get()); + await selectEvent.select(ui.languageSelect.get(), 'C'); + await selectEvent.select(ui.profileCopySelect.get(), ui.newCQualityProfileName); + await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); + await act(async () => { + await user.click(ui.createButton.get(ui.popup.get())); + }); + + expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); }); - expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); + it('should be able to create blank Quality Profile', async () => { + const user = userEvent.setup(); + serviceMock.setAdmin(); + renderQualityProfiles(); + await user.click(await ui.createButton.find()); + await user.click(ui.blankRadio.get()); + await selectEvent.select(ui.languageSelect.get(), 'C'); + await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileName); + await act(async () => { + await user.click(ui.createButton.get(ui.popup.get())); + }); + + expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); + }); }); -it('should be able to create blank Quality Profile', async () => { - // From the list page +it('should be able to restore a quality profile', async () => { const user = userEvent.setup(); serviceMock.setAdmin(); renderQualityProfiles(); - await user.click(await ui.createButton.find()); + await user.click(await ui.restoreButton.find()); + expect(await ui.restoreProfileDialog.find()).toBeInTheDocument(); - await user.click(ui.blankRadio.get()); - await selectEvent.select(ui.languageSelect.get(), 'C'); - await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileName); - await act(async () => { - await user.click(ui.createButton.get(ui.popup.get())); + const profileXMLBackup = new File(['c-sonarsource-06756'], 'c-sonarsource-06756', { + type: 'application/xml', }); + const input = screen.getByLabelText<HTMLInputElement>(/backup/); + // Very hacky way to upload the file in the test + input.removeAttribute('required'); + + await userEvent.upload(input, profileXMLBackup); + expect(input.files?.item(0)).toStrictEqual(profileXMLBackup); + + await user.click(ui.restoreProfileDialog.byRole('button', { name: 'restore' }).get()); - expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); + expect(await screen.findByText(/quality_profiles.restore_profile.success/)).toBeInTheDocument(); }); it('should be able to compare profiles', async () => { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx index 61b0b3d4ef5..c2e38caeefd 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx @@ -43,7 +43,7 @@ interface State { total?: number; } -export class ChangelogContainer extends React.PureComponent<Props, State> { +class ChangelogContainer extends React.PureComponent<Props, State> { mounted = false; state: State = { loading: true }; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx deleted file mode 100644 index 2834f8a31a2..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx +++ /dev/null @@ -1,100 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import Link from '../../../../components/common/Link'; -import { ProfileChangelogEvent } from '../../types'; -import Changelog from '../Changelog'; -import ChangesList from '../ChangesList'; - -function createEvent(overrides?: Partial<ProfileChangelogEvent>): ProfileChangelogEvent { - return { - date: '2016-01-01', - authorName: 'John', - action: 'ACTIVATED', - ruleKey: 'squid1234', - ruleName: 'Do not do this', - params: {}, - ...overrides, - }; -} - -it('should render events', () => { - const events = [createEvent(), createEvent()]; - const changelog = shallow(<Changelog events={events} />); - expect(changelog.find('tbody').find('tr').length).toBe(2); -}); - -it('should render event date', () => { - const events = [createEvent()]; - const changelog = shallow(<Changelog events={events} />); - expect(changelog.find('DateTimeFormatter')).toHaveLength(1); -}); - -it('should render author', () => { - const events = [createEvent()]; - const changelog = shallow(<Changelog events={events} />); - expect(changelog.text()).toContain('John'); -}); - -it('should render system author', () => { - const events = [createEvent({ authorName: undefined })]; - const changelog = shallow(<Changelog events={events} />); - expect(changelog.text()).toContain('System'); -}); - -it('should render action', () => { - const events = [createEvent()]; - const changelog = shallow(<Changelog events={events} />); - expect(changelog.text()).toContain('ACTIVATED'); -}); - -it('should render rule', () => { - const events = [createEvent()]; - const changelog = shallow(<Changelog events={events} />); - expect(changelog.find(Link).prop('to')).toHaveProperty('search', '?rule_key=squid1234'); -}); - -it('should render ChangesList', () => { - const params = { severity: 'BLOCKER' }; - const events = [createEvent({ params })]; - const changelog = shallow(<Changelog events={events} />); - const changesList = changelog.find(ChangesList); - expect(changesList.length).toBe(1); - expect(changesList.prop('changes')).toBe(params); -}); - -it('should render events sorted by time and action', () => { - const events = [ - createEvent({ date: '2019-02-07T14:03:45', action: 'DEACTIVATED' }), - createEvent({ date: '2019-02-07T14:03:14', action: 'DEACTIVATED' }), - createEvent({ date: '2019-02-07T14:03:14', action: 'ACTIVATED' }), - createEvent({ date: '2019-02-07T14:03:07', action: 'ACTIVATED' }), - ]; - const changelog = shallow(<Changelog events={events} />); - const rows = changelog.find('tbody').find('tr'); - - const getAction = (index: number) => rows.at(index).childAt(2).childAt(0).text(); - - expect(getAction(0)).toBe('quality_profiles.changelog.DEACTIVATED'); - expect(getAction(1)).toBe('quality_profiles.changelog.ACTIVATED'); - expect(getAction(2)).toBe('quality_profiles.changelog.DEACTIVATED'); - expect(getAction(3)).toBe('quality_profiles.changelog.ACTIVATED'); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx new file mode 100644 index 00000000000..19a0cc1d802 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx @@ -0,0 +1,140 @@ +/* + * 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 { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import QualityProfilesServiceMock from '../../../../api/mocks/QualityProfilesServiceMock'; +import { mockQualityProfileChangelogEvent } from '../../../../helpers/testMocks'; +import { renderAppRoutes } from '../../../../helpers/testReactTestingUtils'; +import { byRole, byText } from '../../../../helpers/testSelector'; +import routes from '../../routes'; + +jest.mock('../../../../api/quality-profiles'); + +const serviceMock = new QualityProfilesServiceMock(); +const ui = { + row: byRole('row'), + link: byRole('link'), + emptyPage: byText('no_results'), + showMore: byRole('link', { name: 'show_more' }), + startDate: byRole('textbox', { name: 'start_date' }), + endDate: byRole('textbox', { name: 'end_date' }), + reset: byRole('button', { name: 'reset_verb' }), +}; + +beforeEach(() => { + jest.useFakeTimers({ + advanceTimers: true, + now: new Date('2019-04-25T03:12:32+0100'), + }); +}); + +afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + + serviceMock.reset(); +}); + +it('should see the changelog', async () => { + const user = userEvent.setup(); + renderChangeLog(); + + const rows = await ui.row.findAll(); + expect(rows).toHaveLength(6); + expect(ui.emptyPage.query()).not.toBeInTheDocument(); + expect(rows[1]).toHaveTextContent('May 23, 2019'); + expect(rows[1]).not.toHaveTextContent('quality_profiles.severity'); + expect(rows[2]).toHaveTextContent('April 23, 2019'); + expect(rows[2]).toHaveTextContent( + 'Systemquality_profiles.changelog.DEACTIVATEDRule 0quality_profiles.severity_set_to severity.MAJOR' + ); + expect(rows[3]).not.toHaveTextContent('April 23, 2019'); + expect(rows[3]).not.toHaveTextContent('Systemquality_profiles.changelog.DEACTIVATED'); + expect(rows[3]).toHaveTextContent('Rule 1quality_profiles.severity_set_to severity.MAJOR'); + expect(rows[4]).toHaveTextContent('John Doe'); + expect(rows[4]).not.toHaveTextContent('System'); + expect(rows[5]).toHaveTextContent('March 23, 2019'); + expect(rows[5]).toHaveTextContent('John Doequality_profiles.changelog.ACTIVATEDRule 2'); + expect(rows[5]).toHaveTextContent( + 'quality_profiles.severity_set_to severity.CRITICALquality_profiles.parameter_set_to.credentialWords.foo,bar' + ); + await user.click(ui.link.get(rows[1])); + expect(screen.getByText('/coding_rules?rule_key=c%3Arule0')).toBeInTheDocument(); +}); + +it('should filter the changelog', async () => { + const user = userEvent.setup(); + renderChangeLog(); + + expect(await ui.row.findAll()).toHaveLength(6); + await user.click(ui.startDate.get()); + await user.click(screen.getByRole('gridcell', { name: '20' })); + await user.click(document.body); + expect(await ui.row.findAll()).toHaveLength(5); + await user.click(ui.endDate.get()); + await user.click(screen.getByRole('gridcell', { name: '25' })); + await user.click(document.body); + expect(await ui.row.findAll()).toHaveLength(4); + await user.click(ui.reset.get()); + expect(await ui.row.findAll()).toHaveLength(6); +}); + +it('should load more', async () => { + const user = userEvent.setup(); + serviceMock.changelogEvents = new Array(100).fill(null).map((_, i) => + mockQualityProfileChangelogEvent({ + ruleKey: `c:rule${i}`, + ruleName: `Rule ${i}`, + }) + ); + renderChangeLog(); + + expect(await ui.row.findAll()).toHaveLength(51); + expect(ui.showMore.get()).toBeInTheDocument(); + await user.click(ui.showMore.get()); + expect(await ui.row.findAll()).toHaveLength(101); + await user.click(ui.reset.get()); + expect(await ui.row.findAll()).toHaveLength(51); +}); + +it('should see short changelog for php', async () => { + renderChangeLog('php', 'Good old PHP quality profile'); + + const rows = await ui.row.findAll(); + expect(rows).toHaveLength(2); + expect(rows[1]).toHaveTextContent('May 23, 2019'); + expect(rows[1]).toHaveTextContent( + 'Systemquality_profiles.changelog.DEACTIVATEDPHP Rulequality_profiles.severity_set_to severity.MAJOR' + ); + expect(ui.showMore.query()).not.toBeInTheDocument(); +}); + +it('should show empty list for java', async () => { + renderChangeLog('java', 'java quality profile'); + + expect(await ui.emptyPage.find()).toBeInTheDocument(); + expect(ui.row.query()).not.toBeInTheDocument(); + expect(ui.showMore.query()).not.toBeInTheDocument(); +}); + +function renderChangeLog(language = 'c', name = 'c quality profile') { + renderAppRoutes(`profiles/changelog?name=${name}&language=${language}`, routes, {}); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-test.tsx deleted file mode 100644 index 2e94f5d342a..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-test.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { getProfileChangelog } from '../../../../api/quality-profiles'; -import { - mockLocation, - mockPaging, - mockQualityProfile, - mockRouter, -} from '../../../../helpers/testMocks'; -import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils'; -import { ChangelogContainer } from '../ChangelogContainer'; - -beforeEach(() => jest.clearAllMocks()); - -jest.mock('../../../../api/quality-profiles', () => { - const { mockQualityProfileChangelogEvent, mockPaging } = jest.requireActual( - '../../../../helpers/testMocks' - ); - return { - getProfileChangelog: jest.fn().mockResolvedValue({ - events: [ - mockQualityProfileChangelogEvent(), - mockQualityProfileChangelogEvent(), - mockQualityProfileChangelogEvent(), - ], - paging: mockPaging({ - total: 6, - pageIndex: 1, - }), - }), - }; -}); - -it('should render correctly without events', async () => { - (getProfileChangelog as jest.Mock).mockResolvedValueOnce({ - events: [], - paging: mockPaging({ total: 0 }), - }); - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -it('should load more properly', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - - wrapper.instance().loadMore(mockEvent()); - await waitAndUpdate(wrapper); - - expect(getProfileChangelog).toHaveBeenLastCalledWith(undefined, undefined, expect.anything(), 2); - expect(wrapper.state().events?.length).toBe(6); -}); - -function shallowRender() { - return shallow<ChangelogContainer>( - <ChangelogContainer - location={mockLocation()} - profile={mockQualityProfile()} - router={mockRouter()} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx deleted file mode 100644 index 94ce4da9398..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { parseDate } from '../../../../helpers/dates'; -import { click } from '../../../../helpers/testUtils'; -import ChangelogSearch from '../ChangelogSearch'; - -it('should render', () => { - const output = shallow( - <ChangelogSearch - dateRange={{ - from: parseDate('2016-01-01T00:00:00.000Z'), - to: parseDate('2016-05-05T00:00:00.000Z'), - }} - onDateRangeChange={jest.fn()} - onReset={jest.fn()} - /> - ); - expect(output).toMatchSnapshot(); -}); - -it('should reset', () => { - const onReset = jest.fn(); - const output = shallow( - <ChangelogSearch - dateRange={{ - from: parseDate('2016-01-01T00:00:00.000Z'), - to: parseDate('2016-05-05T00:00:00.000Z'), - }} - onDateRangeChange={jest.fn()} - onReset={onReset} - /> - ); - expect(onReset).not.toHaveBeenCalled(); - click(output.find('Button')); - expect(onReset).toHaveBeenCalled(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangesList-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangesList-test.tsx deleted file mode 100644 index b50236cc336..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangesList-test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import ChangesList from '../ChangesList'; -import ParameterChange from '../ParameterChange'; -import SeverityChange from '../SeverityChange'; - -it('should render changes', () => { - const changes = { severity: 'BLOCKER', foo: 'bar' }; - const output = shallow(<ChangesList changes={changes} />); - expect(output.find('li').length).toBe(2); -}); - -it('should render severity change', () => { - const changes = { severity: 'BLOCKER' }; - const output = shallow(<ChangesList changes={changes} />).find(SeverityChange); - expect(output.length).toBe(1); - expect(output.prop('severity')).toBe('BLOCKER'); -}); - -it('should render parameter change', () => { - const changes = { foo: 'bar' }; - const output = shallow(<ChangesList changes={changes} />).find(ParameterChange); - expect(output.length).toBe(1); - expect(output.prop('name')).toBe('foo'); - expect(output.prop('value')).toBe('bar'); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ParameterChange-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ParameterChange-test.tsx deleted file mode 100644 index e2f6587c874..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ParameterChange-test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import ParameterChange from '../ParameterChange'; - -it('should render different messages', () => { - const first = shallow(<ParameterChange name="foo" value={null} />); - const second = shallow(<ParameterChange name="foo" value="bar" />); - expect(first.text()).not.toBe(second.text()); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/SeverityChange-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/SeverityChange-test.tsx deleted file mode 100644 index 68a932cb4f4..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/SeverityChange-test.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import SeverityHelper from '../../../../components/shared/SeverityHelper'; -import SeverityChange from '../SeverityChange'; - -it('should render SeverityHelper', () => { - const output = shallow(<SeverityChange severity="BLOCKER" />).find(SeverityHelper); - expect(output.length).toBe(1); - expect(output.prop('severity')).toBe('BLOCKER'); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogContainer-test.tsx.snap deleted file mode 100644 index b9549ee78b8..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogContainer-test.tsx.snap +++ /dev/null @@ -1,95 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="boxed-group boxed-group-inner js-profile-changelog" -> - <div - className="spacer-bottom" - > - <ChangelogSearch - dateRange={ - { - "from": undefined, - "to": undefined, - } - } - onDateRangeChange={[Function]} - onReset={[Function]} - /> - <DeferredSpinner - className="spacer-left" - loading={false} - /> - </div> - <Changelog - events={ - [ - { - "action": "ACTIVATED", - "date": "2019-04-23T02:12:32+0100", - "params": { - "severity": "MAJOR", - }, - "ruleKey": "rule-key", - "ruleName": "rule-name", - }, - { - "action": "ACTIVATED", - "date": "2019-04-23T02:12:32+0100", - "params": { - "severity": "MAJOR", - }, - "ruleKey": "rule-key", - "ruleName": "rule-name", - }, - { - "action": "ACTIVATED", - "date": "2019-04-23T02:12:32+0100", - "params": { - "severity": "MAJOR", - }, - "ruleKey": "rule-key", - "ruleName": "rule-name", - }, - ] - } - /> - <footer - className="text-center spacer-top small" - > - <a - href="#" - onClick={[Function]} - > - show_more - </a> - </footer> -</div> -`; - -exports[`should render correctly without events 1`] = ` -<div - className="boxed-group boxed-group-inner js-profile-changelog" -> - <div - className="spacer-bottom" - > - <ChangelogSearch - dateRange={ - { - "from": undefined, - "to": undefined, - } - } - onDateRangeChange={[Function]} - onReset={[Function]} - /> - <DeferredSpinner - className="spacer-left" - loading={false} - /> - </div> - <ChangelogEmpty /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogSearch-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogSearch-test.tsx.snap deleted file mode 100644 index 77833089973..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogSearch-test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render 1`] = ` -<div - className="display-flex-end" - id="quality-profile-changelog-form" -> - <DateRangeInput - onChange={[MockFunction]} - value={ - { - "from": 2016-01-01T00:00:00.000Z, - "to": 2016-05-05T00:00:00.000Z, - } - } - /> - <Button - className="spacer-left text-top" - onClick={[MockFunction]} - > - reset_verb - </Button> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.tsx deleted file mode 100644 index e36f1ccf113..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import { translate } from '../../../helpers/l10n'; - -export default function ComparisonEmpty() { - return <div className="big-spacer-top">{translate('quality_profile.empty_comparison')}</div>; -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx index dde41ffd05f..9b3c925f815 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx @@ -27,7 +27,6 @@ import SeverityIcon from '../../../components/icons/SeverityIcon'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getRulesUrl } from '../../../helpers/urls'; import { Dict } from '../../../types/types'; -import ComparisonEmpty from './ComparisonEmpty'; import ComparisonResultActivation from './ComparisonResultActivation'; type Params = Dict<string>; @@ -201,7 +200,7 @@ export default class ComparisonResults extends React.PureComponent<Props> { render() { if (!this.props.inLeft.length && !this.props.inRight.length && !this.props.modified.length) { - return <ComparisonEmpty />; + return <div className="big-spacer-top">{translate('quality_profile.empty_comparison')}</div>; } return ( 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 e27eceb397e..c57b6162977 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 @@ -25,7 +25,6 @@ import { copyProfile, createQualityProfile, deleteProfile, - getQualityProfileBackupUrl, renameProfile, setDefaultProfile, } from '../../../api/quality-profiles'; @@ -57,7 +56,7 @@ interface State { openModal?: ProfileActionModals; } -export class ProfileActions extends React.PureComponent<Props, State> { +class ProfileActions extends React.PureComponent<Props, State> { mounted = false; state: State = { loading: false, @@ -178,12 +177,19 @@ export class ProfileActions extends React.PureComponent<Props, State> { } }; + getQualityProfileBackupUrl = ({ language, name: qualityProfile }: Profile) => { + const queryParams = Object.entries({ language, qualityProfile }) + .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) + .join('&'); + return `/api/qualityprofiles/backup?${queryParams}`; + }; + render() { const { profile, isComparable } = this.props; const { loading, openModal } = this.state; const { actions = {} } = profile; - const backupUrl = `${getBaseUrl()}${getQualityProfileBackupUrl(profile)}`; + const backupUrl = `${getBaseUrl()}${this.getQualityProfileBackupUrl(profile)}`; const activateMoreUrl = getRulesUrl({ qprofile: profile.key, diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileModalForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileModalForm.tsx index 91ca77c9a25..b00ece4ea3f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileModalForm.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileModalForm.tsx @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; +import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; import { translate, translateWithParameters } from '../../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/BuiltInQualityProfileBadge-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/BuiltInQualityProfileBadge-test.tsx deleted file mode 100644 index 99d20926cf8..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/BuiltInQualityProfileBadge-test.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import BuiltInQualityProfileBadge from '../BuiltInQualityProfileBadge'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); - expect(shallowRender({ tooltip: false })).toMatchSnapshot(); -}); - -function shallowRender(props = {}) { - return shallow(<BuiltInQualityProfileBadge {...props} />); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/DeleteProfileForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/DeleteProfileForm-test.tsx deleted file mode 100644 index ca7ccc50d91..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/DeleteProfileForm-test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfile } from '../../../../helpers/testMocks'; -import { mockEvent } from '../../../../helpers/testUtils'; -import DeleteProfileForm, { DeleteProfileFormProps } from '../DeleteProfileForm'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); - expect(shallowRender({ profile: mockQualityProfile({ childrenCount: 2 }) })).toMatchSnapshot( - 'profile has children' - ); -}); - -it('should correctly submit the form', () => { - const onDelete = jest.fn(); - const wrapper = shallowRender({ onDelete }); - - const formOnSubmit = wrapper.find('form').props().onSubmit; - if (formOnSubmit) { - formOnSubmit(mockEvent()); - } - expect(onDelete).toHaveBeenCalled(); -}); - -function shallowRender(props: Partial<DeleteProfileFormProps> = {}) { - return shallow<DeleteProfileFormProps>( - <DeleteProfileForm - loading={false} - onClose={jest.fn()} - onDelete={jest.fn()} - profile={mockQualityProfile()} - {...props} - /> - ); -} 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 deleted file mode 100644 index 4a596640b4f..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx +++ /dev/null @@ -1,342 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { - changeProfileParent, - copyProfile, - createQualityProfile, - deleteProfile, - renameProfile, - setDefaultProfile, -} from '../../../../api/quality-profiles'; -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 DeleteProfileForm from '../DeleteProfileForm'; -import { ProfileActions } from '../ProfileActions'; -import ProfileModalForm from '../ProfileModalForm'; - -jest.mock('../../../../api/quality-profiles', () => { - const { mockQualityProfile } = jest.requireActual('../../../../helpers/testMocks'); - - return { - ...jest.requireActual('../../../../api/quality-profiles'), - copyProfile: jest.fn().mockResolvedValue(null), - changeProfileParent: jest.fn().mockResolvedValue(null), - createQualityProfile: jest - .fn() - .mockResolvedValue({ profile: mockQualityProfile({ key: 'newProfile' }) }), - deleteProfile: jest.fn().mockResolvedValue(null), - setDefaultProfile: jest.fn().mockResolvedValue(null), - renameProfile: jest.fn().mockResolvedValue(null), - }; -}); - -const PROFILE = mockQualityProfile({ - activeRuleCount: 68, - activeDeprecatedRuleCount: 0, - depth: 0, - language: 'js', - rulesUpdatedAt: '2017-06-28T12:58:44+0000', -}); - -beforeEach(() => jest.clearAllMocks()); - -it('renders correctly', () => { - expect(shallowRender()).toMatchSnapshot('no permissions'); - expect(shallowRender({ profile: { ...PROFILE, actions: { edit: true } } })).toMatchSnapshot( - 'edit only' - ); - expect( - shallowRender({ - profile: { - ...PROFILE, - actions: { - copy: true, - edit: true, - delete: true, - setAsDefault: true, - associateProjects: true, - }, - }, - }) - ).toMatchSnapshot('all permissions'); - - expect(shallowRender().setState({ openModal: ProfileActionModals.Copy })).toMatchSnapshot( - 'copy modal' - ); - expect(shallowRender().setState({ openModal: ProfileActionModals.Extend })).toMatchSnapshot( - 'extend modal' - ); - expect(shallowRender().setState({ openModal: ProfileActionModals.Rename })).toMatchSnapshot( - 'rename modal' - ); - expect(shallowRender().setState({ openModal: ProfileActionModals.Delete })).toMatchSnapshot( - 'delete modal' - ); -}); - -describe('copy a profile', () => { - it('should correctly copy a profile', async () => { - const name = 'new-name'; - const updateProfiles = jest.fn().mockResolvedValue(null); - const push = jest.fn(); - const wrapper = shallowRender({ - profile: { ...PROFILE, actions: { copy: true } }, - router: mockRouter({ push }), - updateProfiles, - }); - - click(wrapper.find('.it__quality-profiles__copy')); - expect(wrapper.find(ProfileModalForm).exists()).toBe(true); - - wrapper.find(ProfileModalForm).props().onSubmit(name); - expect(copyProfile).toHaveBeenCalledWith(PROFILE.key, name); - await waitAndUpdate(wrapper); - - expect(updateProfiles).toHaveBeenCalled(); - expect(push).toHaveBeenCalledWith({ - pathname: '/profiles/show', - search: queryToSearch({ name, language: 'js' }), - }); - expect(wrapper.find(ProfileModalForm).exists()).toBe(false); - }); - - it('should correctly keep the modal open in case of an error', async () => { - (copyProfile as jest.Mock).mockRejectedValueOnce(null); - - const name = 'new-name'; - const updateProfiles = jest.fn(); - const push = jest.fn(); - const wrapper = shallowRender({ - profile: { ...PROFILE, actions: { copy: true } }, - router: mockRouter({ push }), - updateProfiles, - }); - wrapper.setState({ openModal: ProfileActionModals.Copy }); - - wrapper.instance().handleProfileCopy(name); - await waitAndUpdate(wrapper); - - expect(updateProfiles).not.toHaveBeenCalled(); - await waitAndUpdate(wrapper); - - expect(push).not.toHaveBeenCalled(); - expect(wrapper.state().openModal).toBe(ProfileActionModals.Copy); - }); -}); - -describe('extend a profile', () => { - it('should correctly extend a profile', async () => { - const name = 'new-name'; - const profile = { ...PROFILE, actions: { copy: true } }; - const updateProfiles = jest.fn().mockResolvedValue(null); - const push = jest.fn(); - const wrapper = shallowRender({ - profile, - router: mockRouter({ push }), - updateProfiles, - }); - - click(wrapper.find('.it__quality-profiles__extend')); - expect(wrapper.find(ProfileModalForm).exists()).toBe(true); - - wrapper.find(ProfileModalForm).props().onSubmit(name); - expect(createQualityProfile).toHaveBeenCalledWith({ language: profile.language, name }); - await waitAndUpdate(wrapper); - expect(changeProfileParent).toHaveBeenCalledWith( - expect.objectContaining({ - key: 'newProfile', - }), - profile - ); - await waitAndUpdate(wrapper); - - expect(updateProfiles).toHaveBeenCalled(); - await waitAndUpdate(wrapper); - - expect(push).toHaveBeenCalledWith({ - pathname: '/profiles/show', - search: queryToSearch({ name, language: 'js' }), - }); - expect(wrapper.find(ProfileModalForm).exists()).toBe(false); - }); - - it('should correctly keep the modal open in case of an error', async () => { - (createQualityProfile as jest.Mock).mockRejectedValueOnce(null); - - const name = 'new-name'; - const updateProfiles = jest.fn(); - const push = jest.fn(); - const wrapper = shallowRender({ - profile: { ...PROFILE, actions: { copy: true } }, - router: mockRouter({ push }), - updateProfiles, - }); - wrapper.setState({ openModal: ProfileActionModals.Extend }); - - wrapper.instance().handleProfileExtend(name); - await waitAndUpdate(wrapper); - - expect(updateProfiles).not.toHaveBeenCalled(); - expect(changeProfileParent).not.toHaveBeenCalled(); - expect(push).not.toHaveBeenCalled(); - expect(wrapper.state().openModal).toBe(ProfileActionModals.Extend); - }); -}); - -describe('rename a profile', () => { - it('should correctly rename a profile', async () => { - const name = 'new-name'; - const updateProfiles = jest.fn().mockResolvedValue(null); - const push = jest.fn(); - const wrapper = shallowRender({ - profile: { ...PROFILE, actions: { edit: true } }, - router: mockRouter({ push }), - updateProfiles, - }); - - click(wrapper.find('.it__quality-profiles__rename')); - expect(wrapper.find(ProfileModalForm).exists()).toBe(true); - - wrapper.find(ProfileModalForm).props().onSubmit(name); - expect(renameProfile).toHaveBeenCalledWith(PROFILE.key, name); - await waitAndUpdate(wrapper); - - expect(updateProfiles).toHaveBeenCalled(); - expect(push).toHaveBeenCalledWith({ - pathname: '/profiles/show', - search: queryToSearch({ name, language: 'js' }), - }); - expect(wrapper.find(ProfileModalForm).exists()).toBe(false); - }); - - it('should correctly keep the modal open in case of an error', async () => { - (renameProfile as jest.Mock).mockRejectedValueOnce(null); - - const name = 'new-name'; - const updateProfiles = jest.fn(); - const push = jest.fn(); - const wrapper = shallowRender({ - profile: { ...PROFILE, actions: { copy: true } }, - router: mockRouter({ push }), - updateProfiles, - }); - wrapper.setState({ openModal: ProfileActionModals.Rename }); - - wrapper.instance().handleProfileRename(name); - await waitAndUpdate(wrapper); - - expect(updateProfiles).not.toHaveBeenCalled(); - await waitAndUpdate(wrapper); - - expect(push).not.toHaveBeenCalled(); - expect(wrapper.state().openModal).toBe(ProfileActionModals.Rename); - }); -}); - -describe('delete a profile', () => { - it('should correctly delete a profile', async () => { - const updateProfiles = jest.fn().mockResolvedValue(null); - const replace = jest.fn(); - const profile = { ...PROFILE, actions: { delete: true } }; - const wrapper = shallowRender({ - profile, - router: mockRouter({ replace }), - updateProfiles, - }); - - click(wrapper.find('.it__quality-profiles__delete')); - expect(wrapper.find(DeleteProfileForm).exists()).toBe(true); - - wrapper.find(DeleteProfileForm).props().onDelete(); - expect(deleteProfile).toHaveBeenCalledWith(profile); - await waitAndUpdate(wrapper); - - expect(updateProfiles).toHaveBeenCalled(); - expect(replace).toHaveBeenCalledWith(PROFILE_PATH); - expect(wrapper.find(ProfileModalForm).exists()).toBe(false); - }); - - it('should correctly keep the modal open in case of an error', async () => { - (deleteProfile as jest.Mock).mockRejectedValueOnce(null); - - const updateProfiles = jest.fn(); - const replace = jest.fn(); - const wrapper = shallowRender({ - profile: { ...PROFILE, actions: { copy: true } }, - router: mockRouter({ replace }), - updateProfiles, - }); - wrapper.setState({ openModal: ProfileActionModals.Delete }); - - wrapper.instance().handleProfileDelete(); - await waitAndUpdate(wrapper); - - expect(updateProfiles).not.toHaveBeenCalled(); - await waitAndUpdate(wrapper); - - expect(replace).not.toHaveBeenCalled(); - expect(wrapper.state().openModal).toBe(ProfileActionModals.Delete); - }); -}); - -it('should correctly set a profile as the default', async () => { - const updateProfiles = jest.fn(); - - const wrapper = shallowRender({ updateProfiles }); - wrapper.instance().handleSetDefaultClick(); - await waitAndUpdate(wrapper); - - expect(setDefaultProfile).toHaveBeenCalledWith(PROFILE); - expect(updateProfiles).toHaveBeenCalled(); -}); - -it('should not allow to set a profile as the default if the profile has no active rules', async () => { - const profile = mockQualityProfile({ - activeRuleCount: 0, - actions: { - setAsDefault: true, - }, - }); - - const wrapper = shallowRender({ profile }); - wrapper.instance().handleSetDefaultClick(); - await waitAndUpdate(wrapper); - - expect(setDefaultProfile).not.toHaveBeenCalled(); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<ProfileActions['props']> = {}) { - const router = mockRouter(); - return shallow<ProfileActions>( - <ProfileActions - isComparable - profile={PROFILE} - router={router} - updateProfiles={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/QualityProfilesApp-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/QualityProfilesApp-test.tsx deleted file mode 100644 index 616750fd1ab..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/QualityProfilesApp-test.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { Actions, getExporters, searchQualityProfiles } from '../../../../api/quality-profiles'; -import { - mockLanguage, - mockQualityProfile, - mockQualityProfileExporter, -} from '../../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import { QualityProfilesApp } from '../QualityProfilesApp'; - -jest.mock('../../../../api/quality-profiles', () => ({ - getExporters: jest.fn().mockResolvedValue([]), - searchQualityProfiles: jest.fn().mockResolvedValue({ profiles: [] }), -})); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot('loading'); - - expect(getExporters).toHaveBeenCalled(); - expect(searchQualityProfiles).toHaveBeenCalled(); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot('full'); -}); - -it('should render child with additional props', () => { - const language = mockLanguage(); - const wrapper = shallowRender({ languages: { [language.key]: language } }); - - const actions: Actions = { create: true }; - const profiles = [mockQualityProfile()]; - const exporters = [mockQualityProfileExporter()]; - - wrapper.setState({ loading: false, actions, profiles, exporters }); - - expect(wrapper.childAt(2).props()).toEqual({ - context: { - actions, - profiles, - languages: [language], - exporters, - updateProfiles: wrapper.instance().updateProfiles, - }, - }); -}); - -it('should handle update', async () => { - const profile1 = mockQualityProfile({ key: 'qp1', name: 'An amazing profile' }); - const profile2 = mockQualityProfile({ key: 'qp2', name: 'Quality Profile' }); - - // Mock one call for the initial load, one for the update - (searchQualityProfiles as jest.Mock) - .mockResolvedValueOnce({ profiles: [] }) - .mockResolvedValueOnce({ profiles: [profile2, profile1] }); - - const wrapper = shallowRender(); - - await waitAndUpdate(wrapper); - expect(wrapper.state().profiles).toHaveLength(0); - - wrapper.instance().updateProfiles(); - - await waitAndUpdate(wrapper); - expect(wrapper.state().profiles).toEqual([profile1, profile2]); -}); - -function shallowRender(props: Partial<QualityProfilesApp['props']> = {}) { - return shallow<QualityProfilesApp>( - <QualityProfilesApp languages={{}} {...props}> - <div /> - </QualityProfilesApp> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/BuiltInQualityProfileBadge-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/BuiltInQualityProfileBadge-test.tsx.snap deleted file mode 100644 index 823cfbafac1..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/BuiltInQualityProfileBadge-test.tsx.snap +++ /dev/null @@ -1,21 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Tooltip - overlay="quality_profiles.built_in.description" -> - <div - className="badge badge-info" - > - quality_profiles.built_in - </div> -</Tooltip> -`; - -exports[`should render correctly 2`] = ` -<div - className="badge badge-info" -> - quality_profiles.built_in -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/DeleteProfileForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/DeleteProfileForm-test.tsx.snap deleted file mode 100644 index d24c01159c2..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/DeleteProfileForm-test.tsx.snap +++ /dev/null @@ -1,134 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<Modal - contentLabel="quality_profiles.delete_confirm_title" - onRequestClose={[MockFunction]} -> - <form - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - quality_profiles.delete_confirm_title - </h2> - </div> - <div - className="modal-body" - > - <p> - quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript - </p> - </div> - <div - className="modal-foot" - > - <SubmitButton - className="button-red" - disabled={false} - > - delete - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; - -exports[`should render correctly: loading 1`] = ` -<Modal - contentLabel="quality_profiles.delete_confirm_title" - onRequestClose={[MockFunction]} -> - <form - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - quality_profiles.delete_confirm_title - </h2> - </div> - <div - className="modal-body" - > - <p> - quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript - </p> - </div> - <div - className="modal-foot" - > - <i - className="spinner spacer-right" - /> - <SubmitButton - className="button-red" - disabled={true} - > - delete - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; - -exports[`should render correctly: profile has children 1`] = ` -<Modal - contentLabel="quality_profiles.delete_confirm_title" - onRequestClose={[MockFunction]} -> - <form - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - quality_profiles.delete_confirm_title - </h2> - </div> - <div - className="modal-body" - > - <div> - <Alert - variant="warning" - > - quality_profiles.this_profile_has_descendants - </Alert> - <p> - quality_profiles.are_you_sure_want_delete_profile_x_and_descendants.name.JavaScript - </p> - </div> - </div> - <div - className="modal-foot" - > - <SubmitButton - className="button-red" - disabled={false} - > - delete - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; 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 deleted file mode 100644 index 2b7d547e5b0..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap +++ /dev/null @@ -1,392 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly: all permissions 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__activate-more-rules" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=key&activation=false", - } - } - > - quality_profiles.activate_more_rules - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__extend" - onClick={[Function]} - tooltipOverlay="quality_profiles.extend_help.name" - tooltipPlacement="left" - > - extend - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__copy" - onClick={[Function]} - tooltipOverlay="quality_profiles.copy_help.name" - tooltipPlacement="left" - > - copy - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__rename" - onClick={[Function]} - > - rename - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__set-as-default" - onClick={[Function]} - > - set_as_default - </ActionsDropdownItem> - <ActionsDropdownDivider /> - <ActionsDropdownItem - className="it__quality-profiles__delete" - destructive={true} - onClick={[Function]} - > - delete - </ActionsDropdownItem> - </ActionsDropdown> -</Fragment> -`; - -exports[`renders correctly: copy modal 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - </ActionsDropdown> - <ProfileModalForm - action="COPY" - loading={false} - onClose={[Function]} - onSubmit={[Function]} - profile={ - { - "activeDeprecatedRuleCount": 0, - "activeRuleCount": 68, - "childrenCount": 0, - "depth": 0, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - "rulesUpdatedAt": "2017-06-28T12:58:44+0000", - } - } - /> -</Fragment> -`; - -exports[`renders correctly: delete modal 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - </ActionsDropdown> - <DeleteProfileForm - loading={false} - onClose={[Function]} - onDelete={[Function]} - profile={ - { - "activeDeprecatedRuleCount": 0, - "activeRuleCount": 68, - "childrenCount": 0, - "depth": 0, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - "rulesUpdatedAt": "2017-06-28T12:58:44+0000", - } - } - /> -</Fragment> -`; - -exports[`renders correctly: edit only 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__activate-more-rules" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=key&activation=false", - } - } - > - quality_profiles.activate_more_rules - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__rename" - onClick={[Function]} - > - rename - </ActionsDropdownItem> - </ActionsDropdown> -</Fragment> -`; - -exports[`renders correctly: extend modal 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - </ActionsDropdown> - <ProfileModalForm - action="EXTEND" - loading={false} - onClose={[Function]} - onSubmit={[Function]} - profile={ - { - "activeDeprecatedRuleCount": 0, - "activeRuleCount": 68, - "childrenCount": 0, - "depth": 0, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - "rulesUpdatedAt": "2017-06-28T12:58:44+0000", - } - } - /> -</Fragment> -`; - -exports[`renders correctly: no permissions 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - </ActionsDropdown> -</Fragment> -`; - -exports[`renders correctly: rename modal 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - </ActionsDropdown> - <ProfileModalForm - action="RENAME" - loading={false} - onClose={[Function]} - onSubmit={[Function]} - profile={ - { - "activeDeprecatedRuleCount": 0, - "activeRuleCount": 68, - "childrenCount": 0, - "depth": 0, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - "rulesUpdatedAt": "2017-06-28T12:58:44+0000", - } - } - /> -</Fragment> -`; - -exports[`should not allow to set a profile as the default if the profile has no active rules 1`] = ` -<Fragment> - <ActionsDropdown - disabled={false} - label="quality_profiles.actions.name.JavaScript" - > - <ActionsDropdownItem - className="it__quality-profiles__backup" - download="key.xml" - to="/api/qualityprofiles/backup?language=js&qualityProfile=name" - > - backup_verb - </ActionsDropdownItem> - <ActionsDropdownItem - className="it__quality-profiles__compare" - to={ - { - "pathname": "/profiles/compare", - "search": "?language=js&name=name", - } - } - > - compare - </ActionsDropdownItem> - <li> - <Tooltip - overlay="quality_profiles.cannot_set_default_no_rules" - placement="left" - > - <span - className="it__quality-profiles__set-as-default text-muted-2" - > - set_as_default - </span> - </Tooltip> - </li> - </ActionsDropdown> -</Fragment> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/QualityProfilesApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/QualityProfilesApp-test.tsx.snap deleted file mode 100644 index 791f5c37ced..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/QualityProfilesApp-test.tsx.snap +++ /dev/null @@ -1,47 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: full 1`] = ` -<div - className="page page-limited" -> - <Suggestions - suggestions="quality_profiles" - /> - <Helmet - defer={false} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="quality_profiles.page" - /> - <Outlet - context={ - { - "actions": {}, - "exporters": [], - "languages": [], - "profiles": [], - "updateProfiles": [Function], - } - } - /> -</div> -`; - -exports[`should render correctly: loading 1`] = ` -<div - className="page page-limited" -> - <Suggestions - suggestions="quality_profiles" - /> - <Helmet - defer={false} - encodeSpecialCharacters={true} - prioritizeSeoTags={false} - title="quality_profiles.page" - /> - <i - className="spinner" - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx index 632dc2336bf..c6a5e7ef240 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx @@ -19,10 +19,10 @@ */ import { sortBy } from 'lodash'; import * as React from 'react'; -import Select from '../../../components/controls/Select'; import { changeProfileParent } from '../../../api/quality-profiles'; -import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; +import Select from '../../../components/controls/Select'; +import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; import { translate } from '../../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx index 17361d35553..29032b3b215 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx @@ -28,24 +28,23 @@ import ProfilePermissions from './ProfilePermissions'; import ProfileProjects from './ProfileProjects'; import ProfileRules from './ProfileRules'; -export interface ProfileDetailsProps { +interface ProfileDetailsProps { exporters: Exporter[]; profile: Profile; profiles: Profile[]; updateProfiles: () => Promise<void>; } -export function ProfileDetails(props: ProfileDetailsProps) { - const { profile } = props; +function ProfileDetails(props: ProfileDetailsProps) { + const { profile, profiles, exporters } = props; + return ( <div> <div className="quality-profile-grid"> <div className="quality-profile-grid-left"> <ProfileRules profile={profile} /> - <ProfileExporters exporters={props.exporters} profile={profile} /> - {profile.actions && profile.actions.edit && !profile.isBuiltIn && ( - <ProfilePermissions profile={profile} /> - )} + <ProfileExporters exporters={exporters} profile={profile} /> + {profile.actions?.edit && !profile.isBuiltIn && <ProfilePermissions profile={profile} />} </div> <div className="quality-profile-grid-right"> {profile.activeRuleCount === 0 && (profile.projectCount || profile.isDefault) && ( @@ -61,7 +60,7 @@ export function ProfileDetails(props: ProfileDetailsProps) { <ProfileInheritance profile={profile} - profiles={props.profiles} + profiles={profiles} updateProfiles={props.updateProfiles} /> <ProfileProjects profile={profile} /> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.tsx index a8569f1c1cd..baf9b2c1f66 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.tsx @@ -37,7 +37,10 @@ export default function ProfileExporters({ exporters, profile }: Props) { } return ( - <div className="boxed-group quality-profile-exporters"> + <section + aria-label={translate('quality_profiles.exporters')} + className="boxed-group quality-profile-exporters" + > <h2>{translate('quality_profiles.exporters')}</h2> <div className="boxed-group-inner"> <Alert className="big-spacer-bottom" variant="warning"> @@ -57,6 +60,6 @@ export default function ProfileExporters({ exporters, profile }: Props) { ))} </ul> </div> - </div> + </section> ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx index 31a5e739521..c8f0b62b6b6 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx @@ -118,7 +118,10 @@ export default class ProfileInheritance extends React.PureComponent<Props, State const extendsBuiltIn = ancestors != null && ancestors.some((profile) => profile.isBuiltIn); return ( - <div className="boxed-group quality-profile-inheritance"> + <section + aria-label={translate('quality_profiles.profile_inheritance')} + className="boxed-group quality-profile-inheritance" + > {profile.actions && profile.actions.edit && !profile.isBuiltIn && ( <div className="boxed-group-actions"> <Button className="pull-right js-change-parent" onClick={this.handleChangeParentClick}> @@ -184,7 +187,7 @@ export default class ProfileInheritance extends React.PureComponent<Props, State profiles={profiles.filter((p) => p !== profile && p.language === profile.language)} /> )} - </div> + </section> ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx index 4d939290446..00d35805c0d 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx @@ -20,9 +20,9 @@ import { sortBy, uniqBy } from 'lodash'; import * as React from 'react'; import { + SearchUsersGroupsParameters, searchGroups, searchUsers, - SearchUsersGroupsParameters, } from '../../../api/quality-profiles'; import { Button } from '../../../components/controls/buttons'; import { translate } from '../../../helpers/l10n'; @@ -138,7 +138,7 @@ export default class ProfilePermissions extends React.PureComponent<Props, State render() { return ( - <div className="boxed-group"> + <section aria-label={translate('permissions.page')} className="boxed-group"> <h2>{translate('permissions.page')}</h2> <div className="boxed-group-inner"> <p className="note">{translate('quality_profiles.default_permissions')}</p> @@ -184,7 +184,7 @@ export default class ProfilePermissions extends React.PureComponent<Props, State profile={this.props.profile} /> )} - </div> + </section> ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx index 2cbb49e7007..3b9cf80c7de 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx @@ -20,10 +20,10 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { removeGroup } from '../../../api/quality-profiles'; -import { Button, DeleteButton, ResetButtonLink } from '../../../components/controls/buttons'; import SimpleModal, { ChildrenProps } from '../../../components/controls/SimpleModal'; +import { Button, DeleteButton, ResetButtonLink } from '../../../components/controls/buttons'; import GroupIcon from '../../../components/icons/GroupIcon'; -import { translate } from '../../../helpers/l10n'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Group } from './ProfilePermissions'; interface Props { @@ -103,6 +103,10 @@ export default class ProfilePermissionsGroup extends React.PureComponent<Props, return ( <div className="clearfix big-spacer-bottom"> <DeleteButton + aria-label={translateWithParameters( + 'quality_profiles.permissions.remove.group_x', + group.name + )} className="pull-right spacer-top spacer-left spacer-right button-small" onClick={this.handleDeleteClick} /> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx index fc343d6e1e2..c12f76e8b4c 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx @@ -23,7 +23,7 @@ import { removeUser } from '../../../api/quality-profiles'; import SimpleModal, { ChildrenProps } from '../../../components/controls/SimpleModal'; import { DeleteButton, ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import LegacyAvatar from '../../../components/ui/LegacyAvatar'; -import { translate } from '../../../helpers/l10n'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; import { UserSelected } from '../../../types/types'; interface Props { @@ -107,6 +107,10 @@ export default class ProfilePermissionsUser extends React.PureComponent<Props, S return ( <div className="clearfix big-spacer-bottom"> <DeleteButton + aria-label={translateWithParameters( + 'quality_profiles.permissions.remove.user_x', + user.name + )} className="pull-right spacer-top spacer-left spacer-right button-small" onClick={this.handleDeleteClick} /> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx index 59e19683ef8..a1447c0d1da 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx @@ -169,7 +169,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { const { profile } = this.props; const hasNoActiveRules = profile.activeRuleCount === 0; return ( - <div className="boxed-group quality-profile-projects"> + <section className="boxed-group quality-profile-projects" aria-label={translate('projects')}> {profile.actions && profile.actions.associateProjects && ( <div className="boxed-group-actions"> <Tooltip @@ -199,7 +199,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { </div> {this.state.formOpen && <ChangeProjectsForm onClose={this.closeForm} profile={profile} />} - </div> + </section> ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx index b58616490df..18f521e76a4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx @@ -20,12 +20,13 @@ import { keyBy } from 'lodash'; import * as React from 'react'; import { getQualityProfile } from '../../../api/quality-profiles'; -import { searchRules, takeFacet } from '../../../api/rules'; +import { searchRules } from '../../../api/rules'; import Link from '../../../components/common/Link'; import Tooltip from '../../../components/controls/Tooltip'; import { Button } from '../../../components/controls/buttons'; import { translate } from '../../../helpers/l10n'; import { getRulesUrl } from '../../../helpers/urls'; +import { SearchRulesResponse } from '../../../types/coding-rules'; import { Dict } from '../../../types/types'; import { Profile } from '../types'; import ProfileRulesDeprecatedWarning from './ProfileRulesDeprecatedWarning'; @@ -118,8 +119,8 @@ export default class ProfileRules extends React.PureComponent<Props, State> { const [allRules, activatedRules, showProfile] = responses; this.setState({ activatedTotal: activatedRules.paging.total, - allByType: keyBy<ByType>(takeFacet(allRules, 'types'), 'val'), - activatedByType: keyBy<ByType>(takeFacet(activatedRules, 'types'), 'val'), + allByType: keyBy<ByType>(this.takeFacet(allRules, 'types'), 'val'), + activatedByType: keyBy<ByType>(this.takeFacet(activatedRules, 'types'), 'val'), compareToSonarWay: showProfile && showProfile.compareToSonarWay, total: allRules.paging.total, }); @@ -140,6 +141,11 @@ export default class ProfileRules extends React.PureComponent<Props, State> { : null; } + takeFacet(response: SearchRulesResponse, property: string) { + const facet = response.facets?.find((f) => f.property === property); + return facet ? facet.values : []; + } + render() { const { profile } = this.props; const { compareToSonarWay } = this.state; @@ -147,7 +153,7 @@ export default class ProfileRules extends React.PureComponent<Props, State> { const { actions = {} } = profile; return ( - <div className="boxed-group quality-profile-rules"> + <section aria-label={translate('rules')} className="boxed-group quality-profile-rules"> <div className="quality-profile-rules-distribution"> <table className="data condensed"> <thead> @@ -212,7 +218,7 @@ export default class ProfileRules extends React.PureComponent<Props, State> { sonarway={compareToSonarWay.profile} /> )} - </div> + </section> ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeParentForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeParentForm-test.tsx deleted file mode 100644 index d8481629d34..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeParentForm-test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfile } from '../../../../helpers/testMocks'; -import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils'; -import ChangeParentForm from '../ChangeParentForm'; - -beforeEach(() => jest.clearAllMocks()); - -jest.mock('../../../../api/quality-profiles', () => ({ - changeProfileParent: jest.fn().mockResolvedValue({}), -})); - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should handle form submit correcty', async () => { - const onChange = jest.fn(); - - const wrapper = shallowRender({ onChange }); - wrapper.instance().handleFormSubmit(mockEvent()); - await waitAndUpdate(wrapper); - - expect(onChange).toHaveBeenCalled(); -}); - -it('should handle select change correcty', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleSelectChange({ value: 'val' }); - await waitAndUpdate(wrapper); - - expect(wrapper.instance().state.selected).toEqual('val'); -}); - -function shallowRender(props?: Partial<ChangeParentForm['props']>) { - return shallow<ChangeParentForm>( - <ChangeParentForm - onChange={jest.fn()} - onClose={jest.fn()} - profile={mockQualityProfile()} - profiles={[ - mockQualityProfile(), - mockQualityProfile(), - mockQualityProfile(), - mockQualityProfile(), - ]} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx deleted file mode 100644 index d48a59bc77f..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { - associateProject, - dissociateProject, - getProfileProjects, -} from '../../../../api/quality-profiles'; -import SelectList, { SelectListFilter } from '../../../../components/controls/SelectList'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import ChangeProjectsForm from '../ChangeProjectsForm'; - -const profile: any = { key: 'profFile_key' }; - -jest.mock('../../../../api/quality-profiles', () => ({ - getProfileProjects: jest.fn().mockResolvedValue({ - paging: { pageIndex: 1, pageSize: 3, total: 55 }, - results: [ - { id: 'test1', key: 'test1', name: 'test1', selected: false }, - { id: 'test2', key: 'test2', name: 'test2', selected: false }, - { id: 'test3', key: 'test3', name: 'test3', selected: true }, - ], - }), - associateProject: jest.fn().mockResolvedValue({}), - dissociateProject: jest.fn().mockResolvedValue({}), -})); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - wrapper.find(SelectList).props().onSearch({ - query: '', - filter: SelectListFilter.Selected, - page: 1, - pageSize: 100, - }); - await waitAndUpdate(wrapper); - - expect(wrapper.instance().mounted).toBe(true); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.instance().renderElement('test1')).toMatchSnapshot(); - expect(wrapper.instance().renderElement('test_foo')).toMatchSnapshot(); - - expect(getProfileProjects).toHaveBeenCalledWith( - expect.objectContaining({ - key: profile.key, - p: 1, - ps: 100, - q: undefined, - selected: SelectListFilter.Selected, - }) - ); - expect(wrapper.state().needToReload).toBe(false); - - wrapper.instance().componentWillUnmount(); - expect(wrapper.instance().mounted).toBe(false); -}); - -it('should handle selection properly', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleSelect('toto'); - await waitAndUpdate(wrapper); - - expect(associateProject).toHaveBeenCalledWith(profile, 'toto'); - expect(wrapper.state().needToReload).toBe(true); -}); - -it('should handle deselection properly', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleUnselect('tata'); - await waitAndUpdate(wrapper); - - expect(dissociateProject).toHaveBeenCalledWith(profile, 'tata'); - expect(wrapper.state().needToReload).toBe(true); -}); - -function shallowRender(props: Partial<ChangeProjectsForm['props']> = {}) { - return shallow<ChangeProjectsForm>( - <ChangeProjectsForm onClose={jest.fn()} profile={profile} {...props} /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx deleted file mode 100644 index 8de89c18ec4..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfile } from '../../../../helpers/testMocks'; -import { ProfileDetails, ProfileDetailsProps } from '../ProfileDetails'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect( - shallowRender({ profile: mockQualityProfile({ actions: { edit: true } }) }) - ).toMatchSnapshot('edit permissions'); - expect( - shallowRender({ - profile: mockQualityProfile({ activeRuleCount: 0, projectCount: 0 }), - }) - ).toMatchSnapshot('no active rules (same as default)'); - expect( - shallowRender({ - profile: mockQualityProfile({ projectCount: 0, isDefault: true, activeRuleCount: 0 }), - }) - ).toMatchSnapshot('is default profile, no active rules'); - expect( - shallowRender({ profile: mockQualityProfile({ projectCount: 10, activeRuleCount: 0 }) }) - ).toMatchSnapshot('projects associated, no active rules'); -}); - -function shallowRender(props: Partial<ProfileDetailsProps> = {}) { - return shallow<ProfileDetailsProps>( - <ProfileDetails - exporters={[]} - profile={mockQualityProfile()} - profiles={[]} - updateProfiles={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileExporters-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileExporters-test.tsx deleted file mode 100644 index ca8d96a1520..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileExporters-test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfile, mockQualityProfileExporter } from '../../../../helpers/testMocks'; -import { FCProps } from '../../../../types/misc'; -import ProfileExporters from '../ProfileExporters'; - -it('should render correctly', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<FCProps<typeof ProfileExporters>> = {}) { - const profile = mockQualityProfile(); - return shallow( - <ProfileExporters - exporters={[mockQualityProfileExporter({ languages: [profile.language] })]} - profile={profile} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritance-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritance-test.tsx deleted file mode 100644 index 88816590512..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritance-test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfile, mockQualityProfileInheritance } from '../../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import ProfileInheritance from '../ProfileInheritance'; - -beforeEach(() => jest.clearAllMocks()); - -jest.mock('../../../../api/quality-profiles', () => ({ - getProfileInheritance: jest.fn().mockResolvedValue({ - children: [mockQualityProfileInheritance()], - ancestors: [mockQualityProfileInheritance()], - }), -})); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - - expect(wrapper).toMatchSnapshot(); -}); - -it('should render modal correctly', async () => { - const wrapper = shallowRender(); - wrapper.instance().handleChangeParentClick(); - await waitAndUpdate(wrapper); - - expect(wrapper).toMatchSnapshot(); -}); - -it('should handle parent change correctly', async () => { - const updateProfiles = jest.fn().mockResolvedValueOnce({}); - - const wrapper = shallowRender({ updateProfiles }); - wrapper.instance().handleParentChange(); - await waitAndUpdate(wrapper); - - expect(updateProfiles).toHaveBeenCalled(); -}); - -function shallowRender(props: Partial<ProfileInheritance['props']> = {}) { - return shallow<ProfileInheritance>( - <ProfileInheritance - profile={mockQualityProfile()} - profiles={[mockQualityProfile()]} - updateProfiles={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritanceBox-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritanceBox-test.tsx deleted file mode 100644 index 78143a90d02..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritanceBox-test.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfileInheritance } from '../../../../helpers/testMocks'; -import ProfileInheritanceBox from '../ProfileInheritanceBox'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); - expect( - shallowRender({ - depth: 3, - displayLink: true, - profile: mockQualityProfileInheritance({ isBuiltIn: true }), - }) - ).toMatchSnapshot(); - expect(shallowRender({ extendsBuiltIn: true })).toMatchSnapshot(); - expect( - shallowRender({ profile: mockQualityProfileInheritance({ overridingRuleCount: 10 }) }) - ).toMatchSnapshot(); -}); - -function shallowRender(props = {}) { - return shallow( - <ProfileInheritanceBox - depth={1} - language="foo" - profile={mockQualityProfileInheritance()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx deleted file mode 100644 index 37fd73766d2..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx +++ /dev/null @@ -1,119 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { mount, shallow } from 'enzyme'; -import * as React from 'react'; -import { searchGroups, searchUsers } from '../../../../api/quality-profiles'; -import { click, waitAndUpdate } from '../../../../helpers/testUtils'; -import ProfilePermissions from '../ProfilePermissions'; - -jest.mock('../../../../api/quality-profiles', () => ({ - searchUsers: jest.fn(() => Promise.resolve([])), - searchGroups: jest.fn(() => Promise.resolve([])), -})); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('renders', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); - - wrapper.setState({ - groups: [{ name: 'Lambda' }], - loading: false, - users: [{ login: 'luke', name: 'Luke Skywalker', selected: false }], - }); - expect(wrapper).toMatchSnapshot(); -}); - -it('should update correctly', () => { - const wrapper = shallowRender(); - - wrapper.setProps({ profile: { key: 'otherKey', name: 'new profile', language: 'js' } }); - - expect(searchGroups).toHaveBeenCalledTimes(2); - expect(searchUsers).toHaveBeenCalledTimes(2); -}); - -it('opens add users form', async () => { - (searchUsers as jest.Mock).mockImplementationOnce(() => - Promise.resolve({ users: [{ login: 'luke', name: 'Luke Skywalker' }] }) - ); - const wrapper = shallowRender(); - expect(searchUsers).toHaveBeenCalled(); - await waitAndUpdate(wrapper); - expect(wrapper.find('ProfilePermissionsForm').exists()).toBe(false); - - click(wrapper.find('Button')); - expect(wrapper.find('ProfilePermissionsForm').exists()).toBe(true); - - wrapper.find('ProfilePermissionsForm').prop<Function>('onClose')(); - wrapper.update(); - expect(wrapper.find('ProfilePermissionsForm').exists()).toBe(false); -}); - -it('removes user', () => { - const wrapper = shallowRender(); - (wrapper.instance() as ProfilePermissions).mounted = true; - - const joda = { login: 'joda', name: 'Joda', selected: false }; - wrapper.setState({ - loading: false, - users: [{ login: 'luke', name: 'Luke Skywalker', selected: false }, joda], - }); - expect(wrapper.find('ProfilePermissionsUser')).toHaveLength(2); - - wrapper.find('ProfilePermissionsUser').first().prop<Function>('onDelete')(joda); - wrapper.update(); - expect(wrapper.find('ProfilePermissionsUser')).toHaveLength(1); -}); - -it('removes group', () => { - const wrapper = shallowRender(); - (wrapper.instance() as ProfilePermissions).mounted = true; - - const lambda = { name: 'Lambda' }; - wrapper.setState({ loading: false, groups: [{ name: 'Atlas' }, lambda] }); - expect(wrapper.find('ProfilePermissionsGroup')).toHaveLength(2); - - wrapper.find('ProfilePermissionsGroup').first().prop<Function>('onDelete')(lambda); - wrapper.update(); - expect(wrapper.find('ProfilePermissionsGroup')).toHaveLength(1); -}); - -it('fetches users and groups on mount', () => { - mount(<ProfilePermissions profile={{ key: 'sonar-way', name: 'Sonar way', language: 'js' }} />); - expect(searchUsers).toHaveBeenCalledWith({ - language: 'js', - qualityProfile: 'Sonar way', - selected: 'selected', - }); - expect(searchGroups).toHaveBeenCalledWith({ - language: 'js', - qualityProfile: 'Sonar way', - selected: 'selected', - }); -}); - -function shallowRender(overrides: Partial<{ key: string; name: string; language: string }> = {}) { - const profile = { key: 'sonar-way', name: 'Sonar way', language: 'js', ...overrides }; - return shallow<ProfilePermissions>(<ProfilePermissions profile={profile} />); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsForm-test.tsx deleted file mode 100644 index 69c079b8062..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsForm-test.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { addGroup, addUser } from '../../../../api/quality-profiles'; -import { mockGroup, mockUser } from '../../../../helpers/testMocks'; -import { submit, waitAndUpdate } from '../../../../helpers/testUtils'; -import { UserSelected } from '../../../../types/types'; -import ProfilePermissionsForm from '../ProfilePermissionsForm'; - -jest.mock('../../../../api/quality-profiles', () => ({ - addUser: jest.fn().mockResolvedValue(null), - addGroup: jest.fn().mockResolvedValue(null), - searchGroups: jest.fn().mockResolvedValue({ groups: [] }), - searchUsers: jest.fn().mockResolvedValue({ users: [] }), -})); - -const PROFILE = { language: 'js', name: 'Sonar way' }; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender().setState({ submitting: true })).toMatchSnapshot('submitting'); -}); - -it('correctly adds users', async () => { - const onUserAdd = jest.fn(); - const wrapper = shallowRender({ onUserAdd }); - - const user: UserSelected = { ...mockUser(), name: 'John doe', active: true, selected: true }; - wrapper.instance().handleValueChange(user); - expect(wrapper.state().selected).toBe(user); - - submit(wrapper.find('form')); - expect(wrapper).toMatchSnapshot(); - expect(addUser).toHaveBeenCalledWith( - expect.objectContaining({ - language: PROFILE.language, - qualityProfile: PROFILE.name, - login: user.login, - }) - ); - - await waitAndUpdate(wrapper); - expect(onUserAdd).toHaveBeenCalledWith(user); -}); - -it('correctly adds groups', async () => { - const onGroupAdd = jest.fn(); - const wrapper = shallowRender({ onGroupAdd }); - - const group = mockGroup(); - wrapper.instance().handleValueChange(group); - expect(wrapper.state().selected).toBe(group); - - submit(wrapper.find('form')); - expect(wrapper).toMatchSnapshot(); - expect(addGroup).toHaveBeenCalledWith( - expect.objectContaining({ - language: PROFILE.language, - qualityProfile: PROFILE.name, - group: group.name, - }) - ); - - await waitAndUpdate(wrapper); - expect(onGroupAdd).toHaveBeenCalledWith(group); -}); - -function shallowRender(props: Partial<ProfilePermissionsForm['props']> = {}) { - return shallow<ProfilePermissionsForm>( - <ProfilePermissionsForm - onClose={jest.fn()} - onGroupAdd={jest.fn()} - onUserAdd={jest.fn()} - profile={PROFILE} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx deleted file mode 100644 index 8cffc0135d4..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { searchGroups, searchUsers } from '../../../../api/quality-profiles'; -import { - mockReactSelectControlProps, - mockReactSelectOptionProps, -} from '../../../../helpers/mocks/react-select'; -import { mockUser } from '../../../../helpers/testMocks'; -import ProfilePermissionsFormSelect from '../ProfilePermissionsFormSelect'; - -jest.mock('lodash', () => { - const lodash = jest.requireActual('lodash'); - lodash.debounce = - (fn: Function) => - (...args: any[]) => - fn(...args); - return lodash; -}); - -jest.mock('../../../../api/quality-profiles', () => ({ - searchGroups: jest.fn().mockResolvedValue([]), - searchUsers: jest.fn().mockResolvedValue([]), -})); - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should handle search', async () => { - (searchUsers as jest.Mock).mockResolvedValueOnce({ users: [mockUser()] }); - (searchGroups as jest.Mock).mockResolvedValueOnce({ groups: [{ name: 'group1' }] }); - - const wrapper = shallowRender(); - const query = 'Waldo'; - const results = await new Promise((resolve) => { - wrapper.instance().handleSearch(query, resolve); - }); - expect(searchUsers).toHaveBeenCalledWith(expect.objectContaining({ q: query })); - expect(searchGroups).toHaveBeenCalledWith(expect.objectContaining({ q: query })); - - expect(results).toHaveLength(2); -}); - -it('should render option correctly', () => { - const wrapper = shallowRender(); - const OptionRenderer = wrapper.instance().optionRenderer; - expect( - shallow(<OptionRenderer {...mockReactSelectOptionProps({ value: 'test', name: 'name' })} />) - ).toMatchSnapshot('option renderer'); -}); - -it('should render value correctly', () => { - const wrapper = shallowRender(); - const ValueRenderer = wrapper.instance().singleValueRenderer; - expect( - shallow(<ValueRenderer {...mockReactSelectOptionProps({ value: 'test', name: 'name' })} />) - ).toMatchSnapshot('value renderer'); -}); - -it('should render control correctly', () => { - const wrapper = shallowRender(); - const ControlRenderer = wrapper.instance().controlRenderer; - expect(shallow(<ControlRenderer {...mockReactSelectControlProps()} />)).toMatchSnapshot( - 'control renderer' - ); -}); - -function shallowRender(overrides: Partial<ProfilePermissionsFormSelect['props']> = {}) { - return shallow<ProfilePermissionsFormSelect>( - <ProfilePermissionsFormSelect - onChange={jest.fn()} - profile={{ language: 'Java', name: 'Sonar Way' }} - {...overrides} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx deleted file mode 100644 index 35bb7d46951..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -/* eslint-disable import/first */ -jest.mock('../../../../api/quality-profiles', () => ({ - removeGroup: jest.fn(() => Promise.resolve()), -})); - -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { click } from '../../../../helpers/testUtils'; -import ProfilePermissionsGroup from '../ProfilePermissionsGroup'; - -const removeGroup = require('../../../../api/quality-profiles').removeGroup as jest.Mock<any>; - -const profile = { language: 'js', name: 'Sonar way' }; -const group = { name: 'lambda' }; - -beforeEach(() => { - removeGroup.mockClear(); -}); - -it('renders', () => { - expect( - shallow(<ProfilePermissionsGroup group={group} onDelete={jest.fn()} profile={profile} />) - ).toMatchSnapshot(); -}); - -it('removes user', async () => { - const onDelete = jest.fn(); - const wrapper = shallow( - <ProfilePermissionsGroup group={group} onDelete={onDelete} profile={profile} /> - ); - (wrapper.instance() as ProfilePermissionsGroup).mounted = true; - expect(wrapper.find('SimpleModal').exists()).toBe(false); - - click(wrapper.find('DeleteButton')); - expect(wrapper.find('SimpleModal').exists()).toBe(true); - - wrapper.find('SimpleModal').prop<Function>('onSubmit')(); - expect(removeGroup).toHaveBeenCalledWith({ - group: 'lambda', - language: 'js', - qualityProfile: 'Sonar way', - }); - - await new Promise(setImmediate); - expect(onDelete).toHaveBeenCalledWith(group); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx deleted file mode 100644 index 3f07228e2c4..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { removeUser } from '../../../../api/quality-profiles'; -import { click } from '../../../../helpers/testUtils'; -import { UserSelected } from '../../../../types/types'; -import ProfilePermissionsUser from '../ProfilePermissionsUser'; - -jest.mock('../../../../api/quality-profiles', () => ({ - removeUser: jest.fn(() => Promise.resolve()), -})); - -const profile = { language: 'js', name: 'Sonar way' }; -const user: UserSelected = { login: 'luke', name: 'Luke Skywalker', selected: true }; - -beforeEach(() => { - jest.clearAllMocks(); -}); - -it('renders', () => { - expect( - shallow(<ProfilePermissionsUser onDelete={jest.fn()} profile={profile} user={user} />) - ).toMatchSnapshot(); -}); - -it('removes user', async () => { - const onDelete = jest.fn(); - const wrapper = shallow( - <ProfilePermissionsUser onDelete={onDelete} profile={profile} user={user} /> - ); - (wrapper.instance() as ProfilePermissionsUser).mounted = true; - expect(wrapper.find('SimpleModal').exists()).toBe(false); - - click(wrapper.find('DeleteButton')); - expect(wrapper.find('SimpleModal').exists()).toBe(true); - - wrapper.find('SimpleModal').prop<Function>('onSubmit')(); - expect(removeUser).toHaveBeenCalledWith({ - language: 'js', - login: 'luke', - qualityProfile: 'Sonar way', - }); - - await new Promise(setImmediate); - expect(onDelete).toHaveBeenCalledWith(user); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileProjects-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileProjects-test.tsx deleted file mode 100644 index c38aaeedff1..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileProjects-test.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { getProfileProjects } from '../../../../api/quality-profiles'; -import { mockQualityProfile } from '../../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import ChangeProjectsForm from '../ChangeProjectsForm'; -import ProfileProjects from '../ProfileProjects'; - -jest.mock('../../../../api/quality-profiles', () => ({ - getProfileProjects: jest.fn().mockResolvedValue({ - results: [ - { - id: '633a5180-1ad7-4008-a5cb-e1d3cec4c816', - key: 'org.sonarsource.xml:xml', - name: 'SonarXML', - selected: true, - }, - ], - paging: { pageIndex: 1, pageSize: 2, total: 10 }, - more: true, - }), -})); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot('loading'); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot('default'); - wrapper.setProps({ - profile: mockQualityProfile({ actions: { associateProjects: false } }), - }); - expect(wrapper).toMatchSnapshot('no rights'); - wrapper.setProps({ - profile: mockQualityProfile({ - projectCount: 0, - activeRuleCount: 0, - actions: { associateProjects: true }, - }), - }); - expect(wrapper).toMatchSnapshot('no active rules, but associated projects'); - wrapper.setProps({ - profile: mockQualityProfile({ activeRuleCount: 0, actions: { associateProjects: true } }), - }); - wrapper.setState({ projects: [] }); - expect(wrapper).toMatchSnapshot('no active rules, no associated projects'); - wrapper.instance().loadMore(); - expect(getProfileProjects).toHaveBeenLastCalledWith({ p: 2, key: 'key' }); -}); - -it('should open and close the form', async () => { - const wrapper = shallowRender(); - await waitAndUpdate(wrapper); - - wrapper.instance().handleChangeClick(); - expect(wrapper.find(ChangeProjectsForm).exists()).toBe(true); - - wrapper.instance().closeForm(); - expect(wrapper.find(ChangeProjectsForm).exists()).toBe(false); -}); - -function shallowRender(props: Partial<ProfileProjects['props']> = {}) { - return shallow<ProfileProjects>( - <ProfileProjects - profile={mockQualityProfile({ actions: { associateProjects: true } })} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx deleted file mode 100644 index 005c1a58f6b..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx +++ /dev/null @@ -1,150 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { getQualityProfile } from '../../../../api/quality-profiles'; -import { mockPaging, mockQualityProfile } from '../../../../helpers/testMocks'; -import { waitAndUpdate } from '../../../../helpers/testUtils'; -import ProfileRules from '../ProfileRules'; - -const PROFILE = mockQualityProfile({ - activeRuleCount: 68, - activeDeprecatedRuleCount: 0, - depth: 0, - language: 'js', - rulesUpdatedAt: '2017-06-28T12:58:44+0000', -}); - -const EDITABLE_PROFILE = { ...PROFILE, actions: { edit: true } }; - -const apiResponseAll = { - facets: [ - { - property: 'types', - values: [ - { val: 'CODE_SMELL', count: 168 }, - { val: 'BUG', count: 68 }, - { val: 'VULNERABILITY', count: 7 }, - { val: 'SECURITY_HOTSPOT', count: 10 }, - ], - }, - ], - paging: mockPaging({ - total: 253, - }), -}; - -const apiResponseActive = { - facets: [ - { - property: 'types', - values: [ - { val: 'BUG', count: 68 }, - { val: 'CODE_SMELL', count: 0 }, - { val: 'VULNERABILITY', count: 0 }, - { val: 'SECURITY_HOTSPOT', count: 0 }, - ], - }, - ], - paging: mockPaging({ - total: 68, - }), -}; - -jest.mock('../../../../api/rules', () => ({ - ...jest.requireActual('../../../../api/rules'), - searchRules: jest - .fn() - .mockImplementation((data: any) => - Promise.resolve(data.activation === 'true' ? apiResponseActive : apiResponseAll) - ), -})); - -jest.mock('../../../../api/quality-profiles', () => ({ - ...jest.requireActual('../../../../api/quality-profiles'), - getQualityProfile: jest.fn().mockImplementation(() => - Promise.resolve({ - compareToSonarWay: { - profile: 'sonarway', - profileName: 'Sonar way', - missingRuleCount: 4, - }, - }) - ), -})); - -beforeEach(jest.clearAllMocks); - -it('should render the quality profiles rules with sonarway comparison', async () => { - const wrapper = shallow(<ProfileRules profile={PROFILE} />); - const instance = wrapper.instance() as ProfileRules; - instance.mounted = true; - instance.loadRules(); - await waitAndUpdate(wrapper); - expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(1); - expect(wrapper).toMatchSnapshot(); -}); - -it('should show a button to activate more rules for admins', () => { - const wrapper = shallow(<ProfileRules profile={EDITABLE_PROFILE} />); - expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); -}); - -it('should show a disabled button to activate more rules for built-in profiles', () => { - const wrapper = shallow( - <ProfileRules profile={{ ...EDITABLE_PROFILE, actions: { copy: true }, isBuiltIn: true }} /> - ); - expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); -}); - -it('should show a deprecated rules warning message', () => { - const wrapper = shallow( - <ProfileRules profile={{ ...EDITABLE_PROFILE, activeDeprecatedRuleCount: 8 }} /> - ); - expect(wrapper.find('ProfileRulesDeprecatedWarning')).toMatchSnapshot(); -}); - -it('should not show a button to activate more rules on built in profiles', () => { - const wrapper = shallow(<ProfileRules profile={{ ...EDITABLE_PROFILE, isBuiltIn: true }} />); - expect(wrapper.find('.js-activate-rules').exists()).toBe(false); -}); - -it('should not show sonarway comparison for built in profiles', async () => { - (getQualityProfile as jest.Mock).mockReturnValueOnce({}); - const wrapper = shallow(<ProfileRules profile={{ ...PROFILE, isBuiltIn: true }} />); - await new Promise(setImmediate); - wrapper.update(); - expect(getQualityProfile).toHaveBeenCalledTimes(0); - expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0); -}); - -it('should not show sonarway comparison if there is no missing rules', async () => { - (getQualityProfile as jest.Mock).mockReturnValueOnce({ - compareToSonarWay: { - profile: 'sonarway', - profileName: 'Sonar way', - missingRuleCount: 0, - }, - }); - const wrapper = shallow(<ProfileRules profile={PROFILE} />); - await waitAndUpdate(wrapper); - expect(getQualityProfile).toHaveBeenCalledTimes(1); - expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.tsx deleted file mode 100644 index 1d69d339ee3..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import ProfileRulesDeprecatedWarning from '../ProfileRulesDeprecatedWarning'; - -it('should render correctly', () => { - expect( - shallow(<ProfileRulesDeprecatedWarning activeDeprecatedRules={18} profile="bar" />) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.tsx deleted file mode 100644 index ba553ffa66d..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import ProfileRulesRowOfType from '../ProfileRulesRowOfType'; - -it('should render correctly', () => { - expect( - shallow(<ProfileRulesRowOfType count={3} qprofile="bar" total={10} type="BUG" />) - ).toMatchSnapshot(); -}); - -it('should render correctly if there is 0 rules', () => { - expect( - shallow(<ProfileRulesRowOfType count={0} qprofile="bar" total={0} type="VULNERABILITY" />) - ).toMatchSnapshot(); -}); - -it('should render correctly if there is missing data', () => { - expect( - shallow(<ProfileRulesRowOfType count={5} qprofile="bar" total={null} type="VULNERABILITY" />) - ).toMatchSnapshot(); - expect( - shallow(<ProfileRulesRowOfType count={null} qprofile="foo" total={10} type="VULNERABILITY" />) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.tsx deleted file mode 100644 index 547dd01ae9d..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import ProfileRulesRowTotal from '../ProfileRulesRowTotal'; - -it('should render correctly', () => { - expect(shallow(<ProfileRulesRowTotal count={3} qprofile="bar" total={10} />)).toMatchSnapshot(); -}); - -it('should render correctly if there is 0 rules', () => { - expect(shallow(<ProfileRulesRowTotal count={0} qprofile="bar" total={0} />)).toMatchSnapshot(); -}); - -it('should render correctly if there is missing data', () => { - expect(shallow(<ProfileRulesRowTotal count={5} qprofile="bar" total={null} />)).toMatchSnapshot(); - expect( - shallow(<ProfileRulesRowTotal count={null} qprofile="foo" total={10} />) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.tsx deleted file mode 100644 index 408e40249c4..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import ProfileRulesSonarWayComparison from '../ProfileRulesSonarWayComparison'; - -it('should render correctly', () => { - expect( - shallow( - <ProfileRulesSonarWayComparison - language="Java" - profile="bar" - sonarWayMissingRules={158} - sonarway="baz" - /> - ) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeParentForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeParentForm-test.tsx.snap deleted file mode 100644 index 74175891828..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeParentForm-test.tsx.snap +++ /dev/null @@ -1,97 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Modal - contentLabel="quality_profiles.change_parent" - onRequestClose={[MockFunction]} - size="small" -> - <form - id="change-profile-parent-form" - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - quality_profiles.change_parent - </h2> - </div> - <div - className="modal-body" - > - <MandatoryFieldsExplanation - className="modal-field" - /> - <div - className="modal-field" - > - <label - htmlFor="change-profile-parent-input" - > - quality_profiles.parent - <MandatoryFieldMarker /> - </label> - <Select - autoFocus={true} - className="width-100" - id="change-profile-parent" - inputId="change-profile-parent-input" - isClearable={false} - isSearchable={true} - name="parentKey" - onChange={[Function]} - options={ - [ - { - "label": "none", - "value": "", - }, - { - "label": "name", - "value": "key", - }, - { - "label": "name", - "value": "key", - }, - { - "label": "name", - "value": "key", - }, - { - "label": "name", - "value": "key", - }, - ] - } - value={ - [ - { - "label": "none", - "value": "", - }, - ] - } - /> - </div> - </div> - <div - className="modal-foot" - > - <SubmitButton - disabled={true} - id="change-profile-parent-submit" - > - change_verb - </SubmitButton> - <ResetButtonLink - id="change-profile-parent-cancel" - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeProjectsForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeProjectsForm-test.tsx.snap deleted file mode 100644 index 68a5559cfca..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeProjectsForm-test.tsx.snap +++ /dev/null @@ -1,79 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Modal - contentLabel="projects" - onRequestClose={[MockFunction]} -> - <div - className="modal-head" - > - <h2> - projects - </h2> - </div> - <div - className="modal-body modal-container" - id="profile-projects" - > - <SelectList - allowBulkSelection={true} - elements={ - [ - "test1", - "test2", - "test3", - ] - } - elementsTotalCount={55} - labelAll="quality_gates.projects.all" - labelSelected="quality_gates.projects.with" - labelUnselected="quality_gates.projects.without" - needToReload={false} - onSearch={[Function]} - onSelect={[Function]} - onUnselect={[Function]} - renderElement={[Function]} - selectedElements={ - [ - "test3", - ] - } - withPaging={true} - /> - </div> - <div - className="modal-foot" - > - <ResetButtonLink - onClick={[MockFunction]} - > - close - </ResetButtonLink> - </div> -</Modal> -`; - -exports[`should render correctly 2`] = ` -<div - className="select-list-list-item" -> - <React.Fragment> - test1 - <br /> - <span - className="note" - > - test1 - </span> - </React.Fragment> -</div> -`; - -exports[`should render correctly 3`] = ` -<div - className="select-list-list-item" -> - test_foo -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap deleted file mode 100644 index dabfc8c2481..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap +++ /dev/null @@ -1,506 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<div> - <div - className="quality-profile-grid" - > - <div - className="quality-profile-grid-left" - > - <ProfileRules - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - } - } - /> - <ProfileExporters - exporters={[]} - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - } - } - /> - </div> - <div - className="quality-profile-grid-right" - > - <ProfileInheritance - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - } - } - profiles={[]} - updateProfiles={[MockFunction]} - /> - <ProfileProjects - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - } - } - /> - </div> - </div> -</div> -`; - -exports[`should render correctly: edit permissions 1`] = ` -<div> - <div - className="quality-profile-grid" - > - <div - className="quality-profile-grid-left" - > - <ProfileRules - profile={ - { - "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, - } - } - /> - <ProfileExporters - exporters={[]} - profile={ - { - "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, - } - } - /> - <ProfilePermissions - profile={ - { - "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, - } - } - /> - </div> - <div - className="quality-profile-grid-right" - > - <ProfileInheritance - profile={ - { - "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, - } - } - profiles={[]} - updateProfiles={[MockFunction]} - /> - <ProfileProjects - profile={ - { - "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, - } - } - /> - </div> - </div> -</div> -`; - -exports[`should render correctly: is default profile, no active rules 1`] = ` -<div> - <div - className="quality-profile-grid" - > - <div - className="quality-profile-grid-left" - > - <ProfileRules - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": true, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - /> - <ProfileExporters - exporters={[]} - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": true, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - /> - </div> - <div - className="quality-profile-grid-right" - > - <Alert - className="big-spacer-bottom" - variant="warning" - > - quality_profiles.warning.is_default_no_rules - </Alert> - <ProfileInheritance - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": true, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - profiles={[]} - updateProfiles={[MockFunction]} - /> - <ProfileProjects - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": true, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - /> - </div> - </div> -</div> -`; - -exports[`should render correctly: no active rules (same as default) 1`] = ` -<div> - <div - className="quality-profile-grid" - > - <div - className="quality-profile-grid-left" - > - <ProfileRules - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - /> - <ProfileExporters - exporters={[]} - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - /> - </div> - <div - className="quality-profile-grid-right" - > - <ProfileInheritance - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - profiles={[]} - updateProfiles={[MockFunction]} - /> - <ProfileProjects - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 0, - } - } - /> - </div> - </div> -</div> -`; - -exports[`should render correctly: projects associated, no active rules 1`] = ` -<div> - <div - className="quality-profile-grid" - > - <div - className="quality-profile-grid-left" - > - <ProfileRules - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 10, - } - } - /> - <ProfileExporters - exporters={[]} - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 10, - } - } - /> - </div> - <div - className="quality-profile-grid-right" - > - <Alert - className="big-spacer-bottom" - variant="warning" - > - quality_profiles.warning.used_by_projects_no_rules - </Alert> - <ProfileInheritance - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 10, - } - } - profiles={[]} - updateProfiles={[MockFunction]} - /> - <ProfileProjects - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 0, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 10, - } - } - /> - </div> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileExporters-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileExporters-test.tsx.snap deleted file mode 100644 index 1f5eff13f09..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileExporters-test.tsx.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="boxed-group quality-profile-exporters" -> - <h2> - quality_profiles.exporters - </h2> - <div - className="boxed-group-inner" - > - <Alert - className="big-spacer-bottom" - variant="warning" - > - quality_profiles.exporters.deprecated - </Alert> - <ul> - <li - data-key="exporter-key" - key="exporter-key" - > - <ForwardRef(Link) - target="_blank" - to="/api/qualityprofiles/export?exporterKey=exporter-key&language=js&qualityProfile=name" - > - exporter-name - </ForwardRef(Link)> - </li> - </ul> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritance-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritance-test.tsx.snap deleted file mode 100644 index 150831cc08e..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritance-test.tsx.snap +++ /dev/null @@ -1,147 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="boxed-group quality-profile-inheritance" -> - <div - className="boxed-group-header" - > - <h2> - quality_profiles.profile_inheritance - </h2> - </div> - <div - className="boxed-group-inner" - > - <table - className="data zebra" - > - <tbody> - <ProfileInheritanceBox - depth={0} - key="foo" - language="js" - profile={ - { - "activeRuleCount": 4, - "isBuiltIn": false, - "key": "foo", - "name": "Foo", - "overridingRuleCount": 0, - } - } - type="ancestor" - /> - <ProfileInheritanceBox - depth={2} - key="foo" - language="js" - profile={ - { - "activeRuleCount": 4, - "isBuiltIn": false, - "key": "foo", - "name": "Foo", - "overridingRuleCount": 0, - } - } - type="child" - /> - </tbody> - </table> - </div> -</div> -`; - -exports[`should render modal correctly 1`] = ` -<div - className="boxed-group quality-profile-inheritance" -> - <div - className="boxed-group-header" - > - <h2> - quality_profiles.profile_inheritance - </h2> - </div> - <div - className="boxed-group-inner" - > - <table - className="data zebra" - > - <tbody> - <ProfileInheritanceBox - depth={0} - key="foo" - language="js" - profile={ - { - "activeRuleCount": 4, - "isBuiltIn": false, - "key": "foo", - "name": "Foo", - "overridingRuleCount": 0, - } - } - type="ancestor" - /> - <ProfileInheritanceBox - depth={2} - key="foo" - language="js" - profile={ - { - "activeRuleCount": 4, - "isBuiltIn": false, - "key": "foo", - "name": "Foo", - "overridingRuleCount": 0, - } - } - type="child" - /> - </tbody> - </table> - </div> - <ChangeParentForm - onChange={[Function]} - onClose={[Function]} - profile={ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - } - } - profiles={ - [ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - }, - ] - } - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritanceBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritanceBox-test.tsx.snap deleted file mode 100644 index 905d5f7b608..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritanceBox-test.tsx.snap +++ /dev/null @@ -1,136 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<tr - className="it__quality-profiles__inheritance-current" -> - <td> - <div - style={ - { - "paddingLeft": 25, - } - } - > - <ProfileLink - className="text-middle" - language="foo" - name="Foo" - > - Foo - </ProfileLink> - </div> - </td> - <td> - quality_profile.x_active_rules.4 - </td> - <td> - <p> - quality_profiles.x_overridden_rules.0 - </p> - </td> -</tr> -`; - -exports[`should render correctly 2`] = ` -<tr - className="it__quality-profiles__inheritance-current" -> - <td> - <div - style={ - { - "paddingLeft": 75, - } - } - > - <ProfileLink - className="text-middle" - language="foo" - name="Foo" - > - Foo - </ProfileLink> - <BuiltInQualityProfileBadge - className="spacer-left" - /> - </div> - </td> - <td> - quality_profile.x_active_rules.4 - </td> - <td> - <p> - quality_profiles.x_overridden_rules.0 - </p> - </td> -</tr> -`; - -exports[`should render correctly 3`] = ` -<tr - className="it__quality-profiles__inheritance-current" -> - <td> - <div - style={ - { - "paddingLeft": 25, - } - } - > - <ProfileLink - className="text-middle" - language="foo" - name="Foo" - > - Foo - </ProfileLink> - <HelpTooltip - className="spacer-left" - overlay="quality_profiles.extends_built_in" - /> - </div> - </td> - <td> - quality_profile.x_active_rules.4 - </td> - <td> - <p> - quality_profiles.x_overridden_rules.0 - </p> - </td> -</tr> -`; - -exports[`should render correctly 4`] = ` -<tr - className="it__quality-profiles__inheritance-current" -> - <td> - <div - style={ - { - "paddingLeft": 25, - } - } - > - <ProfileLink - className="text-middle" - language="foo" - name="Foo" - > - Foo - </ProfileLink> - </div> - </td> - <td> - quality_profile.x_active_rules.4 - </td> - <td> - <p> - quality_profiles.x_overridden_rules.10 - </p> - </td> -</tr> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap deleted file mode 100644 index ce377a5cfa7..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap +++ /dev/null @@ -1,93 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` -<div - className="boxed-group" -> - <h2> - permissions.page - </h2> - <div - className="boxed-group-inner" - > - <p - className="note" - > - quality_profiles.default_permissions - </p> - <div - className="big-spacer-top" - > - <i - className="spinner" - /> - </div> - </div> -</div> -`; - -exports[`renders 2`] = ` -<div - className="boxed-group" -> - <h2> - permissions.page - </h2> - <div - className="boxed-group-inner" - > - <p - className="note" - > - quality_profiles.default_permissions - </p> - <div - className="big-spacer-top" - > - <ProfilePermissionsUser - key="luke" - onDelete={[Function]} - profile={ - { - "key": "sonar-way", - "language": "js", - "name": "Sonar way", - } - } - user={ - { - "login": "luke", - "name": "Luke Skywalker", - "selected": false, - } - } - /> - <ProfilePermissionsGroup - group={ - { - "name": "Lambda", - } - } - key="Lambda" - onDelete={[Function]} - profile={ - { - "key": "sonar-way", - "language": "js", - "name": "Sonar way", - } - } - /> - <div - className="text-right" - > - <Button - onClick={[Function]} - > - quality_profiles.grant_permissions_to_more_users - </Button> - </div> - </div> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap deleted file mode 100644 index d5e25783a6b..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap +++ /dev/null @@ -1,230 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`correctly adds groups 1`] = ` -<Modal - contentLabel="quality_profiles.grant_permissions_to_user_or_group" - onRequestClose={[MockFunction]} -> - <header - className="modal-head" - > - <h2> - quality_profiles.grant_permissions_to_user_or_group - </h2> - </header> - <form - onSubmit={[Function]} - > - <div - className="modal-body" - > - <div - className="modal-field" - > - <label - htmlFor="change-profile-permission-input" - > - quality_profiles.search_description - </label> - <ProfilePermissionsFormSelect - onChange={[Function]} - profile={ - { - "language": "js", - "name": "Sonar way", - } - } - /> - </div> - </div> - <footer - className="modal-foot" - > - <i - className="spinner spacer-right" - /> - <SubmitButton - disabled={true} - > - add_verb - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </footer> - </form> -</Modal> -`; - -exports[`correctly adds users 1`] = ` -<Modal - contentLabel="quality_profiles.grant_permissions_to_user_or_group" - onRequestClose={[MockFunction]} -> - <header - className="modal-head" - > - <h2> - quality_profiles.grant_permissions_to_user_or_group - </h2> - </header> - <form - onSubmit={[Function]} - > - <div - className="modal-body" - > - <div - className="modal-field" - > - <label - htmlFor="change-profile-permission-input" - > - quality_profiles.search_description - </label> - <ProfilePermissionsFormSelect - onChange={[Function]} - profile={ - { - "language": "js", - "name": "Sonar way", - } - } - /> - </div> - </div> - <footer - className="modal-foot" - > - <i - className="spinner spacer-right" - /> - <SubmitButton - disabled={true} - > - add_verb - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </footer> - </form> -</Modal> -`; - -exports[`should render correctly: default 1`] = ` -<Modal - contentLabel="quality_profiles.grant_permissions_to_user_or_group" - onRequestClose={[MockFunction]} -> - <header - className="modal-head" - > - <h2> - quality_profiles.grant_permissions_to_user_or_group - </h2> - </header> - <form - onSubmit={[Function]} - > - <div - className="modal-body" - > - <div - className="modal-field" - > - <label - htmlFor="change-profile-permission-input" - > - quality_profiles.search_description - </label> - <ProfilePermissionsFormSelect - onChange={[Function]} - profile={ - { - "language": "js", - "name": "Sonar way", - } - } - /> - </div> - </div> - <footer - className="modal-foot" - > - <SubmitButton - disabled={true} - > - add_verb - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </footer> - </form> -</Modal> -`; - -exports[`should render correctly: submitting 1`] = ` -<Modal - contentLabel="quality_profiles.grant_permissions_to_user_or_group" - onRequestClose={[MockFunction]} -> - <header - className="modal-head" - > - <h2> - quality_profiles.grant_permissions_to_user_or_group - </h2> - </header> - <form - onSubmit={[Function]} - > - <div - className="modal-body" - > - <div - className="modal-field" - > - <label - htmlFor="change-profile-permission-input" - > - quality_profiles.search_description - </label> - <ProfilePermissionsFormSelect - onChange={[Function]} - profile={ - { - "language": "js", - "name": "Sonar way", - } - } - /> - </div> - </div> - <footer - className="modal-foot" - > - <i - className="spinner spacer-right" - /> - <SubmitButton - disabled={true} - > - add_verb - </SubmitButton> - <ResetButtonLink - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </footer> - </form> -</Modal> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap deleted file mode 100644 index d6b98558024..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap +++ /dev/null @@ -1,78 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render control correctly: control renderer 1`] = ` -<Control - className="abs-height-100" -/> -`; - -exports[`should render correctly 1`] = ` -<SearchSelect - autoFocus={true} - className="width-100" - components={ - { - "Control": [Function], - "Option": [Function], - "SingleValue": [Function], - } - } - defaultOptions={true} - id="change-profile-permission" - inputId="change-profile-permission-input" - isClearable={false} - large={true} - loadOptions={[Function]} - noOptionsMessage={[Function]} - onChange={[MockFunction]} - placeholder="" -/> -`; - -exports[`should render option correctly: option renderer 1`] = ` -<Option - data={ - { - "name": "name", - "value": "test", - } - } -> - <span - className="display-flex-center" - > - <GroupIcon - size={16} - /> - <strong - className="spacer-left" - > - name - </strong> - </span> -</Option> -`; - -exports[`should render value correctly: value renderer 1`] = ` -<SingleValue - data={ - { - "name": "name", - "value": "test", - } - } -> - <span - className="display-flex-center" - > - <GroupIcon - size={16} - /> - <strong - className="spacer-left" - > - name - </strong> - </span> -</SingleValue> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsGroup-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsGroup-test.tsx.snap deleted file mode 100644 index e44d1045924..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsGroup-test.tsx.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` -<div - className="clearfix big-spacer-bottom" -> - <DeleteButton - className="pull-right spacer-top spacer-left spacer-right button-small" - onClick={[Function]} - /> - <GroupIcon - className="pull-left spacer-right" - size={32} - /> - <div - className="overflow-hidden" - style={ - { - "lineHeight": "32px", - } - } - > - <strong> - lambda - </strong> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap deleted file mode 100644 index a1fb25cecb6..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap +++ /dev/null @@ -1,29 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` -<div - className="clearfix big-spacer-bottom" -> - <DeleteButton - className="pull-right spacer-top spacer-left spacer-right button-small" - onClick={[Function]} - /> - <withAppStateContext(LegacyAvatar) - className="pull-left spacer-right" - name="Luke Skywalker" - size={32} - /> - <div - className="overflow-hidden" - > - <strong> - Luke Skywalker - </strong> - <div - className="note" - > - luke - </div> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileProjects-test.tsx.snap deleted file mode 100644 index af24ef184d5..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileProjects-test.tsx.snap +++ /dev/null @@ -1,247 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<div - className="boxed-group quality-profile-projects" -> - <div - className="boxed-group-actions" - > - <Tooltip - overlay={null} - > - <Button - className="js-change-projects" - disabled={false} - onClick={[Function]} - > - quality_profiles.change_projects - </Button> - </Tooltip> - </div> - <header - className="boxed-group-header" - > - <h2> - projects - </h2> - </header> - <div - className="boxed-group-inner" - > - <ul> - <li - className="spacer-top js-profile-project" - data-key="org.sonarsource.xml:xml" - key="org.sonarsource.xml:xml" - > - <ForwardRef(Link) - to={ - { - "pathname": "/dashboard", - "search": "?id=org.sonarsource.xml%3Axml", - } - } - > - <QualifierIcon - qualifier="TRK" - /> - - <span> - SonarXML - </span> - </ForwardRef(Link)> - </li> - </ul> - <ListFooter - count={1} - loadMore={[Function]} - ready={true} - total={10} - /> - </div> -</div> -`; - -exports[`should render correctly: loading 1`] = ` -<div - className="boxed-group quality-profile-projects" -> - <div - className="boxed-group-actions" - > - <Tooltip - overlay={null} - > - <Button - className="js-change-projects" - disabled={false} - onClick={[Function]} - > - quality_profiles.change_projects - </Button> - </Tooltip> - </div> - <header - className="boxed-group-header" - > - <h2> - projects - </h2> - </header> - <div - className="boxed-group-inner" - > - <i - className="spinner" - /> - </div> -</div> -`; - -exports[`should render correctly: no active rules, but associated projects 1`] = ` -<div - className="boxed-group quality-profile-projects" -> - <div - className="boxed-group-actions" - > - <Tooltip - overlay="quality_profiles.cannot_associate_projects_no_rules" - > - <Button - className="js-change-projects" - disabled={true} - onClick={[Function]} - > - quality_profiles.change_projects - </Button> - </Tooltip> - </div> - <header - className="boxed-group-header" - > - <h2> - projects - </h2> - </header> - <div - className="boxed-group-inner" - > - <ul> - <li - className="spacer-top js-profile-project" - data-key="org.sonarsource.xml:xml" - key="org.sonarsource.xml:xml" - > - <ForwardRef(Link) - to={ - { - "pathname": "/dashboard", - "search": "?id=org.sonarsource.xml%3Axml", - } - } - > - <QualifierIcon - qualifier="TRK" - /> - - <span> - SonarXML - </span> - </ForwardRef(Link)> - </li> - </ul> - <ListFooter - count={1} - loadMore={[Function]} - ready={true} - total={10} - /> - </div> -</div> -`; - -exports[`should render correctly: no active rules, no associated projects 1`] = ` -<div - className="boxed-group quality-profile-projects" -> - <div - className="boxed-group-actions" - > - <Tooltip - overlay="quality_profiles.cannot_associate_projects_no_rules" - > - <Button - className="js-change-projects" - disabled={true} - onClick={[Function]} - > - quality_profiles.change_projects - </Button> - </Tooltip> - </div> - <header - className="boxed-group-header" - > - <h2> - projects - </h2> - </header> - <div - className="boxed-group-inner" - > - <div> - quality_profiles.cannot_associate_projects_no_rules - </div> - </div> -</div> -`; - -exports[`should render correctly: no rights 1`] = ` -<div - className="boxed-group quality-profile-projects" -> - <header - className="boxed-group-header" - > - <h2> - projects - </h2> - </header> - <div - className="boxed-group-inner" - > - <ul> - <li - className="spacer-top js-profile-project" - data-key="org.sonarsource.xml:xml" - key="org.sonarsource.xml:xml" - > - <ForwardRef(Link) - to={ - { - "pathname": "/dashboard", - "search": "?id=org.sonarsource.xml%3Axml", - } - } - > - <QualifierIcon - qualifier="TRK" - /> - - <span> - SonarXML - </span> - </ForwardRef(Link)> - </li> - </ul> - <ListFooter - count={1} - loadMore={[Function]} - ready={true} - total={10} - /> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap deleted file mode 100644 index cd08c5b9d8e..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap +++ /dev/null @@ -1,101 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render the quality profiles rules with sonarway comparison 1`] = ` -<div - className="boxed-group quality-profile-rules" -> - <div - className="quality-profile-rules-distribution" - > - <table - className="data condensed" - > - <thead> - <tr> - <th> - <h2> - rules - </h2> - </th> - <th> - active - </th> - <th> - inactive - </th> - </tr> - </thead> - <tbody> - <ProfileRulesRowTotal - count={68} - qprofile="key" - total={253} - /> - <ProfileRulesRowOfType - count={68} - key="BUG" - qprofile="key" - total={68} - type="BUG" - /> - <ProfileRulesRowOfType - count={0} - key="VULNERABILITY" - qprofile="key" - total={7} - type="VULNERABILITY" - /> - <ProfileRulesRowOfType - count={0} - key="CODE_SMELL" - qprofile="key" - total={168} - type="CODE_SMELL" - /> - <ProfileRulesRowOfType - count={0} - key="SECURITY_HOTSPOT" - qprofile="key" - total={10} - type="SECURITY_HOTSPOT" - /> - </tbody> - </table> - </div> - <ProfileRulesSonarWayComparison - language="js" - profile="key" - sonarWayMissingRules={4} - sonarway="sonarway" - /> -</div> -`; - -exports[`should show a button to activate more rules for admins 1`] = ` -<ForwardRef(Link) - className="button js-activate-rules" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=key&activation=false", - } - } -> - quality_profiles.activate_more -</ForwardRef(Link)> -`; - -exports[`should show a deprecated rules warning message 1`] = ` -<ProfileRulesDeprecatedWarning - activeDeprecatedRules={8} - profile="key" -/> -`; - -exports[`should show a disabled button to activate more rules for built-in profiles 1`] = ` -<Button - className="disabled js-activate-rules" -> - quality_profiles.activate_more -</Button> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap deleted file mode 100644 index 4c545386a0b..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap +++ /dev/null @@ -1,32 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="quality-profile-rules-deprecated clearfix" -> - <span - className="pull-left" - > - <span - className="text-middle" - > - quality_profiles.deprecated_rules - </span> - <HelpTooltip - className="spacer-left" - overlay="quality_profiles.deprecated_rules_description" - /> - </span> - <ForwardRef(Link) - className="pull-right" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true&statuses=DEPRECATED", - } - } - > - 18 - </ForwardRef(Link)> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap deleted file mode 100644 index 057ce492474..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap +++ /dev/null @@ -1,132 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<tr> - <td> - <span> - <IssueTypeIcon - className="little-spacer-right" - query="BUG" - /> - issue.type.BUG.plural - </span> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true&types=BUG", - } - } - > - 3 - </ForwardRef(Link)> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - className="small" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=false&types=BUG", - } - } - > - 7 - </ForwardRef(Link)> - </td> -</tr> -`; - -exports[`should render correctly if there is 0 rules 1`] = ` -<tr> - <td> - <span> - <IssueTypeIcon - className="little-spacer-right" - query="VULNERABILITY" - /> - issue.type.VULNERABILITY.plural - </span> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true&types=VULNERABILITY", - } - } - > - 0 - </ForwardRef(Link)> - </td> - <td - className="thin nowrap text-right" - > - <span - className="note" - > - 0 - </span> - </td> -</tr> -`; - -exports[`should render correctly if there is missing data 1`] = ` -<tr> - <td> - <span> - <IssueTypeIcon - className="little-spacer-right" - query="VULNERABILITY" - /> - issue.type.VULNERABILITY.plural - </span> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true&types=VULNERABILITY", - } - } - > - 5 - </ForwardRef(Link)> - </td> - <td - className="thin nowrap text-right" - /> -</tr> -`; - -exports[`should render correctly if there is missing data 2`] = ` -<tr> - <td> - <span> - <IssueTypeIcon - className="little-spacer-right" - query="VULNERABILITY" - /> - issue.type.VULNERABILITY.plural - </span> - </td> - <td - className="thin nowrap text-right" - /> - <td - className="thin nowrap text-right" - /> -</tr> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap deleted file mode 100644 index a8929baa77e..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap +++ /dev/null @@ -1,124 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<tr> - <td> - <strong> - total - </strong> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true", - } - } - > - <strong> - 3 - </strong> - </ForwardRef(Link)> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - className="small" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=false", - } - } - > - <strong> - 7 - </strong> - </ForwardRef(Link)> - </td> -</tr> -`; - -exports[`should render correctly if there is 0 rules 1`] = ` -<tr> - <td> - <strong> - total - </strong> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true", - } - } - > - <strong> - 0 - </strong> - </ForwardRef(Link)> - </td> - <td - className="thin nowrap text-right" - > - <span - className="note" - > - 0 - </span> - </td> -</tr> -`; - -exports[`should render correctly if there is missing data 1`] = ` -<tr> - <td> - <strong> - total - </strong> - </td> - <td - className="thin nowrap text-right" - > - <ForwardRef(Link) - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=true", - } - } - > - <strong> - 5 - </strong> - </ForwardRef(Link)> - </td> - <td - className="thin nowrap text-right" - /> -</tr> -`; - -exports[`should render correctly if there is missing data 2`] = ` -<tr> - <td> - <strong> - total - </strong> - </td> - <td - className="thin nowrap text-right" - /> - <td - className="thin nowrap text-right" - /> -</tr> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap deleted file mode 100644 index 222db872392..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="quality-profile-rules-sonarway-missing clearfix" -> - <span - className="pull-left" - > - <span - className="text-middle" - > - quality_profiles.sonarway_missing_rules - </span> - <HelpTooltip - className="spacer-left" - overlay="quality_profiles.sonarway_missing_rules_description" - /> - </span> - <ForwardRef(Link) - className="pull-right" - data-test="rules" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=bar&activation=false&compareToProfile=baz&languages=Java", - } - } - > - 158 - </ForwardRef(Link)> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx index 71f6f0b38e4..c3f583f44b3 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx @@ -106,7 +106,10 @@ export default class EvolutionDeprecated extends React.PureComponent<Props> { const sortedProfiles = sortBy(profilesWithDeprecations, (p) => -p.activeDeprecatedRuleCount); return ( - <div className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated"> + <section + className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated" + aria-label={translate('quality_profiles.deprecated_rules')} + > <h2 className="h4 spacer-bottom">{translate('quality_profiles.deprecated_rules')}</h2> <div className="spacer-bottom"> {translateWithParameters( @@ -144,7 +147,7 @@ export default class EvolutionDeprecated extends React.PureComponent<Props> { </li> ))} </ul> - </div> + </section> ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx index d45a5ebb11b..2386566554a 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx @@ -106,7 +106,10 @@ export default class EvolutionRules extends React.PureComponent<{}, State> { )}`; return ( - <div className="boxed-group boxed-group-inner quality-profiles-evolution-rules"> + <section + className="boxed-group boxed-group-inner quality-profiles-evolution-rules" + aria-label={newRulesTitle} + > <h2 className="h4 spacer-bottom">{newRulesTitle}</h2> <ul> {latestRules.map((rule) => ( @@ -143,7 +146,7 @@ export default class EvolutionRules extends React.PureComponent<{}, State> { </Link> </div> )} - </div> + </section> ); } } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx index bd7f3e1c532..e5a1d5e4d26 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx @@ -36,7 +36,10 @@ export default function EvolutionStagnant(props: Props) { } return ( - <div className="boxed-group boxed-group-inner quality-profiles-evolution-stagnant"> + <section + className="boxed-group boxed-group-inner quality-profiles-evolution-stagnant" + aria-label={translate('quality_profiles.stagnant_profiles')} + > <h2 className="h4 spacer-bottom">{translate('quality_profiles.stagnant_profiles')}</h2> <div className="spacer-bottom"> {translate('quality_profiles.not_updated_more_than_year')} @@ -69,6 +72,6 @@ export default function EvolutionStagnant(props: Props) { </li> ))} </ul> - </div> + </section> ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx index b1e9213ec2c..a6b24b8b6f0 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { restoreQualityProfile } from '../../../api/quality-profiles'; -import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import Modal from '../../../components/controls/Modal'; +import { ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; import { Alert } from '../../../components/ui/Alert'; import MandatoryFieldMarker from '../../../components/ui/MandatoryFieldMarker'; import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/Evolution-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/Evolution-test.tsx deleted file mode 100644 index 2df1a593437..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/Evolution-test.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import Evolution, { EvolutionProps } from '../Evolution'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<EvolutionProps> = {}) { - return shallow(<Evolution profiles={[]} {...props} />); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx deleted file mode 100644 index 1eef0c4e032..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockQualityProfile } from '../../../../helpers/testMocks'; -import EvolutionDeprecated from '../EvolutionDeprecated'; - -it('should render correctly', () => { - const wrapper = shallow( - <EvolutionDeprecated - profiles={[ - mockQualityProfile({ - key: 'qp-1', - name: 'Quality Profile 1', - activeDeprecatedRuleCount: 0, - }), - mockQualityProfile({ - key: 'qp-2', - name: 'Quality Profile 2', - childrenCount: 1, - activeDeprecatedRuleCount: 2, - }), - mockQualityProfile({ - key: 'qp-3', - name: 'Quality Profile 3', - depth: 2, - activeDeprecatedRuleCount: 2, - parentKey: 'qp-2', - }), - mockQualityProfile({ - key: 'qp-4', - name: 'Quality Profile 4', - depth: 3, - activeDeprecatedRuleCount: 3, - parentKey: 'qp-3', - }), - mockQualityProfile({ - key: 'qp-5', - name: 'Quality Profile 5', - depth: 4, - activeDeprecatedRuleCount: 4, - parentKey: 'qp-4', - }), - ]} - /> - ); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx deleted file mode 100644 index 5d1e9057c8b..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { - mockLanguage, - mockLocation, - mockQualityProfile, - mockRouter, -} from '../../../../helpers/testMocks'; -import { click } from '../../../../helpers/testUtils'; -import { PageHeader } from '../PageHeader'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); - expect(shallowRender({ actions: { create: true } })).toMatchSnapshot(); - expect(shallowRender({ actions: { create: true }, languages: [] })).toMatchSnapshot(); -}); - -it('should show a create form', () => { - const wrapper = shallowRender({ actions: { create: true } }); - click(wrapper.find('#quality-profiles-create')); - expect(wrapper).toMatchSnapshot(); -}); - -it('should show a restore form', () => { - const wrapper = shallowRender({ actions: { create: true } }); - click(wrapper.find('#quality-profiles-restore')); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<PageHeader['props']> = {}) { - return shallow( - <PageHeader - actions={{ create: false }} - languages={[mockLanguage()]} - location={mockLocation()} - profiles={[mockQualityProfile()]} - router={mockRouter()} - updateProfiles={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListHeader-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListHeader-test.tsx deleted file mode 100644 index 4f948502a3f..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListHeader-test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import { mockRouter } from '../../../../helpers/testMocks'; -import { ProfilesListHeader } from '../ProfilesListHeader'; - -it('should render correctly', () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<ProfilesListHeader['props']> = {}) { - return shallow<ProfilesListHeader>( - <ProfilesListHeader - languages={[ - { key: 'js', name: 'JavaScript' }, - { key: 'java', name: 'Java' }, - ]} - router={mockRouter()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx deleted file mode 100644 index e9d3cbe6950..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { shallow } from 'enzyme'; -import * as React from 'react'; -import RestoreProfileForm from '../RestoreProfileForm'; - -it('should render correctly', () => { - expect( - shallow(<RestoreProfileForm onClose={jest.fn()} onRestore={jest.fn()} />) - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/Evolution-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/Evolution-test.tsx.snap deleted file mode 100644 index 088705e5acd..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/Evolution-test.tsx.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="quality-profiles-evolution" -> - <EvolutionDeprecated - profiles={[]} - /> - <EvolutionStagnant - profiles={[]} - /> - <EvolutionRules /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap deleted file mode 100644 index 3fb130a5d83..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap +++ /dev/null @@ -1,176 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated" -> - <h2 - className="h4 spacer-bottom" - > - quality_profiles.deprecated_rules - </h2> - <div - className="spacer-bottom" - > - quality_profiles.deprecated_rules_are_still_activated.4 - </div> - <ul> - <li - className="spacer-top" - key="qp-5" - > - <div - className="text-ellipsis little-spacer-bottom" - > - <ProfileLink - language="js" - name="Quality Profile 5" - > - Quality Profile 5 - </ProfileLink> - </div> - <div - className="note" - > - JavaScript - , - <ForwardRef(Link) - aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.4" - className="link-no-underline" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=qp-5&activation=true&statuses=DEPRECATED", - } - } - > - quality_profile.x_rules.4 - </ForwardRef(Link)> - <div - key="qp-2" - > - - coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 - </div> - <div - key="qp-4" - > - - coding_rules.filters.inheritance.x_inherited_from_y.1.Quality Profile 4 - </div> - </div> - </li> - <li - className="spacer-top" - key="qp-4" - > - <div - className="text-ellipsis little-spacer-bottom" - > - <ProfileLink - language="js" - name="Quality Profile 4" - > - Quality Profile 4 - </ProfileLink> - </div> - <div - className="note" - > - JavaScript - , - <ForwardRef(Link) - aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.3" - className="link-no-underline" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=qp-4&activation=true&statuses=DEPRECATED", - } - } - > - quality_profile.x_rules.3 - </ForwardRef(Link)> - <div - key="qp-2" - > - - coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 - </div> - </div> - </li> - <li - className="spacer-top" - key="qp-2" - > - <div - className="text-ellipsis little-spacer-bottom" - > - <ProfileLink - language="js" - name="Quality Profile 2" - > - Quality Profile 2 - </ProfileLink> - </div> - <div - className="note" - > - JavaScript - , - <ForwardRef(Link) - aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.2" - className="link-no-underline" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=qp-2&activation=true&statuses=DEPRECATED", - } - } - > - quality_profile.x_rules.2 - </ForwardRef(Link)> - </div> - </li> - <li - className="spacer-top" - key="qp-3" - > - <div - className="text-ellipsis little-spacer-bottom" - > - <ProfileLink - language="js" - name="Quality Profile 3" - > - Quality Profile 3 - </ProfileLink> - </div> - <div - className="note" - > - JavaScript - , - <ForwardRef(Link) - aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.2" - className="link-no-underline" - to={ - { - "pathname": "/coding_rules", - "search": "?qprofile=qp-3&activation=true&statuses=DEPRECATED", - } - } - > - quality_profile.x_rules.2 - </ForwardRef(Link)> - <div - key="qp-2" - > - - coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 - </div> - </div> - </li> - </ul> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap deleted file mode 100644 index f0c9b17a863..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap +++ /dev/null @@ -1,248 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<header - className="page-header" -> - <h1 - className="page-title" - > - quality_profiles.page - </h1> - <div - className="page-description markdown" - > - quality_profiles.intro1 - <br /> - quality_profiles.intro2 - <DocLink - className="spacer-left" - to="/instance-administration/quality-profiles/" - > - learn_more - </DocLink> - </div> -</header> -`; - -exports[`should render correctly 2`] = ` -<header - className="page-header" -> - <h1 - className="page-title" - > - quality_profiles.page - </h1> - <div - className="page-actions" - > - <Button - disabled={false} - id="quality-profiles-create" - onClick={[Function]} - > - create - </Button> - <Button - className="little-spacer-left" - id="quality-profiles-restore" - onClick={[Function]} - > - restore - </Button> - </div> - <div - className="page-description markdown" - > - quality_profiles.intro1 - <br /> - quality_profiles.intro2 - <DocLink - className="spacer-left" - to="/instance-administration/quality-profiles/" - > - learn_more - </DocLink> - </div> -</header> -`; - -exports[`should render correctly 3`] = ` -<header - className="page-header" -> - <h1 - className="page-title" - > - quality_profiles.page - </h1> - <div - className="page-actions" - > - <Button - disabled={true} - id="quality-profiles-create" - onClick={[Function]} - > - create - </Button> - <Button - className="little-spacer-left" - id="quality-profiles-restore" - onClick={[Function]} - > - restore - </Button> - <Alert - className="spacer-top" - variant="warning" - > - quality_profiles.no_languages_available - </Alert> - </div> - <div - className="page-description markdown" - > - quality_profiles.intro1 - <br /> - quality_profiles.intro2 - <DocLink - className="spacer-left" - to="/instance-administration/quality-profiles/" - > - learn_more - </DocLink> - </div> -</header> -`; - -exports[`should show a create form 1`] = ` -<header - className="page-header" -> - <h1 - className="page-title" - > - quality_profiles.page - </h1> - <div - className="page-actions" - > - <Button - disabled={false} - id="quality-profiles-create" - onClick={[Function]} - > - create - </Button> - <Button - className="little-spacer-left" - id="quality-profiles-restore" - onClick={[Function]} - > - restore - </Button> - </div> - <div - className="page-description markdown" - > - quality_profiles.intro1 - <br /> - quality_profiles.intro2 - <DocLink - className="spacer-left" - to="/instance-administration/quality-profiles/" - > - learn_more - </DocLink> - </div> - <CreateProfileForm - languages={ - [ - { - "key": "css", - "name": "CSS", - }, - ] - } - location={ - { - "hash": "", - "key": "key", - "pathname": "/path", - "query": {}, - "search": "", - "state": {}, - } - } - onClose={[Function]} - onCreate={[Function]} - profiles={ - [ - { - "activeDeprecatedRuleCount": 2, - "activeRuleCount": 10, - "childrenCount": 0, - "depth": 1, - "isBuiltIn": false, - "isDefault": false, - "isInherited": false, - "key": "key", - "language": "js", - "languageName": "JavaScript", - "name": "name", - "projectCount": 3, - }, - ] - } - /> -</header> -`; - -exports[`should show a restore form 1`] = ` -<header - className="page-header" -> - <h1 - className="page-title" - > - quality_profiles.page - </h1> - <div - className="page-actions" - > - <Button - disabled={false} - id="quality-profiles-create" - onClick={[Function]} - > - create - </Button> - <Button - className="little-spacer-left" - id="quality-profiles-restore" - onClick={[Function]} - > - restore - </Button> - </div> - <div - className="page-description markdown" - > - quality_profiles.intro1 - <br /> - quality_profiles.intro2 - <DocLink - className="spacer-left" - to="/instance-administration/quality-profiles/" - > - learn_more - </DocLink> - </div> - <RestoreProfileForm - onClose={[Function]} - onRestore={[MockFunction]} - /> -</header> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListHeader-test.tsx.snap deleted file mode 100644 index 82c185c43a3..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListHeader-test.tsx.snap +++ /dev/null @@ -1,37 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<div - className="quality-profiles-list-header clearfix" -> - <label - className="spacer-right" - htmlFor="quality-profiles-filter-input" - > - quality_profiles.filter_by - : - </label> - <Select - autoFocus={true} - className="input-medium" - id="quality-profiles-filter" - inputId="quality-profiles-filter-input" - isClearable={true} - isSearchable={true} - onChange={[Function]} - options={ - [ - { - "label": "JavaScript", - "value": "js", - }, - { - "label": "Java", - "value": "java", - }, - ] - } - value={[]} - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap deleted file mode 100644 index 725377d8d2a..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap +++ /dev/null @@ -1,61 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<Modal - contentLabel="quality_profiles.restore_profile" - onRequestClose={[MockFunction]} - size="small" -> - <form - id="restore-profile-form" - onSubmit={[Function]} - > - <div - className="modal-head" - > - <h2> - quality_profiles.restore_profile - </h2> - </div> - <div - className="modal-body" - > - <MandatoryFieldsExplanation - className="modal-field" - /> - <div - className="modal-field" - > - <label - htmlFor="restore-profile-backup" - > - backup - <MandatoryFieldMarker /> - </label> - <input - id="restore-profile-backup" - name="backup" - required={true} - type="file" - /> - </div> - </div> - <div - className="modal-foot" - > - <SubmitButton - disabled={false} - id="restore-profile-submit" - > - restore - </SubmitButton> - <ResetButtonLink - id="restore-profile-cancel" - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> - </form> -</Modal> -`; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/types.ts b/server/sonar-web/src/main/js/apps/quality-profiles/types.ts index a7cd4771856..946cd3ead25 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/types.ts +++ b/server/sonar-web/src/main/js/apps/quality-profiles/types.ts @@ -33,7 +33,7 @@ export interface Exporter { export interface ProfileChangelogEvent { action: string; - authorName: string; + authorName?: string; date: string; params?: Dict<string | null>; ruleKey: string; diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index b3948def2ca..735d6da13f8 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -20,7 +20,7 @@ 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 { Exporter, Profile, ProfileChangelogEvent } from '../apps/quality-profiles/types'; import { LogsLevels } from '../apps/system/utils'; import { Location, Router } from '../components/hoc/withRouter'; import { AppState } from '../types/appstate'; @@ -54,6 +54,7 @@ import { SysInfoLogging, SysInfoStandalone, UserGroupMember, + UserSelected, } from '../types/types'; import { CurrentUser, LoggedInUser, User } from '../types/users'; @@ -500,7 +501,9 @@ export function mockQualityProfileInheritance( }; } -export function mockQualityProfileChangelogEvent(eventOverride?: any) { +export function mockQualityProfileChangelogEvent( + eventOverride?: Partial<ProfileChangelogEvent> +): ProfileChangelogEvent { return { action: 'ACTIVATED', date: '2019-04-23T02:12:32+0100', @@ -677,6 +680,16 @@ export function mockUser(overrides: Partial<User> = {}): User { }; } +export function mockUserSelected(overrides: Partial<UserSelected> = {}): UserSelected { + return { + active: true, + login: 'john.doe', + name: 'John Doe', + selected: true, + ...overrides, + }; +} + export function mockUserGroupMember(overrides: Partial<UserGroupMember> = {}): UserGroupMember { return { login: 'john.doe', 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 b4eb9e98c49..79aa25884c0 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1938,10 +1938,12 @@ quality_profiles.default_permissions=Users with the global "Administer Quality P quality_profiles.grant_permissions_to_more_users=Grant permissions to more users quality_profiles.grant_permissions_to_user_or_group=Grant permissions to a user or a group quality_profiles.additional_user_groups=Additional users / groups: -quality_profiles.search_description=Search users by login or name, and groups by name: +quality_profiles.search_description=Search users by login or name, and groups by name: quality_profiles.permissions.remove.user=Remove permission from user +quality_profiles.permissions.remove.user_x=Remove permission from user {0} quality_profiles.permissions.remove.user.confirmation=Are you sure you want to remove permission on this quality profile from user {user}? quality_profiles.permissions.remove.group=Remove permission from group +quality_profiles.permissions.remove.group_x=Remove permission for {0} quality_profiles.permissions.remove.group.confirmation=Are you sure you want to remove permission on this quality profile from group {user}? quality_profiles.copy_help=Create a new quality profile as a replica of "{0}". The two profiles will then evolve independently. quality_profiles.extend_help=Create a child quality profile inheriting all active rules from "{0}". Changes to "{0}" will impact the child profile. |