]> source.dussan.org Git - sonarqube.git/blob
6080a73c7be649702208d8c59ff278f3219306d4
[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 { RenderContext, renderApp } from '../../../../helpers/testReactTestingUtils';
28 import { Permissions } from '../../../../types/permissions';
29 import { TokenType } from '../../../../types/token';
30 import { getCopyToClipboardValue, getTutorialBuildButtons } 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
73   expect(lastToken).toBeDefined();
74
75   expect(lastToken!.type).toBe(TokenType.Global);
76   expect(
77     within(modal).getByText(`users.tokens.new_token_created.${lastToken!.token}`)
78   ).toBeInTheDocument();
79   await clickButton(user, 'continue', modal);
80
81   // Continue.
82   await goToNextStep(user);
83
84   //// Analysis step: .NET
85   await user.click(getTutorialBuildButtons().dotnetBuildButton.get());
86   assertDotNetStepIsCorrectlyRendered();
87
88   //// Analysis step: Maven
89   await user.click(getTutorialBuildButtons().mavenBuildButton.get());
90   assertMavenStepIsCorrectlyRendered();
91
92   //// Analysis step: Gradle
93   await user.click(getTutorialBuildButtons().gradleBuildButton.get());
94   assertGradleStepIsCorrectlyRendered();
95
96   //// Analysis step: C Family
97   await user.click(getTutorialBuildButtons().cFamilyBuildButton.get());
98
99   // OS's
100   await user.click(getTutorialBuildButtons().linuxButton.get());
101   assertCFamilyStepIsCorrectlyRendered(OSs.Linux);
102
103   await user.click(getTutorialBuildButtons().windowsButton.get());
104   assertCFamilyStepIsCorrectlyRendered(OSs.Windows);
105
106   await user.click(getTutorialBuildButtons().macosButton.get());
107   assertCFamilyStepIsCorrectlyRendered(OSs.MacOS);
108
109   //// Analysis step: Other
110   await user.click(getTutorialBuildButtons().otherBuildButton.get());
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: '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: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
137     })
138   ).toBeInTheDocument();
139   expect(
140     screen.getByRole('button', {
141       name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
142     })
143   ).toBeInTheDocument();
144
145   // Navigate back to the first step.
146   await clickButton(user, 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title');
147
148   // No more clickable steps.
149   expect(
150     screen.queryByRole('button', {
151       name: '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(getTutorialBuildButtons().dotnetBuildButton.get()).toBeInTheDocument();
165   expect(getTutorialBuildButtons().cFamilyBuildButton.query()).not.toBeInTheDocument();
166 });
167
168 function assertDefaultStepIsCorrectlyRendered() {
169   expect(
170     screen.getByRole('heading', {
171       name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
172     })
173   ).toBeInTheDocument();
174 }
175
176 function assertServiceEndpointStepIsCorrectlyRendered() {
177   expect(
178     screen.getByRole('heading', {
179       name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
180     })
181   ).toBeInTheDocument();
182   expect(getCopyToClipboardValue()).toBe('https://sonarqube.example.com/');
183   expect(
184     screen.getByRole('button', { name: 'onboarding.token.generate.long' })
185   ).toBeInTheDocument();
186 }
187
188 function assertDotNetStepIsCorrectlyRendered() {
189   expect(
190     screen.getByRole('heading', {
191       name: 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title',
192     })
193   ).toBeInTheDocument();
194   expect(getCopyToClipboardValue()).toBe('foo');
195 }
196
197 function assertMavenStepIsCorrectlyRendered() {
198   expect(getCopyToClipboardValue()).toMatchSnapshot('maven, copy additional properties');
199 }
200
201 function assertGradleStepIsCorrectlyRendered() {
202   expect(getCopyToClipboardValue()).toMatchSnapshot('gradle, copy additional properties');
203 }
204
205 function assertCFamilyStepIsCorrectlyRendered(os: string) {
206   expect(getCopyToClipboardValue(0)).toMatchSnapshot(`cfamily ${os}, copy shell script`);
207   expect(getCopyToClipboardValue(1)).toBe('foo');
208   expect(getCopyToClipboardValue(2)).toMatchSnapshot(`cfamily ${os}, copy additional properties`);
209   expect(getCopyToClipboardValue(3)).toMatchSnapshot(`cfamily ${os}, copy build-wrapper command`);
210 }
211
212 function assertOtherStepIsCorrectlyRendered() {
213   expect(getCopyToClipboardValue()).toBe('foo');
214 }
215
216 function assertFinishStepIsCorrectlyRendered() {
217   expect(
218     screen.getByRole('heading', {
219       name: 'onboarding.tutorial.ci_outro.all_set.title',
220     })
221   ).toBeInTheDocument();
222 }
223
224 function renderAzurePipelinesTutorial(
225   props: Partial<AzurePipelinesTutorialProps> = {},
226   { languages = { c: mockLanguage({ key: 'c' }) } }: RenderContext = {}
227 ) {
228   return renderApp(
229     '/',
230     <AzurePipelinesTutorial
231       baseUrl="https://sonarqube.example.com/"
232       component={mockComponent({ key: 'foo' })}
233       currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Scan] } })}
234       willRefreshAutomatically
235       {...props}
236     />,
237     { languages }
238   );
239 }
240
241 async function clickButton(user: UserEvent, name: string, context?: HTMLElement) {
242   if (context) {
243     await user.click(within(context).getByRole('button', { name }));
244   } else {
245     await user.click(screen.getByRole('button', { name }));
246   }
247 }
248
249 async function goToNextStep(user: UserEvent) {
250   await clickButton(user, 'continue');
251 }