]> source.dussan.org Git - sonarqube.git/blob
d73192194c0cb0262020395b1b4f94c6b08e1b3b
[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 } 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';
32
33 jest.mock('../../../../../api/alm-settings');
34 jest.mock('../../../../../api/settings');
35
36 let almSettings: AlmSettingsServiceMock;
37 let settings: SettingsServiceMock;
38
39 beforeAll(() => {
40   almSettings = new AlmSettingsServiceMock();
41   settings = new SettingsServiceMock();
42 });
43
44 afterEach(() => {
45   almSettings.reset();
46   settings.reset();
47 });
48
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();
55 });
56
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();
62 });
63
64 it('should display the serverBaseURL message when it is not defined', async () => {
65   const { ui } = getPageObjects();
66   renderAlmIntegration([Feature.BranchSupport]);
67
68   expect(await ui.serverBaseUrlMissingInformation.find()).toBeInTheDocument();
69 });
70
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();
77
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',
85       private_key: 'Key',
86     });
87
88     await ui.editConfiguration('New Name', 'Name', 'client_secret.github', AlmKeys.GitHub);
89
90     await ui.checkConfiguration('New Name');
91
92     rerender(<AlmIntegration />);
93     expect(await screen.findByRole('heading', { name: 'New Name' })).toBeInTheDocument();
94
95     await ui.deleteConfiguration('New Name');
96     expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
97   });
98 });
99
100 describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
101   '%s tab',
102   (almKey: AlmKeys.Azure | AlmKeys.GitLab) => {
103     it('can create/edit/delete new configuration', async () => {
104       const { ui } = getPageObjects();
105
106       renderAlmIntegration();
107       expect(await ui.almHeading.find()).toBeInTheDocument();
108
109       await userEvent.click(ui.tab(almKey).get());
110       expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
111
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',
117       });
118
119       // Cannot create another configuration without Multiple Alm feature
120       expect(ui.createConfigurationButton.get()).toBeDisabled();
121
122       await ui.editConfiguration('New Name', 'Name', 'personal_access_token', almKey);
123
124       await ui.checkConfiguration('New Name');
125
126       await ui.deleteConfiguration('New Name');
127       expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
128     });
129   },
130 );
131
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();
137
138     await userEvent.click(ui.tab(AlmKeys.BitbucketServer).get());
139     expect(ui.emptyIntro(AlmKeys.BitbucketServer).get()).toBeInTheDocument();
140
141     // Create new Bitbucket Server configuration
142     await ui.createConfiguration(
143       'Name',
144       {
145         'name.bitbucket': 'Name',
146         'url.bitbucket': 'https://api.bitbucket.com',
147         personal_access_token: 'Access Token',
148       },
149       AlmKeys.BitbucketServer,
150     );
151
152     // Create new Bitbucket Cloud configuration
153     await ui.createConfiguration(
154       'Name Cloud',
155       {
156         'name.bitbucket': 'Name Cloud',
157         'workspace.bitbucketcloud': 'workspace',
158         'client_id.bitbucketcloud': 'Client ID',
159         'client_secret.bitbucketcloud': 'Client Secret',
160       },
161       AlmKeys.BitbucketCloud,
162     );
163
164     // Edit, check delete Bitbucket Server configuration
165     await ui.editConfiguration(
166       'New Name',
167       'Name',
168       'personal_access_token',
169       AlmKeys.BitbucketServer,
170     );
171
172     await ui.checkConfiguration('New Name');
173
174     await ui.deleteConfiguration('New Name');
175
176     // Cloud configuration still exists
177     expect(screen.getByRole('heading', { name: 'Name Cloud' })).toBeInTheDocument();
178   });
179 });
180
181 function getPageObjects() {
182   const user = userEvent.setup();
183
184   const ui = {
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) =>
196       byRole('button', {
197         name: `settings.almintegration.form.secret.update_field_x.settings.almintegration.form.${key}`,
198       }),
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),
209   };
210
211   async function createConfiguration(
212     name: string,
213     params: { [key: string]: string },
214     almKey?: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer,
215   ) {
216     await userEvent.click(ui.createConfigurationButton.get());
217     expect(ui.saveConfigurationButton.get()).toBeDisabled();
218
219     if (almKey) {
220       await userEvent.click(ui.bitbucketConfiguration(almKey).get());
221     }
222
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);
226     }
227     expect(ui.saveConfigurationButton.get()).toBeEnabled();
228     await userEvent.click(ui.saveConfigurationButton.get());
229
230     // New configuration is created
231     expect(screen.getByRole('heading', { name })).toBeInTheDocument();
232   }
233
234   async function editConfiguration(
235     newName: string,
236     currentName: string,
237     secretId: string,
238     almKey: AlmKeys,
239   ) {
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());
248
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();
253   }
254
255   async function checkConfiguration(name: string) {
256     almSettings.setDefinitionErrorMessage('');
257     await userEvent.click(ui.checkConfigurationButton(name).get());
258     expect(
259       ui.validationMessage('settings.almintegration.configuration_valid').getAll()[0],
260     ).toBeInTheDocument();
261   }
262
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();
267
268     await userEvent.click(ui.deleteConfigurationButton(name).get());
269     await userEvent.click(ui.confirmDelete.get());
270     expect(screen.queryByRole('heading', { name })).not.toBeInTheDocument();
271   }
272
273   return {
274     ui: {
275       ...ui,
276       createConfiguration,
277       editConfiguration,
278       deleteConfiguration,
279       checkConfiguration,
280     },
281     user,
282   };
283 }
284
285 function renderAlmIntegration(features: Feature[] = []) {
286   return renderComponent(
287     <AvailableFeaturesContext.Provider value={features}>
288       <AlmIntegration />
289     </AvailableFeaturesContext.Provider>,
290   );
291 }