]> source.dussan.org Git - sonarqube.git/blob
234a84bebbb7ee4cfa49ab049c275269ceca1f7f
[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, 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 * as React from 'react';
24 import UserTokensMock from '../../../../api/mocks/UserTokensMock';
25 import { mockComponent } from '../../../../helpers/mocks/component';
26 import { mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks';
27 import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils';
28 import { Permissions } from '../../../../types/permissions';
29 import { TokenType } from '../../../../types/token';
30 import { OSs } from '../../types';
31 import AzurePipelinesTutorial, { AzurePipelinesTutorialProps } from '../AzurePipelinesTutorial';
32
33 jest.mock('../../../../api/user-tokens');
34
35 jest.mock('../../../../api/settings', () => ({
36   getAllValues: jest.fn().mockResolvedValue([]),
37 }));
38
39 let tokenMock: UserTokensMock;
40
41 beforeAll(() => {
42   tokenMock = new UserTokensMock();
43 });
44
45 afterEach(() => {
46   tokenMock.reset();
47 });
48
49 it('should render correctly and allow navigating between the different steps', async () => {
50   renderAzurePipelinesTutorial();
51   const user = userEvent.setup();
52
53   expect(
54     screen.getByRole('heading', { name: 'onboarding.tutorial.with.azure_pipelines.title' })
55   ).toBeInTheDocument();
56
57   //// Default step.
58   assertDefaultStepIsCorrectlyRendered();
59
60   // Continue.
61   await goToNextStep(user);
62
63   //// Token step.
64   assertServiceEndpointStepIsCorrectlyRendered();
65
66   // Generate a token.
67   await clickButton(user, 'onboarding.token.generate.long');
68   const modal = screen.getByRole('dialog');
69   await clickButton(user, 'onboarding.token.generate', modal);
70   const lastToken = tokenMock.getLastToken();
71   if (lastToken === undefined) {
72     throw new Error("Couldn't find the latest generated token.");
73   }
74   expect(lastToken.type).toBe(TokenType.Global);
75   expect(within(modal).getByRole('alert')).toHaveTextContent(
76     `users.tokens.new_token_created.${lastToken.token}`
77   );
78   await clickButton(user, 'continue', modal);
79
80   // Continue.
81   await goToNextStep(user);
82
83   //// Analysis step: .NET
84   await clickButton(user, 'onboarding.build.dotnet');
85   assertDotNetStepIsCorrectlyRendered();
86
87   //// Analysis step: Maven
88   await clickButton(user, 'onboarding.build.maven');
89   assertMavenStepIsCorrectlyRendered();
90
91   //// Analysis step: Gradle
92   await clickButton(user, 'onboarding.build.gradle');
93   assertGradleStepIsCorrectlyRendered();
94
95   //// Analysis step: C Family
96   await clickButton(user, 'onboarding.build.cfamily');
97
98   // OS's
99   await clickButton(user, `onboarding.build.other.os.${OSs.Linux}`);
100   assertCFamilyStepIsCorrectlyRendered(OSs.Linux);
101
102   await clickButton(user, `onboarding.build.other.os.${OSs.Windows}`);
103   assertCFamilyStepIsCorrectlyRendered(OSs.Windows);
104
105   await clickButton(user, `onboarding.build.other.os.${OSs.MacOS}`);
106   assertCFamilyStepIsCorrectlyRendered(OSs.MacOS);
107
108   //// Analysis step: Other
109   await clickButton(user, 'onboarding.build.other');
110   assertOtherStepIsCorrectlyRendered();
111
112   //// Finish tutorial
113   await clickButton(user, 'tutorials.finish');
114   assertFinishStepIsCorrectlyRendered();
115 });
116
117 it('allows to navigate back to a previous step', async () => {
118   renderAzurePipelinesTutorial();
119   const user = userEvent.setup();
120
121   // No clickable steps.
122   expect(
123     screen.queryByRole('button', {
124       name: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
125     })
126   ).not.toBeInTheDocument();
127
128   // Go to the next steps.
129   await goToNextStep(user);
130   await goToNextStep(user);
131
132   // The first 2 steps become clickable.
133   expect(
134     screen.getByRole('button', {
135       name: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
136     })
137   ).toBeInTheDocument();
138   expect(
139     screen.getByRole('button', {
140       name: '2 onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
141     })
142   ).toBeInTheDocument();
143
144   // Navigate back to the first step.
145   await clickButton(user, '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title');
146
147   // No more clickable steps.
148   expect(
149     screen.queryByRole('button', {
150       name: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
151     })
152   ).not.toBeInTheDocument();
153 });
154
155 it('should not offer CFamily analysis if the language is not available', async () => {
156   renderAzurePipelinesTutorial(undefined, { languages: {} });
157   const user = userEvent.setup();
158
159   // Go to the analysis step.
160   await goToNextStep(user);
161   await goToNextStep(user);
162
163   expect(screen.getByRole('button', { name: 'onboarding.build.dotnet' })).toBeInTheDocument();
164   expect(
165     screen.queryByRole('button', { name: 'onboarding.build.cfamily' })
166   ).not.toBeInTheDocument();
167 });
168
169 function assertDefaultStepIsCorrectlyRendered() {
170   expect(
171     screen.getByRole('heading', {
172       name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
173     })
174   ).toBeInTheDocument();
175 }
176
177 function assertServiceEndpointStepIsCorrectlyRendered() {
178   expect(
179     screen.getByRole('heading', {
180       name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
181     })
182   ).toBeInTheDocument();
183   expect(getCopyToClipboardValue()).toBe('https://sonarqube.example.com/');
184   expect(
185     screen.getByRole('button', { name: 'onboarding.token.generate.long' })
186   ).toBeInTheDocument();
187 }
188
189 function assertDotNetStepIsCorrectlyRendered() {
190   expect(
191     screen.getByRole('heading', {
192       name: 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title',
193     })
194   ).toBeInTheDocument();
195   expect(getCopyToClipboardValue()).toBe('foo');
196 }
197
198 function assertMavenStepIsCorrectlyRendered() {
199   expect(getCopyToClipboardValue()).toMatchSnapshot('maven, copy additional properties');
200 }
201
202 function assertGradleStepIsCorrectlyRendered() {
203   expect(getCopyToClipboardValue()).toMatchSnapshot('gradle, copy additional properties');
204 }
205
206 function assertCFamilyStepIsCorrectlyRendered(os: string) {
207   expect(getCopyToClipboardValue(0)).toMatchSnapshot(`cfamily ${os}, copy shell script`);
208   expect(getCopyToClipboardValue(1)).toBe('foo');
209   expect(getCopyToClipboardValue(2)).toMatchSnapshot(`cfamily ${os}, copy additional properties`);
210   expect(getCopyToClipboardValue(3)).toMatchSnapshot(`cfamily ${os}, copy build-wrapper command`);
211 }
212
213 function assertOtherStepIsCorrectlyRendered() {
214   expect(getCopyToClipboardValue()).toBe('foo');
215 }
216
217 function assertFinishStepIsCorrectlyRendered() {
218   expect(
219     screen.getByRole('heading', {
220       name: 'onboarding.tutorial.ci_outro.all_set.title',
221     })
222   ).toBeInTheDocument();
223 }
224
225 function renderAzurePipelinesTutorial(
226   props: Partial<AzurePipelinesTutorialProps> = {},
227   { languages = { c: mockLanguage({ key: 'c' }) } }: RenderContext = {}
228 ) {
229   return renderApp(
230     '/',
231     <AzurePipelinesTutorial
232       baseUrl="https://sonarqube.example.com/"
233       component={mockComponent({ key: 'foo' })}
234       currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Scan] } })}
235       willRefreshAutomatically={true}
236       {...props}
237     />,
238     { languages }
239   );
240 }
241
242 async function clickButton(user: UserEvent, name: string, context?: HTMLElement) {
243   if (context) {
244     await user.click(within(context).getByRole('button', { name }));
245   } else {
246     await user.click(screen.getByRole('button', { name }));
247   }
248 }
249
250 async function goToNextStep(user: UserEvent) {
251   await clickButton(user, 'continue');
252 }
253
254 function getCopyToClipboardValue(i = 0, name = 'copy_to_clipboard') {
255   return screen.getAllByRole('button', { name })[i].getAttribute('data-clipboard-text');
256 }