]> source.dussan.org Git - sonarqube.git/blob
cab2d22afa38d518673c896bd10e1fe40a62afe7
[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 { 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';
37
38 let handler: GitlabProvisioningServiceMock;
39 let system: SystemServiceMock;
40 let settingsHandler: SettingsServiceMock;
41 let computeEngineHandler: ComputeEngineServiceMock;
42
43 beforeEach(() => {
44   handler = new GitlabProvisioningServiceMock();
45   system = new SystemServiceMock();
46   settingsHandler = new SettingsServiceMock();
47   computeEngineHandler = new ComputeEngineServiceMock();
48 });
49
50 afterEach(() => {
51   handler.reset();
52   settingsHandler.reset();
53   system.reset();
54   computeEngineHandler.reset();
55 });
56
57 const glContainer = byRole('tabpanel', { name: 'gitlab GitLab' });
58
59 const ui = {
60   noGitlabConfiguration: glContainer.byText('settings.authentication.gitlab.form.not_configured'),
61   createConfigButton: glContainer.byRole('button', {
62     name: 'settings.authentication.form.create',
63   }),
64   editConfigButton: glContainer.byRole('button', {
65     name: 'settings.authentication.form.edit',
66   }),
67   editMappingButton: glContainer.byRole('button', {
68     name: 'settings.authentication.configuration.roles_mapping.button_label',
69   }),
70   deleteConfigButton: glContainer.byRole('button', {
71     name: 'settings.authentication.form.delete',
72   }),
73   enableConfigButton: glContainer.byRole('button', {
74     name: 'settings.authentication.form.enable',
75   }),
76   disableConfigButton: glContainer.byRole('button', {
77     name: 'settings.authentication.form.disable',
78   }),
79   createDialog: byRole('dialog', {
80     name: 'settings.authentication.gitlab.form.create',
81   }),
82   editDialog: byRole('dialog', {
83     name: 'settings.authentication.gitlab.form.edit',
84   }),
85   applicationId: byRole('textbox', {
86     name: 'property.applicationId.name',
87   }),
88   url: byRole('textbox', { name: 'property.url.name' }),
89   secret: byRole('textbox', {
90     name: 'property.secret.name',
91   }),
92   synchronizeGroups: byRole('switch', {
93     description: 'property.synchronizeGroups.description',
94   }),
95   saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
96   jitProvisioningRadioButton: glContainer.byRole('radio', {
97     name: /settings.authentication.gitlab.provisioning_at_login/,
98   }),
99   autoProvisioningRadioButton: glContainer.byRole('radio', {
100     name: /settings.authentication.gitlab.form.provisioning_with_gitlab/,
101   }),
102   jitAllowUsersToSignUpToggle: byRole('switch', {
103     description: 'property.allowUsersToSignUp.description',
104   }),
105   autoProvisioningToken: byRole('textbox', {
106     name: 'property.provisioningToken.name',
107   }),
108   autoProvisioningUpdateTokenButton: byRole('button', {
109     name: 'settings.almintegration.form.secret.update_field',
110   }),
111   groups: byRole('textbox', {
112     name: 'property.allowedGroups.name',
113   }),
114   deleteGroupButton: byRole('button', { name: /delete_value/ }),
115   removeProvisioniongGroup: byRole('button', {
116     name: /settings.definition.delete_value.property.allowedGroups.name./,
117   }),
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',
122   }),
123   confirmJitProvisioningDialog: byRole('alertdialog', {
124     name: 'settings.authentication.gitlab.confirm.JIT',
125   }),
126   confirmInsecureProvisioningDialog: byRole('alertdialog', {
127     name: 'settings.authentication.gitlab.confirm.insecure',
128   }),
129   confirmProvisioningChange: byRole('button', {
130     name: 'settings.authentication.gitlab.provisioning_change.confirm_changes',
131   }),
132   syncSummary: glContainer.byText(/Test summary/),
133   syncWarning: glContainer.byText(/Warning/),
134   gitlabProvisioningPending: glContainer
135     .byRole('list')
136     .byRole('status')
137     .byText(/synchronization_pending/),
138   gitlabProvisioningInProgress: glContainer
139     .byRole('list')
140     .byRole('status')
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/,
146   }),
147   gitlabMissingSecretErrorMessage: byText(
148     'settings.authentication.gitlab.form.secret.required_for_url_change',
149   ),
150   testConfiguration: glContainer.byRole('button', {
151     name: 'settings.authentication.configuration.test',
152   }),
153   continueAutoButton: byRole('button', {
154     name: 'settings.authentication.confirm_auto_provisioning.continue',
155   }),
156   switchJitButton: byRole('button', {
157     name: 'settings.authentication.confirm_auto_provisioning.switch_jit',
158   }),
159   consentDialog: byRole('dialog', {
160     name: 'settings.authentication.confirm_auto_provisioning.header',
161   }),
162   mappingRow: byRole('dialog', {
163     name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
164   }).byRole('row'),
165   mappingCheckbox: byRole('checkbox'),
166   mappingDialogClose: byRole('dialog', {
167     name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
168   }).byRole('button', {
169     name: 'close',
170   }),
171   customRoleInput: byRole('textbox', {
172     name: 'settings.authentication.configuration.roles_mapping.dialog.add_custom_role',
173   }),
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',
185   }),
186   getMappingRowByRole: (text: string) =>
187     ui.mappingRow.getAll().find((row) => within(row).queryByText(text) !== null),
188 };
189
190 it('should create a Gitlab configuration and disable it with proper validation', async () => {
191   handler.setGitlabConfigurations([]);
192   renderAuthentication();
193   const user = userEvent.setup();
194
195   expect(await ui.noGitlabConfiguration.find()).toBeInTheDocument();
196   expect(ui.createConfigButton.get()).toBeInTheDocument();
197
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());
207
208   expect(await ui.editConfigButton.find()).toBeInTheDocument();
209   expect(ui.noGitlabConfiguration.query()).not.toBeInTheDocument();
210   expect(glContainer.get()).toHaveTextContent('https://company.ui.com');
211
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();
216 });
217
218 it('should edit a configuration with proper validation and delete it', async () => {
219   const user = userEvent.setup();
220   renderAuthentication();
221
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();
227
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();
234
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();
240
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());
252
253   expect(glContainer.get()).not.toHaveTextContent('URL');
254   expect(glContainer.get()).toHaveTextContent('www.internet.com');
255
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();
263 });
264
265 it('should be able to save just-in-time with no organizations', async () => {
266   const user = userEvent.setup();
267   renderAuthentication([Feature.GitlabProvisioning]);
268
269   expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
270
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();
275 });
276
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,
282       enabled: true,
283       provisioningType: ProvisioningType.auto,
284       allowedGroups: ['D12'],
285       isProvisioningTokenSet: true,
286     }),
287   ]);
288   renderAuthentication([Feature.GitlabProvisioning]);
289
290   expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
291
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();
296 });
297
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,
303       enabled: true,
304       provisioningType: ProvisioningType.jit,
305       allowedGroups: [],
306       isProvisioningTokenSet: false,
307     }),
308   ]);
309   renderAuthentication([Feature.GitlabProvisioning]);
310
311   expect(await ui.editConfigButton.find()).toBeInTheDocument();
312   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
313
314   await user.click(ui.autoProvisioningRadioButton.get());
315   expect(await ui.autoProvisioningRadioButton.find()).toBeEnabled();
316   expect(ui.saveProvisioning.get()).toBeDisabled();
317
318   await user.type(ui.autoProvisioningToken.get(), 'JRR Tolkien');
319   expect(await ui.saveProvisioning.find()).toBeDisabled();
320
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();
325
326   await user.type(ui.groups.get(), 'Public Enemy');
327   expect(await ui.saveProvisioning.find()).toBeEnabled();
328 });
329
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,
334       enabled: true,
335       provisioningType: ProvisioningType.jit,
336       allowedGroups: ['D12'],
337       isProvisioningTokenSet: true,
338     }),
339   ]);
340   const user = userEvent.setup();
341   renderAuthentication([Feature.GitlabProvisioning]);
342
343   expect(await ui.editConfigButton.find()).toBeInTheDocument();
344   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
345
346   user.click(ui.autoProvisioningRadioButton.get());
347   expect(await ui.autoProvisioningRadioButton.find()).toBeEnabled();
348   expect(await ui.saveProvisioning.find()).toBeEnabled();
349
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');
354
355   expect(ui.saveProvisioning.get()).toBeEnabled();
356 });
357
358 it('should change from auto provisioning to JIT with proper validation', async () => {
359   handler.setGitlabConfigurations([
360     mockGitlabConfiguration({
361       allowUsersToSignUp: false,
362       enabled: true,
363       provisioningType: ProvisioningType.auto,
364       allowedGroups: ['D12'],
365       isProvisioningTokenSet: true,
366     }),
367   ]);
368   const user = userEvent.setup();
369   renderAuthentication([Feature.GitlabProvisioning]);
370
371   expect(await ui.editConfigButton.find()).toBeInTheDocument();
372
373   expect(ui.jitProvisioningRadioButton.get()).not.toBeChecked();
374   expect(ui.autoProvisioningRadioButton.get()).toBeChecked();
375
376   expect(ui.autoProvisioningToken.query()).not.toBeInTheDocument();
377   expect(ui.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
378
379   await user.click(ui.jitProvisioningRadioButton.get());
380   expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
381
382   expect(await ui.saveProvisioning.find()).toBeEnabled();
383
384   await user.click(ui.jitAllowUsersToSignUpToggle.get());
385   await user.click(ui.deleteGroupButton.get());
386
387   await user.click(ui.saveProvisioning.get());
388   expect(
389     ui.confirmJitProvisioningDialog
390       .byText('settings.authentication.gitlab.provisioning_change.insecure_config')
391       .get(),
392   ).toBeInTheDocument();
393   await user.click(ui.confirmProvisioningChange.get());
394   expect(ui.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
395
396   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
397   expect(await ui.saveProvisioning.find()).toBeDisabled();
398 });
399
400 it('should show configuration warning with jit provisioning and no groups', async () => {
401   handler.setGitlabConfigurations([
402     mockGitlabConfiguration({
403       allowUsersToSignUp: false,
404       enabled: true,
405       provisioningType: ProvisioningType.jit,
406       allowedGroups: [],
407       isProvisioningTokenSet: true,
408     }),
409   ]);
410   const user = userEvent.setup();
411   renderAuthentication([Feature.GitlabProvisioning]);
412
413   expect(await ui.editConfigButton.find()).toBeInTheDocument();
414
415   await user.click(ui.jitAllowUsersToSignUpToggle.get());
416   await user.click(ui.saveProvisioning.get());
417
418   expect(
419     ui.confirmInsecureProvisioningDialog
420       .byText('settings.authentication.gitlab.provisioning_change.insecure_config')
421       .get(),
422   ).toBeInTheDocument();
423
424   await user.click(ui.confirmProvisioningChange.get());
425   expect(ui.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
426
427   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
428   expect(await ui.saveProvisioning.find()).toBeDisabled();
429 });
430
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,
435       enabled: true,
436       provisioningType: ProvisioningType.jit,
437     }),
438   ]);
439   const user = userEvent.setup();
440   renderAuthentication([Feature.GitlabProvisioning]);
441
442   expect(await ui.editConfigButton.find()).toBeInTheDocument();
443
444   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
445   expect(ui.autoProvisioningRadioButton.get()).not.toBeChecked();
446
447   expect(ui.jitAllowUsersToSignUpToggle.get()).not.toBeChecked();
448
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());
455
456   await user.click(ui.saveProvisioning.get());
457
458   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
459   expect(ui.jitAllowUsersToSignUpToggle.get()).toBeChecked();
460   expect(await ui.saveProvisioning.find()).toBeDisabled();
461 });
462
463 it('should be able to edit token for Auto provisioning with proper validation', async () => {
464   handler.setGitlabConfigurations([
465     mockGitlabConfiguration({
466       allowUsersToSignUp: false,
467       enabled: true,
468       provisioningType: ProvisioningType.auto,
469       allowedGroups: ['Cypress Hill', 'Public Enemy'],
470       isProvisioningTokenSet: true,
471     }),
472   ]);
473   const user = userEvent.setup();
474   renderAuthentication([Feature.GitlabProvisioning]);
475
476   expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
477   expect(ui.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
478
479   expect(ui.saveProvisioning.get()).toBeDisabled();
480
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();
486 });
487
488 it('should be able to reset Auto Provisioning changes', async () => {
489   handler.setGitlabConfigurations([
490     mockGitlabConfiguration({
491       allowUsersToSignUp: false,
492       enabled: true,
493       provisioningType: ProvisioningType.auto,
494       allowedGroups: ['Cypress Hill', 'Public Enemy'],
495       isProvisioningTokenSet: true,
496     }),
497   ]);
498   const user = userEvent.setup();
499   renderAuthentication([Feature.GitlabProvisioning]);
500
501   expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
502
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();
508 });
509
510 describe('Gitlab Provisioning', () => {
511   beforeEach(() => {
512     jest.useFakeTimers({
513       advanceTimers: true,
514       now: new Date('2022-02-04T12:00:59Z'),
515     });
516     handler.setGitlabConfigurations([
517       mockGitlabConfiguration({
518         enabled: true,
519         provisioningType: ProvisioningType.auto,
520         allowedGroups: ['Test'],
521       }),
522     ]);
523   });
524
525   afterEach(() => {
526     jest.runOnlyPendingTimers();
527     jest.useRealTimers();
528   });
529
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,
536     });
537
538     renderAuthentication([Feature.GitlabProvisioning]);
539     expect(await ui.gitlabProvisioningSuccess.find()).toBeInTheDocument();
540     expect(ui.syncSummary.get()).toBeInTheDocument();
541   });
542
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,
548     });
549     computeEngineHandler.addTask({
550       status: TaskStatuses.Success,
551       executedAt: '2022-02-03T11:45:35+0200',
552       type: TaskTypes.GitlabProvisioning,
553     });
554     renderAuthentication([Feature.GitlabProvisioning]);
555     expect(await ui.gitlabProvisioningSuccess.find()).toBeInTheDocument();
556     expect(ui.gitlabProvisioningPending.get()).toBeInTheDocument();
557   });
558
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,
565     });
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();
570   });
571
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,
577     });
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,
583     });
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();
589   });
590
591   it('should show warning', async () => {
592     computeEngineHandler.addTask({
593       status: TaskStatuses.Success,
594       warnings: ['Warning'],
595       infoMessages: ['Test summary'],
596       type: TaskTypes.GitlabProvisioning,
597     });
598     renderAuthentication([Feature.GitlabProvisioning]);
599
600     expect(await ui.syncWarning.find()).toBeInTheDocument();
601     expect(ui.syncSummary.get()).toBeInTheDocument();
602   });
603
604   it('should show configuration validity', async () => {
605     const user = userEvent.setup();
606     renderAuthentication([Feature.GitlabProvisioning]);
607
608     expect(await ui.gitlabConfigurationStatus.find()).toHaveTextContent(
609       'settings.authentication.gitlab.configuration.valid.AUTO_PROVISIONING',
610     );
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',
616     );
617     handler.setGitlabConfigurations([
618       mockGitlabConfiguration({ ...handler.gitlabConfigurations[0], errorMessage: 'ERROR' }),
619     ]);
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();
624   });
625
626   it('should display a modal if user was already using auto and continue using auto provisioning', async () => {
627     const user = userEvent.setup();
628
629     settingsHandler.set('sonar.auth.gitlab.userConsentForPermissionProvisioningRequired', '');
630     handler.setGitlabConfigurations([
631       mockGitlabConfiguration({
632         allowUsersToSignUp: false,
633         enabled: true,
634         provisioningType: ProvisioningType.auto,
635         allowedGroups: ['D12'],
636         isProvisioningTokenSet: true,
637       }),
638     ]);
639     renderAuthentication([Feature.GitlabProvisioning]);
640
641     expect(await ui.consentDialog.find()).toBeInTheDocument();
642     await user.click(ui.continueAutoButton.get());
643
644     expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
645     expect(ui.consentDialog.query()).not.toBeInTheDocument();
646   });
647
648   it('should display a modal if user was already using auto and switch to JIT', async () => {
649     const user = userEvent.setup();
650
651     settingsHandler.set('sonar.auth.gitlab.userConsentForPermissionProvisioningRequired', '');
652     handler.setGitlabConfigurations([
653       mockGitlabConfiguration({
654         allowUsersToSignUp: false,
655         enabled: true,
656         provisioningType: ProvisioningType.auto,
657         allowedGroups: ['D12'],
658         isProvisioningTokenSet: true,
659       }),
660     ]);
661     renderAuthentication([Feature.GitlabProvisioning]);
662
663     expect(await ui.consentDialog.find()).toBeInTheDocument();
664     await user.click(ui.switchJitButton.get());
665
666     expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
667     expect(ui.consentDialog.query()).not.toBeInTheDocument();
668   });
669
670   it('should sort mapping rows', async () => {
671     const user = userEvent.setup();
672     handler.setGitlabConfigurations([
673       mockGitlabConfiguration({
674         allowUsersToSignUp: false,
675         enabled: true,
676         provisioningType: ProvisioningType.auto,
677         allowedGroups: ['D12'],
678         isProvisioningTokenSet: true,
679       }),
680     ]);
681     renderAuthentication([Feature.GitlabProvisioning]);
682
683     expect(await ui.editMappingButton.find()).toBeInTheDocument();
684     await user.click(ui.editMappingButton.get());
685
686     const rows = (await ui.mappingRow.findAll()).filter(
687       (row) => within(row).queryAllByRole('checkbox').length > 0,
688     );
689
690     expect(rows).toHaveLength(5);
691
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');
697   });
698
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,
704         enabled: true,
705         provisioningType: ProvisioningType.jit,
706         allowedGroups: ['D12'],
707         isProvisioningTokenSet: true,
708       }),
709     ]);
710     renderAuthentication([Feature.GitlabProvisioning]);
711
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);
718
719     let guestCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('guest'));
720     let ownerCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('owner'));
721
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();
734
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());
739
740     await user.click(ui.saveProvisioning.get());
741     await user.click(ui.confirmProvisioningChange.get());
742
743     // Clean local mapping state
744     await user.click(ui.jitProvisioningRadioButton.get());
745     await user.click(ui.autoProvisioningRadioButton.get());
746
747     await user.click(ui.editMappingButton.get());
748     guestCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('guest'));
749     ownerCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('owner'));
750
751     expect(guestCheckboxes[0]).not.toBeChecked();
752     expect(guestCheckboxes[5]).toBeChecked();
753     expect(ownerCheckboxes[5]).not.toBeChecked();
754     await user.click(ui.mappingDialogClose.get());
755   });
756
757   it('should add/remove/update custom roles', async () => {
758     const user = userEvent.setup();
759     handler.setGitlabConfigurations([
760       mockGitlabConfiguration({
761         allowUsersToSignUp: false,
762         enabled: true,
763         provisioningType: ProvisioningType.auto,
764         allowedGroups: ['D12'],
765         isProvisioningTokenSet: true,
766       }),
767     ]);
768     handler.addGitLabCustomRole('custom1', ['user', 'codeViewer', 'scan']);
769     handler.addGitLabCustomRole('custom2', ['user', 'codeViewer', 'issueAdmin', 'scan']);
770     renderAuthentication([Feature.GitlabProvisioning]);
771
772     expect(await ui.saveProvisioning.find()).toBeDisabled();
773     await user.click(ui.editMappingButton.get());
774
775     const rows = (await ui.mappingRow.findAll()).filter(
776       (row) => within(row).queryAllByRole('checkbox').length > 0,
777     );
778
779     expect(rows).toHaveLength(7);
780
781     let custom1Checkboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('custom1'));
782
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();
789
790     await user.click(custom1Checkboxes[1]);
791     await user.click(custom1Checkboxes[2]);
792
793     await user.click(ui.deleteCustomRoleCustom2.get());
794
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());
811
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());
826
827     expect(await ui.saveProvisioning.find()).toBeEnabled();
828     await user.click(ui.saveProvisioning.get());
829
830     // Clean local mapping state
831     await user.click(ui.jitProvisioningRadioButton.get());
832     await user.click(ui.autoProvisioningRadioButton.get());
833
834     await user.click(ui.editMappingButton.get());
835
836     expect(
837       (await ui.mappingRow.findAll()).filter(
838         (row) => within(row).queryAllByRole('checkbox').length > 0,
839       ),
840     ).toHaveLength(7);
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());
857   });
858 });
859
860 function renderAuthentication(features: Feature[] = []) {
861   renderComponent(
862     <AvailableFeaturesContext.Provider value={features}>
863       <Authentication definitions={definitions} />
864     </AvailableFeaturesContext.Provider>,
865     `?tab=${AlmKeys.GitLab}`,
866   );
867 }