]> source.dussan.org Git - sonarqube.git/blob
c811df5d98319b83c49e2cfb2af61b0f2a9e6023
[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 { AvailableFeaturesContext } from '../../../../../app/components/available-features/AvailableFeaturesContext';
25 import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
26 import { byRole, byText } from '../../../../../helpers/testSelector';
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     validationMessage: (text: string) => byText(text),
180   };
181
182   async function createConfiguration(
183     name: string,
184     params: { [key: string]: string },
185     almKey?: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer
186   ) {
187     await userEvent.click(ui.createConfigurationButton.get());
188     expect(ui.saveConfigurationButton.get()).toBeDisabled();
189
190     if (almKey) {
191       await userEvent.click(ui.bitbucketConfiguration(almKey).get());
192     }
193
194     for (const [key, value] of Object.entries(params)) {
195       // eslint-disable-next-line no-await-in-loop
196       await userEvent.type(ui.configurationInput(key).get(), value);
197     }
198     expect(ui.saveConfigurationButton.get()).toBeEnabled();
199     await userEvent.click(ui.saveConfigurationButton.get());
200
201     // New configuration is created
202     expect(screen.getByRole('heading', { name })).toBeInTheDocument();
203   }
204
205   async function editConfiguration(
206     newName: string,
207     currentName: string,
208     secretId: string,
209     almKey: AlmKeys
210   ) {
211     almSettings.setDefinitionErrorMessage('Something is wrong');
212     await userEvent.click(ui.editConfigurationButton(currentName).get());
213     expect(ui.configurationInput(secretId).query()).not.toBeInTheDocument();
214     await userEvent.click(ui.updateSecretValueButton(secretId).get());
215     await userEvent.type(ui.configurationInput(secretId).get(), 'New Secret Value');
216     await userEvent.clear(ui.configurationInput(`name.${almKey}`).get());
217     await userEvent.type(ui.configurationInput(`name.${almKey}`).get(), newName);
218     await userEvent.click(ui.saveConfigurationButton.get());
219
220     // Existing configuration is edited
221     expect(screen.queryByRole('heading', { name: currentName })).not.toBeInTheDocument();
222     expect(screen.getByRole('heading', { name: newName })).toBeInTheDocument();
223     expect(ui.validationMessage('Something is wrong').get()).toBeInTheDocument();
224   }
225
226   async function checkConfiguration(name: string) {
227     almSettings.setDefinitionErrorMessage('');
228     await userEvent.click(ui.checkConfigurationButton(name).get());
229     expect(
230       ui.validationMessage('settings.almintegration.configuration_valid').getAll()[0]
231     ).toBeInTheDocument();
232   }
233
234   async function deleteConfiguration(name: string) {
235     await userEvent.click(ui.deleteConfigurationButton(name).get());
236     await userEvent.click(ui.cancelButton.get());
237     expect(screen.getByRole('heading', { name })).toBeInTheDocument();
238
239     await userEvent.click(ui.deleteConfigurationButton(name).get());
240     await userEvent.click(ui.confirmDelete.get());
241     expect(screen.queryByRole('heading', { name })).not.toBeInTheDocument();
242   }
243
244   return {
245     ui: {
246       ...ui,
247       createConfiguration,
248       editConfiguration,
249       deleteConfiguration,
250       checkConfiguration,
251     },
252     user,
253   };
254 }
255
256 function renderAlmIntegration(features: Feature[] = []) {
257   return renderComponent(
258     <AvailableFeaturesContext.Provider value={features}>
259       <AlmIntegration />
260     </AvailableFeaturesContext.Provider>
261   );
262 }