Procházet zdrojové kódy

SONAR-18441 RTL migration for Quality Profiles

tags/10.2.0.77647
guillaume-peoch-sonarsource před 11 měsíci
rodič
revize
5ebbed2a0f
87 změnil soubory, kde provedl 1288 přidání a 5845 odebrání
  1. 423
    21
      server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts
  2. 0
    7
      server/sonar-web/src/main/js/api/quality-profiles.ts
  3. 0
    5
      server/sonar-web/src/main/js/api/rules.ts
  4. 513
    0
      server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfileApp-it.tsx
  5. 119
    71
      server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx
  6. 1
    1
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx
  7. 0
    100
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx
  8. 140
    0
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx
  9. 0
    88
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-test.tsx
  10. 0
    55
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx
  11. 0
    45
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangesList-test.tsx
  12. 0
    28
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ParameterChange-test.tsx
  13. 0
    29
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/SeverityChange-test.tsx
  14. 0
    95
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogContainer-test.tsx.snap
  15. 0
    24
      server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogSearch-test.tsx.snap
  16. 0
    25
      server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.tsx
  17. 1
    2
      server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx
  18. 9
    3
      server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx
  19. 1
    1
      server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileModalForm.tsx
  20. 0
    31
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/BuiltInQualityProfileBadge-test.tsx
  21. 0
    55
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/DeleteProfileForm-test.tsx
  22. 0
    342
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx
  23. 0
    94
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/QualityProfilesApp-test.tsx
  24. 0
    21
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/BuiltInQualityProfileBadge-test.tsx.snap
  25. 0
    134
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/DeleteProfileForm-test.tsx.snap
  26. 0
    392
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap
  27. 0
    47
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/QualityProfilesApp-test.tsx.snap
  28. 2
    2
      server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx
  29. 7
    8
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx
  30. 5
    2
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.tsx
  31. 5
    2
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx
  32. 3
    3
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx
  33. 6
    2
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx
  34. 5
    1
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx
  35. 2
    2
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx
  36. 11
    5
      server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx
  37. 0
    69
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeParentForm-test.tsx
  38. 0
    102
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx
  39. 0
    55
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx
  40. 0
    40
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileExporters-test.tsx
  41. 0
    69
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritance-test.tsx
  42. 0
    49
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritanceBox-test.tsx
  43. 0
    119
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx
  44. 0
    96
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsForm-test.tsx
  45. 0
    95
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx
  46. 0
    65
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx
  47. 0
    64
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx
  48. 0
    87
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileProjects-test.tsx
  49. 0
    150
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx
  50. 0
    28
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.tsx
  51. 0
    43
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.tsx
  52. 0
    37
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.tsx
  53. 0
    35
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.tsx
  54. 0
    97
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeParentForm-test.tsx.snap
  55. 0
    79
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeProjectsForm-test.tsx.snap
  56. 0
    506
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap
  57. 0
    34
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileExporters-test.tsx.snap
  58. 0
    147
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritance-test.tsx.snap
  59. 0
    136
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritanceBox-test.tsx.snap
  60. 0
    93
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap
  61. 0
    230
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap
  62. 0
    78
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap
  63. 0
    28
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsGroup-test.tsx.snap
  64. 0
    29
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap
  65. 0
    247
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileProjects-test.tsx.snap
  66. 0
    101
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap
  67. 0
    32
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap
  68. 0
    132
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap
  69. 0
    124
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap
  70. 0
    33
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap
  71. 5
    2
      server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx
  72. 5
    2
      server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx
  73. 5
    2
      server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx
  74. 1
    1
      server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx
  75. 0
    30
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/Evolution-test.tsx
  76. 0
    65
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx
  77. 0
    61
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx
  78. 0
    41
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListHeader-test.tsx
  79. 0
    28
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx
  80. 0
    15
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/Evolution-test.tsx.snap
  81. 0
    176
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap
  82. 0
    248
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap
  83. 0
    37
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListHeader-test.tsx.snap
  84. 0
    61
      server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap
  85. 1
    1
      server/sonar-web/src/main/js/apps/quality-profiles/types.ts
  86. 15
    2
      server/sonar-web/src/main/js/helpers/testMocks.ts
  87. 3
    1
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 423
- 21
server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts Zobrazit soubor

@@ -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> {

+ 0
- 7
server/sonar-web/src/main/js/api/quality-profiles.ts Zobrazit soubor

@@ -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

+ 0
- 5
server/sonar-web/src/main/js/api/rules.ts Zobrazit soubor

@@ -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 }>> {

+ 513
- 0
server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfileApp-it.tsx Zobrazit soubor

@@ -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, {});
}

+ 119
- 71
server/sonar-web/src/main/js/apps/quality-profiles/__tests__/QualityProfilesApp-it.tsx Zobrazit soubor

