@@ -18,14 +18,19 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { cloneDeep } from 'lodash'; | |||
import { ProfileChangelogEvent } from '../../apps/quality-profiles/types'; | |||
import { RequestData } from '../../helpers/request'; | |||
import { | |||
mockCompareResult, | |||
mockGroup, | |||
mockPaging, | |||
mockQualityProfile, | |||
mockQualityProfileChangelogEvent, | |||
mockRuleDetails, | |||
mockUserSelected, | |||
} from '../../helpers/testMocks'; | |||
import { SearchRulesResponse } from '../../types/coding-rules'; | |||
import { SearchRulesQuery } from '../../types/rules'; | |||
import { Dict, Paging, ProfileInheritanceDetails, RuleDetails } from '../../types/types'; | |||
import { | |||
CompareResponse, | |||
@@ -34,22 +39,41 @@ import { | |||
SearchQualityProfilesParameters, | |||
SearchQualityProfilesResponse, | |||
activateRule, | |||
addGroup, | |||
addUser, | |||
associateProject, | |||
changeProfileParent, | |||
compareProfiles, | |||
copyProfile, | |||
createQualityProfile, | |||
deleteProfile, | |||
dissociateProject, | |||
getExporters, | |||
getImporters, | |||
getProfileChangelog, | |||
getProfileInheritance, | |||
getProfileProjects, | |||
getQualityProfile, | |||
removeGroup, | |||
removeUser, | |||
renameProfile, | |||
restoreQualityProfile, | |||
searchGroups, | |||
searchQualityProfiles, | |||
searchUsers, | |||
setDefaultProfile, | |||
} from '../quality-profiles'; | |||
import { getRuleDetails, searchRules } from '../rules'; | |||
jest.mock('../../api/rules'); | |||
export default class QualityProfilesServiceMock { | |||
isAdmin = false; | |||
listQualityProfile: Profile[] = []; | |||
languageMapping: Dict<Partial<Profile>> = { | |||
c: { language: 'c', languageName: 'C' }, | |||
php: { language: 'php', languageName: 'PHP' }, | |||
java: { language: 'java', languageName: 'Java' }, | |||
}; | |||
comparisonResult: CompareResponse = mockCompareResult(); | |||
@@ -58,21 +82,43 @@ export default class QualityProfilesServiceMock { | |||
paging: mockPaging(), | |||
}; | |||
profileProjects: { | |||
[profileKey: string]: ProfileProject[]; | |||
} = {}; | |||
changelogEvents: ProfileChangelogEvent[] = []; | |||
constructor() { | |||
this.resetQualityProfile(); | |||
this.resetComparisonResult(); | |||
(searchQualityProfiles as jest.Mock).mockImplementation(this.handleSearchQualityProfiles); | |||
(createQualityProfile as jest.Mock).mockImplementation(this.handleCreateQualityProfile); | |||
(changeProfileParent as jest.Mock).mockImplementation(this.handleChangeProfileParent); | |||
(getProfileInheritance as jest.Mock).mockImplementation(this.handleGetProfileInheritance); | |||
(getProfileProjects as jest.Mock).mockImplementation(this.handleGetProfileProjects); | |||
(copyProfile as jest.Mock).mockImplementation(this.handleCopyProfile); | |||
(getImporters as jest.Mock).mockImplementation(this.handleGetImporters); | |||
(searchRules as jest.Mock).mockImplementation(this.handleSearchRules); | |||
(compareProfiles as jest.Mock).mockImplementation(this.handleCompareQualityProfiles); | |||
(activateRule as jest.Mock).mockImplementation(this.handleActivateRule); | |||
(getRuleDetails as jest.Mock).mockImplementation(this.handleGetRuleDetails); | |||
this.resetChangelogEvents(); | |||
jest.mocked(searchQualityProfiles).mockImplementation(this.handleSearchQualityProfiles); | |||
jest.mocked(createQualityProfile).mockImplementation(this.handleCreateQualityProfile); | |||
jest.mocked(changeProfileParent).mockImplementation(this.handleChangeProfileParent); | |||
jest.mocked(getProfileInheritance).mockImplementation(this.handleGetProfileInheritance); | |||
jest.mocked(getProfileProjects).mockImplementation(this.handleGetProfileProjects); | |||
jest.mocked(copyProfile).mockImplementation(this.handleCopyProfile); | |||
jest.mocked(getImporters).mockImplementation(this.handleGetImporters); | |||
jest.mocked(searchRules).mockImplementation(this.handleSearchRules); | |||
jest.mocked(compareProfiles).mockImplementation(this.handleCompareQualityProfiles); | |||
jest.mocked(activateRule).mockImplementation(this.handleActivateRule); | |||
jest.mocked(getRuleDetails).mockImplementation(this.handleGetRuleDetails); | |||
jest.mocked(restoreQualityProfile).mockImplementation(this.handleRestoreQualityProfile); | |||
jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers); | |||
jest.mocked(addUser).mockImplementation(this.handleAddUser); | |||
jest.mocked(searchGroups).mockImplementation(this.handleSearchGroups); | |||
jest.mocked(addGroup).mockImplementation(this.handleAddGroup); | |||
jest.mocked(removeGroup).mockImplementation(this.handleRemoveGroup); | |||
jest.mocked(removeUser).mockImplementation(this.handleRemoveUser); | |||
jest.mocked(associateProject).mockImplementation(this.handleAssociateProject); | |||
jest.mocked(getProfileChangelog).mockImplementation(this.handleGetProfileChangelog); | |||
jest.mocked(dissociateProject).mockImplementation(this.handleDissociateProject); | |||
jest.mocked(getQualityProfile).mockImplementation(this.handleGetQualityProfile); | |||
jest.mocked(getExporters).mockImplementation(this.handleGetExporters); | |||
jest.mocked(deleteProfile).mockImplementation(this.handleDeleteProfile); | |||
jest.mocked(renameProfile).mockImplementation(this.handleRenameProfile); | |||
jest.mocked(setDefaultProfile).mockImplementation(this.handleSetDefaultProfile); | |||
} | |||
resetQualityProfile() { | |||
@@ -98,9 +144,66 @@ export default class QualityProfilesServiceMock { | |||
name: 'java quality profile #2', | |||
activeDeprecatedRuleCount: 1, | |||
actions: { | |||
edit: true, | |||
edit: this.isAdmin, | |||
}, | |||
}), | |||
mockQualityProfile({ | |||
key: 'sonar', | |||
language: 'java', | |||
languageName: 'Java', | |||
name: 'Sonar way', | |||
isBuiltIn: true, | |||
isDefault: true, | |||
}), | |||
mockQualityProfile({ | |||
key: 'old-php-qp', | |||
language: 'php', | |||
languageName: 'PHP', | |||
name: 'Good old PHP quality profile', | |||
activeDeprecatedRuleCount: 8, | |||
rulesUpdatedAt: '2019-09-16T21:10:36+0000', | |||
parentKey: 'php-sonar-way-1', | |||
actions: { | |||
edit: this.isAdmin, | |||
associateProjects: this.isAdmin, | |||
delete: this.isAdmin, | |||
setAsDefault: this.isAdmin, | |||
copy: this.isAdmin, | |||
}, | |||
}), | |||
mockQualityProfile({ | |||
key: 'no-rule-qp', | |||
activeRuleCount: 0, | |||
actions: { | |||
associateProjects: true, | |||
setAsDefault: this.isAdmin, | |||
}, | |||
}), | |||
mockQualityProfile({ | |||
activeRuleCount: 6, | |||
isBuiltIn: true, | |||
key: 'php-sonar-way-1', | |||
name: 'PHP Sonar way 1', | |||
language: 'php', | |||
languageName: 'PHP', | |||
}), | |||
mockQualityProfile({ | |||
activeRuleCount: 6, | |||
isBuiltIn: false, | |||
key: 'php-sonar-way-2', | |||
name: 'PHP Sonar way 2', | |||
language: 'php', | |||
languageName: 'PHP', | |||
}), | |||
mockQualityProfile({ | |||
activeRuleCount: 3, | |||
isBuiltIn: false, | |||
key: 'php-sonar-way', | |||
parentKey: 'old-php-qp', | |||
name: 'PHP way', | |||
language: 'php', | |||
languageName: 'PHP', | |||
}), | |||
]; | |||
} | |||
@@ -108,17 +211,95 @@ export default class QualityProfilesServiceMock { | |||
this.comparisonResult = mockCompareResult(); | |||
} | |||
resetChangelogEvents() { | |||
this.changelogEvents = [ | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-05-23T04:12:32+0100', | |||
}), | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-05-23T03:12:32+0100', | |||
action: 'DEACTIVATED', | |||
ruleKey: 'php:rule1', | |||
ruleName: 'PHP Rule', | |||
}), | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-05-23T03:12:32+0100', | |||
action: 'ACTIVATED', | |||
ruleKey: 'c:rule0', | |||
ruleName: 'Rule 0', | |||
params: {}, | |||
}), | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-04-23T03:12:32+0100', | |||
action: 'DEACTIVATED', | |||
ruleKey: 'c:rule0', | |||
ruleName: 'Rule 0', | |||
}), | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-04-23T03:12:32+0100', | |||
action: 'DEACTIVATED', | |||
ruleKey: 'c:rule1', | |||
ruleName: 'Rule 1', | |||
}), | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-04-23T02:12:32+0100', | |||
action: 'DEACTIVATED', | |||
ruleKey: 'c:rule2', | |||
ruleName: 'Rule 2', | |||
authorName: 'John Doe', | |||
}), | |||
mockQualityProfileChangelogEvent({ | |||
date: '2019-03-23T02:12:32+0100', | |||
ruleKey: 'c:rule2', | |||
ruleName: 'Rule 2', | |||
authorName: 'John Doe', | |||
params: { | |||
severity: 'CRITICAL', | |||
credentialWords: 'foo,bar', | |||
}, | |||
}), | |||
]; | |||
} | |||
resetSearchRulesResponse() { | |||
this.searchRulesResponse = { | |||
facets: [ | |||
{ | |||
property: 'types', | |||
values: [ | |||
{ val: 'CODE_SMELL', count: 250 }, | |||
{ val: 'BUG', count: 60 }, | |||
{ val: 'VULNERABILITY', count: 40 }, | |||
{ val: 'SECURITY_HOTSPOT', count: 50 }, | |||
], | |||
}, | |||
], | |||
rules: [], | |||
paging: { | |||
pageIndex: 1, | |||
pageSize: 500, | |||
total: 0, | |||
pageSize: 400, | |||
total: 400, | |||
}, | |||
}; | |||
} | |||
resetProfileProjects() { | |||
this.profileProjects = { | |||
'old-php-qp': [ | |||
{ | |||
key: 'Benflix', | |||
name: 'Benflix', | |||
selected: true, | |||
}, | |||
{ | |||
key: 'Twitter', | |||
name: 'Twitter', | |||
selected: false, | |||
}, | |||
], | |||
}; | |||
} | |||
handleGetImporters = () => { | |||
return this.reply([]); | |||
}; | |||
@@ -142,11 +323,19 @@ export default class QualityProfilesServiceMock { | |||
return this.reply(copiedQualityProfile); | |||
}; | |||
handleGetProfileProjects = (): Promise<{ | |||
handleGetProfileProjects = ( | |||
data: RequestData | |||
): Promise<{ | |||
more: boolean; | |||
paging: Paging; | |||
results: ProfileProject[]; | |||
}> => { | |||
const results = (this.profileProjects[data.key] ?? []).filter( | |||
(project) => | |||
project.selected === | |||
(data.selected !== undefined ? Boolean(data.selected === 'selected') : true) | |||
); | |||
return this.reply({ | |||
more: false, | |||
paging: { | |||
@@ -154,7 +343,7 @@ export default class QualityProfilesServiceMock { | |||
pageSize: 10, | |||
total: 0, | |||
}, | |||
results: [], | |||
results, | |||
}); | |||
}; | |||
@@ -173,10 +362,16 @@ export default class QualityProfilesServiceMock { | |||
}); | |||
} | |||
// Lets fake it for now | |||
const ancestors = this.listQualityProfile.filter( | |||
(p) => p.key === profile.parentKey | |||
) as ProfileInheritanceDetails[]; | |||
const children = this.listQualityProfile.filter( | |||
(p) => p.parentKey === profile.key | |||
) as ProfileInheritanceDetails[]; | |||
return this.reply({ | |||
ancestors: [], | |||
children: [], | |||
ancestors, | |||
children, | |||
profile: { | |||
name: profile.name, | |||
activeRuleCount: 0, | |||
@@ -226,10 +421,54 @@ export default class QualityProfilesServiceMock { | |||
return this.reply({ profile: newQualityProfile }); | |||
}; | |||
handleSearchRules = (): Promise<SearchRulesResponse> => { | |||
handleSearchRules = (data: SearchRulesQuery): Promise<SearchRulesResponse> => { | |||
if (data.activation) { | |||
this.setRulesSearchResponse({ | |||
facets: [ | |||
{ | |||
property: 'types', | |||
values: [ | |||
{ | |||
val: 'CODE_SMELL', | |||
count: 200, | |||
}, | |||
{ | |||
val: 'BUG', | |||
count: 50, | |||
}, | |||
{ | |||
val: 'VULNERABILITY', | |||
count: 30, | |||
}, | |||
{ | |||
val: 'SECURITY_HOTSPOT', | |||
count: 20, | |||
}, | |||
], | |||
}, | |||
], | |||
rules: [], | |||
paging: { | |||
pageIndex: 1, | |||
pageSize: 1, | |||
total: 300, | |||
}, | |||
}); | |||
} | |||
return this.reply(this.searchRulesResponse); | |||
}; | |||
handleGetQualityProfile = () => { | |||
return this.reply({ | |||
profile: mockQualityProfile(), | |||
compareToSonarWay: { | |||
profile: '', | |||
profileName: 'Sonar way', | |||
missingRuleCount: 2, | |||
}, | |||
}); | |||
}; | |||
handleSearchQualityProfiles = ( | |||
parameters: SearchQualityProfilesParameters = {} | |||
): Promise<SearchQualityProfilesResponse> => { | |||
@@ -288,8 +527,169 @@ export default class QualityProfilesServiceMock { | |||
}); | |||
}; | |||
handleRestoreQualityProfile = () => { | |||
return this.reply({ | |||
profile: { | |||
key: 'c-sonarsource-06756', | |||
name: 'SonarSource', | |||
language: 'c', | |||
isDefault: false, | |||
isInherited: false, | |||
languageName: 'C', | |||
}, | |||
ruleSuccesses: 231, | |||
ruleFailures: 0, | |||
}); | |||
}; | |||
handleSearchUsers = () => { | |||
return this.reply({ | |||
users: [ | |||
mockUserSelected({ | |||
login: 'buzz', | |||
name: 'Buzz', | |||
}), | |||
], | |||
paging: mockPaging(), | |||
}); | |||
}; | |||
handleAddUser = () => { | |||
return this.reply(undefined); | |||
}; | |||
handleRemoveUser = () => { | |||
return this.reply(undefined); | |||
}; | |||
handleSearchGroups = () => { | |||
return this.reply({ | |||
groups: [mockGroup({ name: 'ACDC' })], | |||
paging: mockPaging(), | |||
}); | |||
}; | |||
handleAddGroup = () => { | |||
return this.reply(undefined); | |||
}; | |||
handleRemoveGroup = () => { | |||
return this.reply(undefined); | |||
}; | |||
handleAssociateProject = ({ key }: Profile, project: string) => { | |||
const projects = this.profileProjects[key].map((profileProject) => { | |||
if (profileProject.key === project) { | |||
return { | |||
...profileProject, | |||
selected: true, | |||
}; | |||
} | |||
return profileProject; | |||
}); | |||
this.profileProjects[key] = projects; | |||
return this.reply({}); | |||
}; | |||
handleDissociateProject = ({ key }: Profile, project: string) => { | |||
const projects = this.profileProjects[key].map((profileProject) => { | |||
if (profileProject.key === project) { | |||
return { | |||
...profileProject, | |||
selected: false, | |||
}; | |||
} | |||
return profileProject; | |||
}); | |||
this.profileProjects[key] = projects; | |||
return this.reply({}); | |||
}; | |||
handleGetProfileChangelog: typeof getProfileChangelog = (since, to, { language }, page) => { | |||
const PAGE_SIZE = 50; | |||
const p = page || 1; | |||
const events = this.changelogEvents.filter((event) => { | |||
if (event.ruleKey.split(':')[0] !== language) { | |||
return false; | |||
} | |||
if (since && new Date(since) >= new Date(event.date)) { | |||
return false; | |||
} | |||
if (to && new Date(to) <= new Date(event.date)) { | |||
return false; | |||
} | |||
return true; | |||
}); | |||
return this.reply({ | |||
events: events.slice((p - 1) * PAGE_SIZE, (p - 1) * PAGE_SIZE + PAGE_SIZE), | |||
paging: mockPaging({ | |||
total: events.length, | |||
pageSize: PAGE_SIZE, | |||
pageIndex: p, | |||
}), | |||
}); | |||
}; | |||
handleGetExporters = () => { | |||
return this.reply([ | |||
{ | |||
key: 'sonarlint-vs', | |||
name: 'SonarLint for Visual Studio', | |||
languages: ['php'], | |||
}, | |||
{ | |||
key: 'sonarlint-eclipse', | |||
name: 'SonarLint for Eclipse', | |||
languages: ['php'], | |||
}, | |||
]); | |||
}; | |||
handleDeleteProfile = ({ name }: Profile) => { | |||
// delete Children | |||
const qualityProfileToDelete = this.listQualityProfile.find((profile) => profile.name === name); | |||
this.listQualityProfile = this.listQualityProfile.filter( | |||
(profile) => profile.parentKey !== qualityProfileToDelete?.key | |||
); | |||
// delete profile | |||
this.listQualityProfile = this.listQualityProfile.filter((profile) => profile.name !== name); | |||
return this.reply({}); | |||
}; | |||
handleRenameProfile = (key: string, newName: string) => { | |||
this.listQualityProfile = this.listQualityProfile.map((profile) => { | |||
if (profile.key === key) { | |||
return { | |||
...profile, | |||
name: newName, | |||
}; | |||
} | |||
return profile; | |||
}); | |||
return this.reply({}); | |||
}; | |||
handleSetDefaultProfile = ({ name }: Profile) => { | |||
this.listQualityProfile = this.listQualityProfile.map((profile) => { | |||
if (profile.name === name) { | |||
return { | |||
...profile, | |||
isDefault: true, | |||
}; | |||
} | |||
return profile; | |||
}); | |||
return Promise.resolve(); | |||
}; | |||
setAdmin() { | |||
this.isAdmin = true; | |||
this.resetQualityProfile(); | |||
} | |||
setRulesSearchResponse(overrides: Partial<SearchRulesResponse>) { | |||
@@ -304,6 +704,8 @@ export default class QualityProfilesServiceMock { | |||
this.resetQualityProfile(); | |||
this.resetComparisonResult(); | |||
this.resetSearchRulesResponse(); | |||
this.resetProfileProjects(); | |||
this.resetChangelogEvents(); | |||
} | |||
reply<T>(response: T): Promise<T> { |
@@ -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 |
@@ -31,11 +31,6 @@ export function searchRules(data: SearchRulesQuery): Promise<SearchRulesResponse | |||
return getJSON('/api/rules/search', data).catch(throwGlobalError); | |||
} | |||
export function takeFacet(response: SearchRulesResponse, property: string) { | |||
const facet = response.facets?.find((f) => f.property === property); | |||
return facet ? facet.values : []; | |||
} | |||
export function getRuleRepositories(parameters: { | |||
q: string; | |||
}): Promise<Array<{ key: string; language: string; name: string }>> { |
@@ -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, {}); | |||
} |
@@ -17,7 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { act, getByText } from '@testing-library/react'; | |||
import { act, getByText, screen } from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import selectEvent from 'react-select-event'; | |||
import QualityProfilesServiceMock from '../../../api/mocks/QualityProfilesServiceMock'; | |||
@@ -50,10 +50,13 @@ const ui = { | |||
name: 'copy', | |||
}), | |||
createButton: byRole('button', { name: 'create' }), | |||
restoreButton: byRole('button', { name: 'restore' }), | |||
compareButton: byRole('link', { name: 'compare' }), | |||
cancelButton: byRole('button', { name: 'cancel' }), | |||
compareDropdown: byRole('combobox', { name: 'quality_profiles.compare_with' }), | |||
changelogLink: byRole('link', { name: 'changelog' }), | |||
popup: byRole('dialog'), | |||
restoreProfileDialog: byRole('dialog', { name: 'quality_profiles.restore_profile' }), | |||
copyRadio: byRole('radio', { | |||
name: 'quality_profiles.creation_from_copy quality_profiles.creation_from_copy_description_1 quality_profiles.creation_from_copy_description_2', | |||
}), | |||
@@ -90,6 +93,9 @@ const ui = { | |||
byRole('table', { | |||
name: `quality_profiles.x_rules_have_different_configuration.${rulesQuantity}`, | |||
}), | |||
deprecatedRulesRegion: byRole('region', { name: 'quality_profiles.deprecated_rules' }), | |||
stagnantProfilesRegion: byRole('region', { name: 'quality_profiles.stagnant_profiles' }), | |||
recentlyAddedRulesRegion: byRole('region', { name: 'quality_profiles.latest_new_rules' }), | |||
newRuleLink: byRole('link', { name: 'Recently Added Rule' }), | |||
seeAllNewRulesLink: byRole('link', { name: 'see_all 20 quality_profiles.latest_new_rules' }), | |||
}; | |||
@@ -115,97 +121,139 @@ it('should list Quality Profiles and filter by language', async () => { | |||
expect(ui.createButton.get(ui.popup.get())).toBeEnabled(); | |||
}); | |||
it('should list recently added rules', async () => { | |||
serviceMock.setAdmin(); | |||
serviceMock.setRulesSearchResponse({ | |||
rules: [mockRule({ name: 'Recently Added Rule' })], | |||
paging: mockPaging({ | |||
total: 20, | |||
}), | |||
describe('Evolution', () => { | |||
it('should list deprecated rules', async () => { | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
expect(await ui.deprecatedRulesRegion.find()).toBeInTheDocument(); | |||
expect( | |||
ui.deprecatedRulesRegion.byRole('link', { name: 'Good old PHP quality profile' }).get() | |||
).toBeInTheDocument(); | |||
expect( | |||
ui.deprecatedRulesRegion.byRole('link', { name: 'java quality profile #2' }).get() | |||
).toBeInTheDocument(); | |||
}); | |||
renderQualityProfiles(); | |||
expect(await ui.newRuleLink.find()).toBeInTheDocument(); | |||
expect(ui.seeAllNewRulesLink.get()).toBeInTheDocument(); | |||
}); | |||
it('should be able to extend Quality Profile', async () => { | |||
// From the list page | |||
const user = userEvent.setup(); | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
await user.click(await ui.profileActions('c quality profile', 'C').find()); | |||
await user.click(ui.extendButton.get()); | |||
it('should list stagnant profiles', async () => { | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
await user.clear(ui.namePropupInput.get()); | |||
await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); | |||
await act(async () => { | |||
await user.click(ui.extendButton.get()); | |||
expect(await ui.stagnantProfilesRegion.find()).toBeInTheDocument(); | |||
expect( | |||
ui.stagnantProfilesRegion.byRole('link', { name: 'Good old PHP quality profile' }).get() | |||
).toBeInTheDocument(); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); | |||
// From the create form | |||
await user.click(ui.returnToList.get()); | |||
await user.click(ui.createButton.get()); | |||
await selectEvent.select(ui.languageSelect.get(), 'C'); | |||
await selectEvent.select(ui.profileExtendSelect.get(), ui.newCQualityProfileName); | |||
await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); | |||
await act(async () => { | |||
await user.click(ui.createButton.get(ui.popup.get())); | |||
it('should list recently added rules', async () => { | |||
serviceMock.setAdmin(); | |||
serviceMock.setRulesSearchResponse({ | |||
rules: [mockRule({ name: 'Recently Added Rule' })], | |||
paging: mockPaging({ | |||
total: 20, | |||
}), | |||
}); | |||
renderQualityProfiles(); | |||
expect(await ui.recentlyAddedRulesRegion.find()).toBeInTheDocument(); | |||
expect(ui.newRuleLink.get()).toBeInTheDocument(); | |||
expect(ui.seeAllNewRulesLink.get()).toBeInTheDocument(); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); | |||
}); | |||
it('should be able to copy Quality Profile', async () => { | |||
// From the list page | |||
const user = userEvent.setup(); | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
describe('Create', () => { | |||
it('should be able to extend an existing Quality Profile', async () => { | |||
const user = userEvent.setup(); | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
await user.click(await ui.profileActions('c quality profile', 'C').find()); | |||
await user.click(ui.copyButton.get()); | |||
await user.clear(ui.namePropupInput.get()); | |||
await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); | |||
await act(async () => { | |||
await user.click(ui.copyButton.get(ui.popup.get())); | |||
await user.click(await ui.profileActions('c quality profile', 'C').find()); | |||
await user.click(ui.extendButton.get()); | |||
await user.clear(ui.namePropupInput.get()); | |||
await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); | |||
await act(async () => { | |||
await user.click(ui.extendButton.get()); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); | |||
await user.click(ui.returnToList.get()); | |||
await user.click(ui.createButton.get()); | |||
await selectEvent.select(ui.languageSelect.get(), 'C'); | |||
await selectEvent.select(ui.profileExtendSelect.get(), ui.newCQualityProfileName); | |||
await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); | |||
await act(async () => { | |||
await user.click(ui.createButton.get(ui.popup.get())); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); | |||
// From the create form | |||
await user.click(ui.returnToList.get()); | |||
await user.click(ui.createButton.get()); | |||
await user.click(ui.copyRadio.get()); | |||
await selectEvent.select(ui.languageSelect.get(), 'C'); | |||
await selectEvent.select(ui.profileCopySelect.get(), ui.newCQualityProfileName); | |||
await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); | |||
await act(async () => { | |||
await user.click(ui.createButton.get(ui.popup.get())); | |||
it('should be able to copy an existing Quality Profile', async () => { | |||
const user = userEvent.setup(); | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
await user.click(await ui.profileActions('c quality profile', 'C').find()); | |||
await user.click(ui.copyButton.get()); | |||
await user.clear(ui.namePropupInput.get()); | |||
await user.type(ui.namePropupInput.get(), ui.newCQualityProfileName); | |||
await act(async () => { | |||
await user.click(ui.copyButton.get(ui.popup.get())); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); | |||
await user.click(ui.returnToList.get()); | |||
await user.click(ui.createButton.get()); | |||
await user.click(ui.copyRadio.get()); | |||
await selectEvent.select(ui.languageSelect.get(), 'C'); | |||
await selectEvent.select(ui.profileCopySelect.get(), ui.newCQualityProfileName); | |||
await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileNameFromCreateButton); | |||
await act(async () => { | |||
await user.click(ui.createButton.get(ui.popup.get())); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfileFromCreateButton.find()).toBeInTheDocument(); | |||
it('should be able to create blank Quality Profile', async () => { | |||
const user = userEvent.setup(); | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
await user.click(await ui.createButton.find()); | |||
await user.click(ui.blankRadio.get()); | |||
await selectEvent.select(ui.languageSelect.get(), 'C'); | |||
await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileName); | |||
await act(async () => { | |||
await user.click(ui.createButton.get(ui.popup.get())); | |||
}); | |||
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); | |||
}); | |||
}); | |||
it('should be able to create blank Quality Profile', async () => { | |||
// From the list page | |||
it('should be able to restore a quality profile', async () => { | |||
const user = userEvent.setup(); | |||
serviceMock.setAdmin(); | |||
renderQualityProfiles(); | |||
await user.click(await ui.createButton.find()); | |||
await user.click(await ui.restoreButton.find()); | |||
expect(await ui.restoreProfileDialog.find()).toBeInTheDocument(); | |||
await user.click(ui.blankRadio.get()); | |||
await selectEvent.select(ui.languageSelect.get(), 'C'); | |||
await user.type(ui.nameCreatePopupInput.get(), ui.newCQualityProfileName); | |||
await act(async () => { | |||
await user.click(ui.createButton.get(ui.popup.get())); | |||
const profileXMLBackup = new File(['c-sonarsource-06756'], 'c-sonarsource-06756', { | |||
type: 'application/xml', | |||
}); | |||
const input = screen.getByLabelText<HTMLInputElement>(/backup/); | |||
// Very hacky way to upload the file in the test | |||
input.removeAttribute('required'); | |||
await userEvent.upload(input, profileXMLBackup); | |||
expect(input.files?.item(0)).toStrictEqual(profileXMLBackup); | |||
await user.click(ui.restoreProfileDialog.byRole('button', { name: 'restore' }).get()); | |||
expect(await ui.listLinkNewCQualityProfile.find()).toBeInTheDocument(); | |||
expect(await screen.findByText(/quality_profiles.restore_profile.success/)).toBeInTheDocument(); | |||
}); | |||
it('should be able to compare profiles', async () => { |
@@ -43,7 +43,7 @@ interface State { | |||
total?: number; | |||
} | |||
export class ChangelogContainer extends React.PureComponent<Props, State> { | |||
class ChangelogContainer extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { loading: true }; | |||
@@ -1,100 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import Link from '../../../../components/common/Link'; | |||
import { ProfileChangelogEvent } from '../../types'; | |||
import Changelog from '../Changelog'; | |||
import ChangesList from '../ChangesList'; | |||
function createEvent(overrides?: Partial<ProfileChangelogEvent>): ProfileChangelogEvent { | |||
return { | |||
date: '2016-01-01', | |||
authorName: 'John', | |||
action: 'ACTIVATED', | |||
ruleKey: 'squid1234', | |||
ruleName: 'Do not do this', | |||
params: {}, | |||
...overrides, | |||
}; | |||
} | |||
it('should render events', () => { | |||
const events = [createEvent(), createEvent()]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
expect(changelog.find('tbody').find('tr').length).toBe(2); | |||
}); | |||
it('should render event date', () => { | |||
const events = [createEvent()]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
expect(changelog.find('DateTimeFormatter')).toHaveLength(1); | |||
}); | |||
it('should render author', () => { | |||
const events = [createEvent()]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
expect(changelog.text()).toContain('John'); | |||
}); | |||
it('should render system author', () => { | |||
const events = [createEvent({ authorName: undefined })]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
expect(changelog.text()).toContain('System'); | |||
}); | |||
it('should render action', () => { | |||
const events = [createEvent()]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
expect(changelog.text()).toContain('ACTIVATED'); | |||
}); | |||
it('should render rule', () => { | |||
const events = [createEvent()]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
expect(changelog.find(Link).prop('to')).toHaveProperty('search', '?rule_key=squid1234'); | |||
}); | |||
it('should render ChangesList', () => { | |||
const params = { severity: 'BLOCKER' }; | |||
const events = [createEvent({ params })]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
const changesList = changelog.find(ChangesList); | |||
expect(changesList.length).toBe(1); | |||
expect(changesList.prop('changes')).toBe(params); | |||
}); | |||
it('should render events sorted by time and action', () => { | |||
const events = [ | |||
createEvent({ date: '2019-02-07T14:03:45', action: 'DEACTIVATED' }), | |||
createEvent({ date: '2019-02-07T14:03:14', action: 'DEACTIVATED' }), | |||
createEvent({ date: '2019-02-07T14:03:14', action: 'ACTIVATED' }), | |||
createEvent({ date: '2019-02-07T14:03:07', action: 'ACTIVATED' }), | |||
]; | |||
const changelog = shallow(<Changelog events={events} />); | |||
const rows = changelog.find('tbody').find('tr'); | |||
const getAction = (index: number) => rows.at(index).childAt(2).childAt(0).text(); | |||
expect(getAction(0)).toBe('quality_profiles.changelog.DEACTIVATED'); | |||
expect(getAction(1)).toBe('quality_profiles.changelog.ACTIVATED'); | |||
expect(getAction(2)).toBe('quality_profiles.changelog.DEACTIVATED'); | |||
expect(getAction(3)).toBe('quality_profiles.changelog.ACTIVATED'); | |||
}); |
@@ -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, {}); | |||
} |
@@ -1,88 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { getProfileChangelog } from '../../../../api/quality-profiles'; | |||
import { | |||
mockLocation, | |||
mockPaging, | |||
mockQualityProfile, | |||
mockRouter, | |||
} from '../../../../helpers/testMocks'; | |||
import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import { ChangelogContainer } from '../ChangelogContainer'; | |||
beforeEach(() => jest.clearAllMocks()); | |||
jest.mock('../../../../api/quality-profiles', () => { | |||
const { mockQualityProfileChangelogEvent, mockPaging } = jest.requireActual( | |||
'../../../../helpers/testMocks' | |||
); | |||
return { | |||
getProfileChangelog: jest.fn().mockResolvedValue({ | |||
events: [ | |||
mockQualityProfileChangelogEvent(), | |||
mockQualityProfileChangelogEvent(), | |||
mockQualityProfileChangelogEvent(), | |||
], | |||
paging: mockPaging({ | |||
total: 6, | |||
pageIndex: 1, | |||
}), | |||
}), | |||
}; | |||
}); | |||
it('should render correctly without events', async () => { | |||
(getProfileChangelog as jest.Mock).mockResolvedValueOnce({ | |||
events: [], | |||
paging: mockPaging({ total: 0 }), | |||
}); | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should load more properly', async () => { | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
wrapper.instance().loadMore(mockEvent()); | |||
await waitAndUpdate(wrapper); | |||
expect(getProfileChangelog).toHaveBeenLastCalledWith(undefined, undefined, expect.anything(), 2); | |||
expect(wrapper.state().events?.length).toBe(6); | |||
}); | |||
function shallowRender() { | |||
return shallow<ChangelogContainer>( | |||
<ChangelogContainer | |||
location={mockLocation()} | |||
profile={mockQualityProfile()} | |||
router={mockRouter()} | |||
/> | |||
); | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { parseDate } from '../../../../helpers/dates'; | |||
import { click } from '../../../../helpers/testUtils'; | |||
import ChangelogSearch from '../ChangelogSearch'; | |||
it('should render', () => { | |||
const output = shallow( | |||
<ChangelogSearch | |||
dateRange={{ | |||
from: parseDate('2016-01-01T00:00:00.000Z'), | |||
to: parseDate('2016-05-05T00:00:00.000Z'), | |||
}} | |||
onDateRangeChange={jest.fn()} | |||
onReset={jest.fn()} | |||
/> | |||
); | |||
expect(output).toMatchSnapshot(); | |||
}); | |||
it('should reset', () => { | |||
const onReset = jest.fn(); | |||
const output = shallow( | |||
<ChangelogSearch | |||
dateRange={{ | |||
from: parseDate('2016-01-01T00:00:00.000Z'), | |||
to: parseDate('2016-05-05T00:00:00.000Z'), | |||
}} | |||
onDateRangeChange={jest.fn()} | |||
onReset={onReset} | |||
/> | |||
); | |||
expect(onReset).not.toHaveBeenCalled(); | |||
click(output.find('Button')); | |||
expect(onReset).toHaveBeenCalled(); | |||
}); |
@@ -1,45 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import ChangesList from '../ChangesList'; | |||
import ParameterChange from '../ParameterChange'; | |||
import SeverityChange from '../SeverityChange'; | |||
it('should render changes', () => { | |||
const changes = { severity: 'BLOCKER', foo: 'bar' }; | |||
const output = shallow(<ChangesList changes={changes} />); | |||
expect(output.find('li').length).toBe(2); | |||
}); | |||
it('should render severity change', () => { | |||
const changes = { severity: 'BLOCKER' }; | |||
const output = shallow(<ChangesList changes={changes} />).find(SeverityChange); | |||
expect(output.length).toBe(1); | |||
expect(output.prop('severity')).toBe('BLOCKER'); | |||
}); | |||
it('should render parameter change', () => { | |||
const changes = { foo: 'bar' }; | |||
const output = shallow(<ChangesList changes={changes} />).find(ParameterChange); | |||
expect(output.length).toBe(1); | |||
expect(output.prop('name')).toBe('foo'); | |||
expect(output.prop('value')).toBe('bar'); | |||
}); |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import ParameterChange from '../ParameterChange'; | |||
it('should render different messages', () => { | |||
const first = shallow(<ParameterChange name="foo" value={null} />); | |||
const second = shallow(<ParameterChange name="foo" value="bar" />); | |||
expect(first.text()).not.toBe(second.text()); | |||
}); |
@@ -1,29 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import SeverityHelper from '../../../../components/shared/SeverityHelper'; | |||
import SeverityChange from '../SeverityChange'; | |||
it('should render SeverityHelper', () => { | |||
const output = shallow(<SeverityChange severity="BLOCKER" />).find(SeverityHelper); | |||
expect(output.length).toBe(1); | |||
expect(output.prop('severity')).toBe('BLOCKER'); | |||
}); |
@@ -1,95 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="boxed-group boxed-group-inner js-profile-changelog" | |||
> | |||
<div | |||
className="spacer-bottom" | |||
> | |||
<ChangelogSearch | |||
dateRange={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
onDateRangeChange={[Function]} | |||
onReset={[Function]} | |||
/> | |||
<DeferredSpinner | |||
className="spacer-left" | |||
loading={false} | |||
/> | |||
</div> | |||
<Changelog | |||
events={ | |||
[ | |||
{ | |||
"action": "ACTIVATED", | |||
"date": "2019-04-23T02:12:32+0100", | |||
"params": { | |||
"severity": "MAJOR", | |||
}, | |||
"ruleKey": "rule-key", | |||
"ruleName": "rule-name", | |||
}, | |||
{ | |||
"action": "ACTIVATED", | |||
"date": "2019-04-23T02:12:32+0100", | |||
"params": { | |||
"severity": "MAJOR", | |||
}, | |||
"ruleKey": "rule-key", | |||
"ruleName": "rule-name", | |||
}, | |||
{ | |||
"action": "ACTIVATED", | |||
"date": "2019-04-23T02:12:32+0100", | |||
"params": { | |||
"severity": "MAJOR", | |||
}, | |||
"ruleKey": "rule-key", | |||
"ruleName": "rule-name", | |||
}, | |||
] | |||
} | |||
/> | |||
<footer | |||
className="text-center spacer-top small" | |||
> | |||
<a | |||
href="#" | |||
onClick={[Function]} | |||
> | |||
show_more | |||
</a> | |||
</footer> | |||
</div> | |||
`; | |||
exports[`should render correctly without events 1`] = ` | |||
<div | |||
className="boxed-group boxed-group-inner js-profile-changelog" | |||
> | |||
<div | |||
className="spacer-bottom" | |||
> | |||
<ChangelogSearch | |||
dateRange={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
onDateRangeChange={[Function]} | |||
onReset={[Function]} | |||
/> | |||
<DeferredSpinner | |||
className="spacer-left" | |||
loading={false} | |||
/> | |||
</div> | |||
<ChangelogEmpty /> | |||
</div> | |||
`; |
@@ -1,24 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render 1`] = ` | |||
<div | |||
className="display-flex-end" | |||
id="quality-profile-changelog-form" | |||
> | |||
<DateRangeInput | |||
onChange={[MockFunction]} | |||
value={ | |||
{ | |||
"from": 2016-01-01T00:00:00.000Z, | |||
"to": 2016-05-05T00:00:00.000Z, | |||
} | |||
} | |||
/> | |||
<Button | |||
className="spacer-left text-top" | |||
onClick={[MockFunction]} | |||
> | |||
reset_verb | |||
</Button> | |||
</div> | |||
`; |
@@ -1,25 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { translate } from '../../../helpers/l10n'; | |||
export default function ComparisonEmpty() { | |||
return <div className="big-spacer-top">{translate('quality_profile.empty_comparison')}</div>; | |||
} |
@@ -27,7 +27,6 @@ import SeverityIcon from '../../../components/icons/SeverityIcon'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { getRulesUrl } from '../../../helpers/urls'; | |||
import { Dict } from '../../../types/types'; | |||
import ComparisonEmpty from './ComparisonEmpty'; | |||
import ComparisonResultActivation from './ComparisonResultActivation'; | |||
type Params = Dict<string>; | |||
@@ -201,7 +200,7 @@ export default class ComparisonResults extends React.PureComponent<Props> { | |||
render() { | |||
if (!this.props.inLeft.length && !this.props.inRight.length && !this.props.modified.length) { | |||
return <ComparisonEmpty />; | |||
return <div className="big-spacer-top">{translate('quality_profile.empty_comparison')}</div>; | |||
} | |||
return ( |
@@ -25,7 +25,6 @@ import { | |||
copyProfile, | |||
createQualityProfile, | |||
deleteProfile, | |||
getQualityProfileBackupUrl, | |||
renameProfile, | |||
setDefaultProfile, | |||
} from '../../../api/quality-profiles'; | |||
@@ -57,7 +56,7 @@ interface State { | |||
openModal?: ProfileActionModals; | |||
} | |||
export class ProfileActions extends React.PureComponent<Props, State> { | |||
class ProfileActions extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { | |||
loading: false, | |||
@@ -178,12 +177,19 @@ export class ProfileActions extends React.PureComponent<Props, State> { | |||
} | |||
}; | |||
getQualityProfileBackupUrl = ({ language, name: qualityProfile }: Profile) => { | |||
const queryParams = Object.entries({ language, qualityProfile }) | |||
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`) | |||
.join('&'); | |||
return `/api/qualityprofiles/backup?${queryParams}`; | |||
}; | |||
render() { | |||
const { profile, isComparable } = this.props; | |||
const { loading, openModal } = this.state; | |||
const { actions = {} } = profile; | |||
const backupUrl = `${getBaseUrl()}${getQualityProfileBackupUrl(profile)}`; | |||
const backupUrl = `${getBaseUrl()}${this.getQualityProfileBackupUrl(profile)}`; | |||
const activateMoreUrl = getRulesUrl({ | |||
qprofile: profile.key, |
@@ -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'; |
@@ -1,31 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import BuiltInQualityProfileBadge from '../BuiltInQualityProfileBadge'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
expect(shallowRender({ tooltip: false })).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props = {}) { | |||
return shallow(<BuiltInQualityProfileBadge {...props} />); | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||
import { mockEvent } from '../../../../helpers/testUtils'; | |||
import DeleteProfileForm, { DeleteProfileFormProps } from '../DeleteProfileForm'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('default'); | |||
expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); | |||
expect(shallowRender({ profile: mockQualityProfile({ childrenCount: 2 }) })).toMatchSnapshot( | |||
'profile has children' | |||
); | |||
}); | |||
it('should correctly submit the form', () => { | |||
const onDelete = jest.fn(); | |||
const wrapper = shallowRender({ onDelete }); | |||
const formOnSubmit = wrapper.find('form').props().onSubmit; | |||
if (formOnSubmit) { | |||
formOnSubmit(mockEvent()); | |||
} | |||
expect(onDelete).toHaveBeenCalled(); | |||
}); | |||
function shallowRender(props: Partial<DeleteProfileFormProps> = {}) { | |||
return shallow<DeleteProfileFormProps>( | |||
<DeleteProfileForm | |||
loading={false} | |||
onClose={jest.fn()} | |||
onDelete={jest.fn()} | |||
profile={mockQualityProfile()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,342 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { | |||
changeProfileParent, | |||
copyProfile, | |||
createQualityProfile, | |||
deleteProfile, | |||
renameProfile, | |||
setDefaultProfile, | |||
} from '../../../../api/quality-profiles'; | |||
import { mockQualityProfile, mockRouter } from '../../../../helpers/testMocks'; | |||
import { click, waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import { queryToSearch } from '../../../../helpers/urls'; | |||
import { PROFILE_PATH } from '../../constants'; | |||
import { ProfileActionModals } from '../../types'; | |||
import DeleteProfileForm from '../DeleteProfileForm'; | |||
import { ProfileActions } from '../ProfileActions'; | |||
import ProfileModalForm from '../ProfileModalForm'; | |||
jest.mock('../../../../api/quality-profiles', () => { | |||
const { mockQualityProfile } = jest.requireActual('../../../../helpers/testMocks'); | |||
return { | |||
...jest.requireActual('../../../../api/quality-profiles'), | |||
copyProfile: jest.fn().mockResolvedValue(null), | |||
changeProfileParent: jest.fn().mockResolvedValue(null), | |||
createQualityProfile: jest | |||
.fn() | |||
.mockResolvedValue({ profile: mockQualityProfile({ key: 'newProfile' }) }), | |||
deleteProfile: jest.fn().mockResolvedValue(null), | |||
setDefaultProfile: jest.fn().mockResolvedValue(null), | |||
renameProfile: jest.fn().mockResolvedValue(null), | |||
}; | |||
}); | |||
const PROFILE = mockQualityProfile({ | |||
activeRuleCount: 68, | |||
activeDeprecatedRuleCount: 0, | |||
depth: 0, | |||
language: 'js', | |||
rulesUpdatedAt: '2017-06-28T12:58:44+0000', | |||
}); | |||
beforeEach(() => jest.clearAllMocks()); | |||
it('renders correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('no permissions'); | |||
expect(shallowRender({ profile: { ...PROFILE, actions: { edit: true } } })).toMatchSnapshot( | |||
'edit only' | |||
); | |||
expect( | |||
shallowRender({ | |||
profile: { | |||
...PROFILE, | |||
actions: { | |||
copy: true, | |||
edit: true, | |||
delete: true, | |||
setAsDefault: true, | |||
associateProjects: true, | |||
}, | |||
}, | |||
}) | |||
).toMatchSnapshot('all permissions'); | |||
expect(shallowRender().setState({ openModal: ProfileActionModals.Copy })).toMatchSnapshot( | |||
'copy modal' | |||
); | |||
expect(shallowRender().setState({ openModal: ProfileActionModals.Extend })).toMatchSnapshot( | |||
'extend modal' | |||
); | |||
expect(shallowRender().setState({ openModal: ProfileActionModals.Rename })).toMatchSnapshot( | |||
'rename modal' | |||
); | |||
expect(shallowRender().setState({ openModal: ProfileActionModals.Delete })).toMatchSnapshot( | |||
'delete modal' | |||
); | |||
}); | |||
describe('copy a profile', () => { | |||
it('should correctly copy a profile', async () => { | |||
const name = 'new-name'; | |||
const updateProfiles = jest.fn().mockResolvedValue(null); | |||
const push = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile: { ...PROFILE, actions: { copy: true } }, | |||
router: mockRouter({ push }), | |||
updateProfiles, | |||
}); | |||
click(wrapper.find('.it__quality-profiles__copy')); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(true); | |||
wrapper.find(ProfileModalForm).props().onSubmit(name); | |||
expect(copyProfile).toHaveBeenCalledWith(PROFILE.key, name); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).toHaveBeenCalled(); | |||
expect(push).toHaveBeenCalledWith({ | |||
pathname: '/profiles/show', | |||
search: queryToSearch({ name, language: 'js' }), | |||
}); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(false); | |||
}); | |||
it('should correctly keep the modal open in case of an error', async () => { | |||
(copyProfile as jest.Mock).mockRejectedValueOnce(null); | |||
const name = 'new-name'; | |||
const updateProfiles = jest.fn(); | |||
const push = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile: { ...PROFILE, actions: { copy: true } }, | |||
router: mockRouter({ push }), | |||
updateProfiles, | |||
}); | |||
wrapper.setState({ openModal: ProfileActionModals.Copy }); | |||
wrapper.instance().handleProfileCopy(name); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).not.toHaveBeenCalled(); | |||
await waitAndUpdate(wrapper); | |||
expect(push).not.toHaveBeenCalled(); | |||
expect(wrapper.state().openModal).toBe(ProfileActionModals.Copy); | |||
}); | |||
}); | |||
describe('extend a profile', () => { | |||
it('should correctly extend a profile', async () => { | |||
const name = 'new-name'; | |||
const profile = { ...PROFILE, actions: { copy: true } }; | |||
const updateProfiles = jest.fn().mockResolvedValue(null); | |||
const push = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile, | |||
router: mockRouter({ push }), | |||
updateProfiles, | |||
}); | |||
click(wrapper.find('.it__quality-profiles__extend')); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(true); | |||
wrapper.find(ProfileModalForm).props().onSubmit(name); | |||
expect(createQualityProfile).toHaveBeenCalledWith({ language: profile.language, name }); | |||
await waitAndUpdate(wrapper); | |||
expect(changeProfileParent).toHaveBeenCalledWith( | |||
expect.objectContaining({ | |||
key: 'newProfile', | |||
}), | |||
profile | |||
); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).toHaveBeenCalled(); | |||
await waitAndUpdate(wrapper); | |||
expect(push).toHaveBeenCalledWith({ | |||
pathname: '/profiles/show', | |||
search: queryToSearch({ name, language: 'js' }), | |||
}); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(false); | |||
}); | |||
it('should correctly keep the modal open in case of an error', async () => { | |||
(createQualityProfile as jest.Mock).mockRejectedValueOnce(null); | |||
const name = 'new-name'; | |||
const updateProfiles = jest.fn(); | |||
const push = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile: { ...PROFILE, actions: { copy: true } }, | |||
router: mockRouter({ push }), | |||
updateProfiles, | |||
}); | |||
wrapper.setState({ openModal: ProfileActionModals.Extend }); | |||
wrapper.instance().handleProfileExtend(name); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).not.toHaveBeenCalled(); | |||
expect(changeProfileParent).not.toHaveBeenCalled(); | |||
expect(push).not.toHaveBeenCalled(); | |||
expect(wrapper.state().openModal).toBe(ProfileActionModals.Extend); | |||
}); | |||
}); | |||
describe('rename a profile', () => { | |||
it('should correctly rename a profile', async () => { | |||
const name = 'new-name'; | |||
const updateProfiles = jest.fn().mockResolvedValue(null); | |||
const push = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile: { ...PROFILE, actions: { edit: true } }, | |||
router: mockRouter({ push }), | |||
updateProfiles, | |||
}); | |||
click(wrapper.find('.it__quality-profiles__rename')); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(true); | |||
wrapper.find(ProfileModalForm).props().onSubmit(name); | |||
expect(renameProfile).toHaveBeenCalledWith(PROFILE.key, name); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).toHaveBeenCalled(); | |||
expect(push).toHaveBeenCalledWith({ | |||
pathname: '/profiles/show', | |||
search: queryToSearch({ name, language: 'js' }), | |||
}); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(false); | |||
}); | |||
it('should correctly keep the modal open in case of an error', async () => { | |||
(renameProfile as jest.Mock).mockRejectedValueOnce(null); | |||
const name = 'new-name'; | |||
const updateProfiles = jest.fn(); | |||
const push = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile: { ...PROFILE, actions: { copy: true } }, | |||
router: mockRouter({ push }), | |||
updateProfiles, | |||
}); | |||
wrapper.setState({ openModal: ProfileActionModals.Rename }); | |||
wrapper.instance().handleProfileRename(name); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).not.toHaveBeenCalled(); | |||
await waitAndUpdate(wrapper); | |||
expect(push).not.toHaveBeenCalled(); | |||
expect(wrapper.state().openModal).toBe(ProfileActionModals.Rename); | |||
}); | |||
}); | |||
describe('delete a profile', () => { | |||
it('should correctly delete a profile', async () => { | |||
const updateProfiles = jest.fn().mockResolvedValue(null); | |||
const replace = jest.fn(); | |||
const profile = { ...PROFILE, actions: { delete: true } }; | |||
const wrapper = shallowRender({ | |||
profile, | |||
router: mockRouter({ replace }), | |||
updateProfiles, | |||
}); | |||
click(wrapper.find('.it__quality-profiles__delete')); | |||
expect(wrapper.find(DeleteProfileForm).exists()).toBe(true); | |||
wrapper.find(DeleteProfileForm).props().onDelete(); | |||
expect(deleteProfile).toHaveBeenCalledWith(profile); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).toHaveBeenCalled(); | |||
expect(replace).toHaveBeenCalledWith(PROFILE_PATH); | |||
expect(wrapper.find(ProfileModalForm).exists()).toBe(false); | |||
}); | |||
it('should correctly keep the modal open in case of an error', async () => { | |||
(deleteProfile as jest.Mock).mockRejectedValueOnce(null); | |||
const updateProfiles = jest.fn(); | |||
const replace = jest.fn(); | |||
const wrapper = shallowRender({ | |||
profile: { ...PROFILE, actions: { copy: true } }, | |||
router: mockRouter({ replace }), | |||
updateProfiles, | |||
}); | |||
wrapper.setState({ openModal: ProfileActionModals.Delete }); | |||
wrapper.instance().handleProfileDelete(); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).not.toHaveBeenCalled(); | |||
await waitAndUpdate(wrapper); | |||
expect(replace).not.toHaveBeenCalled(); | |||
expect(wrapper.state().openModal).toBe(ProfileActionModals.Delete); | |||
}); | |||
}); | |||
it('should correctly set a profile as the default', async () => { | |||
const updateProfiles = jest.fn(); | |||
const wrapper = shallowRender({ updateProfiles }); | |||
wrapper.instance().handleSetDefaultClick(); | |||
await waitAndUpdate(wrapper); | |||
expect(setDefaultProfile).toHaveBeenCalledWith(PROFILE); | |||
expect(updateProfiles).toHaveBeenCalled(); | |||
}); | |||
it('should not allow to set a profile as the default if the profile has no active rules', async () => { | |||
const profile = mockQualityProfile({ | |||
activeRuleCount: 0, | |||
actions: { | |||
setAsDefault: true, | |||
}, | |||
}); | |||
const wrapper = shallowRender({ profile }); | |||
wrapper.instance().handleSetDefaultClick(); | |||
await waitAndUpdate(wrapper); | |||
expect(setDefaultProfile).not.toHaveBeenCalled(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props: Partial<ProfileActions['props']> = {}) { | |||
const router = mockRouter(); | |||
return shallow<ProfileActions>( | |||
<ProfileActions | |||
isComparable | |||
profile={PROFILE} | |||
router={router} | |||
updateProfiles={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,94 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { Actions, getExporters, searchQualityProfiles } from '../../../../api/quality-profiles'; | |||
import { | |||
mockLanguage, | |||
mockQualityProfile, | |||
mockQualityProfileExporter, | |||
} from '../../../../helpers/testMocks'; | |||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import { QualityProfilesApp } from '../QualityProfilesApp'; | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
getExporters: jest.fn().mockResolvedValue([]), | |||
searchQualityProfiles: jest.fn().mockResolvedValue({ profiles: [] }), | |||
})); | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot('loading'); | |||
expect(getExporters).toHaveBeenCalled(); | |||
expect(searchQualityProfiles).toHaveBeenCalled(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot('full'); | |||
}); | |||
it('should render child with additional props', () => { | |||
const language = mockLanguage(); | |||
const wrapper = shallowRender({ languages: { [language.key]: language } }); | |||
const actions: Actions = { create: true }; | |||
const profiles = [mockQualityProfile()]; | |||
const exporters = [mockQualityProfileExporter()]; | |||
wrapper.setState({ loading: false, actions, profiles, exporters }); | |||
expect(wrapper.childAt(2).props()).toEqual({ | |||
context: { | |||
actions, | |||
profiles, | |||
languages: [language], | |||
exporters, | |||
updateProfiles: wrapper.instance().updateProfiles, | |||
}, | |||
}); | |||
}); | |||
it('should handle update', async () => { | |||
const profile1 = mockQualityProfile({ key: 'qp1', name: 'An amazing profile' }); | |||
const profile2 = mockQualityProfile({ key: 'qp2', name: 'Quality Profile' }); | |||
// Mock one call for the initial load, one for the update | |||
(searchQualityProfiles as jest.Mock) | |||
.mockResolvedValueOnce({ profiles: [] }) | |||
.mockResolvedValueOnce({ profiles: [profile2, profile1] }); | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().profiles).toHaveLength(0); | |||
wrapper.instance().updateProfiles(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().profiles).toEqual([profile1, profile2]); | |||
}); | |||
function shallowRender(props: Partial<QualityProfilesApp['props']> = {}) { | |||
return shallow<QualityProfilesApp>( | |||
<QualityProfilesApp languages={{}} {...props}> | |||
<div /> | |||
</QualityProfilesApp> | |||
); | |||
} |
@@ -1,21 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<Tooltip | |||
overlay="quality_profiles.built_in.description" | |||
> | |||
<div | |||
className="badge badge-info" | |||
> | |||
quality_profiles.built_in | |||
</div> | |||
</Tooltip> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<div | |||
className="badge badge-info" | |||
> | |||
quality_profiles.built_in | |||
</div> | |||
`; |
@@ -1,134 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: default 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.delete_confirm_title" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.delete_confirm_title | |||
</h2> | |||
</div> | |||
<div | |||
className="modal-body" | |||
> | |||
<p> | |||
quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript | |||
</p> | |||
</div> | |||
<div | |||
className="modal-foot" | |||
> | |||
<SubmitButton | |||
className="button-red" | |||
disabled={false} | |||
> | |||
delete | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</div> | |||
</form> | |||
</Modal> | |||
`; | |||
exports[`should render correctly: loading 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.delete_confirm_title" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.delete_confirm_title | |||
</h2> | |||
</div> | |||
<div | |||
className="modal-body" | |||
> | |||
<p> | |||
quality_profiles.are_you_sure_want_delete_profile_x.name.JavaScript | |||
</p> | |||
</div> | |||
<div | |||
className="modal-foot" | |||
> | |||
<i | |||
className="spinner spacer-right" | |||
/> | |||
<SubmitButton | |||
className="button-red" | |||
disabled={true} | |||
> | |||
delete | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</div> | |||
</form> | |||
</Modal> | |||
`; | |||
exports[`should render correctly: profile has children 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.delete_confirm_title" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.delete_confirm_title | |||
</h2> | |||
</div> | |||
<div | |||
className="modal-body" | |||
> | |||
<div> | |||
<Alert | |||
variant="warning" | |||
> | |||
quality_profiles.this_profile_has_descendants | |||
</Alert> | |||
<p> | |||
quality_profiles.are_you_sure_want_delete_profile_x_and_descendants.name.JavaScript | |||
</p> | |||
</div> | |||
</div> | |||
<div | |||
className="modal-foot" | |||
> | |||
<SubmitButton | |||
className="button-red" | |||
disabled={false} | |||
> | |||
delete | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</div> | |||
</form> | |||
</Modal> | |||
`; |
@@ -1,392 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders correctly: all permissions 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__activate-more-rules" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=key&activation=false", | |||
} | |||
} | |||
> | |||
quality_profiles.activate_more_rules | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__extend" | |||
onClick={[Function]} | |||
tooltipOverlay="quality_profiles.extend_help.name" | |||
tooltipPlacement="left" | |||
> | |||
extend | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__copy" | |||
onClick={[Function]} | |||
tooltipOverlay="quality_profiles.copy_help.name" | |||
tooltipPlacement="left" | |||
> | |||
copy | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__rename" | |||
onClick={[Function]} | |||
> | |||
rename | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__set-as-default" | |||
onClick={[Function]} | |||
> | |||
set_as_default | |||
</ActionsDropdownItem> | |||
<ActionsDropdownDivider /> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__delete" | |||
destructive={true} | |||
onClick={[Function]} | |||
> | |||
delete | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</Fragment> | |||
`; | |||
exports[`renders correctly: copy modal 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
<ProfileModalForm | |||
action="COPY" | |||
loading={false} | |||
onClose={[Function]} | |||
onSubmit={[Function]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 0, | |||
"activeRuleCount": 68, | |||
"childrenCount": 0, | |||
"depth": 0, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
"rulesUpdatedAt": "2017-06-28T12:58:44+0000", | |||
} | |||
} | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`renders correctly: delete modal 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
<DeleteProfileForm | |||
loading={false} | |||
onClose={[Function]} | |||
onDelete={[Function]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 0, | |||
"activeRuleCount": 68, | |||
"childrenCount": 0, | |||
"depth": 0, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
"rulesUpdatedAt": "2017-06-28T12:58:44+0000", | |||
} | |||
} | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`renders correctly: edit only 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__activate-more-rules" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=key&activation=false", | |||
} | |||
} | |||
> | |||
quality_profiles.activate_more_rules | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__rename" | |||
onClick={[Function]} | |||
> | |||
rename | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</Fragment> | |||
`; | |||
exports[`renders correctly: extend modal 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
<ProfileModalForm | |||
action="EXTEND" | |||
loading={false} | |||
onClose={[Function]} | |||
onSubmit={[Function]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 0, | |||
"activeRuleCount": 68, | |||
"childrenCount": 0, | |||
"depth": 0, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
"rulesUpdatedAt": "2017-06-28T12:58:44+0000", | |||
} | |||
} | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`renders correctly: no permissions 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
</Fragment> | |||
`; | |||
exports[`renders correctly: rename modal 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
</ActionsDropdown> | |||
<ProfileModalForm | |||
action="RENAME" | |||
loading={false} | |||
onClose={[Function]} | |||
onSubmit={[Function]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 0, | |||
"activeRuleCount": 68, | |||
"childrenCount": 0, | |||
"depth": 0, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
"rulesUpdatedAt": "2017-06-28T12:58:44+0000", | |||
} | |||
} | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`should not allow to set a profile as the default if the profile has no active rules 1`] = ` | |||
<Fragment> | |||
<ActionsDropdown | |||
disabled={false} | |||
label="quality_profiles.actions.name.JavaScript" | |||
> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__backup" | |||
download="key.xml" | |||
to="/api/qualityprofiles/backup?language=js&qualityProfile=name" | |||
> | |||
backup_verb | |||
</ActionsDropdownItem> | |||
<ActionsDropdownItem | |||
className="it__quality-profiles__compare" | |||
to={ | |||
{ | |||
"pathname": "/profiles/compare", | |||
"search": "?language=js&name=name", | |||
} | |||
} | |||
> | |||
compare | |||
</ActionsDropdownItem> | |||
<li> | |||
<Tooltip | |||
overlay="quality_profiles.cannot_set_default_no_rules" | |||
placement="left" | |||
> | |||
<span | |||
className="it__quality-profiles__set-as-default text-muted-2" | |||
> | |||
set_as_default | |||
</span> | |||
</Tooltip> | |||
</li> | |||
</ActionsDropdown> | |||
</Fragment> | |||
`; |
@@ -1,47 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: full 1`] = ` | |||
<div | |||
className="page page-limited" | |||
> | |||
<Suggestions | |||
suggestions="quality_profiles" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
prioritizeSeoTags={false} | |||
title="quality_profiles.page" | |||
/> | |||
<Outlet | |||
context={ | |||
{ | |||
"actions": {}, | |||
"exporters": [], | |||
"languages": [], | |||
"profiles": [], | |||
"updateProfiles": [Function], | |||
} | |||
} | |||
/> | |||
</div> | |||
`; | |||
exports[`should render correctly: loading 1`] = ` | |||
<div | |||
className="page page-limited" | |||
> | |||
<Suggestions | |||
suggestions="quality_profiles" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
prioritizeSeoTags={false} | |||
title="quality_profiles.page" | |||
/> | |||
<i | |||
className="spinner" | |||
/> | |||
</div> | |||
`; |
@@ -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'; |
@@ -28,24 +28,23 @@ import ProfilePermissions from './ProfilePermissions'; | |||
import ProfileProjects from './ProfileProjects'; | |||
import ProfileRules from './ProfileRules'; | |||
export interface ProfileDetailsProps { | |||
interface ProfileDetailsProps { | |||
exporters: Exporter[]; | |||
profile: Profile; | |||
profiles: Profile[]; | |||
updateProfiles: () => Promise<void>; | |||
} | |||
export function ProfileDetails(props: ProfileDetailsProps) { | |||
const { profile } = props; | |||
function ProfileDetails(props: ProfileDetailsProps) { | |||
const { profile, profiles, exporters } = props; | |||
return ( | |||
<div> | |||
<div className="quality-profile-grid"> | |||
<div className="quality-profile-grid-left"> | |||
<ProfileRules profile={profile} /> | |||
<ProfileExporters exporters={props.exporters} profile={profile} /> | |||
{profile.actions && profile.actions.edit && !profile.isBuiltIn && ( | |||
<ProfilePermissions profile={profile} /> | |||
)} | |||
<ProfileExporters exporters={exporters} profile={profile} /> | |||
{profile.actions?.edit && !profile.isBuiltIn && <ProfilePermissions profile={profile} />} | |||
</div> | |||
<div className="quality-profile-grid-right"> | |||
{profile.activeRuleCount === 0 && (profile.projectCount || profile.isDefault) && ( | |||
@@ -61,7 +60,7 @@ export function ProfileDetails(props: ProfileDetailsProps) { | |||
<ProfileInheritance | |||
profile={profile} | |||
profiles={props.profiles} | |||
profiles={profiles} | |||
updateProfiles={props.updateProfiles} | |||
/> | |||
<ProfileProjects profile={profile} /> |
@@ -37,7 +37,10 @@ export default function ProfileExporters({ exporters, profile }: Props) { | |||
} | |||
return ( | |||
<div className="boxed-group quality-profile-exporters"> | |||
<section | |||
aria-label={translate('quality_profiles.exporters')} | |||
className="boxed-group quality-profile-exporters" | |||
> | |||
<h2>{translate('quality_profiles.exporters')}</h2> | |||
<div className="boxed-group-inner"> | |||
<Alert className="big-spacer-bottom" variant="warning"> | |||
@@ -57,6 +60,6 @@ export default function ProfileExporters({ exporters, profile }: Props) { | |||
))} | |||
</ul> | |||
</div> | |||
</div> | |||
</section> | |||
); | |||
} |
@@ -118,7 +118,10 @@ export default class ProfileInheritance extends React.PureComponent<Props, State | |||
const extendsBuiltIn = ancestors != null && ancestors.some((profile) => profile.isBuiltIn); | |||
return ( | |||
<div className="boxed-group quality-profile-inheritance"> | |||
<section | |||
aria-label={translate('quality_profiles.profile_inheritance')} | |||
className="boxed-group quality-profile-inheritance" | |||
> | |||
{profile.actions && profile.actions.edit && !profile.isBuiltIn && ( | |||
<div className="boxed-group-actions"> | |||
<Button className="pull-right js-change-parent" onClick={this.handleChangeParentClick}> | |||
@@ -184,7 +187,7 @@ export default class ProfileInheritance extends React.PureComponent<Props, State | |||
profiles={profiles.filter((p) => p !== profile && p.language === profile.language)} | |||
/> | |||
)} | |||
</div> | |||
</section> | |||
); | |||
} | |||
} |
@@ -20,9 +20,9 @@ | |||
import { sortBy, uniqBy } from 'lodash'; | |||
import * as React from 'react'; | |||
import { | |||
SearchUsersGroupsParameters, | |||
searchGroups, | |||
searchUsers, | |||
SearchUsersGroupsParameters, | |||
} from '../../../api/quality-profiles'; | |||
import { Button } from '../../../components/controls/buttons'; | |||
import { translate } from '../../../helpers/l10n'; | |||
@@ -138,7 +138,7 @@ export default class ProfilePermissions extends React.PureComponent<Props, State | |||
render() { | |||
return ( | |||
<div className="boxed-group"> | |||
<section aria-label={translate('permissions.page')} className="boxed-group"> | |||
<h2>{translate('permissions.page')}</h2> | |||
<div className="boxed-group-inner"> | |||
<p className="note">{translate('quality_profiles.default_permissions')}</p> | |||
@@ -184,7 +184,7 @@ export default class ProfilePermissions extends React.PureComponent<Props, State | |||
profile={this.props.profile} | |||
/> | |||
)} | |||
</div> | |||
</section> | |||
); | |||
} | |||
} |
@@ -20,10 +20,10 @@ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { removeGroup } from '../../../api/quality-profiles'; | |||
import { Button, DeleteButton, ResetButtonLink } from '../../../components/controls/buttons'; | |||
import SimpleModal, { ChildrenProps } from '../../../components/controls/SimpleModal'; | |||
import { Button, DeleteButton, ResetButtonLink } from '../../../components/controls/buttons'; | |||
import GroupIcon from '../../../components/icons/GroupIcon'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { Group } from './ProfilePermissions'; | |||
interface Props { | |||
@@ -103,6 +103,10 @@ export default class ProfilePermissionsGroup extends React.PureComponent<Props, | |||
return ( | |||
<div className="clearfix big-spacer-bottom"> | |||
<DeleteButton | |||
aria-label={translateWithParameters( | |||
'quality_profiles.permissions.remove.group_x', | |||
group.name | |||
)} | |||
className="pull-right spacer-top spacer-left spacer-right button-small" | |||
onClick={this.handleDeleteClick} | |||
/> |
@@ -23,7 +23,7 @@ import { removeUser } from '../../../api/quality-profiles'; | |||
import SimpleModal, { ChildrenProps } from '../../../components/controls/SimpleModal'; | |||
import { DeleteButton, ResetButtonLink, SubmitButton } from '../../../components/controls/buttons'; | |||
import LegacyAvatar from '../../../components/ui/LegacyAvatar'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { UserSelected } from '../../../types/types'; | |||
interface Props { | |||
@@ -107,6 +107,10 @@ export default class ProfilePermissionsUser extends React.PureComponent<Props, S | |||
return ( | |||
<div className="clearfix big-spacer-bottom"> | |||
<DeleteButton | |||
aria-label={translateWithParameters( | |||
'quality_profiles.permissions.remove.user_x', | |||
user.name | |||
)} | |||
className="pull-right spacer-top spacer-left spacer-right button-small" | |||
onClick={this.handleDeleteClick} | |||
/> |
@@ -169,7 +169,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { | |||
const { profile } = this.props; | |||
const hasNoActiveRules = profile.activeRuleCount === 0; | |||
return ( | |||
<div className="boxed-group quality-profile-projects"> | |||
<section className="boxed-group quality-profile-projects" aria-label={translate('projects')}> | |||
{profile.actions && profile.actions.associateProjects && ( | |||
<div className="boxed-group-actions"> | |||
<Tooltip | |||
@@ -199,7 +199,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { | |||
</div> | |||
{this.state.formOpen && <ChangeProjectsForm onClose={this.closeForm} profile={profile} />} | |||
</div> | |||
</section> | |||
); | |||
} | |||
} |
@@ -20,12 +20,13 @@ | |||
import { keyBy } from 'lodash'; | |||
import * as React from 'react'; | |||
import { getQualityProfile } from '../../../api/quality-profiles'; | |||
import { searchRules, takeFacet } from '../../../api/rules'; | |||
import { searchRules } from '../../../api/rules'; | |||
import Link from '../../../components/common/Link'; | |||
import Tooltip from '../../../components/controls/Tooltip'; | |||
import { Button } from '../../../components/controls/buttons'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { getRulesUrl } from '../../../helpers/urls'; | |||
import { SearchRulesResponse } from '../../../types/coding-rules'; | |||
import { Dict } from '../../../types/types'; | |||
import { Profile } from '../types'; | |||
import ProfileRulesDeprecatedWarning from './ProfileRulesDeprecatedWarning'; | |||
@@ -118,8 +119,8 @@ export default class ProfileRules extends React.PureComponent<Props, State> { | |||
const [allRules, activatedRules, showProfile] = responses; | |||
this.setState({ | |||
activatedTotal: activatedRules.paging.total, | |||
allByType: keyBy<ByType>(takeFacet(allRules, 'types'), 'val'), | |||
activatedByType: keyBy<ByType>(takeFacet(activatedRules, 'types'), 'val'), | |||
allByType: keyBy<ByType>(this.takeFacet(allRules, 'types'), 'val'), | |||
activatedByType: keyBy<ByType>(this.takeFacet(activatedRules, 'types'), 'val'), | |||
compareToSonarWay: showProfile && showProfile.compareToSonarWay, | |||
total: allRules.paging.total, | |||
}); | |||
@@ -140,6 +141,11 @@ export default class ProfileRules extends React.PureComponent<Props, State> { | |||
: null; | |||
} | |||
takeFacet(response: SearchRulesResponse, property: string) { | |||
const facet = response.facets?.find((f) => f.property === property); | |||
return facet ? facet.values : []; | |||
} | |||
render() { | |||
const { profile } = this.props; | |||
const { compareToSonarWay } = this.state; | |||
@@ -147,7 +153,7 @@ export default class ProfileRules extends React.PureComponent<Props, State> { | |||
const { actions = {} } = profile; | |||
return ( | |||
<div className="boxed-group quality-profile-rules"> | |||
<section aria-label={translate('rules')} className="boxed-group quality-profile-rules"> | |||
<div className="quality-profile-rules-distribution"> | |||
<table className="data condensed"> | |||
<thead> | |||
@@ -212,7 +218,7 @@ export default class ProfileRules extends React.PureComponent<Props, State> { | |||
sonarway={compareToSonarWay.profile} | |||
/> | |||
)} | |||
</div> | |||
</section> | |||
); | |||
} | |||
} |
@@ -1,69 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||
import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import ChangeParentForm from '../ChangeParentForm'; | |||
beforeEach(() => jest.clearAllMocks()); | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
changeProfileParent: jest.fn().mockResolvedValue({}), | |||
})); | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should handle form submit correcty', async () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ onChange }); | |||
wrapper.instance().handleFormSubmit(mockEvent()); | |||
await waitAndUpdate(wrapper); | |||
expect(onChange).toHaveBeenCalled(); | |||
}); | |||
it('should handle select change correcty', async () => { | |||
const wrapper = shallowRender(); | |||
wrapper.instance().handleSelectChange({ value: 'val' }); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.instance().state.selected).toEqual('val'); | |||
}); | |||
function shallowRender(props?: Partial<ChangeParentForm['props']>) { | |||
return shallow<ChangeParentForm>( | |||
<ChangeParentForm | |||
onChange={jest.fn()} | |||
onClose={jest.fn()} | |||
profile={mockQualityProfile()} | |||
profiles={[ | |||
mockQualityProfile(), | |||
mockQualityProfile(), | |||
mockQualityProfile(), | |||
mockQualityProfile(), | |||
]} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,102 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { | |||
associateProject, | |||
dissociateProject, | |||
getProfileProjects, | |||
} from '../../../../api/quality-profiles'; | |||
import SelectList, { SelectListFilter } from '../../../../components/controls/SelectList'; | |||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import ChangeProjectsForm from '../ChangeProjectsForm'; | |||
const profile: any = { key: 'profFile_key' }; | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
getProfileProjects: jest.fn().mockResolvedValue({ | |||
paging: { pageIndex: 1, pageSize: 3, total: 55 }, | |||
results: [ | |||
{ id: 'test1', key: 'test1', name: 'test1', selected: false }, | |||
{ id: 'test2', key: 'test2', name: 'test2', selected: false }, | |||
{ id: 'test3', key: 'test3', name: 'test3', selected: true }, | |||
], | |||
}), | |||
associateProject: jest.fn().mockResolvedValue({}), | |||
dissociateProject: jest.fn().mockResolvedValue({}), | |||
})); | |||
beforeEach(() => { | |||
jest.clearAllMocks(); | |||
}); | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
wrapper.find(SelectList).props().onSearch({ | |||
query: '', | |||
filter: SelectListFilter.Selected, | |||
page: 1, | |||
pageSize: 100, | |||
}); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.instance().mounted).toBe(true); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(wrapper.instance().renderElement('test1')).toMatchSnapshot(); | |||
expect(wrapper.instance().renderElement('test_foo')).toMatchSnapshot(); | |||
expect(getProfileProjects).toHaveBeenCalledWith( | |||
expect.objectContaining({ | |||
key: profile.key, | |||
p: 1, | |||
ps: 100, | |||
q: undefined, | |||
selected: SelectListFilter.Selected, | |||
}) | |||
); | |||
expect(wrapper.state().needToReload).toBe(false); | |||
wrapper.instance().componentWillUnmount(); | |||
expect(wrapper.instance().mounted).toBe(false); | |||
}); | |||
it('should handle selection properly', async () => { | |||
const wrapper = shallowRender(); | |||
wrapper.instance().handleSelect('toto'); | |||
await waitAndUpdate(wrapper); | |||
expect(associateProject).toHaveBeenCalledWith(profile, 'toto'); | |||
expect(wrapper.state().needToReload).toBe(true); | |||
}); | |||
it('should handle deselection properly', async () => { | |||
const wrapper = shallowRender(); | |||
wrapper.instance().handleUnselect('tata'); | |||
await waitAndUpdate(wrapper); | |||
expect(dissociateProject).toHaveBeenCalledWith(profile, 'tata'); | |||
expect(wrapper.state().needToReload).toBe(true); | |||
}); | |||
function shallowRender(props: Partial<ChangeProjectsForm['props']> = {}) { | |||
return shallow<ChangeProjectsForm>( | |||
<ChangeProjectsForm onClose={jest.fn()} profile={profile} {...props} /> | |||
); | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||
import { ProfileDetails, ProfileDetailsProps } from '../ProfileDetails'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('default'); | |||
expect( | |||
shallowRender({ profile: mockQualityProfile({ actions: { edit: true } }) }) | |||
).toMatchSnapshot('edit permissions'); | |||
expect( | |||
shallowRender({ | |||
profile: mockQualityProfile({ activeRuleCount: 0, projectCount: 0 }), | |||
}) | |||
).toMatchSnapshot('no active rules (same as default)'); | |||
expect( | |||
shallowRender({ | |||
profile: mockQualityProfile({ projectCount: 0, isDefault: true, activeRuleCount: 0 }), | |||
}) | |||
).toMatchSnapshot('is default profile, no active rules'); | |||
expect( | |||
shallowRender({ profile: mockQualityProfile({ projectCount: 10, activeRuleCount: 0 }) }) | |||
).toMatchSnapshot('projects associated, no active rules'); | |||
}); | |||
function shallowRender(props: Partial<ProfileDetailsProps> = {}) { | |||
return shallow<ProfileDetailsProps>( | |||
<ProfileDetails | |||
exporters={[]} | |||
profile={mockQualityProfile()} | |||
profiles={[]} | |||
updateProfiles={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,40 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfile, mockQualityProfileExporter } from '../../../../helpers/testMocks'; | |||
import { FCProps } from '../../../../types/misc'; | |||
import ProfileExporters from '../ProfileExporters'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props: Partial<FCProps<typeof ProfileExporters>> = {}) { | |||
const profile = mockQualityProfile(); | |||
return shallow( | |||
<ProfileExporters | |||
exporters={[mockQualityProfileExporter({ languages: [profile.language] })]} | |||
profile={profile} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,69 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfile, mockQualityProfileInheritance } from '../../../../helpers/testMocks'; | |||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import ProfileInheritance from '../ProfileInheritance'; | |||
beforeEach(() => jest.clearAllMocks()); | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
getProfileInheritance: jest.fn().mockResolvedValue({ | |||
children: [mockQualityProfileInheritance()], | |||
ancestors: [mockQualityProfileInheritance()], | |||
}), | |||
})); | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render modal correctly', async () => { | |||
const wrapper = shallowRender(); | |||
wrapper.instance().handleChangeParentClick(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should handle parent change correctly', async () => { | |||
const updateProfiles = jest.fn().mockResolvedValueOnce({}); | |||
const wrapper = shallowRender({ updateProfiles }); | |||
wrapper.instance().handleParentChange(); | |||
await waitAndUpdate(wrapper); | |||
expect(updateProfiles).toHaveBeenCalled(); | |||
}); | |||
function shallowRender(props: Partial<ProfileInheritance['props']> = {}) { | |||
return shallow<ProfileInheritance>( | |||
<ProfileInheritance | |||
profile={mockQualityProfile()} | |||
profiles={[mockQualityProfile()]} | |||
updateProfiles={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,49 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfileInheritance } from '../../../../helpers/testMocks'; | |||
import ProfileInheritanceBox from '../ProfileInheritanceBox'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
expect( | |||
shallowRender({ | |||
depth: 3, | |||
displayLink: true, | |||
profile: mockQualityProfileInheritance({ isBuiltIn: true }), | |||
}) | |||
).toMatchSnapshot(); | |||
expect(shallowRender({ extendsBuiltIn: true })).toMatchSnapshot(); | |||
expect( | |||
shallowRender({ profile: mockQualityProfileInheritance({ overridingRuleCount: 10 }) }) | |||
).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props = {}) { | |||
return shallow( | |||
<ProfileInheritanceBox | |||
depth={1} | |||
language="foo" | |||
profile={mockQualityProfileInheritance()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,119 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { mount, shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { searchGroups, searchUsers } from '../../../../api/quality-profiles'; | |||
import { click, waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import ProfilePermissions from '../ProfilePermissions'; | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
searchUsers: jest.fn(() => Promise.resolve([])), | |||
searchGroups: jest.fn(() => Promise.resolve([])), | |||
})); | |||
beforeEach(() => { | |||
jest.clearAllMocks(); | |||
}); | |||
it('renders', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
wrapper.setState({ | |||
groups: [{ name: 'Lambda' }], | |||
loading: false, | |||
users: [{ login: 'luke', name: 'Luke Skywalker', selected: false }], | |||
}); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should update correctly', () => { | |||
const wrapper = shallowRender(); | |||
wrapper.setProps({ profile: { key: 'otherKey', name: 'new profile', language: 'js' } }); | |||
expect(searchGroups).toHaveBeenCalledTimes(2); | |||
expect(searchUsers).toHaveBeenCalledTimes(2); | |||
}); | |||
it('opens add users form', async () => { | |||
(searchUsers as jest.Mock).mockImplementationOnce(() => | |||
Promise.resolve({ users: [{ login: 'luke', name: 'Luke Skywalker' }] }) | |||
); | |||
const wrapper = shallowRender(); | |||
expect(searchUsers).toHaveBeenCalled(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.find('ProfilePermissionsForm').exists()).toBe(false); | |||
click(wrapper.find('Button')); | |||
expect(wrapper.find('ProfilePermissionsForm').exists()).toBe(true); | |||
wrapper.find('ProfilePermissionsForm').prop<Function>('onClose')(); | |||
wrapper.update(); | |||
expect(wrapper.find('ProfilePermissionsForm').exists()).toBe(false); | |||
}); | |||
it('removes user', () => { | |||
const wrapper = shallowRender(); | |||
(wrapper.instance() as ProfilePermissions).mounted = true; | |||
const joda = { login: 'joda', name: 'Joda', selected: false }; | |||
wrapper.setState({ | |||
loading: false, | |||
users: [{ login: 'luke', name: 'Luke Skywalker', selected: false }, joda], | |||
}); | |||
expect(wrapper.find('ProfilePermissionsUser')).toHaveLength(2); | |||
wrapper.find('ProfilePermissionsUser').first().prop<Function>('onDelete')(joda); | |||
wrapper.update(); | |||
expect(wrapper.find('ProfilePermissionsUser')).toHaveLength(1); | |||
}); | |||
it('removes group', () => { | |||
const wrapper = shallowRender(); | |||
(wrapper.instance() as ProfilePermissions).mounted = true; | |||
const lambda = { name: 'Lambda' }; | |||
wrapper.setState({ loading: false, groups: [{ name: 'Atlas' }, lambda] }); | |||
expect(wrapper.find('ProfilePermissionsGroup')).toHaveLength(2); | |||
wrapper.find('ProfilePermissionsGroup').first().prop<Function>('onDelete')(lambda); | |||
wrapper.update(); | |||
expect(wrapper.find('ProfilePermissionsGroup')).toHaveLength(1); | |||
}); | |||
it('fetches users and groups on mount', () => { | |||
mount(<ProfilePermissions profile={{ key: 'sonar-way', name: 'Sonar way', language: 'js' }} />); | |||
expect(searchUsers).toHaveBeenCalledWith({ | |||
language: 'js', | |||
qualityProfile: 'Sonar way', | |||
selected: 'selected', | |||
}); | |||
expect(searchGroups).toHaveBeenCalledWith({ | |||
language: 'js', | |||
qualityProfile: 'Sonar way', | |||
selected: 'selected', | |||
}); | |||
}); | |||
function shallowRender(overrides: Partial<{ key: string; name: string; language: string }> = {}) { | |||
const profile = { key: 'sonar-way', name: 'Sonar way', language: 'js', ...overrides }; | |||
return shallow<ProfilePermissions>(<ProfilePermissions profile={profile} />); | |||
} |
@@ -1,96 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { addGroup, addUser } from '../../../../api/quality-profiles'; | |||
import { mockGroup, mockUser } from '../../../../helpers/testMocks'; | |||
import { submit, waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import { UserSelected } from '../../../../types/types'; | |||
import ProfilePermissionsForm from '../ProfilePermissionsForm'; | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
addUser: jest.fn().mockResolvedValue(null), | |||
addGroup: jest.fn().mockResolvedValue(null), | |||
searchGroups: jest.fn().mockResolvedValue({ groups: [] }), | |||
searchUsers: jest.fn().mockResolvedValue({ users: [] }), | |||
})); | |||
const PROFILE = { language: 'js', name: 'Sonar way' }; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('default'); | |||
expect(shallowRender().setState({ submitting: true })).toMatchSnapshot('submitting'); | |||
}); | |||
it('correctly adds users', async () => { | |||
const onUserAdd = jest.fn(); | |||
const wrapper = shallowRender({ onUserAdd }); | |||
const user: UserSelected = { ...mockUser(), name: 'John doe', active: true, selected: true }; | |||
wrapper.instance().handleValueChange(user); | |||
expect(wrapper.state().selected).toBe(user); | |||
submit(wrapper.find('form')); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(addUser).toHaveBeenCalledWith( | |||
expect.objectContaining({ | |||
language: PROFILE.language, | |||
qualityProfile: PROFILE.name, | |||
login: user.login, | |||
}) | |||
); | |||
await waitAndUpdate(wrapper); | |||
expect(onUserAdd).toHaveBeenCalledWith(user); | |||
}); | |||
it('correctly adds groups', async () => { | |||
const onGroupAdd = jest.fn(); | |||
const wrapper = shallowRender({ onGroupAdd }); | |||
const group = mockGroup(); | |||
wrapper.instance().handleValueChange(group); | |||
expect(wrapper.state().selected).toBe(group); | |||
submit(wrapper.find('form')); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(addGroup).toHaveBeenCalledWith( | |||
expect.objectContaining({ | |||
language: PROFILE.language, | |||
qualityProfile: PROFILE.name, | |||
group: group.name, | |||
}) | |||
); | |||
await waitAndUpdate(wrapper); | |||
expect(onGroupAdd).toHaveBeenCalledWith(group); | |||
}); | |||
function shallowRender(props: Partial<ProfilePermissionsForm['props']> = {}) { | |||
return shallow<ProfilePermissionsForm>( | |||
<ProfilePermissionsForm | |||
onClose={jest.fn()} | |||
onGroupAdd={jest.fn()} | |||
onUserAdd={jest.fn()} | |||
profile={PROFILE} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,95 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { searchGroups, searchUsers } from '../../../../api/quality-profiles'; | |||
import { | |||
mockReactSelectControlProps, | |||
mockReactSelectOptionProps, | |||
} from '../../../../helpers/mocks/react-select'; | |||
import { mockUser } from '../../../../helpers/testMocks'; | |||
import ProfilePermissionsFormSelect from '../ProfilePermissionsFormSelect'; | |||
jest.mock('lodash', () => { | |||
const lodash = jest.requireActual('lodash'); | |||
lodash.debounce = | |||
(fn: Function) => | |||
(...args: any[]) => | |||
fn(...args); | |||
return lodash; | |||
}); | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
searchGroups: jest.fn().mockResolvedValue([]), | |||
searchUsers: jest.fn().mockResolvedValue([]), | |||
})); | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should handle search', async () => { | |||
(searchUsers as jest.Mock).mockResolvedValueOnce({ users: [mockUser()] }); | |||
(searchGroups as jest.Mock).mockResolvedValueOnce({ groups: [{ name: 'group1' }] }); | |||
const wrapper = shallowRender(); | |||
const query = 'Waldo'; | |||
const results = await new Promise((resolve) => { | |||
wrapper.instance().handleSearch(query, resolve); | |||
}); | |||
expect(searchUsers).toHaveBeenCalledWith(expect.objectContaining({ q: query })); | |||
expect(searchGroups).toHaveBeenCalledWith(expect.objectContaining({ q: query })); | |||
expect(results).toHaveLength(2); | |||
}); | |||
it('should render option correctly', () => { | |||
const wrapper = shallowRender(); | |||
const OptionRenderer = wrapper.instance().optionRenderer; | |||
expect( | |||
shallow(<OptionRenderer {...mockReactSelectOptionProps({ value: 'test', name: 'name' })} />) | |||
).toMatchSnapshot('option renderer'); | |||
}); | |||
it('should render value correctly', () => { | |||
const wrapper = shallowRender(); | |||
const ValueRenderer = wrapper.instance().singleValueRenderer; | |||
expect( | |||
shallow(<ValueRenderer {...mockReactSelectOptionProps({ value: 'test', name: 'name' })} />) | |||
).toMatchSnapshot('value renderer'); | |||
}); | |||
it('should render control correctly', () => { | |||
const wrapper = shallowRender(); | |||
const ControlRenderer = wrapper.instance().controlRenderer; | |||
expect(shallow(<ControlRenderer {...mockReactSelectControlProps()} />)).toMatchSnapshot( | |||
'control renderer' | |||
); | |||
}); | |||
function shallowRender(overrides: Partial<ProfilePermissionsFormSelect['props']> = {}) { | |||
return shallow<ProfilePermissionsFormSelect>( | |||
<ProfilePermissionsFormSelect | |||
onChange={jest.fn()} | |||
profile={{ language: 'Java', name: 'Sonar Way' }} | |||
{...overrides} | |||
/> | |||
); | |||
} |
@@ -1,65 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
/* eslint-disable import/first */ | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
removeGroup: jest.fn(() => Promise.resolve()), | |||
})); | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { click } from '../../../../helpers/testUtils'; | |||
import ProfilePermissionsGroup from '../ProfilePermissionsGroup'; | |||
const removeGroup = require('../../../../api/quality-profiles').removeGroup as jest.Mock<any>; | |||
const profile = { language: 'js', name: 'Sonar way' }; | |||
const group = { name: 'lambda' }; | |||
beforeEach(() => { | |||
removeGroup.mockClear(); | |||
}); | |||
it('renders', () => { | |||
expect( | |||
shallow(<ProfilePermissionsGroup group={group} onDelete={jest.fn()} profile={profile} />) | |||
).toMatchSnapshot(); | |||
}); | |||
it('removes user', async () => { | |||
const onDelete = jest.fn(); | |||
const wrapper = shallow( | |||
<ProfilePermissionsGroup group={group} onDelete={onDelete} profile={profile} /> | |||
); | |||
(wrapper.instance() as ProfilePermissionsGroup).mounted = true; | |||
expect(wrapper.find('SimpleModal').exists()).toBe(false); | |||
click(wrapper.find('DeleteButton')); | |||
expect(wrapper.find('SimpleModal').exists()).toBe(true); | |||
wrapper.find('SimpleModal').prop<Function>('onSubmit')(); | |||
expect(removeGroup).toHaveBeenCalledWith({ | |||
group: 'lambda', | |||
language: 'js', | |||
qualityProfile: 'Sonar way', | |||
}); | |||
await new Promise(setImmediate); | |||
expect(onDelete).toHaveBeenCalledWith(group); | |||
}); |
@@ -1,64 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { removeUser } from '../../../../api/quality-profiles'; | |||
import { click } from '../../../../helpers/testUtils'; | |||
import { UserSelected } from '../../../../types/types'; | |||
import ProfilePermissionsUser from '../ProfilePermissionsUser'; | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
removeUser: jest.fn(() => Promise.resolve()), | |||
})); | |||
const profile = { language: 'js', name: 'Sonar way' }; | |||
const user: UserSelected = { login: 'luke', name: 'Luke Skywalker', selected: true }; | |||
beforeEach(() => { | |||
jest.clearAllMocks(); | |||
}); | |||
it('renders', () => { | |||
expect( | |||
shallow(<ProfilePermissionsUser onDelete={jest.fn()} profile={profile} user={user} />) | |||
).toMatchSnapshot(); | |||
}); | |||
it('removes user', async () => { | |||
const onDelete = jest.fn(); | |||
const wrapper = shallow( | |||
<ProfilePermissionsUser onDelete={onDelete} profile={profile} user={user} /> | |||
); | |||
(wrapper.instance() as ProfilePermissionsUser).mounted = true; | |||
expect(wrapper.find('SimpleModal').exists()).toBe(false); | |||
click(wrapper.find('DeleteButton')); | |||
expect(wrapper.find('SimpleModal').exists()).toBe(true); | |||
wrapper.find('SimpleModal').prop<Function>('onSubmit')(); | |||
expect(removeUser).toHaveBeenCalledWith({ | |||
language: 'js', | |||
login: 'luke', | |||
qualityProfile: 'Sonar way', | |||
}); | |||
await new Promise(setImmediate); | |||
expect(onDelete).toHaveBeenCalledWith(user); | |||
}); |
@@ -1,87 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { getProfileProjects } from '../../../../api/quality-profiles'; | |||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import ChangeProjectsForm from '../ChangeProjectsForm'; | |||
import ProfileProjects from '../ProfileProjects'; | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
getProfileProjects: jest.fn().mockResolvedValue({ | |||
results: [ | |||
{ | |||
id: '633a5180-1ad7-4008-a5cb-e1d3cec4c816', | |||
key: 'org.sonarsource.xml:xml', | |||
name: 'SonarXML', | |||
selected: true, | |||
}, | |||
], | |||
paging: { pageIndex: 1, pageSize: 2, total: 10 }, | |||
more: true, | |||
}), | |||
})); | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot('loading'); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot('default'); | |||
wrapper.setProps({ | |||
profile: mockQualityProfile({ actions: { associateProjects: false } }), | |||
}); | |||
expect(wrapper).toMatchSnapshot('no rights'); | |||
wrapper.setProps({ | |||
profile: mockQualityProfile({ | |||
projectCount: 0, | |||
activeRuleCount: 0, | |||
actions: { associateProjects: true }, | |||
}), | |||
}); | |||
expect(wrapper).toMatchSnapshot('no active rules, but associated projects'); | |||
wrapper.setProps({ | |||
profile: mockQualityProfile({ activeRuleCount: 0, actions: { associateProjects: true } }), | |||
}); | |||
wrapper.setState({ projects: [] }); | |||
expect(wrapper).toMatchSnapshot('no active rules, no associated projects'); | |||
wrapper.instance().loadMore(); | |||
expect(getProfileProjects).toHaveBeenLastCalledWith({ p: 2, key: 'key' }); | |||
}); | |||
it('should open and close the form', async () => { | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
wrapper.instance().handleChangeClick(); | |||
expect(wrapper.find(ChangeProjectsForm).exists()).toBe(true); | |||
wrapper.instance().closeForm(); | |||
expect(wrapper.find(ChangeProjectsForm).exists()).toBe(false); | |||
}); | |||
function shallowRender(props: Partial<ProfileProjects['props']> = {}) { | |||
return shallow<ProfileProjects>( | |||
<ProfileProjects | |||
profile={mockQualityProfile({ actions: { associateProjects: true } })} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,150 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { getQualityProfile } from '../../../../api/quality-profiles'; | |||
import { mockPaging, mockQualityProfile } from '../../../../helpers/testMocks'; | |||
import { waitAndUpdate } from '../../../../helpers/testUtils'; | |||
import ProfileRules from '../ProfileRules'; | |||
const PROFILE = mockQualityProfile({ | |||
activeRuleCount: 68, | |||
activeDeprecatedRuleCount: 0, | |||
depth: 0, | |||
language: 'js', | |||
rulesUpdatedAt: '2017-06-28T12:58:44+0000', | |||
}); | |||
const EDITABLE_PROFILE = { ...PROFILE, actions: { edit: true } }; | |||
const apiResponseAll = { | |||
facets: [ | |||
{ | |||
property: 'types', | |||
values: [ | |||
{ val: 'CODE_SMELL', count: 168 }, | |||
{ val: 'BUG', count: 68 }, | |||
{ val: 'VULNERABILITY', count: 7 }, | |||
{ val: 'SECURITY_HOTSPOT', count: 10 }, | |||
], | |||
}, | |||
], | |||
paging: mockPaging({ | |||
total: 253, | |||
}), | |||
}; | |||
const apiResponseActive = { | |||
facets: [ | |||
{ | |||
property: 'types', | |||
values: [ | |||
{ val: 'BUG', count: 68 }, | |||
{ val: 'CODE_SMELL', count: 0 }, | |||
{ val: 'VULNERABILITY', count: 0 }, | |||
{ val: 'SECURITY_HOTSPOT', count: 0 }, | |||
], | |||
}, | |||
], | |||
paging: mockPaging({ | |||
total: 68, | |||
}), | |||
}; | |||
jest.mock('../../../../api/rules', () => ({ | |||
...jest.requireActual('../../../../api/rules'), | |||
searchRules: jest | |||
.fn() | |||
.mockImplementation((data: any) => | |||
Promise.resolve(data.activation === 'true' ? apiResponseActive : apiResponseAll) | |||
), | |||
})); | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
...jest.requireActual('../../../../api/quality-profiles'), | |||
getQualityProfile: jest.fn().mockImplementation(() => | |||
Promise.resolve({ | |||
compareToSonarWay: { | |||
profile: 'sonarway', | |||
profileName: 'Sonar way', | |||
missingRuleCount: 4, | |||
}, | |||
}) | |||
), | |||
})); | |||
beforeEach(jest.clearAllMocks); | |||
it('should render the quality profiles rules with sonarway comparison', async () => { | |||
const wrapper = shallow(<ProfileRules profile={PROFILE} />); | |||
const instance = wrapper.instance() as ProfileRules; | |||
instance.mounted = true; | |||
instance.loadRules(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(1); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should show a button to activate more rules for admins', () => { | |||
const wrapper = shallow(<ProfileRules profile={EDITABLE_PROFILE} />); | |||
expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); | |||
}); | |||
it('should show a disabled button to activate more rules for built-in profiles', () => { | |||
const wrapper = shallow( | |||
<ProfileRules profile={{ ...EDITABLE_PROFILE, actions: { copy: true }, isBuiltIn: true }} /> | |||
); | |||
expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); | |||
}); | |||
it('should show a deprecated rules warning message', () => { | |||
const wrapper = shallow( | |||
<ProfileRules profile={{ ...EDITABLE_PROFILE, activeDeprecatedRuleCount: 8 }} /> | |||
); | |||
expect(wrapper.find('ProfileRulesDeprecatedWarning')).toMatchSnapshot(); | |||
}); | |||
it('should not show a button to activate more rules on built in profiles', () => { | |||
const wrapper = shallow(<ProfileRules profile={{ ...EDITABLE_PROFILE, isBuiltIn: true }} />); | |||
expect(wrapper.find('.js-activate-rules').exists()).toBe(false); | |||
}); | |||
it('should not show sonarway comparison for built in profiles', async () => { | |||
(getQualityProfile as jest.Mock).mockReturnValueOnce({}); | |||
const wrapper = shallow(<ProfileRules profile={{ ...PROFILE, isBuiltIn: true }} />); | |||
await new Promise(setImmediate); | |||
wrapper.update(); | |||
expect(getQualityProfile).toHaveBeenCalledTimes(0); | |||
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0); | |||
}); | |||
it('should not show sonarway comparison if there is no missing rules', async () => { | |||
(getQualityProfile as jest.Mock).mockReturnValueOnce({ | |||
compareToSonarWay: { | |||
profile: 'sonarway', | |||
profileName: 'Sonar way', | |||
missingRuleCount: 0, | |||
}, | |||
}); | |||
const wrapper = shallow(<ProfileRules profile={PROFILE} />); | |||
await waitAndUpdate(wrapper); | |||
expect(getQualityProfile).toHaveBeenCalledTimes(1); | |||
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0); | |||
}); |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import ProfileRulesDeprecatedWarning from '../ProfileRulesDeprecatedWarning'; | |||
it('should render correctly', () => { | |||
expect( | |||
shallow(<ProfileRulesDeprecatedWarning activeDeprecatedRules={18} profile="bar" />) | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,43 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import ProfileRulesRowOfType from '../ProfileRulesRowOfType'; | |||
it('should render correctly', () => { | |||
expect( | |||
shallow(<ProfileRulesRowOfType count={3} qprofile="bar" total={10} type="BUG" />) | |||
).toMatchSnapshot(); | |||
}); | |||
it('should render correctly if there is 0 rules', () => { | |||
expect( | |||
shallow(<ProfileRulesRowOfType count={0} qprofile="bar" total={0} type="VULNERABILITY" />) | |||
).toMatchSnapshot(); | |||
}); | |||
it('should render correctly if there is missing data', () => { | |||
expect( | |||
shallow(<ProfileRulesRowOfType count={5} qprofile="bar" total={null} type="VULNERABILITY" />) | |||
).toMatchSnapshot(); | |||
expect( | |||
shallow(<ProfileRulesRowOfType count={null} qprofile="foo" total={10} type="VULNERABILITY" />) | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,37 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import ProfileRulesRowTotal from '../ProfileRulesRowTotal'; | |||
it('should render correctly', () => { | |||
expect(shallow(<ProfileRulesRowTotal count={3} qprofile="bar" total={10} />)).toMatchSnapshot(); | |||
}); | |||
it('should render correctly if there is 0 rules', () => { | |||
expect(shallow(<ProfileRulesRowTotal count={0} qprofile="bar" total={0} />)).toMatchSnapshot(); | |||
}); | |||
it('should render correctly if there is missing data', () => { | |||
expect(shallow(<ProfileRulesRowTotal count={5} qprofile="bar" total={null} />)).toMatchSnapshot(); | |||
expect( | |||
shallow(<ProfileRulesRowTotal count={null} qprofile="foo" total={10} />) | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,35 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import ProfileRulesSonarWayComparison from '../ProfileRulesSonarWayComparison'; | |||
it('should render correctly', () => { | |||
expect( | |||
shallow( | |||
<ProfileRulesSonarWayComparison | |||
language="Java" | |||
profile="bar" | |||
sonarWayMissingRules={158} | |||
sonarway="baz" | |||
/> | |||
) | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,97 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.change_parent" | |||
onRequestClose={[MockFunction]} | |||
size="small" | |||
> | |||
<form | |||
id="change-profile-parent-form" | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.change_parent | |||
</h2> | |||
</div> | |||
<div | |||
className="modal-body" | |||
> | |||
<MandatoryFieldsExplanation | |||
className="modal-field" | |||
/> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="change-profile-parent-input" | |||
> | |||
quality_profiles.parent | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<Select | |||
autoFocus={true} | |||
className="width-100" | |||
id="change-profile-parent" | |||
inputId="change-profile-parent-input" | |||
isClearable={false} | |||
isSearchable={true} | |||
name="parentKey" | |||
onChange={[Function]} | |||
options={ | |||
[ | |||
{ | |||
"label": "none", | |||
"value": "", | |||
}, | |||
{ | |||
"label": "name", | |||
"value": "key", | |||
}, | |||
{ | |||
"label": "name", | |||
"value": "key", | |||
}, | |||
{ | |||
"label": "name", | |||
"value": "key", | |||
}, | |||
{ | |||
"label": "name", | |||
"value": "key", | |||
}, | |||
] | |||
} | |||
value={ | |||
[ | |||
{ | |||
"label": "none", | |||
"value": "", | |||
}, | |||
] | |||
} | |||
/> | |||
</div> | |||
</div> | |||
<div | |||
className="modal-foot" | |||
> | |||
<SubmitButton | |||
disabled={true} | |||
id="change-profile-parent-submit" | |||
> | |||
change_verb | |||
</SubmitButton> | |||
<ResetButtonLink | |||
id="change-profile-parent-cancel" | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</div> | |||
</form> | |||
</Modal> | |||
`; |
@@ -1,79 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<Modal | |||
contentLabel="projects" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<div | |||
className="modal-head" | |||
> | |||
<h2> | |||
projects | |||
</h2> | |||
</div> | |||
<div | |||
className="modal-body modal-container" | |||
id="profile-projects" | |||
> | |||
<SelectList | |||
allowBulkSelection={true} | |||
elements={ | |||
[ | |||
"test1", | |||
"test2", | |||
"test3", | |||
] | |||
} | |||
elementsTotalCount={55} | |||
labelAll="quality_gates.projects.all" | |||
labelSelected="quality_gates.projects.with" | |||
labelUnselected="quality_gates.projects.without" | |||
needToReload={false} | |||
onSearch={[Function]} | |||
onSelect={[Function]} | |||
onUnselect={[Function]} | |||
renderElement={[Function]} | |||
selectedElements={ | |||
[ | |||
"test3", | |||
] | |||
} | |||
withPaging={true} | |||
/> | |||
</div> | |||
<div | |||
className="modal-foot" | |||
> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
close | |||
</ResetButtonLink> | |||
</div> | |||
</Modal> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<div | |||
className="select-list-list-item" | |||
> | |||
<React.Fragment> | |||
test1 | |||
<br /> | |||
<span | |||
className="note" | |||
> | |||
test1 | |||
</span> | |||
</React.Fragment> | |||
</div> | |||
`; | |||
exports[`should render correctly 3`] = ` | |||
<div | |||
className="select-list-list-item" | |||
> | |||
test_foo | |||
</div> | |||
`; |
@@ -1,506 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: default 1`] = ` | |||
<div> | |||
<div | |||
className="quality-profile-grid" | |||
> | |||
<div | |||
className="quality-profile-grid-left" | |||
> | |||
<ProfileRules | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
<ProfileExporters | |||
exporters={[]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="quality-profile-grid-right" | |||
> | |||
<ProfileInheritance | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
profiles={[]} | |||
updateProfiles={[MockFunction]} | |||
/> | |||
<ProfileProjects | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: edit permissions 1`] = ` | |||
<div> | |||
<div | |||
className="quality-profile-grid" | |||
> | |||
<div | |||
className="quality-profile-grid-left" | |||
> | |||
<ProfileRules | |||
profile={ | |||
{ | |||
"actions": { | |||
"edit": true, | |||
}, | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
<ProfileExporters | |||
exporters={[]} | |||
profile={ | |||
{ | |||
"actions": { | |||
"edit": true, | |||
}, | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
<ProfilePermissions | |||
profile={ | |||
{ | |||
"actions": { | |||
"edit": true, | |||
}, | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="quality-profile-grid-right" | |||
> | |||
<ProfileInheritance | |||
profile={ | |||
{ | |||
"actions": { | |||
"edit": true, | |||
}, | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
profiles={[]} | |||
updateProfiles={[MockFunction]} | |||
/> | |||
<ProfileProjects | |||
profile={ | |||
{ | |||
"actions": { | |||
"edit": true, | |||
}, | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: is default profile, no active rules 1`] = ` | |||
<div> | |||
<div | |||
className="quality-profile-grid" | |||
> | |||
<div | |||
className="quality-profile-grid-left" | |||
> | |||
<ProfileRules | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": true, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
/> | |||
<ProfileExporters | |||
exporters={[]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": true, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="quality-profile-grid-right" | |||
> | |||
<Alert | |||
className="big-spacer-bottom" | |||
variant="warning" | |||
> | |||
quality_profiles.warning.is_default_no_rules | |||
</Alert> | |||
<ProfileInheritance | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": true, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
profiles={[]} | |||
updateProfiles={[MockFunction]} | |||
/> | |||
<ProfileProjects | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": true, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: no active rules (same as default) 1`] = ` | |||
<div> | |||
<div | |||
className="quality-profile-grid" | |||
> | |||
<div | |||
className="quality-profile-grid-left" | |||
> | |||
<ProfileRules | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
/> | |||
<ProfileExporters | |||
exporters={[]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="quality-profile-grid-right" | |||
> | |||
<ProfileInheritance | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
profiles={[]} | |||
updateProfiles={[MockFunction]} | |||
/> | |||
<ProfileProjects | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 0, | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: projects associated, no active rules 1`] = ` | |||
<div> | |||
<div | |||
className="quality-profile-grid" | |||
> | |||
<div | |||
className="quality-profile-grid-left" | |||
> | |||
<ProfileRules | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 10, | |||
} | |||
} | |||
/> | |||
<ProfileExporters | |||
exporters={[]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 10, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="quality-profile-grid-right" | |||
> | |||
<Alert | |||
className="big-spacer-bottom" | |||
variant="warning" | |||
> | |||
quality_profiles.warning.used_by_projects_no_rules | |||
</Alert> | |||
<ProfileInheritance | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 10, | |||
} | |||
} | |||
profiles={[]} | |||
updateProfiles={[MockFunction]} | |||
/> | |||
<ProfileProjects | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 0, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 10, | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; |
@@ -1,34 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-exporters" | |||
> | |||
<h2> | |||
quality_profiles.exporters | |||
</h2> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<Alert | |||
className="big-spacer-bottom" | |||
variant="warning" | |||
> | |||
quality_profiles.exporters.deprecated | |||
</Alert> | |||
<ul> | |||
<li | |||
data-key="exporter-key" | |||
key="exporter-key" | |||
> | |||
<ForwardRef(Link) | |||
target="_blank" | |||
to="/api/qualityprofiles/export?exporterKey=exporter-key&language=js&qualityProfile=name" | |||
> | |||
exporter-name | |||
</ForwardRef(Link)> | |||
</li> | |||
</ul> | |||
</div> | |||
</div> | |||
`; |
@@ -1,147 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-inheritance" | |||
> | |||
<div | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
quality_profiles.profile_inheritance | |||
</h2> | |||
</div> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<table | |||
className="data zebra" | |||
> | |||
<tbody> | |||
<ProfileInheritanceBox | |||
depth={0} | |||
key="foo" | |||
language="js" | |||
profile={ | |||
{ | |||
"activeRuleCount": 4, | |||
"isBuiltIn": false, | |||
"key": "foo", | |||
"name": "Foo", | |||
"overridingRuleCount": 0, | |||
} | |||
} | |||
type="ancestor" | |||
/> | |||
<ProfileInheritanceBox | |||
depth={2} | |||
key="foo" | |||
language="js" | |||
profile={ | |||
{ | |||
"activeRuleCount": 4, | |||
"isBuiltIn": false, | |||
"key": "foo", | |||
"name": "Foo", | |||
"overridingRuleCount": 0, | |||
} | |||
} | |||
type="child" | |||
/> | |||
</tbody> | |||
</table> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render modal correctly 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-inheritance" | |||
> | |||
<div | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
quality_profiles.profile_inheritance | |||
</h2> | |||
</div> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<table | |||
className="data zebra" | |||
> | |||
<tbody> | |||
<ProfileInheritanceBox | |||
depth={0} | |||
key="foo" | |||
language="js" | |||
profile={ | |||
{ | |||
"activeRuleCount": 4, | |||
"isBuiltIn": false, | |||
"key": "foo", | |||
"name": "Foo", | |||
"overridingRuleCount": 0, | |||
} | |||
} | |||
type="ancestor" | |||
/> | |||
<ProfileInheritanceBox | |||
depth={2} | |||
key="foo" | |||
language="js" | |||
profile={ | |||
{ | |||
"activeRuleCount": 4, | |||
"isBuiltIn": false, | |||
"key": "foo", | |||
"name": "Foo", | |||
"overridingRuleCount": 0, | |||
} | |||
} | |||
type="child" | |||
/> | |||
</tbody> | |||
</table> | |||
</div> | |||
<ChangeParentForm | |||
onChange={[Function]} | |||
onClose={[Function]} | |||
profile={ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
} | |||
} | |||
profiles={ | |||
[ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
}, | |||
] | |||
} | |||
/> | |||
</div> | |||
`; |
@@ -1,136 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<tr | |||
className="it__quality-profiles__inheritance-current" | |||
> | |||
<td> | |||
<div | |||
style={ | |||
{ | |||
"paddingLeft": 25, | |||
} | |||
} | |||
> | |||
<ProfileLink | |||
className="text-middle" | |||
language="foo" | |||
name="Foo" | |||
> | |||
Foo | |||
</ProfileLink> | |||
</div> | |||
</td> | |||
<td> | |||
quality_profile.x_active_rules.4 | |||
</td> | |||
<td> | |||
<p> | |||
quality_profiles.x_overridden_rules.0 | |||
</p> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<tr | |||
className="it__quality-profiles__inheritance-current" | |||
> | |||
<td> | |||
<div | |||
style={ | |||
{ | |||
"paddingLeft": 75, | |||
} | |||
} | |||
> | |||
<ProfileLink | |||
className="text-middle" | |||
language="foo" | |||
name="Foo" | |||
> | |||
Foo | |||
</ProfileLink> | |||
<BuiltInQualityProfileBadge | |||
className="spacer-left" | |||
/> | |||
</div> | |||
</td> | |||
<td> | |||
quality_profile.x_active_rules.4 | |||
</td> | |||
<td> | |||
<p> | |||
quality_profiles.x_overridden_rules.0 | |||
</p> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly 3`] = ` | |||
<tr | |||
className="it__quality-profiles__inheritance-current" | |||
> | |||
<td> | |||
<div | |||
style={ | |||
{ | |||
"paddingLeft": 25, | |||
} | |||
} | |||
> | |||
<ProfileLink | |||
className="text-middle" | |||
language="foo" | |||
name="Foo" | |||
> | |||
Foo | |||
</ProfileLink> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay="quality_profiles.extends_built_in" | |||
/> | |||
</div> | |||
</td> | |||
<td> | |||
quality_profile.x_active_rules.4 | |||
</td> | |||
<td> | |||
<p> | |||
quality_profiles.x_overridden_rules.0 | |||
</p> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly 4`] = ` | |||
<tr | |||
className="it__quality-profiles__inheritance-current" | |||
> | |||
<td> | |||
<div | |||
style={ | |||
{ | |||
"paddingLeft": 25, | |||
} | |||
} | |||
> | |||
<ProfileLink | |||
className="text-middle" | |||
language="foo" | |||
name="Foo" | |||
> | |||
Foo | |||
</ProfileLink> | |||
</div> | |||
</td> | |||
<td> | |||
quality_profile.x_active_rules.4 | |||
</td> | |||
<td> | |||
<p> | |||
quality_profiles.x_overridden_rules.10 | |||
</p> | |||
</td> | |||
</tr> | |||
`; |
@@ -1,93 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders 1`] = ` | |||
<div | |||
className="boxed-group" | |||
> | |||
<h2> | |||
permissions.page | |||
</h2> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<p | |||
className="note" | |||
> | |||
quality_profiles.default_permissions | |||
</p> | |||
<div | |||
className="big-spacer-top" | |||
> | |||
<i | |||
className="spinner" | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`renders 2`] = ` | |||
<div | |||
className="boxed-group" | |||
> | |||
<h2> | |||
permissions.page | |||
</h2> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<p | |||
className="note" | |||
> | |||
quality_profiles.default_permissions | |||
</p> | |||
<div | |||
className="big-spacer-top" | |||
> | |||
<ProfilePermissionsUser | |||
key="luke" | |||
onDelete={[Function]} | |||
profile={ | |||
{ | |||
"key": "sonar-way", | |||
"language": "js", | |||
"name": "Sonar way", | |||
} | |||
} | |||
user={ | |||
{ | |||
"login": "luke", | |||
"name": "Luke Skywalker", | |||
"selected": false, | |||
} | |||
} | |||
/> | |||
<ProfilePermissionsGroup | |||
group={ | |||
{ | |||
"name": "Lambda", | |||
} | |||
} | |||
key="Lambda" | |||
onDelete={[Function]} | |||
profile={ | |||
{ | |||
"key": "sonar-way", | |||
"language": "js", | |||
"name": "Sonar way", | |||
} | |||
} | |||
/> | |||
<div | |||
className="text-right" | |||
> | |||
<Button | |||
onClick={[Function]} | |||
> | |||
quality_profiles.grant_permissions_to_more_users | |||
</Button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
`; |
@@ -1,230 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`correctly adds groups 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.grant_permissions_to_user_or_group" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.grant_permissions_to_user_or_group | |||
</h2> | |||
</header> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="change-profile-permission-input" | |||
> | |||
quality_profiles.search_description | |||
</label> | |||
<ProfilePermissionsFormSelect | |||
onChange={[Function]} | |||
profile={ | |||
{ | |||
"language": "js", | |||
"name": "Sonar way", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<i | |||
className="spinner spacer-right" | |||
/> | |||
<SubmitButton | |||
disabled={true} | |||
> | |||
add_verb | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
</Modal> | |||
`; | |||
exports[`correctly adds users 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.grant_permissions_to_user_or_group" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.grant_permissions_to_user_or_group | |||
</h2> | |||
</header> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="change-profile-permission-input" | |||
> | |||
quality_profiles.search_description | |||
</label> | |||
<ProfilePermissionsFormSelect | |||
onChange={[Function]} | |||
profile={ | |||
{ | |||
"language": "js", | |||
"name": "Sonar way", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<i | |||
className="spinner spacer-right" | |||
/> | |||
<SubmitButton | |||
disabled={true} | |||
> | |||
add_verb | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
</Modal> | |||
`; | |||
exports[`should render correctly: default 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.grant_permissions_to_user_or_group" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.grant_permissions_to_user_or_group | |||
</h2> | |||
</header> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="change-profile-permission-input" | |||
> | |||
quality_profiles.search_description | |||
</label> | |||
<ProfilePermissionsFormSelect | |||
onChange={[Function]} | |||
profile={ | |||
{ | |||
"language": "js", | |||
"name": "Sonar way", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<SubmitButton | |||
disabled={true} | |||
> | |||
add_verb | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
</Modal> | |||
`; | |||
exports[`should render correctly: submitting 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.grant_permissions_to_user_or_group" | |||
onRequestClose={[MockFunction]} | |||
> | |||
<header | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.grant_permissions_to_user_or_group | |||
</h2> | |||
</header> | |||
<form | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-body" | |||
> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="change-profile-permission-input" | |||
> | |||
quality_profiles.search_description | |||
</label> | |||
<ProfilePermissionsFormSelect | |||
onChange={[Function]} | |||
profile={ | |||
{ | |||
"language": "js", | |||
"name": "Sonar way", | |||
} | |||
} | |||
/> | |||
</div> | |||
</div> | |||
<footer | |||
className="modal-foot" | |||
> | |||
<i | |||
className="spinner spacer-right" | |||
/> | |||
<SubmitButton | |||
disabled={true} | |||
> | |||
add_verb | |||
</SubmitButton> | |||
<ResetButtonLink | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</footer> | |||
</form> | |||
</Modal> | |||
`; |
@@ -1,78 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render control correctly: control renderer 1`] = ` | |||
<Control | |||
className="abs-height-100" | |||
/> | |||
`; | |||
exports[`should render correctly 1`] = ` | |||
<SearchSelect | |||
autoFocus={true} | |||
className="width-100" | |||
components={ | |||
{ | |||
"Control": [Function], | |||
"Option": [Function], | |||
"SingleValue": [Function], | |||
} | |||
} | |||
defaultOptions={true} | |||
id="change-profile-permission" | |||
inputId="change-profile-permission-input" | |||
isClearable={false} | |||
large={true} | |||
loadOptions={[Function]} | |||
noOptionsMessage={[Function]} | |||
onChange={[MockFunction]} | |||
placeholder="" | |||
/> | |||
`; | |||
exports[`should render option correctly: option renderer 1`] = ` | |||
<Option | |||
data={ | |||
{ | |||
"name": "name", | |||
"value": "test", | |||
} | |||
} | |||
> | |||
<span | |||
className="display-flex-center" | |||
> | |||
<GroupIcon | |||
size={16} | |||
/> | |||
<strong | |||
className="spacer-left" | |||
> | |||
name | |||
</strong> | |||
</span> | |||
</Option> | |||
`; | |||
exports[`should render value correctly: value renderer 1`] = ` | |||
<SingleValue | |||
data={ | |||
{ | |||
"name": "name", | |||
"value": "test", | |||
} | |||
} | |||
> | |||
<span | |||
className="display-flex-center" | |||
> | |||
<GroupIcon | |||
size={16} | |||
/> | |||
<strong | |||
className="spacer-left" | |||
> | |||
name | |||
</strong> | |||
</span> | |||
</SingleValue> | |||
`; |
@@ -1,28 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders 1`] = ` | |||
<div | |||
className="clearfix big-spacer-bottom" | |||
> | |||
<DeleteButton | |||
className="pull-right spacer-top spacer-left spacer-right button-small" | |||
onClick={[Function]} | |||
/> | |||
<GroupIcon | |||
className="pull-left spacer-right" | |||
size={32} | |||
/> | |||
<div | |||
className="overflow-hidden" | |||
style={ | |||
{ | |||
"lineHeight": "32px", | |||
} | |||
} | |||
> | |||
<strong> | |||
lambda | |||
</strong> | |||
</div> | |||
</div> | |||
`; |
@@ -1,29 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders 1`] = ` | |||
<div | |||
className="clearfix big-spacer-bottom" | |||
> | |||
<DeleteButton | |||
className="pull-right spacer-top spacer-left spacer-right button-small" | |||
onClick={[Function]} | |||
/> | |||
<withAppStateContext(LegacyAvatar) | |||
className="pull-left spacer-right" | |||
name="Luke Skywalker" | |||
size={32} | |||
/> | |||
<div | |||
className="overflow-hidden" | |||
> | |||
<strong> | |||
Luke Skywalker | |||
</strong> | |||
<div | |||
className="note" | |||
> | |||
luke | |||
</div> | |||
</div> | |||
</div> | |||
`; |
@@ -1,247 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: default 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-projects" | |||
> | |||
<div | |||
className="boxed-group-actions" | |||
> | |||
<Tooltip | |||
overlay={null} | |||
> | |||
<Button | |||
className="js-change-projects" | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
quality_profiles.change_projects | |||
</Button> | |||
</Tooltip> | |||
</div> | |||
<header | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
projects | |||
</h2> | |||
</header> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<ul> | |||
<li | |||
className="spacer-top js-profile-project" | |||
data-key="org.sonarsource.xml:xml" | |||
key="org.sonarsource.xml:xml" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/dashboard", | |||
"search": "?id=org.sonarsource.xml%3Axml", | |||
} | |||
} | |||
> | |||
<QualifierIcon | |||
qualifier="TRK" | |||
/> | |||
<span> | |||
SonarXML | |||
</span> | |||
</ForwardRef(Link)> | |||
</li> | |||
</ul> | |||
<ListFooter | |||
count={1} | |||
loadMore={[Function]} | |||
ready={true} | |||
total={10} | |||
/> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: loading 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-projects" | |||
> | |||
<div | |||
className="boxed-group-actions" | |||
> | |||
<Tooltip | |||
overlay={null} | |||
> | |||
<Button | |||
className="js-change-projects" | |||
disabled={false} | |||
onClick={[Function]} | |||
> | |||
quality_profiles.change_projects | |||
</Button> | |||
</Tooltip> | |||
</div> | |||
<header | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
projects | |||
</h2> | |||
</header> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<i | |||
className="spinner" | |||
/> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: no active rules, but associated projects 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-projects" | |||
> | |||
<div | |||
className="boxed-group-actions" | |||
> | |||
<Tooltip | |||
overlay="quality_profiles.cannot_associate_projects_no_rules" | |||
> | |||
<Button | |||
className="js-change-projects" | |||
disabled={true} | |||
onClick={[Function]} | |||
> | |||
quality_profiles.change_projects | |||
</Button> | |||
</Tooltip> | |||
</div> | |||
<header | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
projects | |||
</h2> | |||
</header> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<ul> | |||
<li | |||
className="spacer-top js-profile-project" | |||
data-key="org.sonarsource.xml:xml" | |||
key="org.sonarsource.xml:xml" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/dashboard", | |||
"search": "?id=org.sonarsource.xml%3Axml", | |||
} | |||
} | |||
> | |||
<QualifierIcon | |||
qualifier="TRK" | |||
/> | |||
<span> | |||
SonarXML | |||
</span> | |||
</ForwardRef(Link)> | |||
</li> | |||
</ul> | |||
<ListFooter | |||
count={1} | |||
loadMore={[Function]} | |||
ready={true} | |||
total={10} | |||
/> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: no active rules, no associated projects 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-projects" | |||
> | |||
<div | |||
className="boxed-group-actions" | |||
> | |||
<Tooltip | |||
overlay="quality_profiles.cannot_associate_projects_no_rules" | |||
> | |||
<Button | |||
className="js-change-projects" | |||
disabled={true} | |||
onClick={[Function]} | |||
> | |||
quality_profiles.change_projects | |||
</Button> | |||
</Tooltip> | |||
</div> | |||
<header | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
projects | |||
</h2> | |||
</header> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<div> | |||
quality_profiles.cannot_associate_projects_no_rules | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: no rights 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-projects" | |||
> | |||
<header | |||
className="boxed-group-header" | |||
> | |||
<h2> | |||
projects | |||
</h2> | |||
</header> | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<ul> | |||
<li | |||
className="spacer-top js-profile-project" | |||
data-key="org.sonarsource.xml:xml" | |||
key="org.sonarsource.xml:xml" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/dashboard", | |||
"search": "?id=org.sonarsource.xml%3Axml", | |||
} | |||
} | |||
> | |||
<QualifierIcon | |||
qualifier="TRK" | |||
/> | |||
<span> | |||
SonarXML | |||
</span> | |||
</ForwardRef(Link)> | |||
</li> | |||
</ul> | |||
<ListFooter | |||
count={1} | |||
loadMore={[Function]} | |||
ready={true} | |||
total={10} | |||
/> | |||
</div> | |||
</div> | |||
`; |
@@ -1,101 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render the quality profiles rules with sonarway comparison 1`] = ` | |||
<div | |||
className="boxed-group quality-profile-rules" | |||
> | |||
<div | |||
className="quality-profile-rules-distribution" | |||
> | |||
<table | |||
className="data condensed" | |||
> | |||
<thead> | |||
<tr> | |||
<th> | |||
<h2> | |||
rules | |||
</h2> | |||
</th> | |||
<th> | |||
active | |||
</th> | |||
<th> | |||
inactive | |||
</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<ProfileRulesRowTotal | |||
count={68} | |||
qprofile="key" | |||
total={253} | |||
/> | |||
<ProfileRulesRowOfType | |||
count={68} | |||
key="BUG" | |||
qprofile="key" | |||
total={68} | |||
type="BUG" | |||
/> | |||
<ProfileRulesRowOfType | |||
count={0} | |||
key="VULNERABILITY" | |||
qprofile="key" | |||
total={7} | |||
type="VULNERABILITY" | |||
/> | |||
<ProfileRulesRowOfType | |||
count={0} | |||
key="CODE_SMELL" | |||
qprofile="key" | |||
total={168} | |||
type="CODE_SMELL" | |||
/> | |||
<ProfileRulesRowOfType | |||
count={0} | |||
key="SECURITY_HOTSPOT" | |||
qprofile="key" | |||
total={10} | |||
type="SECURITY_HOTSPOT" | |||
/> | |||
</tbody> | |||
</table> | |||
</div> | |||
<ProfileRulesSonarWayComparison | |||
language="js" | |||
profile="key" | |||
sonarWayMissingRules={4} | |||
sonarway="sonarway" | |||
/> | |||
</div> | |||
`; | |||
exports[`should show a button to activate more rules for admins 1`] = ` | |||
<ForwardRef(Link) | |||
className="button js-activate-rules" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=key&activation=false", | |||
} | |||
} | |||
> | |||
quality_profiles.activate_more | |||
</ForwardRef(Link)> | |||
`; | |||
exports[`should show a deprecated rules warning message 1`] = ` | |||
<ProfileRulesDeprecatedWarning | |||
activeDeprecatedRules={8} | |||
profile="key" | |||
/> | |||
`; | |||
exports[`should show a disabled button to activate more rules for built-in profiles 1`] = ` | |||
<Button | |||
className="disabled js-activate-rules" | |||
> | |||
quality_profiles.activate_more | |||
</Button> | |||
`; |
@@ -1,32 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="quality-profile-rules-deprecated clearfix" | |||
> | |||
<span | |||
className="pull-left" | |||
> | |||
<span | |||
className="text-middle" | |||
> | |||
quality_profiles.deprecated_rules | |||
</span> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay="quality_profiles.deprecated_rules_description" | |||
/> | |||
</span> | |||
<ForwardRef(Link) | |||
className="pull-right" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true&statuses=DEPRECATED", | |||
} | |||
} | |||
> | |||
18 | |||
</ForwardRef(Link)> | |||
</div> | |||
`; |
@@ -1,132 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<tr> | |||
<td> | |||
<span> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="BUG" | |||
/> | |||
issue.type.BUG.plural | |||
</span> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true&types=BUG", | |||
} | |||
} | |||
> | |||
3 | |||
</ForwardRef(Link)> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
className="small" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=false&types=BUG", | |||
} | |||
} | |||
> | |||
7 | |||
</ForwardRef(Link)> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly if there is 0 rules 1`] = ` | |||
<tr> | |||
<td> | |||
<span> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="VULNERABILITY" | |||
/> | |||
issue.type.VULNERABILITY.plural | |||
</span> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true&types=VULNERABILITY", | |||
} | |||
} | |||
> | |||
0 | |||
</ForwardRef(Link)> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<span | |||
className="note" | |||
> | |||
0 | |||
</span> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly if there is missing data 1`] = ` | |||
<tr> | |||
<td> | |||
<span> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="VULNERABILITY" | |||
/> | |||
issue.type.VULNERABILITY.plural | |||
</span> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true&types=VULNERABILITY", | |||
} | |||
} | |||
> | |||
5 | |||
</ForwardRef(Link)> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
/> | |||
</tr> | |||
`; | |||
exports[`should render correctly if there is missing data 2`] = ` | |||
<tr> | |||
<td> | |||
<span> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="VULNERABILITY" | |||
/> | |||
issue.type.VULNERABILITY.plural | |||
</span> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
/> | |||
<td | |||
className="thin nowrap text-right" | |||
/> | |||
</tr> | |||
`; |
@@ -1,124 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<tr> | |||
<td> | |||
<strong> | |||
total | |||
</strong> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true", | |||
} | |||
} | |||
> | |||
<strong> | |||
3 | |||
</strong> | |||
</ForwardRef(Link)> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
className="small" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=false", | |||
} | |||
} | |||
> | |||
<strong> | |||
7 | |||
</strong> | |||
</ForwardRef(Link)> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly if there is 0 rules 1`] = ` | |||
<tr> | |||
<td> | |||
<strong> | |||
total | |||
</strong> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true", | |||
} | |||
} | |||
> | |||
<strong> | |||
0 | |||
</strong> | |||
</ForwardRef(Link)> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<span | |||
className="note" | |||
> | |||
0 | |||
</span> | |||
</td> | |||
</tr> | |||
`; | |||
exports[`should render correctly if there is missing data 1`] = ` | |||
<tr> | |||
<td> | |||
<strong> | |||
total | |||
</strong> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
> | |||
<ForwardRef(Link) | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=true", | |||
} | |||
} | |||
> | |||
<strong> | |||
5 | |||
</strong> | |||
</ForwardRef(Link)> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
/> | |||
</tr> | |||
`; | |||
exports[`should render correctly if there is missing data 2`] = ` | |||
<tr> | |||
<td> | |||
<strong> | |||
total | |||
</strong> | |||
</td> | |||
<td | |||
className="thin nowrap text-right" | |||
/> | |||
<td | |||
className="thin nowrap text-right" | |||
/> | |||
</tr> | |||
`; |
@@ -1,33 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="quality-profile-rules-sonarway-missing clearfix" | |||
> | |||
<span | |||
className="pull-left" | |||
> | |||
<span | |||
className="text-middle" | |||
> | |||
quality_profiles.sonarway_missing_rules | |||
</span> | |||
<HelpTooltip | |||
className="spacer-left" | |||
overlay="quality_profiles.sonarway_missing_rules_description" | |||
/> | |||
</span> | |||
<ForwardRef(Link) | |||
className="pull-right" | |||
data-test="rules" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=bar&activation=false&compareToProfile=baz&languages=Java", | |||
} | |||
} | |||
> | |||
158 | |||
</ForwardRef(Link)> | |||
</div> | |||
`; |
@@ -106,7 +106,10 @@ export default class EvolutionDeprecated extends React.PureComponent<Props> { | |||
const sortedProfiles = sortBy(profilesWithDeprecations, (p) => -p.activeDeprecatedRuleCount); | |||
return ( | |||
<div className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated"> | |||
<section | |||
className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated" | |||
aria-label={translate('quality_profiles.deprecated_rules')} | |||
> | |||
<h2 className="h4 spacer-bottom">{translate('quality_profiles.deprecated_rules')}</h2> | |||
<div className="spacer-bottom"> | |||
{translateWithParameters( | |||
@@ -144,7 +147,7 @@ export default class EvolutionDeprecated extends React.PureComponent<Props> { | |||
</li> | |||
))} | |||
</ul> | |||
</div> | |||
</section> | |||
); | |||
} | |||
} |
@@ -106,7 +106,10 @@ export default class EvolutionRules extends React.PureComponent<{}, State> { | |||
)}`; | |||
return ( | |||
<div className="boxed-group boxed-group-inner quality-profiles-evolution-rules"> | |||
<section | |||
className="boxed-group boxed-group-inner quality-profiles-evolution-rules" | |||
aria-label={newRulesTitle} | |||
> | |||
<h2 className="h4 spacer-bottom">{newRulesTitle}</h2> | |||
<ul> | |||
{latestRules.map((rule) => ( | |||
@@ -143,7 +146,7 @@ export default class EvolutionRules extends React.PureComponent<{}, State> { | |||
</Link> | |||
</div> | |||
)} | |||
</div> | |||
</section> | |||
); | |||
} | |||
} |
@@ -36,7 +36,10 @@ export default function EvolutionStagnant(props: Props) { | |||
} | |||
return ( | |||
<div className="boxed-group boxed-group-inner quality-profiles-evolution-stagnant"> | |||
<section | |||
className="boxed-group boxed-group-inner quality-profiles-evolution-stagnant" | |||
aria-label={translate('quality_profiles.stagnant_profiles')} | |||
> | |||
<h2 className="h4 spacer-bottom">{translate('quality_profiles.stagnant_profiles')}</h2> | |||
<div className="spacer-bottom"> | |||
{translate('quality_profiles.not_updated_more_than_year')} | |||
@@ -69,6 +72,6 @@ export default function EvolutionStagnant(props: Props) { | |||
</li> | |||
))} | |||
</ul> | |||
</div> | |||
</section> | |||
); | |||
} |
@@ -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'; |
@@ -1,30 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import Evolution, { EvolutionProps } from '../Evolution'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props: Partial<EvolutionProps> = {}) { | |||
return shallow(<Evolution profiles={[]} {...props} />); | |||
} |
@@ -1,65 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockQualityProfile } from '../../../../helpers/testMocks'; | |||
import EvolutionDeprecated from '../EvolutionDeprecated'; | |||
it('should render correctly', () => { | |||
const wrapper = shallow( | |||
<EvolutionDeprecated | |||
profiles={[ | |||
mockQualityProfile({ | |||
key: 'qp-1', | |||
name: 'Quality Profile 1', | |||
activeDeprecatedRuleCount: 0, | |||
}), | |||
mockQualityProfile({ | |||
key: 'qp-2', | |||
name: 'Quality Profile 2', | |||
childrenCount: 1, | |||
activeDeprecatedRuleCount: 2, | |||
}), | |||
mockQualityProfile({ | |||
key: 'qp-3', | |||
name: 'Quality Profile 3', | |||
depth: 2, | |||
activeDeprecatedRuleCount: 2, | |||
parentKey: 'qp-2', | |||
}), | |||
mockQualityProfile({ | |||
key: 'qp-4', | |||
name: 'Quality Profile 4', | |||
depth: 3, | |||
activeDeprecatedRuleCount: 3, | |||
parentKey: 'qp-3', | |||
}), | |||
mockQualityProfile({ | |||
key: 'qp-5', | |||
name: 'Quality Profile 5', | |||
depth: 4, | |||
activeDeprecatedRuleCount: 4, | |||
parentKey: 'qp-4', | |||
}), | |||
]} | |||
/> | |||
); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); |
@@ -1,61 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { | |||
mockLanguage, | |||
mockLocation, | |||
mockQualityProfile, | |||
mockRouter, | |||
} from '../../../../helpers/testMocks'; | |||
import { click } from '../../../../helpers/testUtils'; | |||
import { PageHeader } from '../PageHeader'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
expect(shallowRender({ actions: { create: true } })).toMatchSnapshot(); | |||
expect(shallowRender({ actions: { create: true }, languages: [] })).toMatchSnapshot(); | |||
}); | |||
it('should show a create form', () => { | |||
const wrapper = shallowRender({ actions: { create: true } }); | |||
click(wrapper.find('#quality-profiles-create')); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should show a restore form', () => { | |||
const wrapper = shallowRender({ actions: { create: true } }); | |||
click(wrapper.find('#quality-profiles-restore')); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props: Partial<PageHeader['props']> = {}) { | |||
return shallow( | |||
<PageHeader | |||
actions={{ create: false }} | |||
languages={[mockLanguage()]} | |||
location={mockLocation()} | |||
profiles={[mockQualityProfile()]} | |||
router={mockRouter()} | |||
updateProfiles={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,41 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockRouter } from '../../../../helpers/testMocks'; | |||
import { ProfilesListHeader } from '../ProfilesListHeader'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props: Partial<ProfilesListHeader['props']> = {}) { | |||
return shallow<ProfilesListHeader>( | |||
<ProfilesListHeader | |||
languages={[ | |||
{ key: 'js', name: 'JavaScript' }, | |||
{ key: 'java', name: 'Java' }, | |||
]} | |||
router={mockRouter()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,28 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import RestoreProfileForm from '../RestoreProfileForm'; | |||
it('should render correctly', () => { | |||
expect( | |||
shallow(<RestoreProfileForm onClose={jest.fn()} onRestore={jest.fn()} />) | |||
).toMatchSnapshot(); | |||
}); |
@@ -1,15 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="quality-profiles-evolution" | |||
> | |||
<EvolutionDeprecated | |||
profiles={[]} | |||
/> | |||
<EvolutionStagnant | |||
profiles={[]} | |||
/> | |||
<EvolutionRules /> | |||
</div> | |||
`; |
@@ -1,176 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated" | |||
> | |||
<h2 | |||
className="h4 spacer-bottom" | |||
> | |||
quality_profiles.deprecated_rules | |||
</h2> | |||
<div | |||
className="spacer-bottom" | |||
> | |||
quality_profiles.deprecated_rules_are_still_activated.4 | |||
</div> | |||
<ul> | |||
<li | |||
className="spacer-top" | |||
key="qp-5" | |||
> | |||
<div | |||
className="text-ellipsis little-spacer-bottom" | |||
> | |||
<ProfileLink | |||
language="js" | |||
name="Quality Profile 5" | |||
> | |||
Quality Profile 5 | |||
</ProfileLink> | |||
</div> | |||
<div | |||
className="note" | |||
> | |||
JavaScript | |||
, | |||
<ForwardRef(Link) | |||
aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.4" | |||
className="link-no-underline" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=qp-5&activation=true&statuses=DEPRECATED", | |||
} | |||
} | |||
> | |||
quality_profile.x_rules.4 | |||
</ForwardRef(Link)> | |||
<div | |||
key="qp-2" | |||
> | |||
coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 | |||
</div> | |||
<div | |||
key="qp-4" | |||
> | |||
coding_rules.filters.inheritance.x_inherited_from_y.1.Quality Profile 4 | |||
</div> | |||
</div> | |||
</li> | |||
<li | |||
className="spacer-top" | |||
key="qp-4" | |||
> | |||
<div | |||
className="text-ellipsis little-spacer-bottom" | |||
> | |||
<ProfileLink | |||
language="js" | |||
name="Quality Profile 4" | |||
> | |||
Quality Profile 4 | |||
</ProfileLink> | |||
</div> | |||
<div | |||
className="note" | |||
> | |||
JavaScript | |||
, | |||
<ForwardRef(Link) | |||
aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.3" | |||
className="link-no-underline" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=qp-4&activation=true&statuses=DEPRECATED", | |||
} | |||
} | |||
> | |||
quality_profile.x_rules.3 | |||
</ForwardRef(Link)> | |||
<div | |||
key="qp-2" | |||
> | |||
coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 | |||
</div> | |||
</div> | |||
</li> | |||
<li | |||
className="spacer-top" | |||
key="qp-2" | |||
> | |||
<div | |||
className="text-ellipsis little-spacer-bottom" | |||
> | |||
<ProfileLink | |||
language="js" | |||
name="Quality Profile 2" | |||
> | |||
Quality Profile 2 | |||
</ProfileLink> | |||
</div> | |||
<div | |||
className="note" | |||
> | |||
JavaScript | |||
, | |||
<ForwardRef(Link) | |||
aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.2" | |||
className="link-no-underline" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=qp-2&activation=true&statuses=DEPRECATED", | |||
} | |||
} | |||
> | |||
quality_profile.x_rules.2 | |||
</ForwardRef(Link)> | |||
</div> | |||
</li> | |||
<li | |||
className="spacer-top" | |||
key="qp-3" | |||
> | |||
<div | |||
className="text-ellipsis little-spacer-bottom" | |||
> | |||
<ProfileLink | |||
language="js" | |||
name="Quality Profile 3" | |||
> | |||
Quality Profile 3 | |||
</ProfileLink> | |||
</div> | |||
<div | |||
className="note" | |||
> | |||
JavaScript | |||
, | |||
<ForwardRef(Link) | |||
aria-label="quality_profile.lang_deprecated_x_rules.JavaScript.2" | |||
className="link-no-underline" | |||
to={ | |||
{ | |||
"pathname": "/coding_rules", | |||
"search": "?qprofile=qp-3&activation=true&statuses=DEPRECATED", | |||
} | |||
} | |||
> | |||
quality_profile.x_rules.2 | |||
</ForwardRef(Link)> | |||
<div | |||
key="qp-2" | |||
> | |||
coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2 | |||
</div> | |||
</div> | |||
</li> | |||
</ul> | |||
</div> | |||
`; |
@@ -1,248 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<header | |||
className="page-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
quality_profiles.page | |||
</h1> | |||
<div | |||
className="page-description markdown" | |||
> | |||
quality_profiles.intro1 | |||
<br /> | |||
quality_profiles.intro2 | |||
<DocLink | |||
className="spacer-left" | |||
to="/instance-administration/quality-profiles/" | |||
> | |||
learn_more | |||
</DocLink> | |||
</div> | |||
</header> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<header | |||
className="page-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
quality_profiles.page | |||
</h1> | |||
<div | |||
className="page-actions" | |||
> | |||
<Button | |||
disabled={false} | |||
id="quality-profiles-create" | |||
onClick={[Function]} | |||
> | |||
create | |||
</Button> | |||
<Button | |||
className="little-spacer-left" | |||
id="quality-profiles-restore" | |||
onClick={[Function]} | |||
> | |||
restore | |||
</Button> | |||
</div> | |||
<div | |||
className="page-description markdown" | |||
> | |||
quality_profiles.intro1 | |||
<br /> | |||
quality_profiles.intro2 | |||
<DocLink | |||
className="spacer-left" | |||
to="/instance-administration/quality-profiles/" | |||
> | |||
learn_more | |||
</DocLink> | |||
</div> | |||
</header> | |||
`; | |||
exports[`should render correctly 3`] = ` | |||
<header | |||
className="page-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
quality_profiles.page | |||
</h1> | |||
<div | |||
className="page-actions" | |||
> | |||
<Button | |||
disabled={true} | |||
id="quality-profiles-create" | |||
onClick={[Function]} | |||
> | |||
create | |||
</Button> | |||
<Button | |||
className="little-spacer-left" | |||
id="quality-profiles-restore" | |||
onClick={[Function]} | |||
> | |||
restore | |||
</Button> | |||
<Alert | |||
className="spacer-top" | |||
variant="warning" | |||
> | |||
quality_profiles.no_languages_available | |||
</Alert> | |||
</div> | |||
<div | |||
className="page-description markdown" | |||
> | |||
quality_profiles.intro1 | |||
<br /> | |||
quality_profiles.intro2 | |||
<DocLink | |||
className="spacer-left" | |||
to="/instance-administration/quality-profiles/" | |||
> | |||
learn_more | |||
</DocLink> | |||
</div> | |||
</header> | |||
`; | |||
exports[`should show a create form 1`] = ` | |||
<header | |||
className="page-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
quality_profiles.page | |||
</h1> | |||
<div | |||
className="page-actions" | |||
> | |||
<Button | |||
disabled={false} | |||
id="quality-profiles-create" | |||
onClick={[Function]} | |||
> | |||
create | |||
</Button> | |||
<Button | |||
className="little-spacer-left" | |||
id="quality-profiles-restore" | |||
onClick={[Function]} | |||
> | |||
restore | |||
</Button> | |||
</div> | |||
<div | |||
className="page-description markdown" | |||
> | |||
quality_profiles.intro1 | |||
<br /> | |||
quality_profiles.intro2 | |||
<DocLink | |||
className="spacer-left" | |||
to="/instance-administration/quality-profiles/" | |||
> | |||
learn_more | |||
</DocLink> | |||
</div> | |||
<CreateProfileForm | |||
languages={ | |||
[ | |||
{ | |||
"key": "css", | |||
"name": "CSS", | |||
}, | |||
] | |||
} | |||
location={ | |||
{ | |||
"hash": "", | |||
"key": "key", | |||
"pathname": "/path", | |||
"query": {}, | |||
"search": "", | |||
"state": {}, | |||
} | |||
} | |||
onClose={[Function]} | |||
onCreate={[Function]} | |||
profiles={ | |||
[ | |||
{ | |||
"activeDeprecatedRuleCount": 2, | |||
"activeRuleCount": 10, | |||
"childrenCount": 0, | |||
"depth": 1, | |||
"isBuiltIn": false, | |||
"isDefault": false, | |||
"isInherited": false, | |||
"key": "key", | |||
"language": "js", | |||
"languageName": "JavaScript", | |||
"name": "name", | |||
"projectCount": 3, | |||
}, | |||
] | |||
} | |||
/> | |||
</header> | |||
`; | |||
exports[`should show a restore form 1`] = ` | |||
<header | |||
className="page-header" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
quality_profiles.page | |||
</h1> | |||
<div | |||
className="page-actions" | |||
> | |||
<Button | |||
disabled={false} | |||
id="quality-profiles-create" | |||
onClick={[Function]} | |||
> | |||
create | |||
</Button> | |||
<Button | |||
className="little-spacer-left" | |||
id="quality-profiles-restore" | |||
onClick={[Function]} | |||
> | |||
restore | |||
</Button> | |||
</div> | |||
<div | |||
className="page-description markdown" | |||
> | |||
quality_profiles.intro1 | |||
<br /> | |||
quality_profiles.intro2 | |||
<DocLink | |||
className="spacer-left" | |||
to="/instance-administration/quality-profiles/" | |||
> | |||
learn_more | |||
</DocLink> | |||
</div> | |||
<RestoreProfileForm | |||
onClose={[Function]} | |||
onRestore={[MockFunction]} | |||
/> | |||
</header> | |||
`; |
@@ -1,37 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<div | |||
className="quality-profiles-list-header clearfix" | |||
> | |||
<label | |||
className="spacer-right" | |||
htmlFor="quality-profiles-filter-input" | |||
> | |||
quality_profiles.filter_by | |||
: | |||
</label> | |||
<Select | |||
autoFocus={true} | |||
className="input-medium" | |||
id="quality-profiles-filter" | |||
inputId="quality-profiles-filter-input" | |||
isClearable={true} | |||
isSearchable={true} | |||
onChange={[Function]} | |||
options={ | |||
[ | |||
{ | |||
"label": "JavaScript", | |||
"value": "js", | |||
}, | |||
{ | |||
"label": "Java", | |||
"value": "java", | |||
}, | |||
] | |||
} | |||
value={[]} | |||
/> | |||
</div> | |||
`; |
@@ -1,61 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<Modal | |||
contentLabel="quality_profiles.restore_profile" | |||
onRequestClose={[MockFunction]} | |||
size="small" | |||
> | |||
<form | |||
id="restore-profile-form" | |||
onSubmit={[Function]} | |||
> | |||
<div | |||
className="modal-head" | |||
> | |||
<h2> | |||
quality_profiles.restore_profile | |||
</h2> | |||
</div> | |||
<div | |||
className="modal-body" | |||
> | |||
<MandatoryFieldsExplanation | |||
className="modal-field" | |||
/> | |||
<div | |||
className="modal-field" | |||
> | |||
<label | |||
htmlFor="restore-profile-backup" | |||
> | |||
backup | |||
<MandatoryFieldMarker /> | |||
</label> | |||
<input | |||
id="restore-profile-backup" | |||
name="backup" | |||
required={true} | |||
type="file" | |||
/> | |||
</div> | |||
</div> | |||
<div | |||
className="modal-foot" | |||
> | |||
<SubmitButton | |||
disabled={false} | |||
id="restore-profile-submit" | |||
> | |||
restore | |||
</SubmitButton> | |||
<ResetButtonLink | |||
id="restore-profile-cancel" | |||
onClick={[MockFunction]} | |||
> | |||
cancel | |||
</ResetButtonLink> | |||
</div> | |||
</form> | |||
</Modal> | |||
`; |
@@ -33,7 +33,7 @@ export interface Exporter { | |||
export interface ProfileChangelogEvent { | |||
action: string; | |||
authorName: string; | |||
authorName?: string; | |||
date: string; | |||
params?: Dict<string | null>; | |||
ruleKey: string; |
@@ -20,7 +20,7 @@ | |||
import { To } from 'react-router-dom'; | |||
import { CompareResponse } from '../api/quality-profiles'; | |||
import { RuleDescriptionSections } from '../apps/coding-rules/rule'; | |||
import { Exporter, Profile } from '../apps/quality-profiles/types'; | |||
import { Exporter, Profile, ProfileChangelogEvent } from '../apps/quality-profiles/types'; | |||
import { LogsLevels } from '../apps/system/utils'; | |||
import { Location, Router } from '../components/hoc/withRouter'; | |||
import { AppState } from '../types/appstate'; | |||
@@ -54,6 +54,7 @@ import { | |||
SysInfoLogging, | |||
SysInfoStandalone, | |||
UserGroupMember, | |||
UserSelected, | |||
} from '../types/types'; | |||
import { CurrentUser, LoggedInUser, User } from '../types/users'; | |||
@@ -500,7 +501,9 @@ export function mockQualityProfileInheritance( | |||
}; | |||
} | |||
export function mockQualityProfileChangelogEvent(eventOverride?: any) { | |||
export function mockQualityProfileChangelogEvent( | |||
eventOverride?: Partial<ProfileChangelogEvent> | |||
): ProfileChangelogEvent { | |||
return { | |||
action: 'ACTIVATED', | |||
date: '2019-04-23T02:12:32+0100', | |||
@@ -677,6 +680,16 @@ export function mockUser(overrides: Partial<User> = {}): User { | |||
}; | |||
} | |||
export function mockUserSelected(overrides: Partial<UserSelected> = {}): UserSelected { | |||
return { | |||
active: true, | |||
login: 'john.doe', | |||
name: 'John Doe', | |||
selected: true, | |||
...overrides, | |||
}; | |||
} | |||
export function mockUserGroupMember(overrides: Partial<UserGroupMember> = {}): UserGroupMember { | |||
return { | |||
login: 'john.doe', |
@@ -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. |