From 5ebbed2a0f508d5bef63de4ed1e89a57e67e25df Mon Sep 17 00:00:00 2001 From: guillaume-peoch-sonarsource Date: Tue, 4 Jul 2023 17:53:54 +0200 Subject: [PATCH] SONAR-18441 RTL migration for Quality Profiles --- .../api/mocks/QualityProfilesServiceMock.ts | 444 ++++++++++++++- .../src/main/js/api/quality-profiles.ts | 7 - server/sonar-web/src/main/js/api/rules.ts | 5 - .../__tests__/QualityProfileApp-it.tsx | 513 ++++++++++++++++++ .../__tests__/QualityProfilesApp-it.tsx | 190 ++++--- .../changelog/ChangelogContainer.tsx | 2 +- .../changelog/__tests__/Changelog-test.tsx | 100 ---- .../__tests__/ChangelogContainer-it.tsx | 140 +++++ .../__tests__/ChangelogContainer-test.tsx | 88 --- .../__tests__/ChangelogSearch-test.tsx | 55 -- .../changelog/__tests__/ChangesList-test.tsx | 45 -- .../__tests__/ParameterChange-test.tsx | 28 - .../__tests__/SeverityChange-test.tsx | 29 - .../ChangelogContainer-test.tsx.snap | 95 ---- .../ChangelogSearch-test.tsx.snap | 24 - .../compare/ComparisonEmpty.tsx | 25 - .../compare/ComparisonResults.tsx | 3 +- .../components/ProfileActions.tsx | 12 +- .../components/ProfileModalForm.tsx | 2 +- .../BuiltInQualityProfileBadge-test.tsx | 31 -- .../__tests__/DeleteProfileForm-test.tsx | 55 -- .../__tests__/ProfileActions-test.tsx | 342 ------------ .../__tests__/QualityProfilesApp-test.tsx | 94 ---- .../BuiltInQualityProfileBadge-test.tsx.snap | 21 - .../DeleteProfileForm-test.tsx.snap | 134 ----- .../ProfileActions-test.tsx.snap | 392 ------------- .../QualityProfilesApp-test.tsx.snap | 47 -- .../details/ChangeParentForm.tsx | 4 +- .../details/ProfileDetails.tsx | 15 +- .../details/ProfileExporters.tsx | 7 +- .../details/ProfileInheritance.tsx | 7 +- .../details/ProfilePermissions.tsx | 6 +- .../details/ProfilePermissionsGroup.tsx | 8 +- .../details/ProfilePermissionsUser.tsx | 6 +- .../details/ProfileProjects.tsx | 4 +- .../quality-profiles/details/ProfileRules.tsx | 16 +- .../__tests__/ChangeParentForm-test.tsx | 69 --- .../__tests__/ChangeProjectsForm-test.tsx | 102 ---- .../details/__tests__/ProfileDetails-test.tsx | 55 -- .../__tests__/ProfileExporters-test.tsx | 40 -- .../__tests__/ProfileInheritance-test.tsx | 69 --- .../__tests__/ProfileInheritanceBox-test.tsx | 49 -- .../__tests__/ProfilePermissions-test.tsx | 119 ---- .../__tests__/ProfilePermissionsForm-test.tsx | 96 ---- .../ProfilePermissionsFormSelect-test.tsx | 95 ---- .../ProfilePermissionsGroup-test.tsx | 65 --- .../__tests__/ProfilePermissionsUser-test.tsx | 64 --- .../__tests__/ProfileProjects-test.tsx | 87 --- .../details/__tests__/ProfileRules-test.tsx | 150 ----- .../ProfileRulesDeprecatedWarning-test.tsx | 28 - .../__tests__/ProfileRulesRowOfType-test.tsx | 43 -- .../__tests__/ProfileRulesRowTotal-test.tsx | 37 -- .../ProfileRulesSonarWayComparison-test.tsx | 35 -- .../ChangeParentForm-test.tsx.snap | 97 ---- .../ChangeProjectsForm-test.tsx.snap | 79 --- .../ProfileDetails-test.tsx.snap | 506 ----------------- .../ProfileExporters-test.tsx.snap | 34 -- .../ProfileInheritance-test.tsx.snap | 147 ----- .../ProfileInheritanceBox-test.tsx.snap | 136 ----- .../ProfilePermissions-test.tsx.snap | 93 ---- .../ProfilePermissionsForm-test.tsx.snap | 230 -------- ...ProfilePermissionsFormSelect-test.tsx.snap | 78 --- .../ProfilePermissionsGroup-test.tsx.snap | 28 - .../ProfilePermissionsUser-test.tsx.snap | 29 - .../ProfileProjects-test.tsx.snap | 247 --------- .../__snapshots__/ProfileRules-test.tsx.snap | 101 ---- ...rofileRulesDeprecatedWarning-test.tsx.snap | 32 -- .../ProfileRulesRowOfType-test.tsx.snap | 132 ----- .../ProfileRulesRowTotal-test.tsx.snap | 124 ----- ...ofileRulesSonarWayComparison-test.tsx.snap | 33 -- .../home/EvolutionDeprecated.tsx | 7 +- .../quality-profiles/home/EvolutionRules.tsx | 7 +- .../home/EvolutionStagnant.tsx | 7 +- .../home/RestoreProfileForm.tsx | 2 +- .../home/__tests__/Evolution-test.tsx | 30 - .../__tests__/EvolutionDeprecated-test.tsx | 65 --- .../home/__tests__/PageHeader-test.tsx | 61 --- .../__tests__/ProfilesListHeader-test.tsx | 41 -- .../__tests__/RestoreProfileForm-test.tsx | 28 - .../__snapshots__/Evolution-test.tsx.snap | 15 - .../EvolutionDeprecated-test.tsx.snap | 176 ------ .../__snapshots__/PageHeader-test.tsx.snap | 248 --------- .../ProfilesListHeader-test.tsx.snap | 37 -- .../RestoreProfileForm-test.tsx.snap | 61 --- .../main/js/apps/quality-profiles/types.ts | 2 +- .../src/main/js/helpers/testMocks.ts | 17 +- .../resources/org/sonar/l10n/core.properties | 4 +- 87 files changed, 1288 insertions(+), 5845 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfileApp-it.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangesList-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ParameterChange-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/SeverityChange-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogContainer-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogSearch-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/BuiltInQualityProfileBadge-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/DeleteProfileForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/QualityProfilesApp-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/BuiltInQualityProfileBadge-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/DeleteProfileForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/QualityProfilesApp-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeParentForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileExporters-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritance-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritanceBox-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileProjects-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeParentForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeProjectsForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileExporters-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritance-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritanceBox-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsGroup-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileProjects-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/Evolution-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListHeader-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/Evolution-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListHeader-test.tsx.snap delete mode 100644 server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap 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> = { 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 => { + handleSearchRules = (data: SearchRulesQuery): Promise => { + 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 => { @@ -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) { @@ -304,6 +704,8 @@ export default class QualityProfilesServiceMock { this.resetQualityProfile(); this.resetComparisonResult(); this.resetSearchRulesResponse(); + this.resetProfileProjects(); + this.resetChangelogEvents(); } reply(response: T): Promise { 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 f.property === property); - return facet ? facet.values : []; -} - export function getRuleRepositories(parameters: { q: string; }): Promise> { 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(/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 { +class ChangelogContainer extends React.PureComponent { 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 { - 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(); - expect(changelog.find('tbody').find('tr').length).toBe(2); -}); - -it('should render event date', () => { - const events = [createEvent()]; - const changelog = shallow(); - expect(changelog.find('DateTimeFormatter')).toHaveLength(1); -}); - -it('should render author', () => { - const events = [createEvent()]; - const changelog = shallow(); - expect(changelog.text()).toContain('John'); -}); - -it('should render system author', () => { - const events = [createEvent({ authorName: undefined })]; - const changelog = shallow(); - expect(changelog.text()).toContain('System'); -}); - -it('should render action', () => { - const events = [createEvent()]; - const changelog = shallow(); - expect(changelog.text()).toContain('ACTIVATED'); -}); - -it('should render rule', () => { - const events = [createEvent()]; - const changelog = shallow(); - 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(); - 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(); - 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( - - ); -} 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( - - ); - expect(output).toMatchSnapshot(); -}); - -it('should reset', () => { - const onReset = jest.fn(); - const output = shallow( - - ); - 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(); - expect(output.find('li').length).toBe(2); -}); - -it('should render severity change', () => { - const changes = { severity: 'BLOCKER' }; - const output = shallow().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().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(); - const second = shallow(); - 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().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`] = ` -
-
- - -
- - -
-`; - -exports[`should render correctly without events 1`] = ` -
-
- - -
- -
-`; 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`] = ` -
- - -
-`; 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
{translate('quality_profile.empty_comparison')}
; -} 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; @@ -201,7 +200,7 @@ export default class ComparisonResults extends React.PureComponent { render() { if (!this.props.inLeft.length && !this.props.inRight.length && !this.props.modified.length) { - return ; + return
{translate('quality_profile.empty_comparison')}
; } 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 { +class ProfileActions extends React.PureComponent { mounted = false; state: State = { loading: false, @@ -178,12 +177,19 @@ export class ProfileActions extends React.PureComponent { } }; + 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(); -} 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 = {}) { - return shallow( - - ); -} 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 = {}) { - const router = mockRouter(); - return shallow( - - ); -} 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 = {}) { - return shallow( - -
- - ); -} 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`] = ` - -
- quality_profiles.built_in -
-
-`; - -exports[`should render correctly 2`] = ` -
- quality_profiles.built_in -
-`; 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`] = ` - -
-
-

- quality_profiles.delete_confirm_title -

-
-
-

- quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript -

-
-
- - delete - - - cancel - -
-
-
-`; - -exports[`should render correctly: loading 1`] = ` - -
-
-

- quality_profiles.delete_confirm_title -

-
-
-

- quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript -

-
-
- - - delete - - - cancel - -
- -
-`; - -exports[`should render correctly: profile has children 1`] = ` - -
-
-

- quality_profiles.delete_confirm_title -

-
-
-
- - quality_profiles.this_profile_has_descendants - -

- quality_profiles.are_you_sure_want_delete_profile_x_and_descendants.name.JavaScript -

-
-
-
- - delete - - - cancel - -
-
-
-`; 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`] = ` - - - - quality_profiles.activate_more_rules - - - backup_verb - - - compare - - - extend - - - copy - - - rename - - - set_as_default - - - - delete - - - -`; - -exports[`renders correctly: copy modal 1`] = ` - - - - backup_verb - - - compare - - - - -`; - -exports[`renders correctly: delete modal 1`] = ` - - - - backup_verb - - - compare - - - - -`; - -exports[`renders correctly: edit only 1`] = ` - - - - quality_profiles.activate_more_rules - - - backup_verb - - - compare - - - rename - - - -`; - -exports[`renders correctly: extend modal 1`] = ` - - - - backup_verb - - - compare - - - - -`; - -exports[`renders correctly: no permissions 1`] = ` - - - - backup_verb - - - compare - - - -`; - -exports[`renders correctly: rename modal 1`] = ` - - - - backup_verb - - - compare - - - - -`; - -exports[`should not allow to set a profile as the default if the profile has no active rules 1`] = ` - - - - backup_verb - - - compare - -
  • - - - set_as_default - - -
  • -
    -
    -`; 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`] = ` -
    - - - -
    -`; - -exports[`should render correctly: loading 1`] = ` -
    - - - -
    -`; 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; } -export function ProfileDetails(props: ProfileDetailsProps) { - const { profile } = props; +function ProfileDetails(props: ProfileDetailsProps) { + const { profile, profiles, exporters } = props; + return (
    - - {profile.actions && profile.actions.edit && !profile.isBuiltIn && ( - - )} + + {profile.actions?.edit && !profile.isBuiltIn && }
    {profile.activeRuleCount === 0 && (profile.projectCount || profile.isDefault) && ( @@ -61,7 +60,7 @@ export function ProfileDetails(props: ProfileDetailsProps) { 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 ( -
    +

    {translate('quality_profiles.exporters')}

    @@ -57,6 +60,6 @@ export default function ProfileExporters({ exporters, profile }: Props) { ))}
    -
    + ); } 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 profile.isBuiltIn); return ( -
    +
    {profile.actions && profile.actions.edit && !profile.isBuiltIn && (
    +
    ); } } 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 +

    {translate('permissions.page')}

    {translate('quality_profiles.default_permissions')}

    @@ -184,7 +184,7 @@ export default class ProfilePermissions extends React.PureComponent )} -
    +
    ); } } 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 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 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 { const { profile } = this.props; const hasNoActiveRules = profile.activeRuleCount === 0; return ( -
    +
    {profile.actions && profile.actions.associateProjects && (
    {
    {this.state.formOpen && } -
    + ); } } 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 { const [allRules, activatedRules, showProfile] = responses; this.setState({ activatedTotal: activatedRules.paging.total, - allByType: keyBy(takeFacet(allRules, 'types'), 'val'), - activatedByType: keyBy(takeFacet(activatedRules, 'types'), 'val'), + allByType: keyBy(this.takeFacet(allRules, 'types'), 'val'), + activatedByType: keyBy(this.takeFacet(activatedRules, 'types'), 'val'), compareToSonarWay: showProfile && showProfile.compareToSonarWay, total: allRules.paging.total, }); @@ -140,6 +141,11 @@ export default class ProfileRules extends React.PureComponent { : 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 { const { actions = {} } = profile; return ( -
    +
    @@ -212,7 +218,7 @@ export default class ProfileRules extends React.PureComponent { sonarway={compareToSonarWay.profile} /> )} - + ); } } 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) { - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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> = {}) { - const profile = mockQualityProfile(); - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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( - - ); -} 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('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('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('onDelete')(lambda); - wrapper.update(); - expect(wrapper.find('ProfilePermissionsGroup')).toHaveLength(1); -}); - -it('fetches users and groups on mount', () => { - mount(); - 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(); -} 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 = {}) { - return shallow( - - ); -} 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() - ).toMatchSnapshot('option renderer'); -}); - -it('should render value correctly', () => { - const wrapper = shallowRender(); - const ValueRenderer = wrapper.instance().singleValueRenderer; - expect( - shallow() - ).toMatchSnapshot('value renderer'); -}); - -it('should render control correctly', () => { - const wrapper = shallowRender(); - const ControlRenderer = wrapper.instance().controlRenderer; - expect(shallow()).toMatchSnapshot( - 'control renderer' - ); -}); - -function shallowRender(overrides: Partial = {}) { - return shallow( - - ); -} 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; - -const profile = { language: 'js', name: 'Sonar way' }; -const group = { name: 'lambda' }; - -beforeEach(() => { - removeGroup.mockClear(); -}); - -it('renders', () => { - expect( - shallow() - ).toMatchSnapshot(); -}); - -it('removes user', async () => { - const onDelete = jest.fn(); - const wrapper = shallow( - - ); - (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('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() - ).toMatchSnapshot(); -}); - -it('removes user', async () => { - const onDelete = jest.fn(); - const wrapper = shallow( - - ); - (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('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 = {}) { - return shallow( - - ); -} 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(); - 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(); - expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); -}); - -it('should show a disabled button to activate more rules for built-in profiles', () => { - const wrapper = shallow( - - ); - expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); -}); - -it('should show a deprecated rules warning message', () => { - const wrapper = shallow( - - ); - expect(wrapper.find('ProfileRulesDeprecatedWarning')).toMatchSnapshot(); -}); - -it('should not show a button to activate more rules on built in profiles', () => { - const wrapper = shallow(); - 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(); - 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(); - 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() - ).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() - ).toMatchSnapshot(); -}); - -it('should render correctly if there is 0 rules', () => { - expect( - shallow() - ).toMatchSnapshot(); -}); - -it('should render correctly if there is missing data', () => { - expect( - shallow() - ).toMatchSnapshot(); - expect( - shallow() - ).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()).toMatchSnapshot(); -}); - -it('should render correctly if there is 0 rules', () => { - expect(shallow()).toMatchSnapshot(); -}); - -it('should render correctly if there is missing data', () => { - expect(shallow()).toMatchSnapshot(); - expect( - shallow() - ).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( - - ) - ).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`] = ` - -
    -
    -

    - quality_profiles.change_parent -

    -
    -
    - -
    - -
    - - - - -
    -
    -
    -`; - -exports[`should render modal correctly 1`] = ` -
    -
    -

    - quality_profiles.profile_inheritance -

    -
    -
    - - - - - -
    -
    - -
    -`; 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`] = ` - - -
    - - Foo - -
    - - - quality_profile.x_active_rules.4 - - -

    - quality_profiles.x_overridden_rules.0 -

    - - -`; - -exports[`should render correctly 2`] = ` - - -
    - - Foo - - -
    - - - quality_profile.x_active_rules.4 - - -

    - quality_profiles.x_overridden_rules.0 -

    - - -`; - -exports[`should render correctly 3`] = ` - - -
    - - Foo - - -
    - - - quality_profile.x_active_rules.4 - - -

    - quality_profiles.x_overridden_rules.0 -

    - - -`; - -exports[`should render correctly 4`] = ` - - -
    - - Foo - -
    - - - quality_profile.x_active_rules.4 - - -

    - quality_profiles.x_overridden_rules.10 -

    - - -`; 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`] = ` -
    -

    - permissions.page -

    -
    -

    - quality_profiles.default_permissions -

    -
    - -
    -
    -
    -`; - -exports[`renders 2`] = ` -
    -

    - permissions.page -

    -
    -

    - quality_profiles.default_permissions -

    -
    - - -
    - -
    -
    -
    -
    -`; 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`] = ` - -
    -

    - quality_profiles.grant_permissions_to_user_or_group -

    -
    - -
    -
    - - -
    -
    -
    - - - add_verb - - - cancel - -
    - -
    -`; - -exports[`correctly adds users 1`] = ` - -
    -

    - quality_profiles.grant_permissions_to_user_or_group -

    -
    -
    -
    -
    - - -
    -
    -
    - - - add_verb - - - cancel - -
    - -
    -`; - -exports[`should render correctly: default 1`] = ` - -
    -

    - quality_profiles.grant_permissions_to_user_or_group -

    -
    -
    -
    -
    - - -
    -
    -
    - - add_verb - - - cancel - -
    -
    -
    -`; - -exports[`should render correctly: submitting 1`] = ` - -
    -

    - quality_profiles.grant_permissions_to_user_or_group -

    -
    -
    -
    -
    - - -
    -
    -
    - - - add_verb - - - cancel - -
    - -
    -`; 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`] = ` - -`; - -exports[`should render correctly 1`] = ` - -`; - -exports[`should render option correctly: option renderer 1`] = ` - -`; - -exports[`should render value correctly: value renderer 1`] = ` - - - - - name - - - -`; 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`] = ` -
    - - -
    - - lambda - -
    -
    -`; 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`] = ` -
    - - -
    - - Luke Skywalker - -
    - luke -
    -
    -
    -`; 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`] = ` -
    -
    - - - -
    -
    -

    - projects -

    -
    -
    -
      -
    • - - - - - SonarXML - - -
    • -
    - -
    -
    -`; - -exports[`should render correctly: loading 1`] = ` -
    -
    - - - -
    -
    -

    - projects -

    -
    -
    - -
    -
    -`; - -exports[`should render correctly: no active rules, but associated projects 1`] = ` -
    -
    - - - -
    -
    -

    - projects -

    -
    -
    -
      -
    • - - - - - SonarXML - - -
    • -
    - -
    -
    -`; - -exports[`should render correctly: no active rules, no associated projects 1`] = ` -
    -
    - - - -
    -
    -

    - projects -

    -
    -
    -
    - quality_profiles.cannot_associate_projects_no_rules -
    -
    -
    -`; - -exports[`should render correctly: no rights 1`] = ` -
    -
    -

    - projects -

    -
    -
    -
      -
    • - - - - - SonarXML - - -
    • -
    - -
    -
    -`; 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`] = ` -
    -
    - - - - - - - - - - - - - - - -
    -

    - rules -

    -
    - active - - inactive -
    -
    - -
    -`; - -exports[`should show a button to activate more rules for admins 1`] = ` - - quality_profiles.activate_more - -`; - -exports[`should show a deprecated rules warning message 1`] = ` - -`; - -exports[`should show a disabled button to activate more rules for built-in profiles 1`] = ` - -`; 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`] = ` -
    - - - quality_profiles.deprecated_rules - - - - - 18 - -
    -`; 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`] = ` - - - - - issue.type.BUG.plural - - - - - 3 - - - - - 7 - - - -`; - -exports[`should render correctly if there is 0 rules 1`] = ` - - - - - issue.type.VULNERABILITY.plural - - - - - 0 - - - - - 0 - - - -`; - -exports[`should render correctly if there is missing data 1`] = ` - - - - - issue.type.VULNERABILITY.plural - - - - - 5 - - - - -`; - -exports[`should render correctly if there is missing data 2`] = ` - - - - - issue.type.VULNERABILITY.plural - - - - - -`; 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`] = ` - - - - total - - - - - - 3 - - - - - - - 7 - - - - -`; - -exports[`should render correctly if there is 0 rules 1`] = ` - - - - total - - - - - - 0 - - - - - - 0 - - - -`; - -exports[`should render correctly if there is missing data 1`] = ` - - - - total - - - - - - 5 - - - - - -`; - -exports[`should render correctly if there is missing data 2`] = ` - - - - total - - - - - -`; 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`] = ` -
    - - - quality_profiles.sonarway_missing_rules - - - - - 158 - -
    -`; 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 { const sortedProfiles = sortBy(profilesWithDeprecations, (p) => -p.activeDeprecatedRuleCount); return ( -
    +

    {translate('quality_profiles.deprecated_rules')}

    {translateWithParameters( @@ -144,7 +147,7 @@ export default class EvolutionDeprecated extends React.PureComponent { ))} -
    +
    ); } } 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 ( -
    +

    {newRulesTitle}

      {latestRules.map((rule) => ( @@ -143,7 +146,7 @@ export default class EvolutionRules extends React.PureComponent<{}, State> {
    )} -
    + ); } } 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 ( -
    +

    {translate('quality_profiles.stagnant_profiles')}

    {translate('quality_profiles.not_updated_more_than_year')} @@ -69,6 +72,6 @@ export default function EvolutionStagnant(props: Props) { ))} -
    +
    ); } 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 = {}) { - return shallow(); -} 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( - - ); - 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 = {}) { - return shallow( - - ); -} 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 = {}) { - return shallow( - - ); -} 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() - ).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`] = ` -
    - - - -
    -`; 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`] = ` -
    -

    - quality_profiles.deprecated_rules -

    -
    - quality_profiles.deprecated_rules_are_still_activated.4 -
    -
      -
    • -
      - - Quality Profile 5 - -
      -
      - JavaScript - , - - quality_profile.x_rules.4 - -
      - - coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 -
      -
      - - coding_rules.filters.inheritance.x_inherited_from_y.1.Quality Profile 4 -
      -
      -
    • -
    • -
      - - Quality Profile 4 - -
      -
      - JavaScript - , - - quality_profile.x_rules.3 - -
      - - coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 -
      -
      -
    • -
    • -
      - - Quality Profile 2 - -
      -
      - JavaScript - , - - quality_profile.x_rules.2 - -
      -
    • -
    • -
      - - Quality Profile 3 - -
      -
      - JavaScript - , - - quality_profile.x_rules.2 - -
      - - coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 -
      -
      -
    • -
    -
    -`; 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`] = ` -
    -

    - quality_profiles.page -

    -
    - quality_profiles.intro1 -
    - quality_profiles.intro2 - - learn_more - -
    -
    -`; - -exports[`should render correctly 2`] = ` -
    -

    - quality_profiles.page -

    -
    - - -
    -
    - quality_profiles.intro1 -
    - quality_profiles.intro2 - - learn_more - -
    -
    -`; - -exports[`should render correctly 3`] = ` -
    -

    - quality_profiles.page -

    -
    - - - - quality_profiles.no_languages_available - -
    -
    - quality_profiles.intro1 -
    - quality_profiles.intro2 - - learn_more - -
    -
    -`; - -exports[`should show a create form 1`] = ` -
    -

    - quality_profiles.page -

    -
    - - -
    -
    - quality_profiles.intro1 -
    - quality_profiles.intro2 - - learn_more - -
    - -
    -`; - -exports[`should show a restore form 1`] = ` -
    -

    - quality_profiles.page -

    -
    - - -
    -
    - quality_profiles.intro1 -
    - quality_profiles.intro2 - - learn_more - -
    - -
    -`; 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`] = ` -
    - - -
    -
    -
    - - restore - - - cancel - -
    - - -`; 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; 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 { return { action: 'ACTIVATED', date: '2019-04-23T02:12:32+0100', @@ -677,6 +680,16 @@ export function mockUser(overrides: Partial = {}): User { }; } +export function mockUserSelected(overrides: Partial = {}): UserSelected { + return { + active: true, + login: 'john.doe', + name: 'John Doe', + selected: true, + ...overrides, + }; +} + export function mockUserGroupMember(overrides: Partial = {}): 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. -- 2.39.5