3 * Copyright (C) 2009-2024 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
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';
35 PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE,
36 PERMISSIONS_ORDER_FOR_VIEW,
37 } from '../../../../../helpers/permissions';
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';
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;
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();
72 dopTranslationHandler.reset();
73 githubHandler.reset();
74 gitlabHandler.reset();
76 computeEngineHandler.reset();
79 describe('rendering', () => {
81 [ComponentQualifier.Project, 'roles.page.description2', PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE],
82 [ComponentQualifier.Portfolio, 'roles.page.description_portfolio', PERMISSIONS_ORDER_FOR_VIEW],
84 ComponentQualifier.Application,
85 'roles.page.description_application',
86 PERMISSIONS_ORDER_FOR_VIEW,
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 });
94 expect(screen.getByText(description)).toBeInTheDocument();
95 permissions.forEach((permission) => {
96 expect(ui.projectPermissionCheckbox('johndoe', permission).get()).toBeInTheDocument();
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();
108 expect(screen.getByText('sonar-users')).toBeInTheDocument();
109 expect(screen.getByText('johndoe')).toBeInTheDocument();
111 await ui.showOnlyUsers();
112 expect(screen.queryByText('sonar-users')).not.toBeInTheDocument();
113 expect(screen.getByText('johndoe')).toBeInTheDocument();
115 await ui.showOnlyGroups();
116 expect(screen.getByText('sonar-users')).toBeInTheDocument();
117 expect(screen.queryByText('johndoe')).not.toBeInTheDocument();
120 expect(screen.getByText('sonar-users')).toBeInTheDocument();
121 expect(screen.getByText('johndoe')).toBeInTheDocument();
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();
128 await ui.clearSearch();
129 expect(screen.getByText('sonar-users')).toBeInTheDocument();
130 expect(screen.getByText('johndoe')).toBeInTheDocument();
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();
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);
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();
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();
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();
168 expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
170 ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).query(),
171 ).not.toBeInTheDocument();
172 await ui.turnProjectPrivate();
173 expect(ui.visibilityRadio(Visibility.Private).get()).toBeChecked();
175 ui.projectPermissionCheckbox('sonar-users', Permissions.Browse).get(),
176 ).toBeInTheDocument();
178 await ui.turnProjectPublic();
179 expect(ui.makePublicDisclaimer.get()).toBeInTheDocument();
180 await ui.confirmTurnProjectPublic();
181 expect(ui.visibilityRadio(Visibility.Public).get()).toBeChecked();
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();
190 expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
192 await ui.toggleProjectPermission('sonar-users', Permissions.Admin);
193 await ui.appLoaded();
194 expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Admin).get()).toBeChecked();
196 await ui.toggleProjectPermission('sonar-users', Permissions.Admin);
197 await ui.appLoaded();
198 expect(ui.projectPermissionCheckbox('sonar-users', Permissions.Admin).get()).not.toBeChecked();
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();
207 expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
209 await ui.toggleProjectPermission('johndoe', Permissions.Scan);
210 await ui.appLoaded();
211 expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).toBeChecked();
213 await ui.toggleProjectPermission('johndoe', Permissions.Scan);
214 await ui.appLoaded();
215 expect(ui.projectPermissionCheckbox('johndoe', Permissions.Scan).get()).not.toBeChecked();
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();
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();
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}` }));
239 serviceMock.setGroups(groups);
240 serviceMock.setUsers(users);
242 const user = userEvent.setup();
243 const ui = getPageObject(user);
244 renderPermissionsProjectApp();
245 await ui.appLoaded();
247 expect(screen.getAllByRole('row').length).toBe(11);
248 await ui.clickLoadMore();
249 expect(screen.getAllByRole('row').length).toBe(21);
252 describe('GitHub provisioning', () => {
254 systemHandler.setProvider(Provider.Github);
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 }),
263 almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
267 project: 'my-project',
269 renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
270 await ui.appLoaded();
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();
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 }),
285 almHandler.handleSetProjectBinding(AlmKeys.Azure, {
289 project: 'my-project',
291 renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
292 await ui.appLoaded();
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();
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, {
309 project: 'my-project',
311 renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
312 await ui.appLoaded();
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();
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 }),
327 almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
331 project: 'my-project',
333 renderPermissionsProjectApp(
335 { featureList: [Feature.GithubProvisioning] },
337 component: mockComponent({ visibility: Visibility.Private }),
340 await ui.appLoaded();
342 expect(ui.pageTitle.get()).toBeInTheDocument();
344 expect(ui.pageTitle.get()).toHaveAccessibleName(/project_permission.managed/),
346 expect(ui.pageTitle.byRole('img').get()).toBeInTheDocument();
347 expect(ui.githubExplanations.get()).toBeInTheDocument();
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`,
358 await user.click(ui.confirmRemovePermissionDialog.byRole('button', { name: 'confirm' }).get());
359 expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).not.toBeChecked();
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`,
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(
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();
388 expect(ui.applyTemplateBtn.query()).not.toBeInTheDocument();
390 // not possible to grant permissions at all
393 .getAllByRole('checkbox', { checked: false })
394 .every((item) => item.getAttributeNames().includes('disabled')),
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, {
406 project: 'my-project',
408 renderPermissionsProjectApp(
409 { visibility: Visibility.Private },
410 { featureList: [Feature.GithubProvisioning] },
412 await ui.appLoaded();
414 expect(ui.pageTitle.get()).toBeInTheDocument();
415 expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
417 expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
422 .getAllByRole('checkbox')
423 .every((item) => item.getAttributeNames().includes('disabled')),
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 }),
433 renderPermissionsProjectApp({}, { featureList: [Feature.GithubProvisioning] });
434 await ui.appLoaded();
436 expect(ui.pageTitle.get()).toBeInTheDocument();
437 expect(ui.nonGHProjectWarning.get()).toBeInTheDocument();
438 expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
440 expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
445 .getAllByRole('checkbox')
446 .every((item) => item.getAttributeNames().includes('disabled')),
451 describe('GitLab provisioning', () => {
453 systemHandler.setProvider(Provider.Gitlab);
454 computeEngineHandler.addTask({
455 status: TaskStatuses.InProgress,
456 executedAt: '2022-02-03T11:55:35+0200',
457 type: TaskTypes.GitlabProvisioning,
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,
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 }),
473 gitlabHandler.setGitlabConfigurations([
474 mockGitlabConfiguration({ id: '1', enabled: true, provisioningType: ProvisioningType.auto }),
476 almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
480 project: 'my-project',
483 renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
484 await ui.appLoaded();
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();
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 }),
499 almHandler.handleSetProjectBinding(AlmKeys.GitHub, {
503 project: 'my-project',
505 renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
506 await ui.appLoaded();
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();
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 }),
521 almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
525 project: 'my-project',
527 renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
528 await ui.appLoaded();
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();
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 }),
543 almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
547 project: 'my-project',
549 renderPermissionsProjectApp(
551 { featureList: [Feature.GitlabProvisioning] },
553 component: mockComponent({ visibility: Visibility.Private }),
556 await ui.appLoaded();
558 expect(ui.pageTitle.get()).toBeInTheDocument();
560 expect(ui.pageTitle.get()).toHaveAccessibleName(/project_permission.managed/),
562 expect(ui.pageTitle.byRole('img').get()).toBeInTheDocument();
563 expect(ui.gitlabExplanations.get()).toBeInTheDocument();
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`,
574 await user.click(ui.confirmRemovePermissionDialog.byRole('button', { name: 'confirm' }).get());
575 expect(ui.projectPermissionCheckbox('Alexa', Permissions.IssueAdmin).get()).not.toBeChecked();
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`,
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(
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();
604 expect(ui.applyTemplateBtn.query()).not.toBeInTheDocument();
606 // not possible to grant permissions at all
609 .getAllByRole('checkbox', { checked: false })
610 .every((item) => item.getAttributeNames().includes('disabled')),
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 }),
620 almHandler.handleSetProjectBinding(AlmKeys.GitLab, {
624 project: 'my-project',
626 renderPermissionsProjectApp(
627 { visibility: Visibility.Private },
628 { featureList: [Feature.GitlabProvisioning] },
630 await ui.appLoaded();
632 expect(ui.pageTitle.get()).toBeInTheDocument();
633 expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
635 expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
640 .getAllByRole('checkbox')
641 .every((item) => item.getAttributeNames().includes('disabled')),
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 }),
651 renderPermissionsProjectApp({}, { featureList: [Feature.GitlabProvisioning] });
652 await ui.appLoaded();
654 expect(ui.pageTitle.get()).toBeInTheDocument();
655 expect(ui.nonGitLabProjectWarning.get()).toBeInTheDocument();
656 expect(ui.pageTitle.byRole('img').query()).not.toBeInTheDocument();
658 expect(ui.applyTemplateBtn.get()).toBeInTheDocument();
663 .getAllByRole('checkbox')
664 .every((item) => item.getAttributeNames().includes('disabled')),
669 function renderPermissionsProjectApp(
670 override: Partial<Component> = {},
671 contextOverride: Partial<RenderContext> = {},
672 componentContextOverride: Partial<ComponentContextShape> = {},
674 return renderAppWithComponentContext(
675 'project_roles?id=my-project',
676 projectPermissionsRoutes,
679 component: mockComponent({
680 visibility: Visibility.Public,
682 canUpdateProjectVisibilityToPrivate: true,
683 canApplyPermissionTemplate: true,
687 ...componentContextOverride,