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 } from '@testing-library/react';
21 import userEvent from '@testing-library/user-event';
22 import React from 'react';
23 import AlmSettingsServiceMock from '../../../../../api/mocks/AlmSettingsServiceMock';
24 import SettingsServiceMock from '../../../../../api/mocks/SettingsServiceMock';
25 import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
26 import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
27 import { byRole, byText } from '../../../../../helpers/testSelector';
28 import { AlmKeys } from '../../../../../types/alm-settings';
29 import { Feature } from '../../../../../types/features';
30 import { SettingsKey } from '../../../../../types/settings';
31 import AlmIntegration from '../AlmIntegration';
33 jest.mock('../../../../../api/alm-settings');
34 jest.mock('../../../../../api/settings');
36 let almSettings: AlmSettingsServiceMock;
37 let settings: SettingsServiceMock;
40 almSettings = new AlmSettingsServiceMock();
41 settings = new SettingsServiceMock();
49 it('should not display the serverBaseURL message when it is defined', async () => {
50 const { ui } = getPageObjects();
51 settings.set(SettingsKey.ServerBaseUrl, 'http://localhost:9000');
52 renderAlmIntegration([Feature.BranchSupport]);
53 expect(await ui.almHeading.find()).toBeInTheDocument();
54 expect(ui.serverBaseUrlMissingInformation.query()).not.toBeInTheDocument();
57 it('should not display the serverBaseURL message for Community edition', async () => {
58 const { ui } = getPageObjects();
59 renderAlmIntegration();
60 expect(await ui.almHeading.find()).toBeInTheDocument();
61 expect(ui.serverBaseUrlMissingInformation.query()).not.toBeInTheDocument();
64 it('should display the serverBaseURL message when it is not defined', async () => {
65 const { ui } = getPageObjects();
66 renderAlmIntegration([Feature.BranchSupport]);
68 expect(await ui.serverBaseUrlMissingInformation.find()).toBeInTheDocument();
71 describe('github tab', () => {
72 it('can create/edit/delete new configuration', async () => {
73 const { ui } = getPageObjects();
74 const { rerender } = renderAlmIntegration();
75 expect(await ui.almHeading.find()).toBeInTheDocument();
76 expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
78 // Create new configuration
79 await ui.createConfiguration('Name', {
80 'name.github': 'Name',
81 'url.github': 'https://api.github.com',
82 app_id: 'Github App ID',
83 'client_id.github': 'Github Client ID',
84 'client_secret.github': 'Client Secret',
88 await ui.editConfiguration('New Name', 'Name', 'client_secret.github', AlmKeys.GitHub);
90 await ui.checkConfiguration('New Name');
92 rerender(<AlmIntegration />);
93 expect(await screen.findByRole('heading', { name: 'New Name' })).toBeInTheDocument();
95 await ui.deleteConfiguration('New Name');
96 expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
100 describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
102 (almKey: AlmKeys.Azure | AlmKeys.GitLab) => {
103 it('can create/edit/delete new configuration', async () => {
104 const { ui } = getPageObjects();
106 renderAlmIntegration();
107 expect(await ui.almHeading.find()).toBeInTheDocument();
109 await userEvent.click(ui.tab(almKey).get());
110 expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
112 // Create new configuration
113 await ui.createConfiguration('Name', {
114 [`name.${almKey}`]: 'Name',
115 [`url.${almKey}`]: 'https://api.alm.com',
116 personal_access_token: 'Access Token',
119 // Cannot create another configuration without Multiple Alm feature
120 expect(ui.createConfigurationButton.get()).toBeDisabled();
122 await ui.editConfiguration('New Name', 'Name', 'personal_access_token', almKey);
124 await ui.checkConfiguration('New Name');
126 await ui.deleteConfiguration('New Name');
127 expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
132 describe('bitbucket tab', () => {
133 it('can create/edit/delete new configuration', async () => {
134 const { ui } = getPageObjects();
135 renderAlmIntegration([Feature.MultipleAlm]);
136 expect(await ui.almHeading.find()).toBeInTheDocument();
138 await userEvent.click(ui.tab(AlmKeys.BitbucketServer).get());
139 expect(ui.emptyIntro(AlmKeys.BitbucketServer).get()).toBeInTheDocument();
141 // Create new Bitbucket Server configuration
142 await ui.createConfiguration(
145 'name.bitbucket': 'Name',
146 'url.bitbucket': 'https://api.bitbucket.com',
147 personal_access_token: 'Access Token',
149 AlmKeys.BitbucketServer,
152 // Create new Bitbucket Cloud configuration
153 await ui.createConfiguration(
156 'name.bitbucket': 'Name Cloud',
157 'workspace.bitbucketcloud': 'workspace',
158 'client_id.bitbucketcloud': 'Client ID',
159 'client_secret.bitbucketcloud': 'Client Secret',
161 AlmKeys.BitbucketCloud,
164 // Edit, check delete Bitbucket Server configuration
165 await ui.editConfiguration(
168 'personal_access_token',
169 AlmKeys.BitbucketServer,
172 await ui.checkConfiguration('New Name');
174 await ui.deleteConfiguration('New Name');
176 // Cloud configuration still exists
177 expect(screen.getByRole('heading', { name: 'Name Cloud' })).toBeInTheDocument();
181 function getPageObjects() {
182 const user = userEvent.setup();
185 almHeading: byRole('heading', { name: 'settings.almintegration.title' }),
186 serverBaseUrlMissingInformation: byText('settings.almintegration.empty.server_base_url'),
187 emptyIntro: (almKey: AlmKeys) => byText(`settings.almintegration.empty.${almKey}`),
188 createConfigurationButton: byRole('button', { name: 'settings.almintegration.create' }),
189 tab: (almKey: AlmKeys) =>
190 byRole('tab', { name: `${almKey} settings.almintegration.tab.${almKey}` }),
191 bitbucketConfiguration: (almKey: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer) =>
192 byRole('button', { name: `alm.${almKey}.long` }),
193 configurationInput: (id: string) =>
194 byRole('textbox', { name: `settings.almintegration.form.${id} required` }),
195 updateSecretValueButton: (key: string) =>
197 name: `settings.almintegration.form.secret.update_field_x.settings.almintegration.form.${key}`,
199 saveConfigurationButton: byRole('button', { name: 'settings.almintegration.form.save' }),
200 editConfigurationButton: (key: string) =>
201 byRole('button', { name: `settings.almintegration.edit_configuration.${key}` }),
202 deleteConfigurationButton: (key: string) =>
203 byRole('button', { name: `settings.almintegration.delete_configuration.${key}` }),
204 cancelButton: byRole('button', { name: 'cancel' }),
205 confirmDelete: byRole('button', { name: 'delete' }),
206 checkConfigurationButton: (key: string) =>
207 byRole('button', { name: `settings.almintegration.check_configuration_x.${key}` }),
208 validationMessage: (text: string) => byText(text),
211 async function createConfiguration(
213 params: { [key: string]: string },
214 almKey?: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer,
216 await userEvent.click(ui.createConfigurationButton.get());
217 expect(ui.saveConfigurationButton.get()).toBeDisabled();
220 await userEvent.click(ui.bitbucketConfiguration(almKey).get());
223 for (const [key, value] of Object.entries(params)) {
224 // eslint-disable-next-line no-await-in-loop
225 await userEvent.type(ui.configurationInput(key).get(), value);
227 expect(ui.saveConfigurationButton.get()).toBeEnabled();
228 await userEvent.click(ui.saveConfigurationButton.get());
230 // New configuration is created
231 expect(screen.getByRole('heading', { name })).toBeInTheDocument();
234 async function editConfiguration(
240 almSettings.setDefinitionErrorMessage('Something is wrong');
241 await userEvent.click(ui.editConfigurationButton(currentName).get());
242 expect(ui.configurationInput(secretId).query()).not.toBeInTheDocument();
243 await userEvent.click(ui.updateSecretValueButton(secretId).get());
244 await userEvent.type(ui.configurationInput(secretId).get(), 'New Secret Value');
245 await userEvent.clear(ui.configurationInput(`name.${almKey}`).get());
246 await userEvent.type(ui.configurationInput(`name.${almKey}`).get(), newName);
247 await userEvent.click(ui.saveConfigurationButton.get());
249 // Existing configuration is edited
250 expect(screen.queryByRole('heading', { name: currentName })).not.toBeInTheDocument();
251 expect(screen.getByRole('heading', { name: newName })).toBeInTheDocument();
252 expect(ui.validationMessage('Something is wrong').get()).toBeInTheDocument();
255 async function checkConfiguration(name: string) {
256 almSettings.setDefinitionErrorMessage('');
257 await userEvent.click(ui.checkConfigurationButton(name).get());
259 ui.validationMessage('settings.almintegration.configuration_valid').getAll()[0],
260 ).toBeInTheDocument();
263 async function deleteConfiguration(name: string) {
264 await userEvent.click(ui.deleteConfigurationButton(name).get());
265 await userEvent.click(ui.cancelButton.get());
266 expect(screen.getByRole('heading', { name })).toBeInTheDocument();
268 await userEvent.click(ui.deleteConfigurationButton(name).get());
269 await userEvent.click(ui.confirmDelete.get());
270 expect(screen.queryByRole('heading', { name })).not.toBeInTheDocument();
285 function renderAlmIntegration(features: Feature[] = []) {
286 return renderComponent(
287 <AvailableFeaturesContext.Provider value={features}>
289 </AvailableFeaturesContext.Provider>,