]> source.dussan.org Git - sonarqube.git/blob
d8735204ceef2027b05065e43ecb548e2de7da30
[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     name: 'property.synchronizeGroups.name',
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', { name: 'property.allowUsersToSignUp.name' }),
103   autoProvisioningToken: byRole('textbox', {
104     name: 'property.provisioningToken.name',
105   }),
106   autoProvisioningUpdateTokenButton: byRole('button', {
107     name: 'settings.almintegration.form.secret.update_field',
108   }),
109   groups: byRole('textbox', {
110     name: 'property.allowedGroups.name',
111   }),
112   deleteGroupButton: byRole('button', { name: /delete_value/ }),
113   removeProvisioniongGroup: byRole('button', {
114     name: /settings.definition.delete_value.property.allowedGroups.name./,
115   }),
116   saveProvisioning: glContainer.byRole('button', { name: 'save' }),
117   cancelProvisioningChanges: glContainer.byRole('button', { name: 'cancel' }),
118   confirmAutoProvisioningDialog: byRole('dialog', {
119     name: 'settings.authentication.gitlab.confirm.AUTO_PROVISIONING',
120   }),
121   confirmJitProvisioningDialog: byRole('alertdialog', {
122     name: 'settings.authentication.gitlab.confirm.JIT',
123   }),
124   confirmInsecureProvisioningDialog: byRole('alertdialog', {
125     name: 'settings.authentication.gitlab.confirm.insecure',
126   }),
127   confirmProvisioningChange: byRole('button', {
128     name: 'settings.authentication.gitlab.provisioning_change.confirm_changes',
129   }),
130   syncSummary: glContainer.byText(/Test summary/),
131   syncWarning: glContainer.byText(/Warning/),
132   gitlabProvisioningPending: glContainer
133     .byRole('list')
134     .byRole('status')
135     .byText(/synchronization_pending/),
136   gitlabProvisioningInProgress: glContainer
137     .byRole('list')
138     .byRole('status')
139     .byText(/synchronization_in_progress/),
140   gitlabProvisioningSuccess: glContainer.byText(/synchronization_successful/),
141   gitlabProvisioningAlert: glContainer.byText(/synchronization_failed/),
142   gitlabConfigurationStatus: glContainer.byRole('status', {
143     name: /settings.authentication.gitlab.configuration/,
144   }),
145   gitlabMissingSecretErrorMessage: byText(
146     'settings.authentication.gitlab.form.secret.required_for_url_change',
147   ),
148   testConfiguration: glContainer.byRole('button', {
149     name: 'settings.authentication.configuration.test',
150   }),
151   continueAutoButton: byRole('button', {
152     name: 'settings.authentication.confirm_auto_provisioning.continue',
153   }),
154   switchJitButton: byRole('button', {
155     name: 'settings.authentication.confirm_auto_provisioning.switch_jit',
156   }),
157   consentDialog: byRole('dialog', {
158     name: 'settings.authentication.confirm_auto_provisioning.header',
159   }),
160   mappingRow: byRole('dialog', {
161     name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
162   }).byRole('row'),
163   mappingCheckbox: byRole('checkbox'),
164   mappingDialogClose: byRole('dialog', {
165     name: 'settings.authentication.configuration.roles_mapping.dialog.title.alm.gitlab',
166   }).byRole('button', {
167     name: 'close',
168   }),
169   getMappingRowByRole: (text: string) =>
170     ui.mappingRow.getAll().find((row) => within(row).queryByText(text) !== null),
171 };
172
173 it('should create a Gitlab configuration and disable it with proper validation', async () => {
174   handler.setGitlabConfigurations([]);
175   renderAuthentication();
176   const user = userEvent.setup();
177
178   expect(await ui.noGitlabConfiguration.find()).toBeInTheDocument();
179   expect(ui.createConfigButton.get()).toBeInTheDocument();
180
181   await user.click(ui.createConfigButton.get());
182   expect(await ui.createDialog.find()).toBeInTheDocument();
183   await user.type(ui.applicationId.get(), '123');
184   expect(ui.saveConfigButton.get()).toBeDisabled();
185   await user.type(ui.url.get(), 'https://company.ui.com');
186   await user.type(ui.secret.get(), '123');
187   expect(ui.saveConfigButton.get()).toBeEnabled();
188   await user.click(ui.synchronizeGroups.get());
189   await user.click(ui.saveConfigButton.get());
190
191   expect(await ui.editConfigButton.find()).toBeInTheDocument();
192   expect(ui.noGitlabConfiguration.query()).not.toBeInTheDocument();
193   expect(glContainer.get()).toHaveTextContent('https://company.ui.com');
194
195   expect(ui.disableConfigButton.get()).toBeInTheDocument();
196   await user.click(ui.disableConfigButton.get());
197   expect(ui.enableConfigButton.get()).toBeInTheDocument();
198   expect(ui.disableConfigButton.query()).not.toBeInTheDocument();
199 });
200
201 it('should edit a configuration with proper validation and delete it', async () => {
202   const user = userEvent.setup();
203   renderAuthentication();
204
205   expect(await ui.editConfigButton.find()).toBeInTheDocument();
206   expect(glContainer.get()).toHaveTextContent('URL');
207   expect(ui.disableConfigButton.get()).toBeInTheDocument();
208   expect(ui.deleteConfigButton.get()).toBeInTheDocument();
209   expect(ui.deleteConfigButton.get()).toBeDisabled();
210
211   await user.click(ui.editConfigButton.get());
212   expect(await ui.editDialog.find()).toBeInTheDocument();
213   expect(ui.url.get()).toHaveValue('URL');
214   expect(ui.applicationId.get()).toBeInTheDocument();
215   expect(ui.secret.query()).not.toBeInTheDocument();
216   expect(ui.synchronizeGroups.get()).toBeChecked();
217
218   expect(ui.applicationId.get()).toBeInTheDocument();
219   await user.clear(ui.applicationId.get());
220   expect(ui.saveConfigButton.get()).toBeDisabled();
221   await user.type(ui.applicationId.get(), '456');
222   expect(ui.saveConfigButton.get()).toBeEnabled();
223
224   expect(ui.url.get()).toBeInTheDocument();
225   await user.clear(ui.url.get());
226   expect(ui.saveConfigButton.get()).toBeDisabled();
227   await user.type(ui.url.get(), 'www.internet.com');
228   expect(ui.saveConfigButton.get()).toBeDisabled();
229   expect(ui.gitlabMissingSecretErrorMessage.get()).toBeInTheDocument();
230   await user.click(ui.autoProvisioningUpdateTokenButton.get());
231   await user.type(ui.secret.get(), '123');
232   expect(ui.gitlabMissingSecretErrorMessage.query()).not.toBeInTheDocument();
233   expect(ui.saveConfigButton.get()).toBeEnabled();
234   await user.click(ui.saveConfigButton.get());
235
236   expect(glContainer.get()).not.toHaveTextContent('URL');
237   expect(glContainer.get()).toHaveTextContent('www.internet.com');
238
239   expect(ui.disableConfigButton.get()).toBeInTheDocument();
240   await user.click(ui.disableConfigButton.get());
241   expect(await ui.enableConfigButton.find()).toBeInTheDocument();
242   expect(ui.deleteConfigButton.get()).toBeEnabled();
243   await user.click(ui.deleteConfigButton.get());
244   expect(await ui.noGitlabConfiguration.find()).toBeInTheDocument();
245   expect(ui.editConfigButton.query()).not.toBeInTheDocument();
246 });
247
248 it('should be able to save just-in-time with no organizations', async () => {
249   const user = userEvent.setup();
250   renderAuthentication([Feature.GitlabProvisioning]);
251
252   expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
253
254   expect(ui.groups.get()).toHaveValue('Cypress Hill');
255   expect(await ui.saveProvisioning.find()).toBeDisabled();
256   await user.click(ui.deleteGroupButton.get());
257   expect(await ui.saveProvisioning.find()).toBeEnabled();
258 });
259
260 it('should not be able to save Auto provisioning with no organizations', async () => {
261   const user = userEvent.setup();
262   handler.setGitlabConfigurations([
263     mockGitlabConfiguration({
264       allowUsersToSignUp: false,
265       enabled: true,
266       provisioningType: ProvisioningType.auto,
267       allowedGroups: ['D12'],
268       isProvisioningTokenSet: true,
269     }),
270   ]);
271   renderAuthentication([Feature.GitlabProvisioning]);
272
273   expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
274
275   expect(ui.groups.get()).toHaveValue('D12');
276   expect(ui.saveProvisioning.get()).toBeDisabled();
277   await user.click(ui.deleteGroupButton.get());
278   expect(await ui.saveProvisioning.find()).toBeDisabled();
279 });
280
281 it('should change from just-in-time to Auto Provisioning if auto was never set before', async () => {
282   const user = userEvent.setup();
283   handler.setGitlabConfigurations([
284     mockGitlabConfiguration({
285       allowUsersToSignUp: false,
286       enabled: true,
287       provisioningType: ProvisioningType.jit,
288       allowedGroups: [],
289       isProvisioningTokenSet: false,
290     }),
291   ]);
292   renderAuthentication([Feature.GitlabProvisioning]);
293
294   expect(await ui.editConfigButton.find()).toBeInTheDocument();
295   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
296
297   await user.click(ui.autoProvisioningRadioButton.get());
298   expect(await ui.autoProvisioningRadioButton.find()).toBeEnabled();
299   expect(ui.saveProvisioning.get()).toBeDisabled();
300
301   await user.type(ui.autoProvisioningToken.get(), 'JRR Tolkien');
302   expect(await ui.saveProvisioning.find()).toBeDisabled();
303
304   await user.type(ui.groups.get(), 'Run DMC');
305   expect(await ui.saveProvisioning.find()).toBeEnabled();
306   await user.click(ui.deleteGroupButton.get());
307   expect(await ui.saveProvisioning.find()).toBeDisabled();
308
309   await user.type(ui.groups.get(), 'Public Enemy');
310   expect(await ui.saveProvisioning.find()).toBeEnabled();
311 });
312
313 it('should change from just-in-time to Auto Provisioning if auto was set before', async () => {
314   handler.setGitlabConfigurations([
315     mockGitlabConfiguration({
316       allowUsersToSignUp: false,
317       enabled: true,
318       provisioningType: ProvisioningType.jit,
319       allowedGroups: ['D12'],
320       isProvisioningTokenSet: true,
321     }),
322   ]);
323   const user = userEvent.setup();
324   renderAuthentication([Feature.GitlabProvisioning]);
325
326   expect(await ui.editConfigButton.find()).toBeInTheDocument();
327   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
328
329   user.click(ui.autoProvisioningRadioButton.get());
330   expect(await ui.autoProvisioningRadioButton.find()).toBeEnabled();
331   expect(await ui.saveProvisioning.find()).toBeEnabled();
332
333   expect(ui.groups.get()).toHaveValue('D12');
334   await user.click(ui.deleteGroupButton.get());
335   expect(await ui.saveProvisioning.find()).toBeDisabled();
336   await user.type(ui.groups.get(), 'Wu Tang Clan');
337
338   expect(ui.saveProvisioning.get()).toBeEnabled();
339 });
340
341 it('should change from auto provisioning to JIT with proper validation', async () => {
342   handler.setGitlabConfigurations([
343     mockGitlabConfiguration({
344       allowUsersToSignUp: false,
345       enabled: true,
346       provisioningType: ProvisioningType.auto,
347       allowedGroups: ['D12'],
348       isProvisioningTokenSet: true,
349     }),
350   ]);
351   const user = userEvent.setup();
352   renderAuthentication([Feature.GitlabProvisioning]);
353
354   expect(await ui.editConfigButton.find()).toBeInTheDocument();
355
356   expect(ui.jitProvisioningRadioButton.get()).not.toBeChecked();
357   expect(ui.autoProvisioningRadioButton.get()).toBeChecked();
358
359   expect(ui.autoProvisioningToken.query()).not.toBeInTheDocument();
360   expect(ui.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
361
362   await user.click(ui.jitProvisioningRadioButton.get());
363   expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
364
365   expect(await ui.saveProvisioning.find()).toBeEnabled();
366
367   await user.click(ui.jitAllowUsersToSignUpToggle.get());
368   await user.click(ui.deleteGroupButton.get());
369
370   await user.click(ui.saveProvisioning.get());
371   expect(
372     ui.confirmJitProvisioningDialog
373       .byText('settings.authentication.gitlab.provisioning_change.insecure_config')
374       .get(),
375   ).toBeInTheDocument();
376   await user.click(ui.confirmProvisioningChange.get());
377   expect(ui.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
378
379   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
380   expect(await ui.saveProvisioning.find()).toBeDisabled();
381 });
382
383 it('should show configuration warning with jit provisioning and no groups', async () => {
384   handler.setGitlabConfigurations([
385     mockGitlabConfiguration({
386       allowUsersToSignUp: false,
387       enabled: true,
388       provisioningType: ProvisioningType.jit,
389       allowedGroups: [],
390       isProvisioningTokenSet: true,
391     }),
392   ]);
393   const user = userEvent.setup();
394   renderAuthentication([Feature.GitlabProvisioning]);
395
396   expect(await ui.editConfigButton.find()).toBeInTheDocument();
397
398   await user.click(ui.jitAllowUsersToSignUpToggle.get());
399   await user.click(ui.saveProvisioning.get());
400
401   expect(
402     ui.confirmInsecureProvisioningDialog
403       .byText('settings.authentication.gitlab.provisioning_change.insecure_config')
404       .get(),
405   ).toBeInTheDocument();
406
407   await user.click(ui.confirmProvisioningChange.get());
408   expect(ui.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
409
410   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
411   expect(await ui.saveProvisioning.find()).toBeDisabled();
412 });
413
414 it('should be able to allow user to sign up for JIT with proper validation', async () => {
415   handler.setGitlabConfigurations([
416     mockGitlabConfiguration({
417       allowUsersToSignUp: false,
418       enabled: true,
419       provisioningType: ProvisioningType.jit,
420     }),
421   ]);
422   const user = userEvent.setup();
423   renderAuthentication([Feature.GitlabProvisioning]);
424
425   expect(await ui.editConfigButton.find()).toBeInTheDocument();
426
427   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
428   expect(ui.autoProvisioningRadioButton.get()).not.toBeChecked();
429
430   expect(ui.jitAllowUsersToSignUpToggle.get()).not.toBeChecked();
431
432   expect(ui.saveProvisioning.get()).toBeDisabled();
433   await user.click(ui.jitAllowUsersToSignUpToggle.get());
434   expect(ui.saveProvisioning.get()).toBeEnabled();
435   await user.click(ui.jitAllowUsersToSignUpToggle.get());
436   expect(ui.saveProvisioning.get()).toBeDisabled();
437   await user.click(ui.jitAllowUsersToSignUpToggle.get());
438
439   await user.click(ui.saveProvisioning.get());
440
441   expect(ui.jitProvisioningRadioButton.get()).toBeChecked();
442   expect(ui.jitAllowUsersToSignUpToggle.get()).toBeChecked();
443   expect(await ui.saveProvisioning.find()).toBeDisabled();
444 });
445
446 it('should be able to edit token for Auto provisioning with proper validation', async () => {
447   handler.setGitlabConfigurations([
448     mockGitlabConfiguration({
449       allowUsersToSignUp: false,
450       enabled: true,
451       provisioningType: ProvisioningType.auto,
452       allowedGroups: ['Cypress Hill', 'Public Enemy'],
453       isProvisioningTokenSet: true,
454     }),
455   ]);
456   const user = userEvent.setup();
457   renderAuthentication([Feature.GitlabProvisioning]);
458
459   expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
460   expect(ui.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
461
462   expect(ui.saveProvisioning.get()).toBeDisabled();
463
464   // Changing the Provisioning token should enable save
465   await user.click(ui.autoProvisioningUpdateTokenButton.get());
466   expect(ui.saveProvisioning.get()).toBeDisabled();
467   await user.click(ui.cancelProvisioningChanges.get());
468   expect(ui.saveProvisioning.get()).toBeDisabled();
469 });
470
471 it('should be able to reset Auto Provisioning changes', async () => {
472   handler.setGitlabConfigurations([
473     mockGitlabConfiguration({
474       allowUsersToSignUp: false,
475       enabled: true,
476       provisioningType: ProvisioningType.auto,
477       allowedGroups: ['Cypress Hill', 'Public Enemy'],
478       isProvisioningTokenSet: true,
479     }),
480   ]);
481   const user = userEvent.setup();
482   renderAuthentication([Feature.GitlabProvisioning]);
483
484   expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
485
486   await user.click(ui.autoProvisioningUpdateTokenButton.get());
487   await user.type(ui.autoProvisioningToken.get(), 'ToToken!');
488   expect(ui.saveProvisioning.get()).toBeEnabled();
489   await user.click(ui.cancelProvisioningChanges.get());
490   expect(ui.saveProvisioning.get()).toBeDisabled();
491 });
492
493 describe('Gitlab Provisioning', () => {
494   beforeEach(() => {
495     jest.useFakeTimers({
496       advanceTimers: true,
497       now: new Date('2022-02-04T12:00:59Z'),
498     });
499     handler.setGitlabConfigurations([
500       mockGitlabConfiguration({
501         enabled: true,
502         provisioningType: ProvisioningType.auto,
503         allowedGroups: ['Test'],
504       }),
505     ]);
506   });
507
508   afterEach(() => {
509     jest.runOnlyPendingTimers();
510     jest.useRealTimers();
511   });
512
513   it('should display a success status when the synchronisation is a success', async () => {
514     computeEngineHandler.addTask({
515       status: TaskStatuses.Success,
516       executedAt: '2022-02-03T11:45:35+0200',
517       infoMessages: ['Test summary'],
518       type: TaskTypes.GitlabProvisioning,
519     });
520
521     renderAuthentication([Feature.GitlabProvisioning]);
522     expect(await ui.gitlabProvisioningSuccess.find()).toBeInTheDocument();
523     expect(ui.syncSummary.get()).toBeInTheDocument();
524   });
525
526   it('should display a success status even when another task is pending', async () => {
527     computeEngineHandler.addTask({
528       status: TaskStatuses.Pending,
529       executedAt: '2022-02-03T11:55:35+0200',
530       type: TaskTypes.GitlabProvisioning,
531     });
532     computeEngineHandler.addTask({
533       status: TaskStatuses.Success,
534       executedAt: '2022-02-03T11:45:35+0200',
535       type: TaskTypes.GitlabProvisioning,
536     });
537     renderAuthentication([Feature.GitlabProvisioning]);
538     expect(await ui.gitlabProvisioningSuccess.find()).toBeInTheDocument();
539     expect(ui.gitlabProvisioningPending.get()).toBeInTheDocument();
540   });
541
542   it('should display an error alert when the synchronisation failed', async () => {
543     computeEngineHandler.addTask({
544       status: TaskStatuses.Failed,
545       executedAt: '2022-02-03T11:45:35+0200',
546       errorMessage: "T'es mauvais Jacques",
547       type: TaskTypes.GitlabProvisioning,
548     });
549     renderAuthentication([Feature.GitlabProvisioning]);
550     expect(await ui.gitlabProvisioningAlert.find()).toBeInTheDocument();
551     expect(glContainer.get()).toHaveTextContent("T'es mauvais Jacques");
552     expect(ui.gitlabProvisioningSuccess.query()).not.toBeInTheDocument();
553   });
554
555   it('should display an error alert even when another task is in progress', async () => {
556     computeEngineHandler.addTask({
557       status: TaskStatuses.InProgress,
558       executedAt: '2022-02-03T11:55:35+0200',
559       type: TaskTypes.GitlabProvisioning,
560     });
561     computeEngineHandler.addTask({
562       status: TaskStatuses.Failed,
563       executedAt: '2022-02-03T11:45:35+0200',
564       errorMessage: "T'es mauvais Jacques",
565       type: TaskTypes.GitlabProvisioning,
566     });
567     renderAuthentication([Feature.GitlabProvisioning]);
568     expect(await ui.gitlabProvisioningAlert.find()).toBeInTheDocument();
569     expect(glContainer.get()).toHaveTextContent("T'es mauvais Jacques");
570     expect(ui.gitlabProvisioningSuccess.query()).not.toBeInTheDocument();
571     expect(ui.gitlabProvisioningInProgress.get()).toBeInTheDocument();
572   });
573
574   it('should show warning', async () => {
575     computeEngineHandler.addTask({
576       status: TaskStatuses.Success,
577       warnings: ['Warning'],
578       infoMessages: ['Test summary'],
579       type: TaskTypes.GitlabProvisioning,
580     });
581     renderAuthentication([Feature.GitlabProvisioning]);
582
583     expect(await ui.syncWarning.find()).toBeInTheDocument();
584     expect(ui.syncSummary.get()).toBeInTheDocument();
585   });
586
587   it('should show configuration validity', async () => {
588     const user = userEvent.setup();
589     renderAuthentication([Feature.GitlabProvisioning]);
590
591     expect(await ui.gitlabConfigurationStatus.find()).toHaveTextContent(
592       'settings.authentication.gitlab.configuration.valid.AUTO_PROVISIONING',
593     );
594     await user.click(ui.jitProvisioningRadioButton.get());
595     await user.click(ui.saveProvisioning.get());
596     await user.click(ui.confirmProvisioningChange.get());
597     expect(ui.gitlabConfigurationStatus.get()).toHaveTextContent(
598       'settings.authentication.gitlab.configuration.valid.JIT',
599     );
600     handler.setGitlabConfigurations([
601       mockGitlabConfiguration({ ...handler.gitlabConfigurations[0], errorMessage: 'ERROR' }),
602     ]);
603     await user.click(ui.testConfiguration.get());
604     expect(glContainer.get()).toHaveTextContent('ERROR');
605     await user.click(ui.disableConfigButton.get());
606     expect(ui.gitlabConfigurationStatus.query()).not.toBeInTheDocument();
607   });
608
609   it('should display a modal if user was already using auto and continue using auto provisioning', async () => {
610     const user = userEvent.setup();
611
612     settingsHandler.set('sonar.auth.gitlab.userConsentForPermissionProvisioningRequired', '');
613     handler.setGitlabConfigurations([
614       mockGitlabConfiguration({
615         allowUsersToSignUp: false,
616         enabled: true,
617         provisioningType: ProvisioningType.auto,
618         allowedGroups: ['D12'],
619         isProvisioningTokenSet: true,
620       }),
621     ]);
622     renderAuthentication([Feature.GitlabProvisioning]);
623
624     expect(await ui.consentDialog.find()).toBeInTheDocument();
625     await user.click(ui.continueAutoButton.get());
626
627     expect(await ui.autoProvisioningRadioButton.find()).toBeChecked();
628     expect(ui.consentDialog.query()).not.toBeInTheDocument();
629   });
630
631   it('should display a modal if user was already using auto and switch to JIT', async () => {
632     const user = userEvent.setup();
633
634     settingsHandler.set('sonar.auth.gitlab.userConsentForPermissionProvisioningRequired', '');
635     handler.setGitlabConfigurations([
636       mockGitlabConfiguration({
637         allowUsersToSignUp: false,
638         enabled: true,
639         provisioningType: ProvisioningType.auto,
640         allowedGroups: ['D12'],
641         isProvisioningTokenSet: true,
642       }),
643     ]);
644     renderAuthentication([Feature.GitlabProvisioning]);
645
646     expect(await ui.consentDialog.find()).toBeInTheDocument();
647     await user.click(ui.switchJitButton.get());
648
649     expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
650     expect(ui.consentDialog.query()).not.toBeInTheDocument();
651   });
652
653   it('should sort mapping rows', async () => {
654     const user = userEvent.setup();
655     handler.setGitlabConfigurations([
656       mockGitlabConfiguration({
657         allowUsersToSignUp: false,
658         enabled: true,
659         provisioningType: ProvisioningType.auto,
660         allowedGroups: ['D12'],
661         isProvisioningTokenSet: true,
662       }),
663     ]);
664     renderAuthentication([Feature.GitlabProvisioning]);
665
666     expect(await ui.editMappingButton.find()).toBeInTheDocument();
667     await user.click(ui.editMappingButton.get());
668
669     const rows = (await ui.mappingRow.findAll()).filter(
670       (row) => within(row).queryAllByRole('checkbox').length > 0,
671     );
672
673     expect(rows).toHaveLength(5);
674
675     expect(rows[0]).toHaveTextContent('guest');
676     expect(rows[1]).toHaveTextContent('reporter');
677     expect(rows[2]).toHaveTextContent('developer');
678     expect(rows[3]).toHaveTextContent('maintainer');
679     expect(rows[4]).toHaveTextContent('owner');
680   });
681
682   it('should apply new mapping and new provisioning type at the same time', async () => {
683     const user = userEvent.setup();
684     handler.setGitlabConfigurations([
685       mockGitlabConfiguration({
686         allowUsersToSignUp: false,
687         enabled: true,
688         provisioningType: ProvisioningType.jit,
689         allowedGroups: ['D12'],
690         isProvisioningTokenSet: true,
691       }),
692     ]);
693     renderAuthentication([Feature.GitlabProvisioning]);
694
695     expect(await ui.jitProvisioningRadioButton.find()).toBeChecked();
696     expect(ui.editMappingButton.query()).not.toBeInTheDocument();
697     await user.click(ui.autoProvisioningRadioButton.get());
698     expect(await ui.editMappingButton.find()).toBeInTheDocument();
699     await user.click(ui.editMappingButton.get());
700
701     expect(await ui.mappingRow.findAll()).toHaveLength(6);
702
703     let guestCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('guest'));
704     let ownerCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('owner'));
705
706     expect(guestCheckboxes[0]).toBeChecked();
707     expect(guestCheckboxes[1]).toBeChecked();
708     expect(guestCheckboxes[2]).not.toBeChecked();
709     expect(guestCheckboxes[3]).not.toBeChecked();
710     expect(guestCheckboxes[4]).not.toBeChecked();
711     expect(guestCheckboxes[5]).not.toBeChecked();
712     expect(ownerCheckboxes[0]).toBeChecked();
713     expect(ownerCheckboxes[1]).toBeChecked();
714     expect(ownerCheckboxes[2]).toBeChecked();
715     expect(ownerCheckboxes[3]).toBeChecked();
716     expect(ownerCheckboxes[4]).toBeChecked();
717     expect(ownerCheckboxes[5]).toBeChecked();
718
719     await user.click(guestCheckboxes[0]);
720     await user.click(guestCheckboxes[5]);
721     await user.click(ownerCheckboxes[5]);
722     await user.click(ui.mappingDialogClose.get());
723
724     await user.click(ui.saveProvisioning.get());
725     await user.click(ui.confirmProvisioningChange.get());
726
727     // Clean local mapping state
728     await user.click(ui.jitProvisioningRadioButton.get());
729     await user.click(ui.autoProvisioningRadioButton.get());
730
731     await user.click(ui.editMappingButton.get());
732     guestCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('guest'));
733     ownerCheckboxes = ui.mappingCheckbox.getAll(ui.getMappingRowByRole('owner'));
734
735     expect(guestCheckboxes[0]).not.toBeChecked();
736     expect(guestCheckboxes[5]).toBeChecked();
737     expect(ownerCheckboxes[5]).not.toBeChecked();
738     await user.click(ui.mappingDialogClose.get());
739   });
740 });
741
742 function renderAuthentication(features: Feature[] = []) {
743   renderComponent(
744     <AvailableFeaturesContext.Provider value={features}>
745       <Authentication definitions={definitions} />
746     </AvailableFeaturesContext.Provider>,
747     `?tab=${AlmKeys.GitLab}`,
748   );
749 }