]> source.dussan.org Git - sonarqube.git/blob
1cb65f52ee6b7b806692b0a45a9eca692aac8137
[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 { byRole, byText } from 'testing-library-selector';
24 import AlmSettingsServiceMock from '../../../../../api/mocks/AlmSettingsServiceMock';
25 import { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
26 import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
27 import { AlmKeys } from '../../../../../types/alm-settings';
28 import { Feature } from '../../../../../types/features';
29 import AlmIntegration from '../AlmIntegration';
30
31 jest.mock('../../../../../api/alm-settings');
32
33 let almSettings: AlmSettingsServiceMock;
34
35 beforeAll(() => {
36   almSettings = new AlmSettingsServiceMock();
37 });
38
39 afterEach(() => {
40   almSettings.reset();
41 });
42
43 describe('github tab', () => {
44   it('can create/edit/delete new configuration', async () => {
45     const { ui } = getPageObjects();
46     const { rerender } = renderAlmIntegration();
47     expect(await ui.almHeading.find()).toBeInTheDocument();
48     expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
49
50     // Create new configuration
51     await ui.createConfiguration('Name', {
52       'name.github': 'Name',
53       'url.github': 'https://api.github.com',
54       app_id: 'Github App ID',
55       'client_id.github': 'Github Client ID',
56       'client_secret.github': 'Client Secret',
57       private_key: 'Key',
58     });
59
60     await ui.editConfiguration('New Name', 'Name', 'client_secret.github', AlmKeys.GitHub);
61
62     await ui.checkConfiguration('New Name');
63
64     rerender(<AlmIntegration />);
65     expect(await screen.findByRole('heading', { name: 'New Name' })).toBeInTheDocument();
66
67     await ui.deleteConfiguration('New Name');
68     expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
69   });
70 });
71
72 describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
73   '%s tab',
74   (almKey: AlmKeys.Azure | AlmKeys.GitLab) => {
75     it('can create/edit/delete new configuration', async () => {
76       const { ui } = getPageObjects();
77
78       renderAlmIntegration();
79       expect(await ui.almHeading.find()).toBeInTheDocument();
80
81       await userEvent.click(ui.tab(almKey).get());
82       expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
83
84       // Create new configuration
85       await ui.createConfiguration('Name', {
86         [`name.${almKey}`]: 'Name',
87         [`url.${almKey}`]: 'https://api.alm.com',
88         personal_access_token: 'Access Token',
89       });
90
91       // Cannot create another configuration without Multiple Alm feature
92       expect(ui.createConfigurationButton.get()).toBeDisabled();
93
94       await ui.editConfiguration('New Name', 'Name', 'personal_access_token', almKey);
95
96       await ui.checkConfiguration('New Name');
97
98       await ui.deleteConfiguration('New Name');
99       expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
100     });
101   }
102 );
103
104 describe('bitbucket tab', () => {
105   it('can create/edit/delete new configuration', async () => {
106     const { ui } = getPageObjects();
107     renderAlmIntegration([Feature.MultipleAlm]);
108     expect(await ui.almHeading.find()).toBeInTheDocument();
109
110     await userEvent.click(ui.tab(AlmKeys.BitbucketServer).get());
111     expect(ui.emptyIntro(AlmKeys.BitbucketServer).get()).toBeInTheDocument();
112
113     // Create new Bitbucket Server configuration
114     await ui.createConfiguration(
115       'Name',
116       {
117         'name.bitbucket': 'Name',
118         'url.bitbucket': 'https://api.bitbucket.com',
119         personal_access_token: 'Access Token',
120       },
121       AlmKeys.BitbucketServer
122     );
123
124     // Create new Bitbucket Cloud configuration
125     await ui.createConfiguration(
126       'Name Cloud',
127       {
128         'name.bitbucket': 'Name Cloud',
129         'workspace.bitbucketcloud': 'workspace',
130         'client_id.bitbucketcloud': 'Client ID',
131         'client_secret.bitbucketcloud': 'Client Secret',
132       },
133       AlmKeys.BitbucketCloud
134     );
135
136     // Edit, check delete Bitbucket Server configuration
137     await ui.editConfiguration(
138       'New Name',
139       'Name',
140       'personal_access_token',
141       AlmKeys.BitbucketServer
142     );
143
144     await ui.checkConfiguration('New Name');
145
146     await ui.deleteConfiguration('New Name');
147
148     // Cloud configuration still exists
149     expect(screen.getByRole('heading', { name: 'Name Cloud' })).toBeInTheDocument();
150   });
151 });
152
153 function getPageObjects() {
154   const user = userEvent.setup();
155
156   const ui = {
157     almHeading: byRole('heading', { name: 'settings.almintegration.title' }),
158     emptyIntro: (almKey: AlmKeys) => byText(`settings.almintegration.empty.${almKey}`),
159     createConfigurationButton: byRole('button', { name: 'settings.almintegration.create' }),
160     tab: (almKey: AlmKeys) =>
161       byRole('tab', { name: `${almKey} settings.almintegration.tab.${almKey}` }),
162     bitbucketConfiguration: (almKey: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer) =>
163       byRole('button', { name: `alm.${almKey}.long` }),
164     configurationInput: (id: string) =>
165       byRole('textbox', { name: `settings.almintegration.form.${id}` }),
166     updateSecretValueButton: (key: string) =>
167       byRole('button', {
168         name: `settings.almintegration.form.secret.update_field_x.settings.almintegration.form.${key}`,
169       }),
170     saveConfigurationButton: byRole('button', { name: 'settings.almintegration.form.save' }),
171     editConfigurationButton: (key: string) =>
172       byRole('button', { name: `settings.almintegration.edit_configuration.${key}` }),
173     deleteConfigurationButton: (key: string) =>
174       byRole('button', { name: `settings.almintegration.delete_configuration.${key}` }),
175     cancelButton: byRole('button', { name: 'cancel' }),
176     confirmDelete: byRole('button', { name: 'delete' }),
177     checkConfigurationButton: (key: string) =>
178       byRole('button', { name: `settings.almintegration.check_configuration_x.${key}` }),
179     validationErrorMessage: byRole('alert'),
180     validationSuccessMessage: byRole('status'),
181   };
182
183   async function createConfiguration(
184     name: string,
185     params: { [key: string]: string },
186     almKey?: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer
187   ) {
188     await userEvent.click(ui.createConfigurationButton.get());
189     expect(ui.saveConfigurationButton.get()).toBeDisabled();
190
191     if (almKey) {
192       await userEvent.click(ui.bitbucketConfiguration(almKey).get());
193     }
194
195     for (const [key, value] of Object.entries(params)) {
196       // eslint-disable-next-line no-await-in-loop
197       await userEvent.type(ui.configurationInput(key).get(), value);
198     }
199     expect(ui.saveConfigurationButton.get()).toBeEnabled();
200     await userEvent.click(ui.saveConfigurationButton.get());
201
202     // New configuration is created
203     expect(screen.getByRole('heading', { name })).toBeInTheDocument();
204   }
205
206   async function editConfiguration(
207     newName: string,
208     currentName: string,
209     secretId: string,
210     almKey: AlmKeys
211   ) {
212     almSettings.setDefinitionErrorMessage('Something is wrong');
213     await userEvent.click(ui.editConfigurationButton(currentName).get());
214     expect(ui.configurationInput(secretId).query()).not.toBeInTheDocument();
215     await userEvent.click(ui.updateSecretValueButton(secretId).get());
216     await userEvent.type(ui.configurationInput(secretId).get(), 'New Secret Value');
217     await userEvent.clear(ui.configurationInput(`name.${almKey}`).get());
218     await userEvent.type(ui.configurationInput(`name.${almKey}`).get(), newName);
219     await userEvent.click(ui.saveConfigurationButton.get());
220
221     // Existing configuration is edited
222     expect(screen.queryByRole('heading', { name: currentName })).not.toBeInTheDocument();
223     expect(screen.getByRole('heading', { name: newName })).toBeInTheDocument();
224     expect(ui.validationErrorMessage.get()).toHaveTextContent('Something is wrong');
225   }
226
227   async function checkConfiguration(name: string) {
228     almSettings.setDefinitionErrorMessage('');
229     await userEvent.click(ui.checkConfigurationButton(name).get());
230     expect(ui.validationSuccessMessage.getAll()[0]).toHaveTextContent(
231       'alert.tooltip.successsettings.almintegration.configuration_valid'
232     );
233   }
234
235   async function deleteConfiguration(name: string) {
236     await userEvent.click(ui.deleteConfigurationButton(name).get());
237     await userEvent.click(ui.cancelButton.get());
238     expect(screen.getByRole('heading', { name })).toBeInTheDocument();
239
240     await userEvent.click(ui.deleteConfigurationButton(name).get());
241     await userEvent.click(ui.confirmDelete.get());
242     expect(screen.queryByRole('heading', { name })).not.toBeInTheDocument();
243   }
244
245   return {
246     ui: {
247       ...ui,
248       createConfiguration,
249       editConfiguration,
250       deleteConfiguration,
251       checkConfiguration,
252     },
253     user,
254   };
255 }
256
257 function renderAlmIntegration(features: Feature[] = []) {
258   return renderComponent(
259     <AvailableFeaturesContext.Provider value={features}>
260       <AlmIntegration />
261     </AvailableFeaturesContext.Provider>
262   );
263 }