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 { within } from '@testing-library/react';
21 import userEvent from '@testing-library/user-event';
22 import React from 'react';
23 import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
24 import ComputeEngineServiceMock from '../../../../../api/mocks/ComputeEngineServiceMock';
25 import GitlabProvisioningServiceMock from '../../../../../api/mocks/GitlabProvisioningServiceMock';
26 import SettingsServiceMock from '../../../../../api/mocks/SettingsServiceMock';
27 import SystemServiceMock from '../../../../../api/mocks/SystemServiceMock';
28 import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
29 import { mockGitlabConfiguration } from '../../../../../helpers/mocks/alm-integrations';
30 import { definitions } from '../../../../../helpers/mocks/definitions-list';
31 import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
32 import { AlmKeys } from '../../../../../types/alm-settings';
33 import { Feature } from '../../../../../types/features';
34 import { ProvisioningType } from '../../../../../types/provisioning';
35 import { TaskStatuses, TaskTypes } from '../../../../../types/tasks';
36 import Authentication from '../Authentication';
38 let handler: GitlabProvisioningServiceMock;
39 let system: SystemServiceMock;
40 let settingsHandler: SettingsServiceMock;
41 let computeEngineHandler: ComputeEngineServiceMock;
44 handler = new GitlabProvisioningServiceMock();
45 system = new SystemServiceMock();
46 settingsHandler = new SettingsServiceMock();
47 computeEngineHandler = new ComputeEngineServiceMock();
52 settingsHandler.reset();
54 computeEngineHandler.reset();
57 const glContainer = byRole('tabpanel', { name: 'gitlab GitLab' });
60 noGitlabConfiguration: glContainer.byText('settings.authentication.gitlab.form.not_configured'),
61 createConfigButton: glContainer.byRole('button', {
62 name: 'settings.authentication.form.create',
64 editConfigButton: glContainer.byRole('button', {
65 name: 'settings.authentication.form.edit',
67 editMappingButton: glContainer.byRole('button', {
68 name: 'settings.authentication.configuration.roles_mapping.button_label',
70 deleteConfigButton: glContainer.byRole('button', {
71 name: 'settings.authentication.form.delete',
73 enableConfigButton: glContainer.byRole('button', {
74 name: 'settings.authentication.form.enable',
76 disableConfigButton: glContainer.byRole('button', {
77 name: 'settings.authentication.form.disable',
79 createDialog: byRole('dialog', {
80 name: 'settings.authentication.gitlab.form.create',
82 editDialog: byRole('dialog', {
83 name: 'settings.authentication.gitlab.form.edit',
85 applicationId: byRole('textbox', {
86 name: 'property.applicationId.name',
88 url: byRole('textbox', { name: 'property.url.name' }),
89 secret: byRole('textbox', {
90 name: 'property.secret.name',
92 synchronizeGroups: byRole('switch', {
93 description: 'property.synchronizeGroups.description',
95 saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
96 jitProvisioningRadioButton: glContainer.byRole('radio', {
97 name: /settings.authentication.gitlab.provisioning_at_login/,
99 autoProvisioningRadioButton: glContainer.byRole('radio', {
100 name: /settings.authentication.gitlab.form.provisioning_with_gitlab/,
102 jitAllowUsersToSignUpToggle: byRole('switch', {
103 description: 'property.allowUsersToSignUp.description',
105 autoProvisioningToken: byRole('textbox', {
106 name: 'property.provisioningToken.name',
108 autoProvisioningUpdateTokenButton: byRole('button', {
109 name: 'settings.almintegration.form.secret.update_field',
111 groups: byRole('textbox', {
112 name: 'property.allowedGroups.name',
114 deleteGroupButton: byRole('button', { name: /delete_value/ }),
115 removeProvisioniongGroup: byRole('button', {
116 name: /settings.definition.delete_value.property.allowedGroups.name./,
118 saveProvisioning: glContainer.byRole('button', { name: 'save' }),
119 cancelProvisioningChanges: glContainer.byRole('button', { name: 'cancel' }),
120 confirmAutoProvisioningDialog: byRole('dialog', {
121 name: 'settings.authentication.gitlab.confirm.AUTO_PROVISIONING',
123 confirmJitProvisioningDialog: byRole('alertdialog', {
124 name: 'settings.authentication.gitlab.confirm.JIT',
126 confirmInsecureProvisioningDialog: byRole('alertdialog', {
127 name: 'settings.authentication.gitlab.confirm.insecure',
129 confirmProvisioningChange: byRole('button', {
130 name: 'settings.authentication.gitlab.provisioning_change.confirm_changes',
132 syncSummary: glContainer.byText(/Test summary/),
133 syncWarning: glContainer.byText(/Warning/),
134 gitlabProvisioningPending: glContainer
137 .byText(/synchronization_pending/),
138 gitlabProvisioningInProgress: glContainer
141 .byText(/synchronization_in_progress/),
142 gitlabProvisioningSuccess: glContainer.byText(/synchronization_successful/),
143 gitlabProvisioningAlert: glContainer.byText(/synchronization_failed/),
144 gitlabConfigurationStatus: glContainer.byRole('status', {
145 name: /settings.authentication.gitlab.configuration/,
147 gitlabMissingSecretErrorMessage: byText(
148 'settings.authentication.gitlab.form.secret.required_for_url_change',
150 testConfiguration: glContainer.byRole('button', {
151 name: 'settings.authentication.configuration.test',
153 continueAutoButton: byRole('button', {
154 name: 'settings.authentication.confirm_auto_provisioning.continue',
156 switchJitButton: byRole('button', {
157 name: 'settings.authentication.confirm_auto_provisioning.switch_jit',
159 consentDialog: byRole('dialog', {
160 name: 'settings.authentication.confirm_auto_provisioning.header',
162 mappingRow: byRole('dialog', {
163 name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
165 mappingCheckbox: byRole('checkbox'),
166 mappingDialogClose: byRole('dialog', {
167 name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
168 }).byRole('button', {
171 customRoleInput: byRole('textbox', {
172 name: 'settings.authentication.configuration.roles_mapping.dialog.add_custom_role',
174 customRoleAddBtn: byRole('dialog', {
175 name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
176 }).byRole('button', { name: 'add_verb' }),
177 roleExistsError: byRole('dialog', {
178 name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
179 }).byText('settings.authentication.configuration.roles_mapping.role_exists'),
180 emptyRoleError: byRole('dialog', {
181 name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
182 }).byText('settings.authentication.configuration.roles_mapping.empty_custom_role'),
183 deleteCustomRoleCustom2: byRole('button', {
184 name: 'settings.authentication.configuration.roles_mapping.dialog.delete_custom_role.custom2',
186 getMappingRowByRole: (text: string) =>
187 ui.mappingRow.getAll().find((row) => within(row).queryByText(text) !== null),
190 it('should create a Gitlab configuration and disable it with proper validation', async () => {
191 handler.setGitlabConfigurations([]);
192 renderAuthentication();
193 const user = userEvent.setup();
195 expect(await ui.noGitlabConfiguration.find()).toBeInTheDocument();
196 expect(ui.createConfigButton.get()).toBeInTheDocument();
198 await user.click(ui.createConfigButton.get());
199 expect(await ui.createDialog.find()).toBeInTheDocument();
200 await user.type(ui.applicationId.get(), '123');
201 expect(ui.saveConfigButton.get()).toBeDisabled();
202 await user.type(ui.url.get(), 'https://company.ui.com');
203 await user.type(ui.secret.get(), '123');
204 expect(ui.saveConfigButton.get()).toBeEnabled();
205 await user.click(ui.synchronizeGroups.get());
206 await user.click(ui.saveConfigButton.get());
208 expect(await ui.editConfigButton.find()).toBeInTheDocument();
209 expect(ui.noGitlabConfiguration.query()).not.toBeInTheDocument();
210 expect(glContainer.get()).toHaveTextContent('https://company.ui.com');
212 expect(ui.disableConfigButton.get()).toBeInTheDocument();
213 await user.click(ui.disableConfigButton.get());
214 expect(ui.enableConfigButton.get()).toBeInTheDocument();
215 expect(ui.disableConfigButton.query()).not.toBeInTheDocument();
218 it('should edit a configuration with proper validation and delete it', async () => {
219 const user = userEvent.setup();
220 renderAuthentication();
222 expect(await ui.editConfigButton.find()).toBeInTheDocument();
223 expect(glContainer.get()).toHaveTextContent('URL');
224 expect(ui.disableConfigButton.get()).toBeInTheDocument();
225 expect(ui.deleteConfigButton.get()).toBeInTheDocument();
226 expect(ui.deleteConfigButton.get()).toBeDisabled();
228 await user.click(ui.editConfigButton.get());
229 expect(await ui.editDialog.find()).toBeInTheDocument();
230 expect(ui.url.get()).toHaveValue('URL');
231 expect(ui.applicationId.get()).toBeInTheDocument();
232 expect(ui.secret.query()).not.toBeInTheDocument();
233 expect(ui.synchronizeGroups.get()).toBeChecked();
235 expect(ui.applicationId.get()).toBeInTheDocument();
236 await user.clear(ui.applicationId.get());
237 expect(ui.saveConfigButton.get()).toBeDisabled();
238 await user.type(ui.applicationId.get(), '456');
239 expect(ui.saveConfigButton.get()).toBeEnabled();
241 expect(ui.url.get()).toBeInTheDocument();
242 await user.clear(ui.url.get());
243 expect(ui.saveConfigButton.get()).toBeDisabled();
244 await user.type(ui.url.get(), 'www.internet.com');
245 expect(ui.saveConfigButton.get()).toBeDisabled();
246 expect(ui.gitlabMissingSecretErrorMessage.get()).toBeInTheDocument();
247 await user.click(ui.autoProvisioningUpdateTokenButton.get());
248 await user.type(ui.secret.get(), '123');
249 expect(ui.gitlabMissingSecretErrorMessage.query()).not.toBeInTheDocument();
250 expect(ui.saveConfigButton.get()).toBeEnabled();
251 await user.click(ui.saveConfigButton.get());
253 expect(glContainer.get()).not.toHaveTextContent('URL');
254 expect(glContainer.get()).toHaveTextContent('www.internet.com');
256 expect(ui.disableConfigButton.get()).toBeInTheDocument();
257 await user.click(ui.disableConfigButton.get());
258 expect(await ui.enableConfigButton.find()).toBeInTheDocument();
259 expect(ui.deleteConfigButton.get()).toBeEnabled();
260 await user.click(ui.deleteConfigButton.get());
261 expect(await ui.noGitlabConfiguration.find()).toBeInTheDocument();
262 expect(ui.editConfigButton.query()).not.toBeInTheDocument();
265 it('should be able to save just-in-time with no organizations', async () => {
266 const user = userEvent.setup();
267 renderAuthentication([Feature.GitlabProvisioning]);
269 expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
271 expect(ui.groups.get()).toHaveValue('Cypress Hill');
272 expect(await ui.saveProvisioning.find()).toBeDisabled();
273 await user.click(ui.deleteGroupButton.get());
274 expect(await ui.saveProvisioning.find()).toBeEnabled();
277 it('should not be able to save Auto provisioning with no organizations', async () => {
278 const user = userEvent.setup();
279 handler.setGitlabConfigurations([
280 mockGitlabConfiguration({
281 allowUsersToSignUp: false,
283 provisioningType: ProvisioningType.auto,
284 allowedGroups: ['D12'],
285 isProvisioningTokenSet: true,
288 renderAuthentication([Feature.GitlabProvisioning]);
290 expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
292 expect(ui.groups.get()).toHaveValue('D12');
293 expect(ui.saveProvisioning.get()).toBeDisabled();
294 await user.click(ui.deleteGroupButton.get());
295 expect(await ui.saveProvisioning.find()).toBeDisabled();
298 it('should change from just-in-time to Auto Provisioning if auto was never set before', async () => {
299 const user = userEvent.setup();
300 handler.setGitlabConfigurations([
301 mockGitlabConfiguration({
302 allowUsersToSignUp: false,
304 provisioningType: ProvisioningType.jit,
306 isProvisioningTokenSet: false,
309 renderAuthentication([Feature.GitlabProvisioning]);
311 expect(await ui.editConfigButton.find()).toBeInTheDocument();
312 expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
314 await user.click(ui.autoProvisioningRadioButton.get());
315 expect(await ui.autoProvisioningRadioButton.find()).toBeEnabled();
316 expect(ui.saveProvisioning.get()).toBeDisabled();
318 await user.type(ui.autoProvisioningToken.get(), 'JRR Tolkien');
319 expect(await ui.saveProvisioning.find()).toBeDisabled();
321 await user.type(ui.groups.get(), 'Run DMC');
322 expect(await ui.saveProvisioning.find()).toBeEnabled();
323 await user.click(ui.deleteGroupButton.get());
324 expect(await ui.saveProvisioning.find()).toBeDisabled();
326 await user.type(ui.groups.get(), 'Public Enemy');
327 expect(await ui.saveProvisioning.find()).toBeEnabled();
330 it('should change from just-in-time to Auto Provisioning if auto was set before', async () => {
331 handler.setGitlabConfigurations([
332 mockGitlabConfiguration({
333 allowUsersToSignUp: false,
335 provisioningType: ProvisioningType.jit,
336 allowedGroups: ['D12'],
337 isProvisioningTokenSet: true,
340 const user = userEvent.setup();
341 renderAuthentication([Feature.GitlabProvisioning]);
343 expect(await ui.editConfigButton.find()).toBeInTheDocument();
344 expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
346 user.click(ui.autoProvisioningRadioButton.get());
347 expect(await ui.autoProvisioningRadioButton.find()).toBeEnabled();
348 expect(await ui.saveProvisioning.find()).toBeEnabled();
350 expect(ui.groups.get()).toHaveValue('D12');
351 await user.click(ui.deleteGroupButton.get());
352 expect(await ui.saveProvisioning.find()).toBeDisabled();
353 await user.type(ui.groups.get(), 'Wu Tang Clan');
355 expect(ui.saveProvisioning.get()).toBeEnabled();
358 it('should change from auto provisioning to JIT with proper validation', async () => {
359 handler.setGitlabConfigurations([
360 mockGitlabConfiguration({
361 allowUsersToSignUp: false,
363 provisioningType: ProvisioningType.auto,
364 allowedGroups: ['D12'],
365 isProvisioningTokenSet: true,
368 const user = userEvent.setup();
369 renderAuthentication([Feature.GitlabProvisioning]);
371 expect(await ui.editConfigButton.find()).toBeInTheDocument();
373 expect(ui.jitProvisioningRadioButton.get()).not.toBeChecked();
374 expect(ui.autoProvisioningRadioButton.get()).toBeChecked();
376 expect(ui.autoProvisioningToken.query()).not.toBeInTheDocument();
377 expect(ui.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
379 await user.click(ui.jitProvisioningRadioButton.get());
380 expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
382 expect(await ui.saveProvisioning.find()).toBeEnabled();
384 await user.click(ui.jitAllowUsersToSignUpToggle.get());
385 await user.click(ui.deleteGroupButton.get());
387 await user.click(ui.saveProvisioning.get());
389 ui.confirmJitProvisioningDialog
390 .byText('settings.authentication.gitlab.provisioning_change.insecure_config')
392 ).toBeInTheDocument();
393 await user.click(ui.confirmProvisioningChange.get());
394 expect(ui.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
396 expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
397 expect(await ui.saveProvisioning.find()).toBeDisabled();
400 it('should show configuration warning with jit provisioning and no groups', async () => {
401 handler.setGitlabConfigurations([
402 mockGitlabConfiguration({
403 allowUsersToSignUp: false,
405 provisioningType: ProvisioningType.jit,
407 isProvisioningTokenSet: true,
410 const user = userEvent.setup();
411 renderAuthentication([Feature.GitlabProvisioning]);
413 expect(await ui.editConfigButton.find()).toBeInTheDocument();
415 await user.click(ui.jitAllowUsersToSignUpToggle.get());
416 await user.click(ui.saveProvisioning.get());
419 ui.confirmInsecureProvisioningDialog
420 .byText('settings.authentication.gitlab.provisioning_change.insecure_config')
422 ).toBeInTheDocument();
424 await user.click(ui.confirmProvisioningChange.get());
425 expect(ui.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
427 expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
428 expect(await ui.saveProvisioning.find()).toBeDisabled();
431 it('should be able to allow user to sign up for JIT with proper validation', async () => {
432 handler.setGitlabConfigurations([
433 mockGitlabConfiguration({
434 allowUsersToSignUp: false,
436 provisioningType: ProvisioningType.jit,
439 const user = userEvent.setup();
440 renderAuthentication([Feature.GitlabProvisioning]);
442 expect(await ui.editConfigButton.find()).toBeInTheDocument();
444 expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
445 expect(ui.autoProvisioningRadioButton.get()).not.toBeChecked();
447 expect(ui.jitAllowUsersToSignUpToggle.get()).not.toBeChecked();
449 expect(ui.saveProvisioning.get()).toBeDisabled();
450 await user.click(ui.jitAllowUsersToSignUpToggle.get());
451 expect(ui.saveProvisioning.get()).toBeEnabled();
452 await user.click(ui.jitAllowUsersToSignUpToggle.get());
453 expect(ui.saveProvisioning.get()).toBeDisabled();
454 await user.click(ui.jitAllowUsersToSignUpToggle.get());
456 await user.click(ui.saveProvisioning.get());
458 expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
459 expect(ui.jitAllowUsersToSignUpToggle.get()).toBeChecked();
460 expect(await ui.saveProvisioning.find()).toBeDisabled();
463 it('should be able to edit token for Auto provisioning with proper validation', async () => {
464 handler.setGitlabConfigurations([
465 mockGitlabConfiguration({
466 allowUsersToSignUp: false,
468 provisioningType: ProvisioningType.auto,
469 allowedGroups: ['Cypress Hill', 'Public Enemy'],
470 isProvisioningTokenSet: true,
473 const user = userEvent.setup();
474 renderAuthentication([Feature.GitlabProvisioning]);
476 expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
477 expect(ui.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
479 expect(ui.saveProvisioning.get()).toBeDisabled();
481 // Changing the Provisioning token should enable save
482 await user.click(ui.autoProvisioningUpdateTokenButton.get());
483 expect(ui.saveProvisioning.get()).toBeDisabled();
484 await user.click(ui.cancelProvisioningChanges.get());
485 expect(ui.saveProvisioning.get()).toBeDisabled();
488 it('should be able to reset Auto Provisioning changes', async () => {
489 handler.setGitlabConfigurations([
490 mockGitlabConfiguration({
491 allowUsersToSignUp: false,
493 provisioningType: ProvisioningType.auto,
494 allowedGroups: ['Cypress Hill', 'Public Enemy'],
495 isProvisioningTokenSet: true,
498 const user = userEvent.setup();
499 renderAuthentication([Feature.GitlabProvisioning]);
501 expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
503 await user.click(ui.autoProvisioningUpdateTokenButton.get());
504 await user.type(ui.autoProvisioningToken.get(), 'ToToken!');
505 expect(ui.saveProvisioning.get()).toBeEnabled();
506 await user.click(ui.cancelProvisioningChanges.get());
507 expect(ui.saveProvisioning.get()).toBeDisabled();
510 describe('Gitlab Provisioning', () => {
514 now: new Date('2022-02-04T12:00:59Z'),
516 handler.setGitlabConfigurations([
517 mockGitlabConfiguration({
519 provisioningType: ProvisioningType.auto,
520 allowedGroups: ['Test'],
526 jest.runOnlyPendingTimers();
527 jest.useRealTimers();
530 it('should display a success status when the synchronisation is a success', async () => {
531 computeEngineHandler.addTask({
532 status: TaskStatuses.Success,
533 executedAt: '2022-02-03T11:45:35+0200',
534 infoMessages: ['Test summary'],
535 type: TaskTypes.GitlabProvisioning,
538 renderAuthentication([Feature.GitlabProvisioning]);
539 expect(await ui.gitlabProvisioningSuccess.find()).toBeInTheDocument();
540 expect(ui.syncSummary.get()).toBeInTheDocument();
543 it('should display a success status even when another task is pending', async () => {
544 computeEngineHandler.addTask({
545 status: TaskStatuses.Pending,
546 executedAt: '2022-02-03T11:55:35+0200',
547 type: TaskTypes.GitlabProvisioning,
549 computeEngineHandler.addTask({
550 status: TaskStatuses.Success,
551 executedAt: '2022-02-03T11:45:35+0200',
552 type: TaskTypes.GitlabProvisioning,
554 renderAuthentication([Feature.GitlabProvisioning]);
555 expect(await ui.gitlabProvisioningSuccess.find()).toBeInTheDocument();
556 expect(ui.gitlabProvisioningPending.get()).toBeInTheDocument();
559 it('should display an error alert when the synchronisation failed', async () => {
560 computeEngineHandler.addTask({
561 status: TaskStatuses.Failed,
562 executedAt: '2022-02-03T11:45:35+0200',
563 errorMessage: "T'es mauvais Jacques",
564 type: TaskTypes.GitlabProvisioning,
566 renderAuthentication([Feature.GitlabProvisioning]);
567 expect(await ui.gitlabProvisioningAlert.find()).toBeInTheDocument();
568 expect(glContainer.get()).toHaveTextContent("T'es mauvais Jacques");
569 expect(ui.gitlabProvisioningSuccess.query()).not.toBeInTheDocument();
572 it('should display an error alert even when another task is in progress', async () => {
573 computeEngineHandler.addTask({
574 status: TaskStatuses.InProgress,
575 executedAt: '2022-02-03T11:55:35+0200',
576 type: TaskTypes.GitlabProvisioning,
578 computeEngineHandler.addTask({
579 status: TaskStatuses.Failed,
580 executedAt: '2022-02-03T11:45:35+0200',
581 errorMessage: "T'es mauvais Jacques",
582 type: TaskTypes.GitlabProvisioning,
584 renderAuthentication([Feature.GitlabProvisioning]);
585 expect(await ui.gitlabProvisioningAlert.find()).toBeInTheDocument();
586 expect(glContainer.get()).toHaveTextContent("T'es mauvais Jacques");
587 expect(ui.gitlabProvisioningSuccess.query()).not.toBeInTheDocument();
588 expect(ui.gitlabProvisioningInProgress.get()).toBeInTheDocument();
591 it('should show warning', async () => {
592 computeEngineHandler.addTask({
593 status: TaskStatuses.Success,
594 warnings: ['Warning'],
595 infoMessages: ['Test summary'],
596 type: TaskTypes.GitlabProvisioning,
598 renderAuthentication([Feature.GitlabProvisioning]);
600 expect(await ui.syncWarning.find()).toBeInTheDocument();
601 expect(ui.syncSummary.get()).toBeInTheDocument();
604 it('should show configuration validity', async () => {
605 const user = userEvent.setup();
606 renderAuthentication([Feature.GitlabProvisioning]);
608 expect(await ui.gitlabConfigurationStatus.find()).toHaveTextContent(
609 'settings.authentication.gitlab.configuration.valid.AUTO_PROVISIONING',
611 await user.click(ui.jitProvisioningRadioButton.get());
612 await user.click(ui.saveProvisioning.get());
613 await user.click(ui.confirmProvisioningChange.get());
614 expect(ui.gitlabConfigurationStatus.get()).toHaveTextContent(
615 'settings.authentication.gitlab.configuration.valid.JIT',
617 handler.setGitlabConfigurations([
618 mockGitlabConfiguration({ ...handler.gitlabConfigurations[0], errorMessage: 'ERROR' }),
620 await user.click(ui.testConfiguration.get());
621 expect(glContainer.get()).toHaveTextContent('ERROR');
622 await user.click(ui.disableConfigButton.get());
623 expect(ui.gitlabConfigurationStatus.query()).not.toBeInTheDocument();
626 it('should display a modal if user was already using auto and continue using auto provisioning', async () => {
627 const user = userEvent.setup();
629 settingsHandler.set('sonar.auth.gitlab.userConsentForPermissionProvisioningRequired', '');
630 handler.setGitlabConfigurations([
631 mockGitlabConfiguration({
632 allowUsersToSignUp: false,
634 provisioningType: ProvisioningType.auto,
635 allowedGroups: ['D12'],
636 isProvisioningTokenSet: true,
639 renderAuthentication([Feature.GitlabProvisioning]);
641 expect(await ui.consentDialog.find()).toBeInTheDocument();
642 await user.click(ui.continueAutoButton.get());
644 expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
645 expect(ui.consentDialog.query()).not.toBeInTheDocument();
648 it('should display a modal if user was already using auto and switch to JIT', async () => {
649 const user = userEvent.setup();
651 settingsHandler.set('sonar.auth.gitlab.userConsentForPermissionProvisioningRequired', '');
652 handler.setGitlabConfigurations([
653 mockGitlabConfiguration({
654 allowUsersToSignUp: false,
656 provisioningType: ProvisioningType.auto,
657 allowedGroups: ['D12'],
658 isProvisioningTokenSet: true,
661 renderAuthentication([Feature.GitlabProvisioning]);
663 expect(await ui.consentDialog.find()).toBeInTheDocument();
664 await user.click(ui.switchJitButton.get());
666 expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
667 expect(ui.consentDialog.query()).not.toBeInTheDocument();
670 it('should sort mapping rows', async () => {
671 const user = userEvent.setup();
672 handler.setGitlabConfigurations([
673 mockGitlabConfiguration({
674 allowUsersToSignUp: false,
676 provisioningType: ProvisioningType.auto,
677 allowedGroups: ['D12'],
678 isProvisioningTokenSet: true,
681 renderAuthentication([Feature.GitlabProvisioning]);
683 expect(await ui.editMappingButton.find()).toBeInTheDocument();
684 await user.click(ui.editMappingButton.get());
686 const rows = (await ui.mappingRow.findAll()).filter(
687 (row) => within(row).queryAllByRole('checkbox').length > 0,
690 expect(rows).toHaveLength(5);
692 expect(rows[0]).toHaveTextContent('guest');
693 expect(rows[1]).toHaveTextContent('reporter');
694 expect(rows[2]).toHaveTextContent('developer');
695 expect(rows[3]).toHaveTextContent('maintainer');
696 expect(rows[4]).toHaveTextContent('owner');
699 it('should apply new mapping and new provisioning type at the same time', async () => {
700 const user = userEvent.setup();
701 handler.setGitlabConfigurations([
702 mockGitlabConfiguration({
703 allowUsersToSignUp: false,
705 provisioningType: ProvisioningType.jit,
706 allowedGroups: ['D12'],
707 isProvisioningTokenSet: true,
710 renderAuthentication([Feature.GitlabProvisioning]);
712 expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
713 expect(ui.editMappingButton.query()).not.toBeInTheDocument();
714 await user.click(ui.autoProvisioningRadioButton.get());
715 expect(await ui.editMappingButton.find()).toBeInTheDocument();
716 await user.click(ui.editMappingButton.get());
717 expect(await ui.mappingRow.findAll()).toHaveLength(7);
719 let guestCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('guest'));
720 let ownerCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('owner'));
722 expect(guestCheckboxes[0]).toBeChecked();
723 expect(guestCheckboxes[1]).toBeChecked();
724 expect(guestCheckboxes[2]).not.toBeChecked();
725 expect(guestCheckboxes[3]).not.toBeChecked();
726 expect(guestCheckboxes[4]).not.toBeChecked();
727 expect(guestCheckboxes[5]).not.toBeChecked();
728 expect(ownerCheckboxes[0]).toBeChecked();
729 expect(ownerCheckboxes[1]).toBeChecked();
730 expect(ownerCheckboxes[2]).toBeChecked();
731 expect(ownerCheckboxes[3]).toBeChecked();
732 expect(ownerCheckboxes[4]).toBeChecked();
733 expect(ownerCheckboxes[5]).toBeChecked();
735 await user.click(guestCheckboxes[0]);
736 await user.click(guestCheckboxes[5]);
737 await user.click(ownerCheckboxes[5]);
738 await user.click(ui.mappingDialogClose.get());
740 await user.click(ui.saveProvisioning.get());
741 await user.click(ui.confirmProvisioningChange.get());
743 // Clean local mapping state
744 await user.click(ui.jitProvisioningRadioButton.get());
745 await user.click(ui.autoProvisioningRadioButton.get());
747 await user.click(ui.editMappingButton.get());
748 guestCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('guest'));
749 ownerCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('owner'));
751 expect(guestCheckboxes[0]).not.toBeChecked();
752 expect(guestCheckboxes[5]).toBeChecked();
753 expect(ownerCheckboxes[5]).not.toBeChecked();
754 await user.click(ui.mappingDialogClose.get());
757 it('should add/remove/update custom roles', async () => {
758 const user = userEvent.setup();
759 handler.setGitlabConfigurations([
760 mockGitlabConfiguration({
761 allowUsersToSignUp: false,
763 provisioningType: ProvisioningType.auto,
764 allowedGroups: ['D12'],
765 isProvisioningTokenSet: true,
768 handler.addGitLabCustomRole('custom1', ['user', 'codeViewer', 'scan']);
769 handler.addGitLabCustomRole('custom2', ['user', 'codeViewer', 'issueAdmin', 'scan']);
770 renderAuthentication([Feature.GitlabProvisioning]);
772 expect(await ui.saveProvisioning.find()).toBeDisabled();
773 await user.click(ui.editMappingButton.get());
775 const rows = (await ui.mappingRow.findAll()).filter(
776 (row) => within(row).queryAllByRole('checkbox').length > 0,
779 expect(rows).toHaveLength(7);
781 let custom1Checkboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('custom1'));
783 expect(custom1Checkboxes[0]).toBeChecked();
784 expect(custom1Checkboxes[1]).toBeChecked();
785 expect(custom1Checkboxes[2]).not.toBeChecked();
786 expect(custom1Checkboxes[3]).not.toBeChecked();
787 expect(custom1Checkboxes[4]).not.toBeChecked();
788 expect(custom1Checkboxes[5]).toBeChecked();
790 await user.click(custom1Checkboxes[1]);
791 await user.click(custom1Checkboxes[2]);
793 await user.click(ui.deleteCustomRoleCustom2.get());
795 expect(ui.customRoleInput.get()).toHaveValue('');
796 await user.type(ui.customRoleInput.get(), 'guest');
797 await user.click(ui.customRoleAddBtn.get());
798 expect(await ui.roleExistsError.find()).toBeInTheDocument();
799 expect(ui.customRoleAddBtn.get()).toBeDisabled();
800 await user.clear(ui.customRoleInput.get());
801 expect(ui.roleExistsError.query()).not.toBeInTheDocument();
802 await user.type(ui.customRoleInput.get(), 'custom1');
803 await user.click(ui.customRoleAddBtn.get());
804 expect(await ui.roleExistsError.find()).toBeInTheDocument();
805 expect(ui.customRoleAddBtn.get()).toBeDisabled();
806 await user.clear(ui.customRoleInput.get());
807 await user.type(ui.customRoleInput.get(), 'custom3');
808 expect(ui.roleExistsError.query()).not.toBeInTheDocument();
809 expect(ui.customRoleAddBtn.get()).toBeEnabled();
810 await user.click(ui.customRoleAddBtn.get());
812 let custom3Checkboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('custom3'));
813 expect(custom3Checkboxes[0]).toBeChecked();
814 expect(custom3Checkboxes[1]).not.toBeChecked();
815 expect(custom3Checkboxes[2]).not.toBeChecked();
816 expect(custom3Checkboxes[3]).not.toBeChecked();
817 expect(custom3Checkboxes[4]).not.toBeChecked();
818 expect(custom3Checkboxes[5]).not.toBeChecked();
819 await user.click(custom3Checkboxes[0]);
820 expect(await ui.emptyRoleError.find()).toBeInTheDocument();
821 expect(ui.mappingDialogClose.get()).toBeDisabled();
822 await user.click(custom3Checkboxes[1]);
823 expect(ui.emptyRoleError.query()).not.toBeInTheDocument();
824 expect(ui.mappingDialogClose.get()).toBeEnabled();
825 await user.click(ui.mappingDialogClose.get());
827 expect(await ui.saveProvisioning.find()).toBeEnabled();
828 await user.click(ui.saveProvisioning.get());
830 // Clean local mapping state
831 await user.click(ui.jitProvisioningRadioButton.get());
832 await user.click(ui.autoProvisioningRadioButton.get());
834 await user.click(ui.editMappingButton.get());
837 (await ui.mappingRow.findAll()).filter(
838 (row) => within(row).queryAllByRole('checkbox').length > 0,
841 custom1Checkboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('custom1'));
842 custom3Checkboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('custom3'));
843 expect(ui.getMappingRowByRole('custom2')).toBeUndefined();
844 expect(custom1Checkboxes[0]).toBeChecked();
845 expect(custom1Checkboxes[1]).not.toBeChecked();
846 expect(custom1Checkboxes[2]).toBeChecked();
847 expect(custom1Checkboxes[3]).not.toBeChecked();
848 expect(custom1Checkboxes[4]).not.toBeChecked();
849 expect(custom1Checkboxes[5]).toBeChecked();
850 expect(custom3Checkboxes[0]).not.toBeChecked();
851 expect(custom3Checkboxes[1]).toBeChecked();
852 expect(custom3Checkboxes[2]).not.toBeChecked();
853 expect(custom3Checkboxes[3]).not.toBeChecked();
854 expect(custom3Checkboxes[4]).not.toBeChecked();
855 expect(custom3Checkboxes[5]).not.toBeChecked();
856 await user.click(ui.mappingDialogClose.get());
860 function renderAuthentication(features: Feature[] = []) {
862 <AvailableFeaturesContext.Provider value={features}>
863 <Authentication definitions={definitions} />
864 </AvailableFeaturesContext.Provider>,
865 `?tab=${AlmKeys.GitLab}`,