]> source.dussan.org Git - sonarqube.git/blob
f8d1a8684d54517eae11d3e229d4638b50a3a53d
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 import { screen, waitFor, within } from '@testing-library/react';
21 import userEvent from '@testing-library/user-event';
22 import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup';
23 import React from 'react';
24 import AuthenticationServiceMock from '../../../../../api/mocks/AuthenticationServiceMock';
25 import ComputeEngineServiceMock from '../../../../../api/mocks/ComputeEngineServiceMock';
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 { byRole, byText } from '../../../../../helpers/testSelector';
33 import { Feature } from '../../../../../types/features';
34 import { GitHubProvisioningStatus, ProvisioningType } from '../../../../../types/provisioning';
35 import { TaskStatuses, TaskTypes } from '../../../../../types/tasks';
36 import Authentication from '../Authentication';
37
38 let handler: AuthenticationServiceMock;
39 let system: SystemServiceMock;
40 let settingsHandler: SettingsServiceMock;
41 let computeEngineHandler: ComputeEngineServiceMock;
42
43 beforeEach(() => {
44   handler = new AuthenticationServiceMock();
45   system = new SystemServiceMock();
46   settingsHandler = new SettingsServiceMock();
47   computeEngineHandler = new ComputeEngineServiceMock();
48   [
49     {
50       key: 'sonar.auth.saml.signature.enabled',
51       value: 'false',
52     },
53     {
54       key: 'sonar.auth.saml.enabled',
55       value: 'false',
56     },
57     {
58       key: 'sonar.auth.saml.applicationId',
59       value: 'sonarqube',
60     },
61     {
62       key: 'sonar.auth.saml.providerName',
63       value: 'SAML',
64     },
65   ].forEach((setting: any) => settingsHandler.set(setting.key, setting.value));
66 });
67
68 afterEach(() => {
69   handler.reset();
70   settingsHandler.reset();
71   system.reset();
72   computeEngineHandler.reset();
73 });
74
75 const ghContainer = byRole('tabpanel', { name: 'github GitHub' });
76 const glContainer = byRole('tabpanel', { name: 'gitlab GitLab' });
77 const samlContainer = byRole('tabpanel', { name: 'SAML' });
78
79 const ui = {
80   saveButton: byRole('button', { name: 'settings.authentication.saml.form.save' }),
81   customMessageInformation: byText('settings.authentication.custom_message_information'),
82   enabledToggle: byRole('switch'),
83   testButton: byText('settings.authentication.saml.form.test'),
84   textbox1: byRole('textbox', { name: 'test1' }),
85   textbox2: byRole('textbox', { name: 'test2' }),
86   saml: {
87     noSamlConfiguration: byText('settings.authentication.saml.form.not_configured'),
88     createConfigButton: samlContainer.byRole('button', {
89       name: 'settings.authentication.form.create',
90     }),
91     providerName: byRole('textbox', { name: 'property.sonar.auth.saml.providerName.name' }),
92     providerId: byRole('textbox', { name: 'property.sonar.auth.saml.providerId.name' }),
93     providerCertificate: byRole('textbox', {
94       name: 'property.sonar.auth.saml.certificate.secured.name',
95     }),
96     loginUrl: byRole('textbox', { name: 'property.sonar.auth.saml.loginUrl.name' }),
97     userLoginAttribute: byRole('textbox', { name: 'property.sonar.auth.saml.user.login.name' }),
98     userNameAttribute: byRole('textbox', { name: 'property.sonar.auth.saml.user.name.name' }),
99     saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
100     confirmProvisioningButton: byRole('button', {
101       name: 'yes',
102     }),
103     saveScim: samlContainer.byRole('button', { name: 'save' }),
104     enableConfigButton: samlContainer.byRole('button', {
105       name: 'settings.authentication.form.enable',
106     }),
107     disableConfigButton: samlContainer.byRole('button', {
108       name: 'settings.authentication.form.disable',
109     }),
110     editConfigButton: samlContainer.byRole('button', {
111       name: 'settings.authentication.form.edit',
112     }),
113     enableFirstMessage: byText('settings.authentication.saml.enable_first'),
114     jitProvisioningButton: byRole('radio', {
115       name: 'settings.authentication.saml.form.provisioning_at_login',
116     }),
117     scimProvisioningButton: byRole('radio', {
118       name: 'settings.authentication.saml.form.provisioning_with_scim',
119     }),
120     fillForm: async (user: UserEvent) => {
121       const { saml } = ui;
122
123       await user.clear(saml.providerName.get());
124       await user.type(saml.providerName.get(), 'Awsome SAML config');
125       await user.type(saml.providerId.get(), 'okta-1234');
126       await user.type(saml.loginUrl.get(), 'http://test.org');
127       await user.type(saml.providerCertificate.get(), '-secret-');
128       await user.type(saml.userLoginAttribute.get(), 'login');
129       await user.type(saml.userNameAttribute.get(), 'name');
130     },
131     createConfiguration: async (user: UserEvent) => {
132       const { saml } = ui;
133
134       await user.click(await saml.createConfigButton.find());
135       await saml.fillForm(user);
136       await user.click(saml.saveConfigButton.get());
137     },
138   },
139   github: {
140     tab: byRole('tab', { name: 'github GitHub' }),
141     noGithubConfiguration: byText('settings.authentication.github.form.not_configured'),
142     createConfigButton: ghContainer.byRole('button', {
143       name: 'settings.authentication.form.create',
144     }),
145     clientId: byRole('textbox', {
146       name: 'property.sonar.auth.github.clientId.secured.name',
147     }),
148     appId: byRole('textbox', { name: 'property.sonar.auth.github.appId.name' }),
149     privateKey: byRole('textbox', {
150       name: 'property.sonar.auth.github.privateKey.secured.name',
151     }),
152     clientSecret: byRole('textbox', {
153       name: 'property.sonar.auth.github.clientSecret.secured.name',
154     }),
155     githubApiUrl: byRole('textbox', { name: 'property.sonar.auth.github.apiUrl.name' }),
156     githubWebUrl: byRole('textbox', { name: 'property.sonar.auth.github.webUrl.name' }),
157     allowUserToSignUp: byRole('switch', {
158       name: 'sonar.auth.github.allowUsersToSignUp',
159     }),
160     organizations: byRole('textbox', {
161       name: 'property.sonar.auth.github.organizations.name',
162     }),
163     saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
164     confirmProvisioningButton: byRole('button', {
165       name: 'settings.authentication.github.provisioning_change.confirm_changes',
166     }),
167     saveGithubProvisioning: ghContainer.byRole('button', { name: 'save' }),
168     groupAttribute: byRole('textbox', {
169       name: 'property.sonar.auth.github.group.name.name',
170     }),
171     enableConfigButton: ghContainer.byRole('button', {
172       name: 'settings.authentication.form.enable',
173     }),
174     disableConfigButton: ghContainer.byRole('button', {
175       name: 'settings.authentication.form.disable',
176     }),
177     editConfigButton: ghContainer.byRole('button', {
178       name: 'settings.authentication.form.edit',
179     }),
180     editMappingButton: ghContainer.byRole('button', {
181       name: 'settings.authentication.github.configuration.roles_mapping.button_label',
182     }),
183     mappingRow: byRole('dialog', {
184       name: 'settings.authentication.github.configuration.roles_mapping.dialog.title',
185     }).byRole('row'),
186     customRoleInput: byRole('textbox', {
187       name: 'settings.authentication.github.configuration.roles_mapping.dialog.add_custom_role',
188     }),
189     customRoleAddBtn: byRole('dialog', {
190       name: 'settings.authentication.github.configuration.roles_mapping.dialog.title',
191     }).byRole('button', { name: 'add_verb' }),
192     roleExistsError: byRole('dialog', {
193       name: 'settings.authentication.github.configuration.roles_mapping.dialog.title',
194     }).byText('settings.authentication.github.configuration.roles_mapping.role_exists'),
195     emptyRoleError: byRole('dialog', {
196       name: 'settings.authentication.github.configuration.roles_mapping.dialog.title',
197     }).byText('settings.authentication.github.configuration.roles_mapping.empty_custom_role'),
198     deleteCustomRoleCustom2: byRole('button', {
199       name: 'settings.authentication.github.configuration.roles_mapping.dialog.delete_custom_role.custom2',
200     }),
201     getMappingRowByRole: (text: string) =>
202       ui.github.mappingRow.getAll().find((row) => within(row).queryByText(text) !== null),
203     mappingCheckbox: byRole('checkbox'),
204     mappingDialogClose: byRole('dialog', {
205       name: 'settings.authentication.github.configuration.roles_mapping.dialog.title',
206     }).byRole('button', {
207       name: 'close',
208     }),
209     deleteOrg: (org: string) =>
210       byRole('button', {
211         name: `settings.definition.delete_value.property.sonar.auth.github.organizations.name.${org}`,
212       }),
213     enableFirstMessage: ghContainer.byText('settings.authentication.github.enable_first'),
214     jitProvisioningButton: ghContainer.byRole('radio', {
215       name: 'settings.authentication.form.provisioning_at_login',
216     }),
217     githubProvisioningButton: ghContainer.byRole('radio', {
218       name: 'settings.authentication.github.form.provisioning_with_github',
219     }),
220     githubProvisioningPending: ghContainer.byText(/synchronization_pending/),
221     githubProvisioningInProgress: ghContainer.byText(/synchronization_in_progress/),
222     githubProvisioningSuccess: ghContainer.byText(/synchronization_successful/),
223     githubProvisioningAlert: ghContainer.byText(/synchronization_failed/),
224     configurationValidityLoading: ghContainer.byRole('status', {
225       name: /github.configuration.validation.loading/,
226     }),
227     configurationValiditySuccess: ghContainer.byRole('status', {
228       name: /github.configuration.validation.valid/,
229     }),
230     configurationValidityError: ghContainer.byRole('status', {
231       name: /github.configuration.validation.invalid/,
232     }),
233     syncWarning: ghContainer.byText(/Warning/),
234     syncSummary: ghContainer.byText(/Test summary/),
235     configurationValidityWarning: ghContainer.byRole('status', {
236       name: /github.configuration.validation.valid.short/,
237     }),
238     checkConfigButton: ghContainer.byRole('button', {
239       name: 'settings.authentication.github.configuration.validation.test',
240     }),
241     viewConfigValidityDetailsButton: ghContainer.byRole('button', {
242       name: 'settings.authentication.github.configuration.validation.details',
243     }),
244     configDetailsDialog: byRole('dialog', {
245       name: 'settings.authentication.github.configuration.validation.details.title',
246     }),
247     continueAutoButton: byRole('button', {
248       name: 'settings.authentication.github.confirm_auto_provisioning.continue',
249     }),
250     switchJitButton: byRole('button', {
251       name: 'settings.authentication.github.confirm_auto_provisioning.switch_jit',
252     }),
253     consentDialog: byRole('dialog', {
254       name: 'settings.authentication.github.confirm_auto_provisioning.header',
255     }),
256     getConfigDetailsTitle: () => within(ui.github.configDetailsDialog.get()).getByRole('heading'),
257     getOrgs: () => within(ui.github.configDetailsDialog.get()).getAllByRole('listitem'),
258     fillForm: async (user: UserEvent) => {
259       const { github } = ui;
260
261       await user.type(await github.clientId.find(), 'Awsome GITHUB config');
262       await user.type(github.clientSecret.get(), 'Client shut');
263       await user.type(github.appId.get(), 'App id');
264       await user.type(github.privateKey.get(), 'Private Key');
265       await user.type(github.githubApiUrl.get(), 'API Url');
266       await user.type(github.githubWebUrl.get(), 'WEb Url');
267       await user.type(github.organizations.get(), 'organization1');
268     },
269     createConfiguration: async (user: UserEvent) => {
270       const { github } = ui;
271
272       await user.click(await github.createConfigButton.find());
273       await github.fillForm(user);
274
275       await user.click(github.saveConfigButton.get());
276     },
277     enableConfiguration: async (user: UserEvent) => {
278       const { github } = ui;
279       await user.click(await github.tab.find());
280       await github.createConfiguration(user);
281       await user.click(await github.enableConfigButton.find());
282     },
283     enableProvisioning: async (user: UserEvent) => {
284       const { github } = ui;
285       await user.click(await github.tab.find());
286
287       await github.createConfiguration(user);
288
289       await user.click(await github.enableConfigButton.find());
290       await user.click(await github.githubProvisioningButton.find());
291       await user.click(github.saveGithubProvisioning.get());
292       await user.click(github.confirmProvisioningButton.get());
293     },
294   },
295   gitlab: {
296     tab: byRole('tab', { name: 'gitlab GitLab' }),
297     noGitlabConfiguration: glContainer.byText('settings.authentication.gitlab.form.not_configured'),
298     createConfigButton: glContainer.byRole('button', {
299       name: 'settings.authentication.form.create',
300     }),
301     editConfigButton: glContainer.byRole('button', {
302       name: 'settings.authentication.form.edit',
303     }),
304     deleteConfigButton: glContainer.byRole('button', {
305       name: 'settings.authentication.form.delete',
306     }),
307     enableConfigButton: glContainer.byRole('button', {
308       name: 'settings.authentication.form.enable',
309     }),
310     disableConfigButton: glContainer.byRole('button', {
311       name: 'settings.authentication.form.disable',
312     }),
313     createDialog: byRole('dialog', {
314       name: 'settings.authentication.gitlab.form.create',
315     }),
316     editDialog: byRole('dialog', {
317       name: 'settings.authentication.gitlab.form.edit',
318     }),
319     applicationId: byRole('textbox', {
320       name: 'property.applicationId.name',
321     }),
322     url: byRole('textbox', { name: 'property.url.name' }),
323     clientSecret: byRole('textbox', {
324       name: 'property.clientSecret.name',
325     }),
326     synchronizeUserGroups: byRole('switch', {
327       name: 'synchronizeUserGroups',
328     }),
329     saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
330     jitProvisioningRadioButton: glContainer.byRole('radio', {
331       name: 'settings.authentication.gitlab.provisioning_at_login',
332     }),
333     autoProvisioningRadioButton: glContainer.byRole('radio', {
334       name: 'settings.authentication.gitlab.form.provisioning_with_gitlab',
335     }),
336     jitAllowUsersToSignUpToggle: byRole('switch', { name: 'sonar.auth.gitlab.allowUsersToSignUp' }),
337     autoProvisioningToken: byRole('textbox', {
338       name: 'property.provisioning.gitlab.token.secured.name',
339     }),
340     autoProvisioningUpdateTokenButton: byRole('button', {
341       name: 'settings.almintegration.form.secret.update_field',
342     }),
343     autoProvisioningGroupsInput: byRole('textbox', {
344       name: 'property.provisioning.gitlab.groups.name',
345     }),
346     removeProvisioniongGroup: byRole('button', {
347       name: /settings.definition.delete_value.property.provisioning.gitlab.groups.name./,
348     }),
349     saveProvisioning: glContainer.byRole('button', { name: 'save' }),
350     cancelProvisioningChanges: glContainer.byRole('button', { name: 'cancel' }),
351     confirmAutoProvisioningDialog: byRole('dialog', {
352       name: 'settings.authentication.gitlab.confirm.Auto',
353     }),
354     confirmJitProvisioningDialog: byRole('dialog', {
355       name: 'settings.authentication.gitlab.confirm.JIT',
356     }),
357     confirmProvisioningChange: byRole('button', {
358       name: 'settings.authentication.gitlab.provisioning_change.confirm_changes',
359     }),
360     syncSummary: glContainer.byText(/Test summary/),
361     syncWarning: glContainer.byText(/Warning/),
362     gitlabProvisioningPending: glContainer.byText(/synchronization_pending/),
363     gitlabProvisioningInProgress: glContainer.byText(/synchronization_in_progress/),
364     gitlabProvisioningSuccess: glContainer.byText(/synchronization_successful/),
365     gitlabProvisioningAlert: glContainer.byText(/synchronization_failed/),
366   },
367 };
368
369 it('should render tabs and allow navigation', async () => {
370   const user = userEvent.setup();
371   renderAuthentication();
372
373   expect(screen.getAllByRole('tab')).toHaveLength(4);
374
375   expect(screen.getByRole('tab', { name: 'SAML' })).toHaveAttribute('aria-selected', 'true');
376
377   await user.click(screen.getByRole('tab', { name: 'github GitHub' }));
378
379   expect(screen.getByRole('tab', { name: 'SAML' })).toHaveAttribute('aria-selected', 'false');
380   expect(screen.getByRole('tab', { name: 'github GitHub' })).toHaveAttribute(
381     'aria-selected',
382     'true',
383   );
384 });
385
386 it('should not display the login message feature info box', () => {
387   renderAuthentication();
388
389   expect(ui.customMessageInformation.query()).not.toBeInTheDocument();
390 });
391
392 it('should display the login message feature info box', () => {
393   renderAuthentication([Feature.LoginMessage]);
394
395   expect(ui.customMessageInformation.get()).toBeInTheDocument();
396 });
397
398 describe('SAML tab', () => {
399   const { saml } = ui;
400
401   it('should render an empty SAML configuration', async () => {
402     renderAuthentication();
403     expect(await saml.noSamlConfiguration.find()).toBeInTheDocument();
404   });
405
406   it('should be able to create a configuration', async () => {
407     const user = userEvent.setup();
408     renderAuthentication();
409
410     await user.click(await saml.createConfigButton.find());
411
412     expect(saml.saveConfigButton.get()).toBeDisabled();
413     await saml.fillForm(user);
414     expect(saml.saveConfigButton.get()).toBeEnabled();
415
416     await user.click(saml.saveConfigButton.get());
417
418     expect(await saml.editConfigButton.find()).toBeInTheDocument();
419   });
420
421   it('should be able to enable/disable configuration', async () => {
422     const { saml } = ui;
423     const user = userEvent.setup();
424     renderAuthentication();
425
426     await saml.createConfiguration(user);
427     await user.click(await saml.enableConfigButton.find());
428
429     expect(await saml.disableConfigButton.find()).toBeInTheDocument();
430     await user.click(saml.disableConfigButton.get());
431     await waitFor(() => expect(saml.disableConfigButton.query()).not.toBeInTheDocument());
432
433     expect(await saml.enableConfigButton.find()).toBeInTheDocument();
434   });
435
436   it('should be able to choose provisioning', async () => {
437     const { saml } = ui;
438     const user = userEvent.setup();
439
440     renderAuthentication([Feature.Scim]);
441
442     await saml.createConfiguration(user);
443
444     expect(await saml.enableFirstMessage.find()).toBeInTheDocument();
445     await user.click(await saml.enableConfigButton.find());
446
447     expect(await saml.jitProvisioningButton.find()).toBeChecked();
448     expect(saml.saveScim.get()).toBeDisabled();
449
450     await user.click(saml.scimProvisioningButton.get());
451     expect(saml.saveScim.get()).toBeEnabled();
452     await user.click(saml.saveScim.get());
453     await user.click(saml.confirmProvisioningButton.get());
454
455     expect(await saml.scimProvisioningButton.find()).toBeChecked();
456     expect(await saml.saveScim.find()).toBeDisabled();
457   });
458
459   it('should not allow editions below Enterprise to select SCIM provisioning', async () => {
460     const { saml } = ui;
461     const user = userEvent.setup();
462
463     renderAuthentication();
464
465     await saml.createConfiguration(user);
466     await user.click(await saml.enableConfigButton.find());
467
468     expect(await saml.jitProvisioningButton.find()).toBeChecked();
469     expect(saml.scimProvisioningButton.get()).toHaveAttribute('aria-disabled', 'true');
470   });
471 });
472
473 describe('Github tab', () => {
474   const { github } = ui;
475
476   it('should render an empty Github configuration', async () => {
477     renderAuthentication();
478     const user = userEvent.setup();
479     await user.click(await github.tab.find());
480     expect(await github.noGithubConfiguration.find()).toBeInTheDocument();
481   });
482
483   it('should be able to create a configuration', async () => {
484     const user = userEvent.setup();
485     renderAuthentication();
486
487     await user.click(await github.tab.find());
488     await user.click(await github.createConfigButton.find());
489
490     expect(github.saveConfigButton.get()).toBeDisabled();
491
492     await github.fillForm(user);
493     expect(github.saveConfigButton.get()).toBeEnabled();
494
495     await user.click(github.saveConfigButton.get());
496
497     expect(await github.editConfigButton.find()).toBeInTheDocument();
498   });
499
500   it('should be able to edit configuration', async () => {
501     const { github } = ui;
502     const user = userEvent.setup();
503     renderAuthentication();
504     await user.click(await github.tab.find());
505
506     await github.createConfiguration(user);
507
508     await user.click(github.editConfigButton.get());
509     await user.click(github.deleteOrg('organization1').get());
510
511     await user.click(github.saveConfigButton.get());
512
513     await user.click(await github.editConfigButton.find());
514
515     expect(github.organizations.get()).toHaveValue('');
516   });
517
518   it('should be able to enable/disable configuration', async () => {
519     const { github } = ui;
520     const user = userEvent.setup();
521     renderAuthentication();
522     await user.click(await github.tab.find());
523
524     await github.createConfiguration(user);
525
526     await user.click(await github.enableConfigButton.find());
527
528     expect(await github.disableConfigButton.find()).toBeInTheDocument();
529     await user.click(github.disableConfigButton.get());
530     await waitFor(() => expect(github.disableConfigButton.query()).not.toBeInTheDocument());
531
532     expect(await github.enableConfigButton.find()).toBeInTheDocument();
533   });
534
535   it('should not allow edtion below Enterprise to select Github provisioning', async () => {
536     const { github } = ui;
537     const user = userEvent.setup();
538
539     renderAuthentication();
540     await user.click(await github.tab.find());
541
542     await github.createConfiguration(user);
543     await user.click(await github.enableConfigButton.find());
544
545     expect(await github.jitProvisioningButton.find()).toBeChecked();
546     expect(github.githubProvisioningButton.get()).toHaveAttribute('aria-disabled', 'true');
547   });
548
549   it('should be able to choose provisioning', async () => {
550     const { github } = ui;
551     const user = userEvent.setup();
552
553     renderAuthentication([Feature.GithubProvisioning]);
554     await user.click(await github.tab.find());
555
556     await github.createConfiguration(user);
557
558     expect(await github.enableFirstMessage.find()).toBeInTheDocument();
559     await user.click(await github.enableConfigButton.find());
560
561     expect(await github.jitProvisioningButton.find()).toBeChecked();
562
563     expect(github.saveGithubProvisioning.get()).toBeDisabled();
564     await user.click(github.allowUserToSignUp.get());
565
566     expect(github.saveGithubProvisioning.get()).toBeEnabled();
567     await user.click(github.saveGithubProvisioning.get());
568
569     await waitFor(() => expect(github.saveGithubProvisioning.query()).toBeDisabled());
570
571     await user.click(github.githubProvisioningButton.get());
572
573     expect(github.saveGithubProvisioning.get()).toBeEnabled();
574     await user.click(github.saveGithubProvisioning.get());
575     await user.click(github.confirmProvisioningButton.get());
576
577     expect(await github.githubProvisioningButton.find()).toBeChecked();
578     expect(github.disableConfigButton.get()).toBeDisabled();
579     expect(github.saveGithubProvisioning.get()).toBeDisabled();
580   });
581
582   describe('Github Provisioning', () => {
583     let user: UserEvent;
584
585     beforeEach(() => {
586       jest.useFakeTimers({
587         advanceTimers: true,
588         now: new Date('2022-02-04T12:00:59Z'),
589       });
590       user = userEvent.setup();
591     });
592
593     afterEach(() => {
594       jest.runOnlyPendingTimers();
595       jest.useRealTimers();
596     });
597
598     it('should display a success status when the synchronisation is a success', async () => {
599       handler.addProvisioningTask({
600         status: TaskStatuses.Success,
601         executedAt: '2022-02-03T11:45:35+0200',
602       });
603
604       renderAuthentication([Feature.GithubProvisioning]);
605       await github.enableProvisioning(user);
606       expect(github.githubProvisioningSuccess.get()).toBeInTheDocument();
607       expect(github.syncSummary.get()).toBeInTheDocument();
608     });
609
610     it('should display a success status even when another task is pending', async () => {
611       handler.addProvisioningTask({
612         status: TaskStatuses.Pending,
613         executedAt: '2022-02-03T11:55:35+0200',
614       });
615       handler.addProvisioningTask({
616         status: TaskStatuses.Success,
617         executedAt: '2022-02-03T11:45:35+0200',
618       });
619       renderAuthentication([Feature.GithubProvisioning]);
620       await github.enableProvisioning(user);
621       expect(github.githubProvisioningSuccess.get()).toBeInTheDocument();
622       expect(github.githubProvisioningPending.get()).toBeInTheDocument();
623     });
624
625     it('should display an error alert when the synchronisation failed', async () => {
626       handler.addProvisioningTask({
627         status: TaskStatuses.Failed,
628         executedAt: '2022-02-03T11:45:35+0200',
629         errorMessage: "T'es mauvais Jacques",
630       });
631       renderAuthentication([Feature.GithubProvisioning]);
632       await github.enableProvisioning(user);
633       expect(github.githubProvisioningAlert.get()).toBeInTheDocument();
634       expect(github.githubProvisioningButton.get()).toHaveTextContent("T'es mauvais Jacques");
635       expect(github.githubProvisioningSuccess.query()).not.toBeInTheDocument();
636     });
637
638     it('should display an error alert even when another task is in progress', async () => {
639       handler.addProvisioningTask({
640         status: TaskStatuses.InProgress,
641         executedAt: '2022-02-03T11:55:35+0200',
642       });
643       handler.addProvisioningTask({
644         status: TaskStatuses.Failed,
645         executedAt: '2022-02-03T11:45:35+0200',
646         errorMessage: "T'es mauvais Jacques",
647       });
648       renderAuthentication([Feature.GithubProvisioning]);
649       await github.enableProvisioning(user);
650       expect(github.githubProvisioningAlert.get()).toBeInTheDocument();
651       expect(github.githubProvisioningButton.get()).toHaveTextContent("T'es mauvais Jacques");
652       expect(github.githubProvisioningSuccess.query()).not.toBeInTheDocument();
653       expect(github.githubProvisioningInProgress.get()).toBeInTheDocument();
654     });
655
656     it('should display that config is valid for both provisioning with 1 org', async () => {
657       renderAuthentication([Feature.GithubProvisioning]);
658       await github.enableConfiguration(user);
659
660       await appLoaded();
661
662       await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
663     });
664
665     it('should display that config is valid for both provisioning with multiple orgs', async () => {
666       handler.setConfigurationValidity({
667         installations: [
668           {
669             organization: 'org1',
670             autoProvisioning: { status: GitHubProvisioningStatus.Success },
671             jit: { status: GitHubProvisioningStatus.Success },
672           },
673           {
674             organization: 'org2',
675             autoProvisioning: { status: GitHubProvisioningStatus.Success },
676             jit: { status: GitHubProvisioningStatus.Success },
677           },
678         ],
679       });
680       renderAuthentication([Feature.GithubProvisioning]);
681       await github.enableConfiguration(user);
682
683       await appLoaded();
684
685       await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
686       expect(github.configurationValiditySuccess.get()).toHaveTextContent('2');
687
688       await user.click(github.viewConfigValidityDetailsButton.get());
689       expect(github.getConfigDetailsTitle()).toHaveTextContent(
690         'settings.authentication.github.configuration.validation.details.valid_label',
691       );
692       expect(github.getOrgs()[0]).toHaveTextContent(
693         'settings.authentication.github.configuration.validation.details.valid_labelorg1',
694       );
695       expect(github.getOrgs()[1]).toHaveTextContent(
696         'settings.authentication.github.configuration.validation.details.valid_labelorg2',
697       );
698     });
699
700     it('should display that config is invalid for autoprovisioning if some apps are suspended but valid for jit', async () => {
701       const errorMessage = 'Installation suspended';
702       handler.setConfigurationValidity({
703         installations: [
704           {
705             organization: 'org1',
706             autoProvisioning: {
707               status: GitHubProvisioningStatus.Failed,
708               errorMessage,
709             },
710             jit: {
711               status: GitHubProvisioningStatus.Failed,
712               errorMessage,
713             },
714           },
715         ],
716       });
717
718       renderAuthentication([Feature.GithubProvisioning]);
719       await github.enableConfiguration(user);
720
721       await appLoaded();
722
723       await waitFor(() => expect(github.configurationValidityWarning.get()).toBeInTheDocument());
724       expect(github.configurationValidityWarning.get()).toHaveTextContent(errorMessage);
725
726       await user.click(github.viewConfigValidityDetailsButton.get());
727       expect(github.getConfigDetailsTitle()).toHaveTextContent(
728         'settings.authentication.github.configuration.validation.details.valid_label',
729       );
730       expect(github.getOrgs()[0]).toHaveTextContent(
731         'settings.authentication.github.configuration.validation.details.invalid_labelorg1 - Installation suspended',
732       );
733
734       await user.click(github.configDetailsDialog.byRole('button', { name: 'close' }).get());
735
736       await user.click(github.githubProvisioningButton.get());
737       await waitFor(() => expect(github.configurationValidityError.get()).toBeInTheDocument());
738       expect(github.configurationValidityError.get()).toHaveTextContent(errorMessage);
739     });
740
741     it('should display that config is valid but some organizations were not found', async () => {
742       handler.setConfigurationValidity({
743         installations: [
744           {
745             organization: 'org1',
746             autoProvisioning: { status: GitHubProvisioningStatus.Success },
747             jit: { status: GitHubProvisioningStatus.Success },
748           },
749         ],
750       });
751
752       renderAuthentication([Feature.GithubProvisioning]);
753       await github.enableConfiguration(user);
754
755       await appLoaded();
756
757       await waitFor(() => expect(github.configurationValiditySuccess.get()).toBeInTheDocument());
758       expect(github.configurationValiditySuccess.get()).toHaveTextContent('1');
759
760       await user.click(github.viewConfigValidityDetailsButton.get());
761       expect(github.getConfigDetailsTitle()).toHaveTextContent(
762         'settings.authentication.github.configuration.validation.details.valid_label',
763       );
764       expect(github.getOrgs()[0]).toHaveTextContent(
765         'settings.authentication.github.configuration.validation.details.valid_labelorg1',
766       );
767       expect(github.getOrgs()[1]).toHaveTextContent(
768         'settings.authentication.github.configuration.validation.details.org_not_found.organization1',
769       );
770     });
771
772     it('should display that config is invalid', async () => {
773       const errorMessage = 'Test error';
774       handler.setConfigurationValidity({
775         application: {
776           jit: {
777             status: GitHubProvisioningStatus.Failed,
778             errorMessage,
779           },
780           autoProvisioning: {
781             status: GitHubProvisioningStatus.Failed,
782             errorMessage,
783           },
784         },
785       });
786       renderAuthentication([Feature.GithubProvisioning]);
787       await github.enableConfiguration(user);
788
789       await appLoaded();
790
791       await waitFor(() => expect(github.configurationValidityError.query()).toBeInTheDocument());
792       expect(github.configurationValidityError.get()).toHaveTextContent(errorMessage);
793
794       await user.click(github.viewConfigValidityDetailsButton.get());
795       expect(github.getConfigDetailsTitle()).toHaveTextContent(
796         'settings.authentication.github.configuration.validation.details.invalid_label',
797       );
798       expect(github.configDetailsDialog.get()).toHaveTextContent(errorMessage);
799     });
800
801     it('should display that config is valid for jit, but not for auto', async () => {
802       const errorMessage = 'Test error';
803       handler.setConfigurationValidity({
804         application: {
805           jit: {
806             status: GitHubProvisioningStatus.Success,
807           },
808           autoProvisioning: {
809             status: GitHubProvisioningStatus.Failed,
810             errorMessage,
811           },
812         },
813       });
814       renderAuthentication([Feature.GithubProvisioning]);
815       await github.enableConfiguration(user);
816
817       await appLoaded();
818
819       await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
820       expect(github.configurationValiditySuccess.get()).not.toHaveTextContent(errorMessage);
821
822       await user.click(github.viewConfigValidityDetailsButton.get());
823       expect(github.getConfigDetailsTitle()).toHaveTextContent(
824         'settings.authentication.github.configuration.validation.details.valid_label',
825       );
826       await user.click(github.configDetailsDialog.byRole('button', { name: 'close' }).get());
827
828       await user.click(github.githubProvisioningButton.get());
829
830       expect(github.configurationValidityError.get()).toBeInTheDocument();
831       expect(github.configurationValidityError.get()).toHaveTextContent(errorMessage);
832
833       await user.click(github.viewConfigValidityDetailsButton.get());
834       expect(github.getConfigDetailsTitle()).toHaveTextContent(
835         'settings.authentication.github.configuration.validation.details.invalid_label',
836       );
837     });
838
839     it('should display that config is invalid because of orgs', async () => {
840       const errorMessage = 'Test error';
841       handler.setConfigurationValidity({
842         installations: [
843           {
844             organization: 'org1',
845             autoProvisioning: { status: GitHubProvisioningStatus.Success },
846             jit: { status: GitHubProvisioningStatus.Success },
847           },
848           {
849             organization: 'org2',
850             jit: { status: GitHubProvisioningStatus.Failed, errorMessage },
851             autoProvisioning: { status: GitHubProvisioningStatus.Failed, errorMessage },
852           },
853         ],
854       });
855       renderAuthentication([Feature.GithubProvisioning]);
856       await github.enableConfiguration(user);
857
858       await appLoaded();
859
860       await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
861
862       await user.click(github.viewConfigValidityDetailsButton.get());
863
864       expect(github.getOrgs()[0]).toHaveTextContent(
865         'settings.authentication.github.configuration.validation.details.valid_labelorg1',
866       );
867       expect(github.getOrgs()[1]).toHaveTextContent(
868         'settings.authentication.github.configuration.validation.details.invalid_labelorg2 - Test error',
869       );
870
871       await user.click(github.configDetailsDialog.byRole('button', { name: 'close' }).get());
872
873       await user.click(github.githubProvisioningButton.get());
874
875       expect(github.configurationValidityError.get()).toBeInTheDocument();
876       expect(github.configurationValidityError.get()).toHaveTextContent(
877         `settings.authentication.github.configuration.validation.invalid_org.org2.${errorMessage}`,
878       );
879       await user.click(github.viewConfigValidityDetailsButton.get());
880       expect(github.getOrgs()[1]).toHaveTextContent(
881         `settings.authentication.github.configuration.validation.details.invalid_labelorg2 - ${errorMessage}`,
882       );
883     });
884
885     it('should update provisioning validity after clicking Test Configuration', async () => {
886       const errorMessage = 'Test error';
887       handler.setConfigurationValidity({
888         application: {
889           jit: {
890             status: GitHubProvisioningStatus.Failed,
891             errorMessage,
892           },
893           autoProvisioning: {
894             status: GitHubProvisioningStatus.Failed,
895             errorMessage,
896           },
897         },
898       });
899       renderAuthentication([Feature.GithubProvisioning]);
900       await github.enableConfiguration(user);
901       handler.setConfigurationValidity({
902         application: {
903           jit: {
904             status: GitHubProvisioningStatus.Success,
905           },
906           autoProvisioning: {
907             status: GitHubProvisioningStatus.Success,
908           },
909         },
910       });
911
912       await appLoaded();
913
914       expect(await github.configurationValidityError.find()).toBeInTheDocument();
915
916       await user.click(github.checkConfigButton.get());
917
918       expect(github.configurationValiditySuccess.get()).toBeInTheDocument();
919       expect(github.configurationValidityError.query()).not.toBeInTheDocument();
920     });
921
922     it('should show warning', async () => {
923       handler.addProvisioningTask({
924         status: TaskStatuses.Success,
925         warnings: ['Warning'],
926       });
927       renderAuthentication([Feature.GithubProvisioning]);
928       await github.enableProvisioning(user);
929
930       expect(await github.syncWarning.find()).toBeInTheDocument();
931       expect(github.syncSummary.get()).toBeInTheDocument();
932     });
933
934     it('should display a modal if user was already using auto and continue using auto provisioning', async () => {
935       const user = userEvent.setup();
936       settingsHandler.presetGithubAutoProvisioning();
937       handler.enableGithubProvisioning();
938       settingsHandler.set('sonar.auth.github.userConsentForPermissionProvisioningRequired', '');
939       renderAuthentication([Feature.GithubProvisioning]);
940
941       await user.click(await github.tab.find());
942
943       expect(await github.consentDialog.find()).toBeInTheDocument();
944       await user.click(github.continueAutoButton.get());
945
946       expect(await github.githubProvisioningButton.find()).toBeChecked();
947       expect(github.consentDialog.query()).not.toBeInTheDocument();
948     });
949
950     it('should display a modal if user was already using auto and switch to JIT', async () => {
951       const user = userEvent.setup();
952       settingsHandler.presetGithubAutoProvisioning();
953       handler.enableGithubProvisioning();
954       settingsHandler.set('sonar.auth.github.userConsentForPermissionProvisioningRequired', '');
955       renderAuthentication([Feature.GithubProvisioning]);
956
957       await user.click(await github.tab.find());
958
959       expect(await github.consentDialog.find()).toBeInTheDocument();
960       await user.click(github.switchJitButton.get());
961
962       expect(await github.jitProvisioningButton.find()).toBeChecked();
963       expect(github.consentDialog.query()).not.toBeInTheDocument();
964     });
965
966     it('should sort mapping rows', async () => {
967       const user = userEvent.setup();
968       settingsHandler.presetGithubAutoProvisioning();
969       handler.enableGithubProvisioning();
970       renderAuthentication([Feature.GithubProvisioning]);
971       await user.click(await github.tab.find());
972
973       expect(await github.editMappingButton.find()).toBeInTheDocument();
974       await user.click(github.editMappingButton.get());
975
976       const rows = (await github.mappingRow.findAll()).filter(
977         (row) => within(row).queryAllByRole('checkbox').length > 0,
978       );
979
980       expect(rows).toHaveLength(5);
981
982       expect(rows[0]).toHaveTextContent('read');
983       expect(rows[1]).toHaveTextContent('triage');
984       expect(rows[2]).toHaveTextContent('write');
985       expect(rows[3]).toHaveTextContent('maintain');
986       expect(rows[4]).toHaveTextContent('admin');
987     });
988
989     it('should apply new mapping and new provisioning type at the same time', async () => {
990       const user = userEvent.setup();
991       renderAuthentication([Feature.GithubProvisioning]);
992       await user.click(await github.tab.find());
993
994       await github.createConfiguration(user);
995       await user.click(await github.enableConfigButton.find());
996
997       expect(await github.jitProvisioningButton.find()).toBeChecked();
998       expect(github.editMappingButton.query()).not.toBeInTheDocument();
999       await user.click(github.githubProvisioningButton.get());
1000       expect(await github.editMappingButton.find()).toBeInTheDocument();
1001       await user.click(github.editMappingButton.get());
1002
1003       expect(await github.mappingRow.findAll()).toHaveLength(7);
1004
1005       let readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'));
1006       let adminCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('admin'));
1007
1008       expect(readCheckboxes[0]).toBeChecked();
1009       expect(readCheckboxes[5]).not.toBeChecked();
1010       expect(adminCheckboxes[5]).toBeChecked();
1011
1012       await user.click(readCheckboxes[0]);
1013       await user.click(readCheckboxes[5]);
1014       await user.click(adminCheckboxes[5]);
1015       await user.click(github.mappingDialogClose.get());
1016
1017       await user.click(github.saveGithubProvisioning.get());
1018       await user.click(github.confirmProvisioningButton.get());
1019
1020       // Clean local mapping state
1021       await user.click(github.jitProvisioningButton.get());
1022       await user.click(github.githubProvisioningButton.get());
1023
1024       await user.click(github.editMappingButton.get());
1025       readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'));
1026       adminCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('admin'));
1027
1028       expect(readCheckboxes[0]).not.toBeChecked();
1029       expect(readCheckboxes[5]).toBeChecked();
1030       expect(adminCheckboxes[5]).not.toBeChecked();
1031       await user.click(github.mappingDialogClose.get());
1032     });
1033
1034     it('should apply new mapping on auto-provisioning', async () => {
1035       const user = userEvent.setup();
1036       settingsHandler.presetGithubAutoProvisioning();
1037       handler.enableGithubProvisioning();
1038       renderAuthentication([Feature.GithubProvisioning]);
1039       await user.click(await github.tab.find());
1040
1041       expect(await github.saveGithubProvisioning.find()).toBeDisabled();
1042       await user.click(github.editMappingButton.get());
1043
1044       expect(await github.mappingRow.findAll()).toHaveLength(7);
1045
1046       let readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'))[0];
1047
1048       expect(readCheckboxes).toBeChecked();
1049
1050       await user.click(readCheckboxes);
1051       await user.click(github.mappingDialogClose.get());
1052
1053       expect(await github.saveGithubProvisioning.find()).toBeEnabled();
1054
1055       await user.click(github.saveGithubProvisioning.get());
1056
1057       // Clean local mapping state
1058       await user.click(github.jitProvisioningButton.get());
1059       await user.click(github.githubProvisioningButton.get());
1060
1061       await user.click(github.editMappingButton.get());
1062       readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'))[0];
1063
1064       expect(readCheckboxes).not.toBeChecked();
1065       await user.click(github.mappingDialogClose.get());
1066     });
1067
1068     it('should add/remove/update custom roles', async () => {
1069       const user = userEvent.setup();
1070       settingsHandler.presetGithubAutoProvisioning();
1071       handler.enableGithubProvisioning();
1072       handler.addGitHubCustomRole('custom1', ['user', 'codeViewer', 'scan']);
1073       handler.addGitHubCustomRole('custom2', ['user', 'codeViewer', 'issueAdmin', 'scan']);
1074       renderAuthentication([Feature.GithubProvisioning]);
1075       await user.click(await github.tab.find());
1076
1077       expect(await github.saveGithubProvisioning.find()).toBeDisabled();
1078       await user.click(github.editMappingButton.get());
1079
1080       const rows = (await github.mappingRow.findAll()).filter(
1081         (row) => within(row).queryAllByRole('checkbox').length > 0,
1082       );
1083
1084       expect(rows).toHaveLength(7);
1085
1086       let custom1Checkboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('custom1'));
1087
1088       expect(custom1Checkboxes[0]).toBeChecked();
1089       expect(custom1Checkboxes[1]).toBeChecked();
1090       expect(custom1Checkboxes[2]).not.toBeChecked();
1091       expect(custom1Checkboxes[3]).not.toBeChecked();
1092       expect(custom1Checkboxes[4]).not.toBeChecked();
1093       expect(custom1Checkboxes[5]).toBeChecked();
1094
1095       await user.click(custom1Checkboxes[1]);
1096       await user.click(custom1Checkboxes[2]);
1097
1098       await user.click(github.deleteCustomRoleCustom2.get());
1099
1100       expect(github.customRoleInput.get()).toHaveValue('');
1101       await user.type(github.customRoleInput.get(), 'read');
1102       await user.click(github.customRoleAddBtn.get());
1103       expect(await github.roleExistsError.find()).toBeInTheDocument();
1104       expect(github.customRoleAddBtn.get()).toBeDisabled();
1105       await user.clear(github.customRoleInput.get());
1106       expect(github.roleExistsError.query()).not.toBeInTheDocument();
1107       await user.type(github.customRoleInput.get(), 'custom1');
1108       await user.click(github.customRoleAddBtn.get());
1109       expect(await github.roleExistsError.find()).toBeInTheDocument();
1110       expect(github.customRoleAddBtn.get()).toBeDisabled();
1111       await user.clear(github.customRoleInput.get());
1112       await user.type(github.customRoleInput.get(), 'custom3');
1113       expect(github.roleExistsError.query()).not.toBeInTheDocument();
1114       expect(github.customRoleAddBtn.get()).toBeEnabled();
1115       await user.click(github.customRoleAddBtn.get());
1116
1117       let custom3Checkboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('custom3'));
1118       expect(custom3Checkboxes[0]).toBeChecked();
1119       expect(custom3Checkboxes[1]).not.toBeChecked();
1120       expect(custom3Checkboxes[2]).not.toBeChecked();
1121       expect(custom3Checkboxes[3]).not.toBeChecked();
1122       expect(custom3Checkboxes[4]).not.toBeChecked();
1123       expect(custom3Checkboxes[5]).not.toBeChecked();
1124       await user.click(custom3Checkboxes[0]);
1125       expect(await github.emptyRoleError.find()).toBeInTheDocument();
1126       expect(github.mappingDialogClose.get()).toBeDisabled();
1127       await user.click(custom3Checkboxes[1]);
1128       expect(github.emptyRoleError.query()).not.toBeInTheDocument();
1129       expect(github.mappingDialogClose.get()).toBeEnabled();
1130       await user.click(github.mappingDialogClose.get());
1131
1132       expect(await github.saveGithubProvisioning.find()).toBeEnabled();
1133       await user.click(github.saveGithubProvisioning.get());
1134
1135       // Clean local mapping state
1136       await user.click(github.jitProvisioningButton.get());
1137       await user.click(github.githubProvisioningButton.get());
1138
1139       await user.click(github.editMappingButton.get());
1140
1141       expect(
1142         (await github.mappingRow.findAll()).filter(
1143           (row) => within(row).queryAllByRole('checkbox').length > 0,
1144         ),
1145       ).toHaveLength(7);
1146       custom1Checkboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('custom1'));
1147       custom3Checkboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('custom3'));
1148       expect(github.getMappingRowByRole('custom2')).toBeUndefined();
1149       expect(custom1Checkboxes[0]).toBeChecked();
1150       expect(custom1Checkboxes[1]).not.toBeChecked();
1151       expect(custom1Checkboxes[2]).toBeChecked();
1152       expect(custom1Checkboxes[3]).not.toBeChecked();
1153       expect(custom1Checkboxes[4]).not.toBeChecked();
1154       expect(custom1Checkboxes[5]).toBeChecked();
1155       expect(custom3Checkboxes[0]).not.toBeChecked();
1156       expect(custom3Checkboxes[1]).toBeChecked();
1157       expect(custom3Checkboxes[2]).not.toBeChecked();
1158       expect(custom3Checkboxes[3]).not.toBeChecked();
1159       expect(custom3Checkboxes[4]).not.toBeChecked();
1160       expect(custom3Checkboxes[5]).not.toBeChecked();
1161       await user.click(github.mappingDialogClose.get());
1162     });
1163   });
1164 });
1165
1166 describe('GitLab', () => {
1167   const { gitlab } = ui;
1168
1169   it('should create a Gitlab configuration and disable it', async () => {
1170     handler.setGitlabConfigurations([]);
1171     renderAuthentication();
1172     const user = userEvent.setup();
1173     await user.click(await gitlab.tab.find());
1174
1175     expect(await gitlab.noGitlabConfiguration.find()).toBeInTheDocument();
1176     expect(gitlab.createConfigButton.get()).toBeInTheDocument();
1177
1178     await user.click(gitlab.createConfigButton.get());
1179     expect(await gitlab.createDialog.find()).toBeInTheDocument();
1180     await user.type(gitlab.applicationId.get(), '123');
1181     await user.type(gitlab.url.get(), 'https://company.gitlab.com');
1182     await user.type(gitlab.clientSecret.get(), '123');
1183     await user.click(gitlab.synchronizeUserGroups.get());
1184     await user.click(gitlab.saveConfigButton.get());
1185
1186     expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1187     expect(gitlab.noGitlabConfiguration.query()).not.toBeInTheDocument();
1188     expect(glContainer.get()).toHaveTextContent('https://company.gitlab.com');
1189
1190     expect(gitlab.disableConfigButton.get()).toBeInTheDocument();
1191     await user.click(gitlab.disableConfigButton.get());
1192     expect(gitlab.enableConfigButton.get()).toBeInTheDocument();
1193     expect(gitlab.disableConfigButton.query()).not.toBeInTheDocument();
1194   });
1195
1196   it('should edit/delete configuration', async () => {
1197     const user = userEvent.setup();
1198     renderAuthentication();
1199     await user.click(await gitlab.tab.find());
1200
1201     expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1202     expect(glContainer.get()).toHaveTextContent('URL');
1203     expect(gitlab.disableConfigButton.get()).toBeInTheDocument();
1204     expect(gitlab.deleteConfigButton.get()).toBeInTheDocument();
1205     expect(gitlab.deleteConfigButton.get()).toBeDisabled();
1206
1207     await user.click(gitlab.editConfigButton.get());
1208     expect(await gitlab.editDialog.find()).toBeInTheDocument();
1209     expect(gitlab.url.get()).toHaveValue('URL');
1210     expect(gitlab.applicationId.query()).not.toBeInTheDocument();
1211     expect(gitlab.clientSecret.query()).not.toBeInTheDocument();
1212     expect(gitlab.synchronizeUserGroups.get()).toBeChecked();
1213     await user.clear(gitlab.url.get());
1214     await user.type(gitlab.url.get(), 'https://company.gitlab.com');
1215     await user.click(gitlab.saveConfigButton.get());
1216
1217     expect(glContainer.get()).not.toHaveTextContent('URL');
1218     expect(glContainer.get()).toHaveTextContent('https://company.gitlab.com');
1219
1220     expect(gitlab.disableConfigButton.get()).toBeInTheDocument();
1221     await user.click(gitlab.disableConfigButton.get());
1222     expect(await gitlab.enableConfigButton.find()).toBeInTheDocument();
1223     expect(gitlab.deleteConfigButton.get()).toBeEnabled();
1224     await user.click(gitlab.deleteConfigButton.get());
1225     expect(await gitlab.noGitlabConfiguration.find()).toBeInTheDocument();
1226     expect(gitlab.editConfigButton.query()).not.toBeInTheDocument();
1227   });
1228
1229   it('should change from just-in-time to Auto Provisioning with proper validation', async () => {
1230     const user = userEvent.setup();
1231     renderAuthentication([Feature.GitlabProvisioning]);
1232     await user.click(await gitlab.tab.find());
1233
1234     expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1235     expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1236
1237     user.click(gitlab.autoProvisioningRadioButton.get());
1238     expect(await gitlab.autoProvisioningRadioButton.find()).toBeEnabled();
1239     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1240
1241     await user.type(gitlab.autoProvisioningToken.get(), 'JRR Tolkien');
1242     expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1243
1244     await user.type(gitlab.autoProvisioningGroupsInput.get(), 'NWA');
1245     user.click(gitlab.autoProvisioningRadioButton.get());
1246     expect(await gitlab.saveProvisioning.find()).toBeEnabled();
1247
1248     await user.click(gitlab.removeProvisioniongGroup.get());
1249     expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1250     await user.type(gitlab.autoProvisioningGroupsInput.get(), 'Wu-Tang Clan');
1251     expect(await gitlab.saveProvisioning.find()).toBeEnabled();
1252
1253     await user.clear(gitlab.autoProvisioningToken.get());
1254     expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1255     await user.type(gitlab.autoProvisioningToken.get(), 'tiktoken');
1256     expect(await gitlab.saveProvisioning.find()).toBeEnabled();
1257
1258     await user.click(gitlab.saveProvisioning.get());
1259     expect(gitlab.confirmAutoProvisioningDialog.get()).toBeInTheDocument();
1260     await user.click(gitlab.confirmProvisioningChange.get());
1261     expect(gitlab.confirmAutoProvisioningDialog.query()).not.toBeInTheDocument();
1262
1263     expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1264     expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1265   });
1266
1267   it('should change from auto provisioning to JIT with proper validation', async () => {
1268     handler.setGitlabConfigurations([
1269       mockGitlabConfiguration({
1270         allowUsersToSignUp: false,
1271         enabled: true,
1272         type: ProvisioningType.auto,
1273         groups: ['D12'],
1274       }),
1275     ]);
1276     const user = userEvent.setup();
1277     renderAuthentication([Feature.GitlabProvisioning]);
1278     await user.click(await gitlab.tab.find());
1279
1280     expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1281
1282     expect(gitlab.jitProvisioningRadioButton.get()).not.toBeChecked();
1283     expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1284     expect(gitlab.autoProvisioningGroupsInput.get()).toHaveValue('D12');
1285
1286     expect(gitlab.autoProvisioningToken.query()).not.toBeInTheDocument();
1287     expect(gitlab.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
1288
1289     await user.click(gitlab.jitProvisioningRadioButton.get());
1290     expect(await gitlab.jitProvisioningRadioButton.find()).toBeChecked();
1291
1292     expect(await gitlab.saveProvisioning.find()).toBeEnabled();
1293
1294     expect(gitlab.jitAllowUsersToSignUpToggle.get()).toBeInTheDocument();
1295
1296     await user.click(gitlab.saveProvisioning.get());
1297     expect(gitlab.confirmJitProvisioningDialog.get()).toBeInTheDocument();
1298     await user.click(gitlab.confirmProvisioningChange.get());
1299     expect(gitlab.confirmJitProvisioningDialog.query()).not.toBeInTheDocument();
1300
1301     expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1302     expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1303   });
1304
1305   it('should be able to allow user to sign up for JIT with proper validation', async () => {
1306     handler.setGitlabConfigurations([
1307       mockGitlabConfiguration({
1308         allowUsersToSignUp: false,
1309         enabled: true,
1310         type: ProvisioningType.jit,
1311       }),
1312     ]);
1313     const user = userEvent.setup();
1314     renderAuthentication([Feature.GitlabProvisioning]);
1315     await user.click(await gitlab.tab.find());
1316
1317     expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1318
1319     expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1320     expect(gitlab.autoProvisioningRadioButton.get()).not.toBeChecked();
1321
1322     expect(gitlab.jitAllowUsersToSignUpToggle.get()).not.toBeChecked();
1323
1324     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1325     await user.click(gitlab.jitAllowUsersToSignUpToggle.get());
1326     expect(gitlab.saveProvisioning.get()).toBeEnabled();
1327     await user.click(gitlab.jitAllowUsersToSignUpToggle.get());
1328     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1329     await user.click(gitlab.jitAllowUsersToSignUpToggle.get());
1330
1331     await user.click(gitlab.saveProvisioning.get());
1332
1333     expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1334     expect(gitlab.jitAllowUsersToSignUpToggle.get()).toBeChecked();
1335     expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1336   });
1337
1338   it('should be able to edit groups and token for Auto provisioning with proper validation', async () => {
1339     handler.setGitlabConfigurations([
1340       mockGitlabConfiguration({
1341         allowUsersToSignUp: false,
1342         enabled: true,
1343         type: ProvisioningType.auto,
1344         groups: ['Cypress Hill', 'Public Enemy'],
1345       }),
1346     ]);
1347     const user = userEvent.setup();
1348     renderAuthentication([Feature.GitlabProvisioning]);
1349     await user.click(await gitlab.tab.find());
1350
1351     expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1352     expect(gitlab.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
1353     expect(gitlab.autoProvisioningGroupsInput.get()).toHaveValue('Cypress Hill');
1354
1355     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1356
1357     // Changing the Provisioning token should enable save
1358     await user.click(gitlab.autoProvisioningUpdateTokenButton.get());
1359     await user.type(gitlab.autoProvisioningGroupsInput.get(), 'Tok Token!');
1360     expect(gitlab.saveProvisioning.get()).toBeEnabled();
1361     await user.click(gitlab.cancelProvisioningChanges.get());
1362     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1363
1364     // Adding a group should enable save
1365     await user.click(gitlab.autoProvisioningGroupsInput.get());
1366     await user.tab();
1367     await user.tab();
1368     await user.tab();
1369     await user.tab();
1370     await user.keyboard('Run DMC');
1371     expect(gitlab.saveProvisioning.get()).toBeEnabled();
1372     await user.tab();
1373     await user.keyboard('{Enter}');
1374     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1375
1376     // Removing a group should enable save
1377     await user.click(gitlab.autoProvisioningGroupsInput.get());
1378     await user.tab();
1379     await user.keyboard('{Enter}');
1380     expect(gitlab.saveProvisioning.get()).toBeEnabled();
1381
1382     // Removing all groups should disable save
1383     await user.click(gitlab.autoProvisioningGroupsInput.get());
1384     await user.tab();
1385     await user.keyboard('{Enter}');
1386     expect(gitlab.saveProvisioning.get()).toBeDisabled();
1387   });
1388
1389   it('should be able to reset Auto Provisioning changes', async () => {
1390     handler.setGitlabConfigurations([
1391       mockGitlabConfiguration({
1392         allowUsersToSignUp: false,
1393         enabled: true,
1394         type: ProvisioningType.auto,
1395         groups: ['Cypress Hill', 'Public Enemy'],
1396       }),
1397     ]);
1398     const user = userEvent.setup();
1399     renderAuthentication([Feature.GitlabProvisioning]);
1400     await user.click(await gitlab.tab.find());
1401
1402     expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1403
1404     // Cancel doesn't fully work yet as the AuthenticationFormField needs to be worked on
1405     await user.click(gitlab.autoProvisioningGroupsInput.get());
1406     await user.tab();
1407     await user.tab();
1408     await user.tab();
1409     await user.tab();
1410     await user.keyboard('A Tribe Called Quest');
1411     await user.click(gitlab.autoProvisioningGroupsInput.get());
1412     await user.tab();
1413     await user.keyboard('{Enter}');
1414     await user.click(gitlab.autoProvisioningUpdateTokenButton.get());
1415     await user.type(gitlab.autoProvisioningGroupsInput.get(), 'ToToken!');
1416     expect(gitlab.saveProvisioning.get()).toBeEnabled();
1417     await user.click(gitlab.cancelProvisioningChanges.get());
1418     // expect(gitlab.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
1419     expect(gitlab.autoProvisioningGroupsInput.get()).toHaveValue('Cypress Hill');
1420   });
1421
1422   describe('Gitlab Provisioning', () => {
1423     beforeEach(() => {
1424       jest.useFakeTimers({
1425         advanceTimers: true,
1426         now: new Date('2022-02-04T12:00:59Z'),
1427       });
1428       handler.setGitlabConfigurations([
1429         mockGitlabConfiguration({
1430           id: '1',
1431           enabled: true,
1432           type: ProvisioningType.auto,
1433           groups: ['Test'],
1434         }),
1435       ]);
1436     });
1437
1438     afterEach(() => {
1439       jest.runOnlyPendingTimers();
1440       jest.useRealTimers();
1441     });
1442
1443     it('should display a success status when the synchronisation is a success', async () => {
1444       computeEngineHandler.addTask({
1445         status: TaskStatuses.Success,
1446         executedAt: '2022-02-03T11:45:35+0200',
1447         infoMessages: ['Test summary'],
1448         type: TaskTypes.GitlabProvisioning,
1449       });
1450
1451       renderAuthentication([Feature.GitlabProvisioning]);
1452       expect(await gitlab.gitlabProvisioningSuccess.find()).toBeInTheDocument();
1453       expect(gitlab.syncSummary.get()).toBeInTheDocument();
1454     });
1455
1456     it('should display a success status even when another task is pending', async () => {
1457       computeEngineHandler.addTask({
1458         status: TaskStatuses.Pending,
1459         executedAt: '2022-02-03T11:55:35+0200',
1460         type: TaskTypes.GitlabProvisioning,
1461       });
1462       computeEngineHandler.addTask({
1463         status: TaskStatuses.Success,
1464         executedAt: '2022-02-03T11:45:35+0200',
1465         type: TaskTypes.GitlabProvisioning,
1466       });
1467       renderAuthentication([Feature.GitlabProvisioning]);
1468       expect(await gitlab.gitlabProvisioningSuccess.find()).toBeInTheDocument();
1469       expect(gitlab.gitlabProvisioningPending.get()).toBeInTheDocument();
1470     });
1471
1472     it('should display an error alert when the synchronisation failed', async () => {
1473       computeEngineHandler.addTask({
1474         status: TaskStatuses.Failed,
1475         executedAt: '2022-02-03T11:45:35+0200',
1476         errorMessage: "T'es mauvais Jacques",
1477         type: TaskTypes.GitlabProvisioning,
1478       });
1479       renderAuthentication([Feature.GitlabProvisioning]);
1480       expect(await gitlab.gitlabProvisioningAlert.find()).toBeInTheDocument();
1481       expect(gitlab.autoProvisioningRadioButton.get()).toHaveTextContent("T'es mauvais Jacques");
1482       expect(gitlab.gitlabProvisioningSuccess.query()).not.toBeInTheDocument();
1483     });
1484
1485     it('should display an error alert even when another task is in progress', async () => {
1486       computeEngineHandler.addTask({
1487         status: TaskStatuses.InProgress,
1488         executedAt: '2022-02-03T11:55:35+0200',
1489         type: TaskTypes.GitlabProvisioning,
1490       });
1491       computeEngineHandler.addTask({
1492         status: TaskStatuses.Failed,
1493         executedAt: '2022-02-03T11:45:35+0200',
1494         errorMessage: "T'es mauvais Jacques",
1495         type: TaskTypes.GitlabProvisioning,
1496       });
1497       renderAuthentication([Feature.GitlabProvisioning]);
1498       expect(await gitlab.gitlabProvisioningAlert.find()).toBeInTheDocument();
1499       expect(gitlab.autoProvisioningRadioButton.get()).toHaveTextContent("T'es mauvais Jacques");
1500       expect(gitlab.gitlabProvisioningSuccess.query()).not.toBeInTheDocument();
1501       expect(gitlab.gitlabProvisioningInProgress.get()).toBeInTheDocument();
1502     });
1503
1504     it('should show warning', async () => {
1505       computeEngineHandler.addTask({
1506         status: TaskStatuses.Success,
1507         warnings: ['Warning'],
1508         infoMessages: ['Test summary'],
1509         type: TaskTypes.GitlabProvisioning,
1510       });
1511       renderAuthentication([Feature.GitlabProvisioning]);
1512
1513       expect(await gitlab.syncWarning.find()).toBeInTheDocument();
1514       expect(gitlab.syncSummary.get()).toBeInTheDocument();
1515     });
1516   });
1517 });
1518
1519 const appLoaded = async () => {
1520   await waitFor(async () => {
1521     expect(await screen.findByText('loading')).not.toBeInTheDocument();
1522   });
1523 };
1524
1525 function renderAuthentication(features: Feature[] = []) {
1526   renderComponent(
1527     <AvailableFeaturesContext.Provider value={features}>
1528       <Authentication definitions={definitions} />
1529     </AvailableFeaturesContext.Provider>,
1530   );
1531 }