3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 import { screen, waitFor, 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';
38 let handler: AuthenticationServiceMock;
39 let system: SystemServiceMock;
40 let settingsHandler: SettingsServiceMock;
41 let computeEngineHandler: ComputeEngineServiceMock;
44 handler = new AuthenticationServiceMock();
45 system = new SystemServiceMock();
46 settingsHandler = new SettingsServiceMock();
47 computeEngineHandler = new ComputeEngineServiceMock();
50 key: 'sonar.auth.saml.signature.enabled',
54 key: 'sonar.auth.saml.enabled',
58 key: 'sonar.auth.saml.applicationId',
62 key: 'sonar.auth.saml.providerName',
65 ].forEach((setting: any) => settingsHandler.set(setting.key, setting.value));
70 settingsHandler.reset();
72 computeEngineHandler.reset();
75 const ghContainer = byRole('tabpanel', { name: 'github GitHub' });
76 const glContainer = byRole('tabpanel', { name: 'gitlab GitLab' });
77 const samlContainer = byRole('tabpanel', { name: 'SAML' });
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' }),
87 noSamlConfiguration: byText('settings.authentication.saml.form.not_configured'),
88 createConfigButton: samlContainer.byRole('button', {
89 name: 'settings.authentication.form.create',
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',
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', {
103 saveScim: samlContainer.byRole('button', { name: 'save' }),
104 enableConfigButton: samlContainer.byRole('button', {
105 name: 'settings.authentication.form.enable',
107 disableConfigButton: samlContainer.byRole('button', {
108 name: 'settings.authentication.form.disable',
110 editConfigButton: samlContainer.byRole('button', {
111 name: 'settings.authentication.form.edit',
113 enableFirstMessage: byText('settings.authentication.saml.enable_first'),
114 jitProvisioningButton: byRole('radio', {
115 name: 'settings.authentication.saml.form.provisioning_at_login',
117 scimProvisioningButton: byRole('radio', {
118 name: 'settings.authentication.saml.form.provisioning_with_scim',
120 fillForm: async (user: UserEvent) => {
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');
131 createConfiguration: async (user: UserEvent) => {
134 await user.click(await saml.createConfigButton.find());
135 await saml.fillForm(user);
136 await user.click(saml.saveConfigButton.get());
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',
145 clientId: byRole('textbox', {
146 name: 'property.sonar.auth.github.clientId.secured.name',
148 appId: byRole('textbox', { name: 'property.sonar.auth.github.appId.name' }),
149 privateKey: byRole('textbox', {
150 name: 'property.sonar.auth.github.privateKey.secured.name',
152 clientSecret: byRole('textbox', {
153 name: 'property.sonar.auth.github.clientSecret.secured.name',
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',
160 organizations: byRole('textbox', {
161 name: 'property.sonar.auth.github.organizations.name',
163 saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
164 confirmProvisioningButton: byRole('button', {
165 name: 'settings.authentication.github.provisioning_change.confirm_changes',
167 saveGithubProvisioning: ghContainer.byRole('button', { name: 'save' }),
168 groupAttribute: byRole('textbox', {
169 name: 'property.sonar.auth.github.group.name.name',
171 enableConfigButton: ghContainer.byRole('button', {
172 name: 'settings.authentication.form.enable',
174 disableConfigButton: ghContainer.byRole('button', {
175 name: 'settings.authentication.form.disable',
177 editConfigButton: ghContainer.byRole('button', {
178 name: 'settings.authentication.form.edit',
180 editMappingButton: ghContainer.byRole('button', {
181 name: 'settings.authentication.github.configuration.roles_mapping.button_label',
183 mappingRow: byRole('dialog', {
184 name: 'settings.authentication.github.configuration.roles_mapping.dialog.title',
186 customRoleInput: byRole('textbox', {
187 name: 'settings.authentication.github.configuration.roles_mapping.dialog.add_custom_role',
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',
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', {
209 deleteOrg: (org: string) =>
211 name: `settings.definition.delete_value.property.sonar.auth.github.organizations.name.${org}`,
213 enableFirstMessage: ghContainer.byText('settings.authentication.github.enable_first'),
214 jitProvisioningButton: ghContainer.byRole('radio', {
215 name: 'settings.authentication.form.provisioning_at_login',
217 githubProvisioningButton: ghContainer.byRole('radio', {
218 name: 'settings.authentication.github.form.provisioning_with_github',
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/,
227 configurationValiditySuccess: ghContainer.byRole('status', {
228 name: /github.configuration.validation.valid/,
230 configurationValidityError: ghContainer.byRole('status', {
231 name: /github.configuration.validation.invalid/,
233 syncWarning: ghContainer.byText(/Warning/),
234 syncSummary: ghContainer.byText(/Test summary/),
235 configurationValidityWarning: ghContainer.byRole('status', {
236 name: /github.configuration.validation.valid.short/,
238 checkConfigButton: ghContainer.byRole('button', {
239 name: 'settings.authentication.github.configuration.validation.test',
241 viewConfigValidityDetailsButton: ghContainer.byRole('button', {
242 name: 'settings.authentication.github.configuration.validation.details',
244 configDetailsDialog: byRole('dialog', {
245 name: 'settings.authentication.github.configuration.validation.details.title',
247 continueAutoButton: byRole('button', {
248 name: 'settings.authentication.github.confirm_auto_provisioning.continue',
250 switchJitButton: byRole('button', {
251 name: 'settings.authentication.github.confirm_auto_provisioning.switch_jit',
253 consentDialog: byRole('dialog', {
254 name: 'settings.authentication.github.confirm_auto_provisioning.header',
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;
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');
269 createConfiguration: async (user: UserEvent) => {
270 const { github } = ui;
272 await user.click(await github.createConfigButton.find());
273 await github.fillForm(user);
275 await user.click(github.saveConfigButton.get());
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());
283 enableProvisioning: async (user: UserEvent) => {
284 const { github } = ui;
285 await user.click(await github.tab.find());
287 await github.createConfiguration(user);
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());
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',
301 editConfigButton: glContainer.byRole('button', {
302 name: 'settings.authentication.form.edit',
304 deleteConfigButton: glContainer.byRole('button', {
305 name: 'settings.authentication.form.delete',
307 enableConfigButton: glContainer.byRole('button', {
308 name: 'settings.authentication.form.enable',
310 disableConfigButton: glContainer.byRole('button', {
311 name: 'settings.authentication.form.disable',
313 createDialog: byRole('dialog', {
314 name: 'settings.authentication.gitlab.form.create',
316 editDialog: byRole('dialog', {
317 name: 'settings.authentication.gitlab.form.edit',
319 applicationId: byRole('textbox', {
320 name: 'property.applicationId.name',
322 url: byRole('textbox', { name: 'property.url.name' }),
323 clientSecret: byRole('textbox', {
324 name: 'property.clientSecret.name',
326 synchronizeUserGroups: byRole('switch', {
327 name: 'synchronizeUserGroups',
329 saveConfigButton: byRole('button', { name: 'settings.almintegration.form.save' }),
330 jitProvisioningRadioButton: glContainer.byRole('radio', {
331 name: 'settings.authentication.gitlab.provisioning_at_login',
333 autoProvisioningRadioButton: glContainer.byRole('radio', {
334 name: 'settings.authentication.gitlab.form.provisioning_with_gitlab',
336 jitAllowUsersToSignUpToggle: byRole('switch', { name: 'sonar.auth.gitlab.allowUsersToSignUp' }),
337 autoProvisioningToken: byRole('textbox', {
338 name: 'property.provisioning.gitlab.token.secured.name',
340 autoProvisioningUpdateTokenButton: byRole('button', {
341 name: 'settings.almintegration.form.secret.update_field',
343 autoProvisioningGroupsInput: byRole('textbox', {
344 name: 'property.provisioning.gitlab.groups.name',
346 removeProvisioniongGroup: byRole('button', {
347 name: /settings.definition.delete_value.property.provisioning.gitlab.groups.name./,
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',
354 confirmJitProvisioningDialog: byRole('dialog', {
355 name: 'settings.authentication.gitlab.confirm.JIT',
357 confirmProvisioningChange: byRole('button', {
358 name: 'settings.authentication.gitlab.provisioning_change.confirm_changes',
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/),
369 it('should render tabs and allow navigation', async () => {
370 const user = userEvent.setup();
371 renderAuthentication();
373 expect(screen.getAllByRole('tab')).toHaveLength(4);
375 expect(screen.getByRole('tab', { name: 'SAML' })).toHaveAttribute('aria-selected', 'true');
377 await user.click(screen.getByRole('tab', { name: 'github GitHub' }));
379 expect(screen.getByRole('tab', { name: 'SAML' })).toHaveAttribute('aria-selected', 'false');
380 expect(screen.getByRole('tab', { name: 'github GitHub' })).toHaveAttribute(
386 it('should not display the login message feature info box', () => {
387 renderAuthentication();
389 expect(ui.customMessageInformation.query()).not.toBeInTheDocument();
392 it('should display the login message feature info box', () => {
393 renderAuthentication([Feature.LoginMessage]);
395 expect(ui.customMessageInformation.get()).toBeInTheDocument();
398 describe('SAML tab', () => {
401 it('should render an empty SAML configuration', async () => {
402 renderAuthentication();
403 expect(await saml.noSamlConfiguration.find()).toBeInTheDocument();
406 it('should be able to create a configuration', async () => {
407 const user = userEvent.setup();
408 renderAuthentication();
410 await user.click(await saml.createConfigButton.find());
412 expect(saml.saveConfigButton.get()).toBeDisabled();
413 await saml.fillForm(user);
414 expect(saml.saveConfigButton.get()).toBeEnabled();
416 await user.click(saml.saveConfigButton.get());
418 expect(await saml.editConfigButton.find()).toBeInTheDocument();
421 it('should be able to enable/disable configuration', async () => {
423 const user = userEvent.setup();
424 renderAuthentication();
426 await saml.createConfiguration(user);
427 await user.click(await saml.enableConfigButton.find());
429 expect(await saml.disableConfigButton.find()).toBeInTheDocument();
430 await user.click(saml.disableConfigButton.get());
431 await waitFor(() => expect(saml.disableConfigButton.query()).not.toBeInTheDocument());
433 expect(await saml.enableConfigButton.find()).toBeInTheDocument();
436 it('should be able to choose provisioning', async () => {
438 const user = userEvent.setup();
440 renderAuthentication([Feature.Scim]);
442 await saml.createConfiguration(user);
444 expect(await saml.enableFirstMessage.find()).toBeInTheDocument();
445 await user.click(await saml.enableConfigButton.find());
447 expect(await saml.jitProvisioningButton.find()).toBeChecked();
448 expect(saml.saveScim.get()).toBeDisabled();
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());
455 expect(await saml.scimProvisioningButton.find()).toBeChecked();
456 expect(await saml.saveScim.find()).toBeDisabled();
459 it('should not allow editions below Enterprise to select SCIM provisioning', async () => {
461 const user = userEvent.setup();
463 renderAuthentication();
465 await saml.createConfiguration(user);
466 await user.click(await saml.enableConfigButton.find());
468 expect(await saml.jitProvisioningButton.find()).toBeChecked();
469 expect(saml.scimProvisioningButton.get()).toHaveAttribute('aria-disabled', 'true');
473 describe('Github tab', () => {
474 const { github } = ui;
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();
483 it('should be able to create a configuration', async () => {
484 const user = userEvent.setup();
485 renderAuthentication();
487 await user.click(await github.tab.find());
488 await user.click(await github.createConfigButton.find());
490 expect(github.saveConfigButton.get()).toBeDisabled();
492 await github.fillForm(user);
493 expect(github.saveConfigButton.get()).toBeEnabled();
495 await user.click(github.saveConfigButton.get());
497 expect(await github.editConfigButton.find()).toBeInTheDocument();
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());
506 await github.createConfiguration(user);
508 await user.click(github.editConfigButton.get());
509 await user.click(github.deleteOrg('organization1').get());
511 await user.click(github.saveConfigButton.get());
513 await user.click(await github.editConfigButton.find());
515 expect(github.organizations.get()).toHaveValue('');
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());
524 await github.createConfiguration(user);
526 await user.click(await github.enableConfigButton.find());
528 expect(await github.disableConfigButton.find()).toBeInTheDocument();
529 await user.click(github.disableConfigButton.get());
530 await waitFor(() => expect(github.disableConfigButton.query()).not.toBeInTheDocument());
532 expect(await github.enableConfigButton.find()).toBeInTheDocument();
535 it('should not allow edtion below Enterprise to select Github provisioning', async () => {
536 const { github } = ui;
537 const user = userEvent.setup();
539 renderAuthentication();
540 await user.click(await github.tab.find());
542 await github.createConfiguration(user);
543 await user.click(await github.enableConfigButton.find());
545 expect(await github.jitProvisioningButton.find()).toBeChecked();
546 expect(github.githubProvisioningButton.get()).toHaveAttribute('aria-disabled', 'true');
549 it('should be able to choose provisioning', async () => {
550 const { github } = ui;
551 const user = userEvent.setup();
553 renderAuthentication([Feature.GithubProvisioning]);
554 await user.click(await github.tab.find());
556 await github.createConfiguration(user);
558 expect(await github.enableFirstMessage.find()).toBeInTheDocument();
559 await user.click(await github.enableConfigButton.find());
561 expect(await github.jitProvisioningButton.find()).toBeChecked();
563 expect(github.saveGithubProvisioning.get()).toBeDisabled();
564 await user.click(github.allowUserToSignUp.get());
566 expect(github.saveGithubProvisioning.get()).toBeEnabled();
567 await user.click(github.saveGithubProvisioning.get());
569 await waitFor(() => expect(github.saveGithubProvisioning.query()).toBeDisabled());
571 await user.click(github.githubProvisioningButton.get());
573 expect(github.saveGithubProvisioning.get()).toBeEnabled();
574 await user.click(github.saveGithubProvisioning.get());
575 await user.click(github.confirmProvisioningButton.get());
577 expect(await github.githubProvisioningButton.find()).toBeChecked();
578 expect(github.disableConfigButton.get()).toBeDisabled();
579 expect(github.saveGithubProvisioning.get()).toBeDisabled();
582 describe('Github Provisioning', () => {
588 now: new Date('2022-02-04T12:00:59Z'),
590 user = userEvent.setup();
594 jest.runOnlyPendingTimers();
595 jest.useRealTimers();
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',
604 renderAuthentication([Feature.GithubProvisioning]);
605 await github.enableProvisioning(user);
606 expect(github.githubProvisioningSuccess.get()).toBeInTheDocument();
607 expect(github.syncSummary.get()).toBeInTheDocument();
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',
615 handler.addProvisioningTask({
616 status: TaskStatuses.Success,
617 executedAt: '2022-02-03T11:45:35+0200',
619 renderAuthentication([Feature.GithubProvisioning]);
620 await github.enableProvisioning(user);
621 expect(github.githubProvisioningSuccess.get()).toBeInTheDocument();
622 expect(github.githubProvisioningPending.get()).toBeInTheDocument();
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",
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();
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',
643 handler.addProvisioningTask({
644 status: TaskStatuses.Failed,
645 executedAt: '2022-02-03T11:45:35+0200',
646 errorMessage: "T'es mauvais Jacques",
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();
656 it('should display that config is valid for both provisioning with 1 org', async () => {
657 renderAuthentication([Feature.GithubProvisioning]);
658 await github.enableConfiguration(user);
662 await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
665 it('should display that config is valid for both provisioning with multiple orgs', async () => {
666 handler.setConfigurationValidity({
669 organization: 'org1',
670 autoProvisioning: { status: GitHubProvisioningStatus.Success },
671 jit: { status: GitHubProvisioningStatus.Success },
674 organization: 'org2',
675 autoProvisioning: { status: GitHubProvisioningStatus.Success },
676 jit: { status: GitHubProvisioningStatus.Success },
680 renderAuthentication([Feature.GithubProvisioning]);
681 await github.enableConfiguration(user);
685 await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
686 expect(github.configurationValiditySuccess.get()).toHaveTextContent('2');
688 await user.click(github.viewConfigValidityDetailsButton.get());
689 expect(github.getConfigDetailsTitle()).toHaveTextContent(
690 'settings.authentication.github.configuration.validation.details.valid_label',
692 expect(github.getOrgs()[0]).toHaveTextContent(
693 'settings.authentication.github.configuration.validation.details.valid_labelorg1',
695 expect(github.getOrgs()[1]).toHaveTextContent(
696 'settings.authentication.github.configuration.validation.details.valid_labelorg2',
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({
705 organization: 'org1',
707 status: GitHubProvisioningStatus.Failed,
711 status: GitHubProvisioningStatus.Failed,
718 renderAuthentication([Feature.GithubProvisioning]);
719 await github.enableConfiguration(user);
723 await waitFor(() => expect(github.configurationValidityWarning.get()).toBeInTheDocument());
724 expect(github.configurationValidityWarning.get()).toHaveTextContent(errorMessage);
726 await user.click(github.viewConfigValidityDetailsButton.get());
727 expect(github.getConfigDetailsTitle()).toHaveTextContent(
728 'settings.authentication.github.configuration.validation.details.valid_label',
730 expect(github.getOrgs()[0]).toHaveTextContent(
731 'settings.authentication.github.configuration.validation.details.invalid_labelorg1 - Installation suspended',
734 await user.click(github.configDetailsDialog.byRole('button', { name: 'close' }).get());
736 await user.click(github.githubProvisioningButton.get());
737 await waitFor(() => expect(github.configurationValidityError.get()).toBeInTheDocument());
738 expect(github.configurationValidityError.get()).toHaveTextContent(errorMessage);
741 it('should display that config is valid but some organizations were not found', async () => {
742 handler.setConfigurationValidity({
745 organization: 'org1',
746 autoProvisioning: { status: GitHubProvisioningStatus.Success },
747 jit: { status: GitHubProvisioningStatus.Success },
752 renderAuthentication([Feature.GithubProvisioning]);
753 await github.enableConfiguration(user);
757 await waitFor(() => expect(github.configurationValiditySuccess.get()).toBeInTheDocument());
758 expect(github.configurationValiditySuccess.get()).toHaveTextContent('1');
760 await user.click(github.viewConfigValidityDetailsButton.get());
761 expect(github.getConfigDetailsTitle()).toHaveTextContent(
762 'settings.authentication.github.configuration.validation.details.valid_label',
764 expect(github.getOrgs()[0]).toHaveTextContent(
765 'settings.authentication.github.configuration.validation.details.valid_labelorg1',
767 expect(github.getOrgs()[1]).toHaveTextContent(
768 'settings.authentication.github.configuration.validation.details.org_not_found.organization1',
772 it('should display that config is invalid', async () => {
773 const errorMessage = 'Test error';
774 handler.setConfigurationValidity({
777 status: GitHubProvisioningStatus.Failed,
781 status: GitHubProvisioningStatus.Failed,
786 renderAuthentication([Feature.GithubProvisioning]);
787 await github.enableConfiguration(user);
791 await waitFor(() => expect(github.configurationValidityError.query()).toBeInTheDocument());
792 expect(github.configurationValidityError.get()).toHaveTextContent(errorMessage);
794 await user.click(github.viewConfigValidityDetailsButton.get());
795 expect(github.getConfigDetailsTitle()).toHaveTextContent(
796 'settings.authentication.github.configuration.validation.details.invalid_label',
798 expect(github.configDetailsDialog.get()).toHaveTextContent(errorMessage);
801 it('should display that config is valid for jit, but not for auto', async () => {
802 const errorMessage = 'Test error';
803 handler.setConfigurationValidity({
806 status: GitHubProvisioningStatus.Success,
809 status: GitHubProvisioningStatus.Failed,
814 renderAuthentication([Feature.GithubProvisioning]);
815 await github.enableConfiguration(user);
819 await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
820 expect(github.configurationValiditySuccess.get()).not.toHaveTextContent(errorMessage);
822 await user.click(github.viewConfigValidityDetailsButton.get());
823 expect(github.getConfigDetailsTitle()).toHaveTextContent(
824 'settings.authentication.github.configuration.validation.details.valid_label',
826 await user.click(github.configDetailsDialog.byRole('button', { name: 'close' }).get());
828 await user.click(github.githubProvisioningButton.get());
830 expect(github.configurationValidityError.get()).toBeInTheDocument();
831 expect(github.configurationValidityError.get()).toHaveTextContent(errorMessage);
833 await user.click(github.viewConfigValidityDetailsButton.get());
834 expect(github.getConfigDetailsTitle()).toHaveTextContent(
835 'settings.authentication.github.configuration.validation.details.invalid_label',
839 it('should display that config is invalid because of orgs', async () => {
840 const errorMessage = 'Test error';
841 handler.setConfigurationValidity({
844 organization: 'org1',
845 autoProvisioning: { status: GitHubProvisioningStatus.Success },
846 jit: { status: GitHubProvisioningStatus.Success },
849 organization: 'org2',
850 jit: { status: GitHubProvisioningStatus.Failed, errorMessage },
851 autoProvisioning: { status: GitHubProvisioningStatus.Failed, errorMessage },
855 renderAuthentication([Feature.GithubProvisioning]);
856 await github.enableConfiguration(user);
860 await waitFor(() => expect(github.configurationValiditySuccess.query()).toBeInTheDocument());
862 await user.click(github.viewConfigValidityDetailsButton.get());
864 expect(github.getOrgs()[0]).toHaveTextContent(
865 'settings.authentication.github.configuration.validation.details.valid_labelorg1',
867 expect(github.getOrgs()[1]).toHaveTextContent(
868 'settings.authentication.github.configuration.validation.details.invalid_labelorg2 - Test error',
871 await user.click(github.configDetailsDialog.byRole('button', { name: 'close' }).get());
873 await user.click(github.githubProvisioningButton.get());
875 expect(github.configurationValidityError.get()).toBeInTheDocument();
876 expect(github.configurationValidityError.get()).toHaveTextContent(
877 `settings.authentication.github.configuration.validation.invalid_org.org2.${errorMessage}`,
879 await user.click(github.viewConfigValidityDetailsButton.get());
880 expect(github.getOrgs()[1]).toHaveTextContent(
881 `settings.authentication.github.configuration.validation.details.invalid_labelorg2 - ${errorMessage}`,
885 it('should update provisioning validity after clicking Test Configuration', async () => {
886 const errorMessage = 'Test error';
887 handler.setConfigurationValidity({
890 status: GitHubProvisioningStatus.Failed,
894 status: GitHubProvisioningStatus.Failed,
899 renderAuthentication([Feature.GithubProvisioning]);
900 await github.enableConfiguration(user);
901 handler.setConfigurationValidity({
904 status: GitHubProvisioningStatus.Success,
907 status: GitHubProvisioningStatus.Success,
914 expect(await github.configurationValidityError.find()).toBeInTheDocument();
916 await user.click(github.checkConfigButton.get());
918 expect(github.configurationValiditySuccess.get()).toBeInTheDocument();
919 expect(github.configurationValidityError.query()).not.toBeInTheDocument();
922 it('should show warning', async () => {
923 handler.addProvisioningTask({
924 status: TaskStatuses.Success,
925 warnings: ['Warning'],
927 renderAuthentication([Feature.GithubProvisioning]);
928 await github.enableProvisioning(user);
930 expect(await github.syncWarning.find()).toBeInTheDocument();
931 expect(github.syncSummary.get()).toBeInTheDocument();
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]);
941 await user.click(await github.tab.find());
943 expect(await github.consentDialog.find()).toBeInTheDocument();
944 await user.click(github.continueAutoButton.get());
946 expect(await github.githubProvisioningButton.find()).toBeChecked();
947 expect(github.consentDialog.query()).not.toBeInTheDocument();
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]);
957 await user.click(await github.tab.find());
959 expect(await github.consentDialog.find()).toBeInTheDocument();
960 await user.click(github.switchJitButton.get());
962 expect(await github.jitProvisioningButton.find()).toBeChecked();
963 expect(github.consentDialog.query()).not.toBeInTheDocument();
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());
973 expect(await github.editMappingButton.find()).toBeInTheDocument();
974 await user.click(github.editMappingButton.get());
976 const rows = (await github.mappingRow.findAll()).filter(
977 (row) => within(row).queryAllByRole('checkbox').length > 0,
980 expect(rows).toHaveLength(5);
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');
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());
994 await github.createConfiguration(user);
995 await user.click(await github.enableConfigButton.find());
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());
1003 expect(await github.mappingRow.findAll()).toHaveLength(7);
1005 let readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'));
1006 let adminCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('admin'));
1008 expect(readCheckboxes[0]).toBeChecked();
1009 expect(readCheckboxes[5]).not.toBeChecked();
1010 expect(adminCheckboxes[5]).toBeChecked();
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());
1017 await user.click(github.saveGithubProvisioning.get());
1018 await user.click(github.confirmProvisioningButton.get());
1020 // Clean local mapping state
1021 await user.click(github.jitProvisioningButton.get());
1022 await user.click(github.githubProvisioningButton.get());
1024 await user.click(github.editMappingButton.get());
1025 readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'));
1026 adminCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('admin'));
1028 expect(readCheckboxes[0]).not.toBeChecked();
1029 expect(readCheckboxes[5]).toBeChecked();
1030 expect(adminCheckboxes[5]).not.toBeChecked();
1031 await user.click(github.mappingDialogClose.get());
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());
1041 expect(await github.saveGithubProvisioning.find()).toBeDisabled();
1042 await user.click(github.editMappingButton.get());
1044 expect(await github.mappingRow.findAll()).toHaveLength(7);
1046 let readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'))[0];
1048 expect(readCheckboxes).toBeChecked();
1050 await user.click(readCheckboxes);
1051 await user.click(github.mappingDialogClose.get());
1053 expect(await github.saveGithubProvisioning.find()).toBeEnabled();
1055 await user.click(github.saveGithubProvisioning.get());
1057 // Clean local mapping state
1058 await user.click(github.jitProvisioningButton.get());
1059 await user.click(github.githubProvisioningButton.get());
1061 await user.click(github.editMappingButton.get());
1062 readCheckboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('read'))[0];
1064 expect(readCheckboxes).not.toBeChecked();
1065 await user.click(github.mappingDialogClose.get());
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());
1077 expect(await github.saveGithubProvisioning.find()).toBeDisabled();
1078 await user.click(github.editMappingButton.get());
1080 const rows = (await github.mappingRow.findAll()).filter(
1081 (row) => within(row).queryAllByRole('checkbox').length > 0,
1084 expect(rows).toHaveLength(7);
1086 let custom1Checkboxes = github.mappingCheckbox.getAll(github.getMappingRowByRole('custom1'));
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();
1095 await user.click(custom1Checkboxes[1]);
1096 await user.click(custom1Checkboxes[2]);
1098 await user.click(github.deleteCustomRoleCustom2.get());
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());
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());
1132 expect(await github.saveGithubProvisioning.find()).toBeEnabled();
1133 await user.click(github.saveGithubProvisioning.get());
1135 // Clean local mapping state
1136 await user.click(github.jitProvisioningButton.get());
1137 await user.click(github.githubProvisioningButton.get());
1139 await user.click(github.editMappingButton.get());
1142 (await github.mappingRow.findAll()).filter(
1143 (row) => within(row).queryAllByRole('checkbox').length > 0,
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());
1166 describe('GitLab', () => {
1167 const { gitlab } = ui;
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());
1175 expect(await gitlab.noGitlabConfiguration.find()).toBeInTheDocument();
1176 expect(gitlab.createConfigButton.get()).toBeInTheDocument();
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());
1186 expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1187 expect(gitlab.noGitlabConfiguration.query()).not.toBeInTheDocument();
1188 expect(glContainer.get()).toHaveTextContent('https://company.gitlab.com');
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();
1196 it('should edit/delete configuration', async () => {
1197 const user = userEvent.setup();
1198 renderAuthentication();
1199 await user.click(await gitlab.tab.find());
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();
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());
1217 expect(glContainer.get()).not.toHaveTextContent('URL');
1218 expect(glContainer.get()).toHaveTextContent('https://company.gitlab.com');
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();
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());
1234 expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1235 expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1237 user.click(gitlab.autoProvisioningRadioButton.get());
1238 expect(await gitlab.autoProvisioningRadioButton.find()).toBeEnabled();
1239 expect(gitlab.saveProvisioning.get()).toBeDisabled();
1241 await user.type(gitlab.autoProvisioningToken.get(), 'JRR Tolkien');
1242 expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1244 await user.type(gitlab.autoProvisioningGroupsInput.get(), 'NWA');
1245 user.click(gitlab.autoProvisioningRadioButton.get());
1246 expect(await gitlab.saveProvisioning.find()).toBeEnabled();
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();
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();
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();
1263 expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1264 expect(await gitlab.saveProvisioning.find()).toBeDisabled();
1267 it('should change from auto provisioning to JIT with proper validation', async () => {
1268 handler.setGitlabConfigurations([
1269 mockGitlabConfiguration({
1270 allowUsersToSignUp: false,
1272 type: ProvisioningType.auto,
1276 const user = userEvent.setup();
1277 renderAuthentication([Feature.GitlabProvisioning]);
1278 await user.click(await gitlab.tab.find());
1280 expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1282 expect(gitlab.jitProvisioningRadioButton.get()).not.toBeChecked();
1283 expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1284 expect(gitlab.autoProvisioningGroupsInput.get()).toHaveValue('D12');
1286 expect(gitlab.autoProvisioningToken.query()).not.toBeInTheDocument();
1287 expect(gitlab.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
1289 await user.click(gitlab.jitProvisioningRadioButton.get());
1290 expect(await gitlab.jitProvisioningRadioButton.find()).toBeChecked();
1292 expect(await gitlab.saveProvisioning.find()).toBeEnabled();
1294 expect(gitlab.jitAllowUsersToSignUpToggle.get()).toBeInTheDocument();
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();
1301 expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1302 expect(await gitlab.saveProvisioning.find()).toBeDisabled();
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,
1310 type: ProvisioningType.jit,
1313 const user = userEvent.setup();
1314 renderAuthentication([Feature.GitlabProvisioning]);
1315 await user.click(await gitlab.tab.find());
1317 expect(await gitlab.editConfigButton.find()).toBeInTheDocument();
1319 expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1320 expect(gitlab.autoProvisioningRadioButton.get()).not.toBeChecked();
1322 expect(gitlab.jitAllowUsersToSignUpToggle.get()).not.toBeChecked();
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());
1331 await user.click(gitlab.saveProvisioning.get());
1333 expect(gitlab.jitProvisioningRadioButton.get()).toBeChecked();
1334 expect(gitlab.jitAllowUsersToSignUpToggle.get()).toBeChecked();
1335 expect(await gitlab.saveProvisioning.find()).toBeDisabled();
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,
1343 type: ProvisioningType.auto,
1344 groups: ['Cypress Hill', 'Public Enemy'],
1347 const user = userEvent.setup();
1348 renderAuthentication([Feature.GitlabProvisioning]);
1349 await user.click(await gitlab.tab.find());
1351 expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1352 expect(gitlab.autoProvisioningUpdateTokenButton.get()).toBeInTheDocument();
1353 expect(gitlab.autoProvisioningGroupsInput.get()).toHaveValue('Cypress Hill');
1355 expect(gitlab.saveProvisioning.get()).toBeDisabled();
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();
1364 // Adding a group should enable save
1365 await user.click(gitlab.autoProvisioningGroupsInput.get());
1370 await user.keyboard('Run DMC');
1371 expect(gitlab.saveProvisioning.get()).toBeEnabled();
1373 await user.keyboard('{Enter}');
1374 expect(gitlab.saveProvisioning.get()).toBeDisabled();
1376 // Removing a group should enable save
1377 await user.click(gitlab.autoProvisioningGroupsInput.get());
1379 await user.keyboard('{Enter}');
1380 expect(gitlab.saveProvisioning.get()).toBeEnabled();
1382 // Removing all groups should disable save
1383 await user.click(gitlab.autoProvisioningGroupsInput.get());
1385 await user.keyboard('{Enter}');
1386 expect(gitlab.saveProvisioning.get()).toBeDisabled();
1389 it('should be able to reset Auto Provisioning changes', async () => {
1390 handler.setGitlabConfigurations([
1391 mockGitlabConfiguration({
1392 allowUsersToSignUp: false,
1394 type: ProvisioningType.auto,
1395 groups: ['Cypress Hill', 'Public Enemy'],
1398 const user = userEvent.setup();
1399 renderAuthentication([Feature.GitlabProvisioning]);
1400 await user.click(await gitlab.tab.find());
1402 expect(gitlab.autoProvisioningRadioButton.get()).toBeChecked();
1404 // Cancel doesn't fully work yet as the AuthenticationFormField needs to be worked on
1405 await user.click(gitlab.autoProvisioningGroupsInput.get());
1410 await user.keyboard('A Tribe Called Quest');
1411 await user.click(gitlab.autoProvisioningGroupsInput.get());
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');
1422 describe('Gitlab Provisioning', () => {
1424 jest.useFakeTimers({
1425 advanceTimers: true,
1426 now: new Date('2022-02-04T12:00:59Z'),
1428 handler.setGitlabConfigurations([
1429 mockGitlabConfiguration({
1432 type: ProvisioningType.auto,
1439 jest.runOnlyPendingTimers();
1440 jest.useRealTimers();
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,
1451 renderAuthentication([Feature.GitlabProvisioning]);
1452 expect(await gitlab.gitlabProvisioningSuccess.find()).toBeInTheDocument();
1453 expect(gitlab.syncSummary.get()).toBeInTheDocument();
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,
1462 computeEngineHandler.addTask({
1463 status: TaskStatuses.Success,
1464 executedAt: '2022-02-03T11:45:35+0200',
1465 type: TaskTypes.GitlabProvisioning,
1467 renderAuthentication([Feature.GitlabProvisioning]);
1468 expect(await gitlab.gitlabProvisioningSuccess.find()).toBeInTheDocument();
1469 expect(gitlab.gitlabProvisioningPending.get()).toBeInTheDocument();
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,
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();
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,
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,
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();
1504 it('should show warning', async () => {
1505 computeEngineHandler.addTask({
1506 status: TaskStatuses.Success,
1507 warnings: ['Warning'],
1508 infoMessages: ['Test summary'],
1509 type: TaskTypes.GitlabProvisioning,
1511 renderAuthentication([Feature.GitlabProvisioning]);
1513 expect(await gitlab.syncWarning.find()).toBeInTheDocument();
1514 expect(gitlab.syncSummary.get()).toBeInTheDocument();
1519 const appLoaded = async () => {
1520 await waitFor(async () => {
1521 expect(await screen.findByText('loading')).not.toBeInTheDocument();
1525 function renderAuthentication(features: Feature[] = []) {
1527 <AvailableFeaturesContext.Provider value={features}>
1528 <Authentication definitions={definitions} />
1529 </AvailableFeaturesContext.Provider>,