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