]> source.dussan.org Git - sonarqube.git/blob
e011d4c9c86d8439faba47ac2437e6ce63a051b8
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 import { screen, waitFor } from '@testing-library/react';
21 import userEvent from '@testing-library/user-event';
22 import { ComponentQualifier, Visibility } from '~sonar-aligned/types/component';
23 import AlmSettingsServiceMock from '../../../../../api/mocks/AlmSettingsServiceMock';
24 import ComputeEngineServiceMock from '../../../../../api/mocks/ComputeEngineServiceMock';
25 import DopTranslationServiceMock from '../../../../../api/mocks/DopTranslationServiceMock';
26 import GithubProvisioningServiceMock from '../../../../../api/mocks/GithubProvisioningServiceMock';
27 import GitlabProvisioningServiceMock from '../../../../../api/mocks/GitlabProvisioningServiceMock';
28 import PermissionsServiceMock from '../../../../../api/mocks/PermissionsServiceMock';
29 import SystemServiceMock from '../../../../../api/mocks/SystemServiceMock';
30 import { mockGitlabConfiguration } from '../../../../../helpers/mocks/alm-integrations';
31 import { mockComponent } from '../../../../../helpers/mocks/component';
32 import { mockGitHubConfiguration } from '../../../../../helpers/mocks/dop-translation';
33 import { mockPermissionGroup, mockPermissionUser } from '../../../../../helpers/mocks/permissions';
34 import {
35   PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE,
36   PERMISSIONS_ORDER_FOR_VIEW,
37 } from '../../../../../helpers/permissions';
38 import {
39   RenderContext,
40   renderAppWithComponentContext,
41 } from '../../../../../helpers/testReactTestingUtils';
42 import { AlmKeys } from '../../../../../types/alm-settings';
43 import { ComponentContextShape } from '../../../../../types/component';
44 import { Feature } from '../../../../../types/features';
45 import { Permissions } from '../../../../../types/permissions';
46 import { ProvisioningType } from '../../../../../types/provisioning';
47 import { TaskStatuses, TaskTypes } from '../../../../../types/tasks';
48 import { Component, PermissionGroup, PermissionUser, Provider } from '../../../../../types/types';
49 import { projectPermissionsRoutes } from '../../../routes';
50 import { getPageObject } from '../../../test-utils';
51
52 let serviceMock: PermissionsServiceMock;
53 let dopTranslationHandler: DopTranslationServiceMock;
54 let githubHandler: GithubProvisioningServiceMock;
55 let gitlabHandler: GitlabProvisioningServiceMock;
56 let almHandler: AlmSettingsServiceMock;
57 let systemHandler: SystemServiceMock;
58 let computeEngineHandler: ComputeEngineServiceMock;
59
60 beforeAll(() => {
61   serviceMock = new PermissionsServiceMock();
62   dopTranslationHandler = new DopTranslationServiceMock();
63   githubHandler = new GithubProvisioningServiceMock(dopTranslationHandler);
64   gitlabHandler = new GitlabProvisioningServiceMock();
65   almHandler = new AlmSettingsServiceMock();
66   systemHandler = new SystemServiceMock();
67   computeEngineHandler = new ComputeEngineServiceMock();
68 });
69
70 afterEach(() => {
71   serviceMock.reset();
72   dopTranslationHandler.reset();
73   githubHandler.reset();
74   gitlabHandler.reset();
75   almHandler.reset();
76   computeEngineHandler.reset();
77 });
78
79 describe('rendering', () => {
80   it.each([
81     [ComponentQualifier.Project, 'roles.page.description2', PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE],
82     [ComponentQualifier.Portfolio, 'roles.page.description_portfolio', PERMISSIONS_ORDER_FOR_VIEW],
83     [
84       ComponentQualifier.Application,
85       'roles.page.description_application',
86       PERMISSIONS_ORDER_FOR_VIEW,
87     ],
88   ])('should render correctly for %s', async (qualifier, description, permissions) => {
89     const user = userEvent.setup();
90     const ui = getPageObject(user);
91     renderPermissionsProjectApp({ qualifier, visibility: Visibility.Private });
92     await ui.appLoaded();
93
94     expect(screen.getByText(description)).toBeInTheDocument();
95     permissions.forEach((permission) => {
96       expect(ui.projectPermissionCheckbox('johndoe', permission).get()).toBeInTheDocument();
97     });
98   });
99 });
100
101 describe('filtering', () => {
102   it('should allow to filter permission holders', async () => {
103     const user = userEvent.setup();
104     const ui = getPageObject(user);
105     renderPermissionsProjectApp();
106     await ui.appLoaded();
107
108     expect(screen.getByText('sonar-users')).toBeInTheDocument();
109     expect(screen.getByText('johndoe')).toBeInTheDocument();
110
111     await ui.showOnlyUsers();
112     expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
113     expect(screen.getByText('johndoe')).toBeInTheDocument();
114
115     await ui.showOnlyGroups();
116     expect(screen.getByText('sonar-users')).toBeInTheDocument();
117     expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
118
119     await ui.showAll();
120     expect(screen.getByText('sonar-users')).toBeInTheDocument();
121     expect(screen.getByText('johndoe')).toBeInTheDocument();
122
123     await ui.searchFor('sonar-adm');
124     expect(screen.getByText('sonar-admins')).toBeInTheDocument();
125     expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
126     expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
127
128     await ui.clearSearch();
129     expect(screen.getByText('sonar-users')).toBeInTheDocument();
130     expect(screen.getByText('johndoe')).toBeInTheDocument();
131   });
132
133   it('should allow to show only permission holders with a specific permission', async () => {
134     const user = userEvent.setup();
135     const ui = getPageObject(user);
136     renderPermissionsProjectApp();
137     await ui.appLoaded();
138
139     expect(screen.getAllByRole('row').length).toBe(10);
140     await ui.toggleFilterByPermission(Permissions.Admin);
141     expect(screen.getAllByRole('row').length).toBe(3);
142     await ui.toggleFilterByPermission(Permissions.Admin);
143     expect(screen.getAllByRole('row').length).toBe(10);
144   });
145 });
146
147 describe('assigning/revoking permissions', () => {
148   it('should allow to apply a permission template', async () => {
149     const user = userEvent.setup();
150     const ui = getPageObject(user);
151     renderPermissionsProjectApp();
152     await ui.appLoaded();
153
154     await ui.openTemplateModal();
155     expect(ui.confirmApplyTemplateBtn.get()).toBeDisabled();
156     await ui.chooseTemplate('Permission Template 2');
157     expect(ui.templateSuccessfullyApplied.get()).toBeInTheDocument();
158     await ui.closeTemplateModal();
159     expect(ui.templateSuccessfullyApplied.query()).not.toBeInTheDocument();
160   });
161
162   it('should allow to turn a public project private (and vice-versa)', async () => {
163     const user = userEvent.setup();
164     const ui = getPageObject(user);
165     renderPermissionsProjectApp();
166     await ui.appLoaded();
167
168     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
169     expect(
170       ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).query(),
171     ).not.toBeInTheDocument();
172     await ui.turnProjectPrivate();
173     expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
174     expect(
175       ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get(),
176     ).toBeInTheDocument();
177
178     await ui.turnProjectPublic();
179     expect(ui.makePublicDisclaimer.get()).toBeInTheDocument();
180     await ui.confirmTurnProjectPublic();
181     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
182   });
183
184   it('should add and remove permissions to/from a group', async () => {
185     const user = userEvent.setup();
186     const ui = getPageObject(user);
187     renderPermissionsProjectApp();
188     await ui.appLoaded();
189
190     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
191
192     await ui.toggleProjectPermission('sonar-users', Permissions.Admin);
193     await ui.appLoaded();
194     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Admin).get()).toBeChecked();
195
196     await ui.toggleProjectPermission('sonar-users', Permissions.Admin);
197     await ui.appLoaded();
198     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
199   });
200
201   it('should add and remove permissions to/from a user', async () => {
202     const user = userEvent.setup();
203     const ui = getPageObject(user);
204     renderPermissionsProjectApp();
205     await ui.appLoaded();
206
207     expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
208
209     await ui.toggleProjectPermission('johndoe', Permissions.Scan);
210     await ui.appLoaded();
211     expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).toBeChecked();
212
213     await ui.toggleProjectPermission('johndoe', Permissions.Scan);
214     await ui.appLoaded();
215     expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
216   });
217
218   it('should handle errors correctly', async () => {
219     serviceMock.setIsAllowedToChangePermissions(false);
220     const user = userEvent.setup();
221     const ui = getPageObject(user);
222     renderPermissionsProjectApp();
223     await ui.appLoaded();
224
225     expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
226     await ui.toggleProjectPermission('johndoe', Permissions.Scan);
227     await ui.appLoaded();
228     expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
229   });
230 });
231
232 it('should correctly handle pagination', async () => {
233   const groups: PermissionGroup[] = [];
234   const users: PermissionUser[] = [];
235   Array.from(Array(20).keys()).forEach((i) => {
236     groups.push(mockPermissionGroup({ name: `Group ${i}` }));
237     users.push(mockPermissionUser({ login: `user-${i}` }));
238   });
239   serviceMock.setGroups(groups);
240   serviceMock.setUsers(users);
241
242   const user = userEvent.setup();
243   const ui = getPageObject(user);
244   renderPermissionsProjectApp();
245   await ui.appLoaded();
246
247   expect(screen.getAllByRole('row').length).toBe(11);
248   await ui.clickLoadMore();
249   expect(screen.getAllByRole('row').length).toBe(21);
250 });
251
252 describe('GitHub provisioning', () => {
253   beforeEach(() => {
254     systemHandler.setProvider(Provider.Github);
255   });
256
257   it('should not allow to change visibility for GH Project with auto-provisioning', async () => {
258     const user = userEvent.setup();
259     const ui = getPageObject(user);
260     dopTranslationHandler.gitHubConfigurations.push(
261       mockGitHubConfiguration({ provisioningType: ProvisioningType.auto }),
262     );
263     almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
264       almSetting: 'test',
265       repository: 'test',
266       monorepo: false,
267       project: 'my-project',
268     });
269     renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
270     await ui.appLoaded();
271
272     expect(ui.visibilityRadio(Visibility.Public).get()).toBeDisabled();
273     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
274     expect(ui.visibilityRadio(Visibility.Private).get()).toBeDisabled();
275     await ui.turnProjectPrivate();
276     expect(ui.visibilityRadio(Visibility.Private).get()).not.toBeChecked();
277   });
278
279   it('should allow to change visibility for non-GH Project', async () => {
280     const user = userEvent.setup();
281     const ui = getPageObject(user);
282     dopTranslationHandler.gitHubConfigurations.push(
283       mockGitHubConfiguration({ provisioningType: ProvisioningType.auto }),
284     );
285     almHandler.handleSetProjectBinding(AlmKeys.Azure, {
286       almSetting: 'test',
287       repository: 'test',
288       monorepo: false,
289       project: 'my-project',
290     });
291     renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
292     await ui.appLoaded();
293
294     expect(ui.visibilityRadio(Visibility.Public).get()).not.toHaveClass('disabled');
295     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
296     expect(ui.visibilityRadio(Visibility.Private).get()).not.toHaveClass('disabled');
297     await ui.turnProjectPrivate();
298     expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
299   });
300
301   it('should allow to change visibility for GH Project with disabled auto-provisioning', async () => {
302     const user = userEvent.setup();
303     const ui = getPageObject(user);
304     dopTranslationHandler.gitHubConfigurations.push(mockGitHubConfiguration());
305     almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
306       almSetting: 'test',
307       repository: 'test',
308       monorepo: false,
309       project: 'my-project',
310     });
311     renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
312     await ui.appLoaded();
313
314     expect(ui.visibilityRadio(Visibility.Public).get()).not.toHaveClass('disabled');
315     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
316     expect(ui.visibilityRadio(Visibility.Private).get()).not.toHaveClass('disabled');
317     await ui.turnProjectPrivate();
318     expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
319   });
320
321   it('should have disabled permissions for GH Project', async () => {
322     const user = userEvent.setup();
323     const ui = getPageObject(user);
324     dopTranslationHandler.gitHubConfigurations.push(
325       mockGitHubConfiguration({ provisioningType: ProvisioningType.auto }),
326     );
327     almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
328       almSetting: 'test',
329       repository: 'test',
330       monorepo: false,
331       project: 'my-project',
332     });
333     renderPermissionsProjectApp(
334       {},
335       { featureList: [Feature.GithubProvisioning] },
336       {
337         component: mockComponent({ visibility: Visibility.Private }),
338       },
339     );
340     await ui.appLoaded();
341
342     expect(ui.pageTitle.get()).toBeInTheDocument();
343     await waitFor(() =>
344       expect(ui.pageTitle.get()).toHaveAccessibleName(/project_permission.managed/),
345     );
346     expect(ui.pageTitle.byRole('img').get()).toBeInTheDocument();
347     expect(ui.githubExplanations.get()).toBeInTheDocument();
348
349     expect(ui.projectPermissionCheckbox('John', Permissions.Admin).get()).toBeChecked();
350     expect(ui.projectPermissionCheckbox('John', Permissions.Admin).get()).toBeDisabled();
351     expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).toBeChecked();
352     expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).toBeEnabled();
353     await ui.toggleProjectPermission('Alexa', Permissions.IssueAdmin);
354     expect(ui.confirmRemovePermissionDialog.get()).toBeInTheDocument();
355     expect(ui.confirmRemovePermissionDialog.get()).toHaveTextContent(
356       `${Permissions.IssueAdmin}Alexa`,
357     );
358     await user.click(ui.confirmRemovePermissionDialog.byRole('button', { name: 'confirm' }).get());
359     expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).not.toBeChecked();
360
361     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get()).toBeChecked();
362     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get()).toBeEnabled();
363     await ui.toggleProjectPermission('sonar-users', Permissions.Browse);
364     expect(ui.confirmRemovePermissionDialog.get()).toBeInTheDocument();
365     expect(ui.confirmRemovePermissionDialog.get()).toHaveTextContent(
366       `${Permissions.Browse}sonar-users`,
367     );
368     await user.click(ui.confirmRemovePermissionDialog.byRole('button', { name: 'confirm' }).get());
369     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get()).not.toBeChecked();
370     expect(ui.projectPermissionCheckbox('sonar-admins', Permissions.Admin).get()).toBeChecked();
371     expect(ui.projectPermissionCheckbox('sonar-admins', Permissions.Admin).get()).toHaveAttribute(
372       'disabled',
373     );
374
375     const johnRow = screen.getAllByRole('row')[4];
376     expect(johnRow).toHaveTextContent('John');
377     expect(ui.githubLogo.get(johnRow)).toBeInTheDocument();
378     const alexaRow = screen.getAllByRole('row')[5];
379     expect(alexaRow).toHaveTextContent('Alexa');
380     expect(ui.githubLogo.query(alexaRow)).not.toBeInTheDocument();
381     const usersGroupRow = screen.getAllByRole('row')[1];
382     expect(usersGroupRow).toHaveTextContent('sonar-users');
383     expect(ui.githubLogo.query(usersGroupRow)).not.toBeInTheDocument();
384     const adminsGroupRow = screen.getAllByRole('row')[2];
385     expect(adminsGroupRow).toHaveTextContent('sonar-admins');
386     expect(ui.githubLogo.query(adminsGroupRow)).toBeInTheDocument();
387
388     expect(ui.applyTemplateBtn.query()).not.toBeInTheDocument();
389
390     // not possible to grant permissions at all
391     expect(
392       screen
393         .getAllByRole('checkbox', { checked: false })
394         .every((item) => item.getAttributeNames().includes('disabled')),
395     ).toBe(true);
396   });
397
398   it('should allow to change permissions for GH Project without auto-provisioning', async () => {
399     const user = userEvent.setup();
400     const ui = getPageObject(user);
401     dopTranslationHandler.gitHubConfigurations.push(mockGitHubConfiguration());
402     almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
403       almSetting: 'test',
404       repository: 'test',
405       monorepo: false,
406       project: 'my-project',
407     });
408     renderPermissionsProjectApp(
409       { visibility: Visibility.Private },
410       { featureList: [Feature.GithubProvisioning] },
411     );
412     await ui.appLoaded();
413
414     expect(ui.pageTitle.get()).toBeInTheDocument();
415     expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
416
417     expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
418
419     // no restrictions
420     expect(
421       screen
422         .getAllByRole('checkbox')
423         .every((item) => item.getAttributeNames().includes('disabled')),
424     ).toBe(false);
425   });
426
427   it('should allow to change permissions for non-GH Project', async () => {
428     const user = userEvent.setup();
429     const ui = getPageObject(user);
430     dopTranslationHandler.gitHubConfigurations.push(
431       mockGitHubConfiguration({ provisioningType: ProvisioningType.auto }),
432     );
433     renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
434     await ui.appLoaded();
435
436     expect(ui.pageTitle.get()).toBeInTheDocument();
437     expect(ui.nonGHProjectWarning.get()).toBeInTheDocument();
438     expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
439
440     expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
441
442     // no restrictions
443     expect(
444       screen
445         .getAllByRole('checkbox')
446         .every((item) => item.getAttributeNames().includes('disabled')),
447     ).toBe(false);
448   });
449 });
450
451 describe('GitLab provisioning', () => {
452   beforeEach(() => {
453     systemHandler.setProvider(Provider.Gitlab);
454     computeEngineHandler.addTask({
455       status: TaskStatuses.InProgress,
456       executedAt: '2022-02-03T11:55:35+0200',
457       type: TaskTypes.GitlabProvisioning,
458     });
459     computeEngineHandler.addTask({
460       status: TaskStatuses.Failed,
461       executedAt: '2022-02-03T11:45:35+0200',
462       errorMessage: "T'es mauvais Jacques",
463       type: TaskTypes.GitlabProvisioning,
464     });
465   });
466
467   it('should not allow to change visibility for GitLab Project with auto-provisioning', async () => {
468     const user = userEvent.setup();
469     const ui = getPageObject(user);
470     dopTranslationHandler.gitHubConfigurations.push(
471       mockGitHubConfiguration({ provisioningType: ProvisioningType.jit }),
472     );
473     gitlabHandler.setGitlabConfigurations([
474       mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }),
475     ]);
476     almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
477       almSetting: 'test',
478       repository: 'test',
479       monorepo: false,
480       project: 'my-project',
481     });
482
483     renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
484     await ui.appLoaded();
485
486     expect(ui.visibilityRadio(Visibility.Public).get()).toBeDisabled();
487     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
488     expect(ui.visibilityRadio(Visibility.Private).get()).toBeDisabled();
489     await ui.turnProjectPrivate();
490     expect(ui.visibilityRadio(Visibility.Private).get()).not.toBeChecked();
491   });
492
493   it('should allow to change visibility for non-GitLab Project', async () => {
494     const user = userEvent.setup();
495     const ui = getPageObject(user);
496     gitlabHandler.setGitlabConfigurations([
497       mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }),
498     ]);
499     almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
500       almSetting: 'test',
501       repository: 'test',
502       monorepo: false,
503       project: 'my-project',
504     });
505     renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
506     await ui.appLoaded();
507
508     expect(ui.visibilityRadio(Visibility.Public).get()).not.toHaveClass('disabled');
509     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
510     expect(ui.visibilityRadio(Visibility.Private).get()).not.toHaveClass('disabled');
511     await ui.turnProjectPrivate();
512     expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
513   });
514
515   it('should allow to change visibility for GitLab Project with disabled auto-provisioning', async () => {
516     const user = userEvent.setup();
517     const ui = getPageObject(user);
518     gitlabHandler.setGitlabConfigurations([
519       mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.jit }),
520     ]);
521     almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
522       almSetting: 'test',
523       repository: 'test',
524       monorepo: false,
525       project: 'my-project',
526     });
527     renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
528     await ui.appLoaded();
529
530     expect(ui.visibilityRadio(Visibility.Public).get()).not.toHaveClass('disabled');
531     expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
532     expect(ui.visibilityRadio(Visibility.Private).get()).not.toHaveClass('disabled');
533     await ui.turnProjectPrivate();
534     expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
535   });
536
537   it('should have disabled permissions for GitLab Project', async () => {
538     const user = userEvent.setup();
539     const ui = getPageObject(user);
540     gitlabHandler.setGitlabConfigurations([
541       mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }),
542     ]);
543     almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
544       almSetting: 'test',
545       repository: 'test',
546       monorepo: false,
547       project: 'my-project',
548     });
549     renderPermissionsProjectApp(
550       {},
551       { featureList: [Feature.GitlabProvisioning] },
552       {
553         component: mockComponent({ visibility: Visibility.Private }),
554       },
555     );
556     await ui.appLoaded();
557
558     expect(ui.pageTitle.get()).toBeInTheDocument();
559     await waitFor(() =>
560       expect(ui.pageTitle.get()).toHaveAccessibleName(/project_permission.managed/),
561     );
562     expect(ui.pageTitle.byRole('img').get()).toBeInTheDocument();
563     expect(ui.gitlabExplanations.get()).toBeInTheDocument();
564
565     expect(ui.projectPermissionCheckbox('John', Permissions.Admin).get()).toBeChecked();
566     expect(ui.projectPermissionCheckbox('John', Permissions.Admin).get()).toBeDisabled();
567     expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).toBeChecked();
568     expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).toBeEnabled();
569     await ui.toggleProjectPermission('Alexa', Permissions.IssueAdmin);
570     expect(ui.confirmRemovePermissionDialog.get()).toBeInTheDocument();
571     expect(ui.confirmRemovePermissionDialog.get()).toHaveTextContent(
572       `${Permissions.IssueAdmin}Alexa`,
573     );
574     await user.click(ui.confirmRemovePermissionDialog.byRole('button', { name: 'confirm' }).get());
575     expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).not.toBeChecked();
576
577     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get()).toBeChecked();
578     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get()).toBeEnabled();
579     await ui.toggleProjectPermission('sonar-users', Permissions.Browse);
580     expect(ui.confirmRemovePermissionDialog.get()).toBeInTheDocument();
581     expect(ui.confirmRemovePermissionDialog.get()).toHaveTextContent(
582       `${Permissions.Browse}sonar-users`,
583     );
584     await user.click(ui.confirmRemovePermissionDialog.byRole('button', { name: 'confirm' }).get());
585     expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get()).not.toBeChecked();
586     expect(ui.projectPermissionCheckbox('sonar-admins', Permissions.Admin).get()).toBeChecked();
587     expect(ui.projectPermissionCheckbox('sonar-admins', Permissions.Admin).get()).toHaveAttribute(
588       'disabled',
589     );
590
591     const johnRow = screen.getAllByRole('row')[4];
592     expect(johnRow).toHaveTextContent('John');
593     expect(ui.gitlabLogo.get(johnRow)).toBeInTheDocument();
594     const alexaRow = screen.getAllByRole('row')[5];
595     expect(alexaRow).toHaveTextContent('Alexa');
596     expect(ui.gitlabLogo.query(alexaRow)).not.toBeInTheDocument();
597     const usersGroupRow = screen.getAllByRole('row')[1];
598     expect(usersGroupRow).toHaveTextContent('sonar-users');
599     expect(ui.gitlabLogo.query(usersGroupRow)).not.toBeInTheDocument();
600     const adminsGroupRow = screen.getAllByRole('row')[2];
601     expect(adminsGroupRow).toHaveTextContent('sonar-admins');
602     expect(ui.gitlabLogo.query(adminsGroupRow)).toBeInTheDocument();
603
604     expect(ui.applyTemplateBtn.query()).not.toBeInTheDocument();
605
606     // not possible to grant permissions at all
607     expect(
608       screen
609         .getAllByRole('checkbox', { checked: false })
610         .every((item) => item.getAttributeNames().includes('disabled')),
611     ).toBe(true);
612   });
613
614   it('should allow to change permissions for GitLab Project without auto-provisioning', async () => {
615     const user = userEvent.setup();
616     const ui = getPageObject(user);
617     gitlabHandler.setGitlabConfigurations([
618       mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.jit }),
619     ]);
620     almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
621       almSetting: 'test',
622       repository: 'test',
623       monorepo: false,
624       project: 'my-project',
625     });
626     renderPermissionsProjectApp(
627       { visibility: Visibility.Private },
628       { featureList: [Feature.GitlabProvisioning] },
629     );
630     await ui.appLoaded();
631
632     expect(ui.pageTitle.get()).toBeInTheDocument();
633     expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
634
635     expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
636
637     // no restrictions
638     expect(
639       screen
640         .getAllByRole('checkbox')
641         .every((item) => item.getAttributeNames().includes('disabled')),
642     ).toBe(false);
643   });
644
645   it('should allow to change permissions for non-GitLab Project', async () => {
646     const user = userEvent.setup();
647     const ui = getPageObject(user);
648     gitlabHandler.setGitlabConfigurations([
649       mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }),
650     ]);
651     renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
652     await ui.appLoaded();
653
654     expect(ui.pageTitle.get()).toBeInTheDocument();
655     expect(ui.nonGitLabProjectWarning.get()).toBeInTheDocument();
656     expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
657
658     expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
659
660     // no restrictions
661     expect(
662       screen
663         .getAllByRole('checkbox')
664         .every((item) => item.getAttributeNames().includes('disabled')),
665     ).toBe(false);
666   });
667 });
668
669 function renderPermissionsProjectApp(
670   override: Partial<Component> = {},
671   contextOverride: Partial<RenderContext> = {},
672   componentContextOverride: Partial<ComponentContextShape> = {},
673 ) {
674   return renderAppWithComponentContext(
675     'project_roles?id=my-project',
676     projectPermissionsRoutes,
677     contextOverride,
678     {
679       component: mockComponent({
680         visibility: Visibility.Public,
681         configuration: {
682           canUpdateProjectVisibilityToPrivate: true,
683           canApplyPermissionTemplate: true,
684         },
685         ...override,
686       }),
687       ...componentContextOverride,
688     },
689   );
690 }