@@ -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 () => {

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx Zobrazit soubor

@@ -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 };


+ 0
- 100
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/Changelog-test.tsx Zobrazit soubor

@@ -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');
});

+ 140
- 0
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx Zobrazit soubor

@@ -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, {});
}

+ 0
- 88
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-test.tsx Zobrazit soubor

@@ -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()}
/>
);
}

+ 0
- 55
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogSearch-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 45
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangesList-test.tsx Zobrazit soubor

@@ -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');
});

+ 0
- 28
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ParameterChange-test.tsx Zobrazit soubor

@@ -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());
});

+ 0
- 29
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/SeverityChange-test.tsx Zobrazit soubor

@@ -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');
});

+ 0
- 95
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogContainer-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 24
server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/__snapshots__/ChangelogSearch-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 25
server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.tsx Zobrazit soubor

@@ -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>;
}

+ 1
- 2
server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.tsx Zobrazit soubor

@@ -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 (

+ 9
- 3
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx Zobrazit soubor

@@ -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,

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileModalForm.tsx Zobrazit soubor

@@ -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';

+ 0
- 31
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/BuiltInQualityProfileBadge-test.tsx Zobrazit soubor

@@ -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} />);
}

+ 0
- 55
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/DeleteProfileForm-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 342
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 94
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/QualityProfilesApp-test.tsx Zobrazit soubor

@@ -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>
);
}

+ 0
- 21
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/BuiltInQualityProfileBadge-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 134
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/DeleteProfileForm-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 392
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 47
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/QualityProfilesApp-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 2
- 2
server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx Zobrazit soubor

@@ -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';

+ 7
- 8
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx Zobrazit soubor

@@ -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} />

+ 5
- 2
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.tsx Zobrazit soubor

@@ -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>
);
}

+ 5
- 2
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx Zobrazit soubor

@@ -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>
);
}
}

+ 3
- 3
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissions.tsx Zobrazit soubor

@@ -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>
);
}
}

+ 6
- 2
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsGroup.tsx Zobrazit soubor

@@ -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}
/>

+ 5
- 1
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfilePermissionsUser.tsx Zobrazit soubor

@@ -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}
/>

+ 2
- 2
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx Zobrazit soubor

@@ -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>
);
}
}

+ 11
- 5
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx Zobrazit soubor

@@ -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>
);
}
}

+ 0
- 69
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeParentForm-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 102
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx Zobrazit soubor

@@ -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} />
);
}

+ 0
- 55
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 40
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileExporters-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 69
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritance-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 49
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileInheritanceBox-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 119
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx Zobrazit soubor

@@ -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} />);
}

+ 0
- 96
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsForm-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 95
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 65
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsGroup-test.tsx Zobrazit soubor

@@ -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);
});

+ 0
- 64
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsUser-test.tsx Zobrazit soubor

@@ -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);
});

+ 0
- 87
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileProjects-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 150
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx Zobrazit soubor

@@ -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);
});

+ 0
- 28
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesDeprecatedWarning-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 43
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowOfType-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 37
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesRowTotal-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 35
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRulesSonarWayComparison-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 97
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeParentForm-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 79
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ChangeProjectsForm-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 506
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 34
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileExporters-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 147
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritance-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 136
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileInheritanceBox-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 93
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissions-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 230
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsForm-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 78
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 28
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsGroup-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 29
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsUser-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 247
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileProjects-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 101
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 32
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 132
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowOfType-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 124
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesRowTotal-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 33
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 5
- 2
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx Zobrazit soubor

@@ -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>
);
}
}

+ 5
- 2
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.tsx Zobrazit soubor

@@ -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>
);
}
}

+ 5
- 2
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.tsx Zobrazit soubor

@@ -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>
);
}

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx Zobrazit soubor

@@ -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';

+ 0
- 30
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/Evolution-test.tsx Zobrazit soubor

@@ -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} />);
}

+ 0
- 65
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 61
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/PageHeader-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 41
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/ProfilesListHeader-test.tsx Zobrazit soubor

@@ -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}
/>
);
}

+ 0
- 28
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/RestoreProfileForm-test.tsx Zobrazit soubor

@@ -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();
});

+ 0
- 15
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/Evolution-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 176
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 248
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/PageHeader-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 37
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/ProfilesListHeader-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 0
- 61
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/RestoreProfileForm-test.tsx.snap Zobrazit soubor

@@ -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>
`;

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-profiles/types.ts Zobrazit soubor

@@ -33,7 +33,7 @@ export interface Exporter {

export interface ProfileChangelogEvent {
action: string;
authorName: string;
authorName?: string;
date: string;
params?: Dict<string | null>;
ruleKey: string;

+ 15
- 2
server/sonar-web/src/main/js/helpers/testMocks.ts Zobrazit soubor

@@ -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',

+ 3
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties Zobrazit soubor

@@ -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.

Načítá se…
Zrušit
Uložit