3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
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';
34 jest.mock('../../../../api/user-tokens');
36 jest.mock('../../../../api/settings', () => ({
37 getAllValues: jest.fn().mockResolvedValue([]),
40 let tokenMock: UserTokensMock;
43 tokenMock = new UserTokensMock();
50 it('should render correctly and allow navigating between the different steps', async () => {
51 renderAzurePipelinesTutorial();
52 const user = userEvent.setup();
55 screen.getByRole('heading', { name: 'onboarding.tutorial.with.azure_pipelines.title' })
56 ).toBeInTheDocument();
59 assertDefaultStepIsCorrectlyRendered();
62 await goToNextStep(user);
65 assertServiceEndpointStepIsCorrectlyRendered();
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();
73 expect(lastToken).toBeDefined();
75 expect(lastToken!.type).toBe(TokenType.Global);
77 within(modal).getByText(`users.tokens.new_token_created.${lastToken!.token}`)
78 ).toBeInTheDocument();
79 await clickButton(user, 'continue', modal);
82 await goToNextStep(user);
84 //// Analysis step: .NET
85 await user.click(getTutorialBuildButtons().dotnetBuildButton.get());
86 assertDotNetStepIsCorrectlyRendered();
88 //// Analysis step: Maven
89 await user.click(getTutorialBuildButtons().mavenBuildButton.get());
90 assertMavenStepIsCorrectlyRendered();
92 //// Analysis step: Gradle
93 await user.click(getTutorialBuildButtons().gradleBuildButton.get());
94 assertGradleStepIsCorrectlyRendered();
96 //// Analysis step: C Family
97 await user.click(getTutorialBuildButtons().cFamilyBuildButton.get());
100 await user.click(getTutorialBuildButtons().linuxButton.get());
101 assertCFamilyStepIsCorrectlyRendered(OSs.Linux);
103 await user.click(getTutorialBuildButtons().windowsButton.get());
104 assertCFamilyStepIsCorrectlyRendered(OSs.Windows);
106 await user.click(getTutorialBuildButtons().macosButton.get());
107 assertCFamilyStepIsCorrectlyRendered(OSs.MacOS);
109 //// Analysis step: Other
110 await user.click(getTutorialBuildButtons().otherBuildButton.get());
111 assertOtherStepIsCorrectlyRendered();
114 await clickButton(user, 'tutorials.finish');
115 assertFinishStepIsCorrectlyRendered();
118 it('allows to navigate back to a previous step', async () => {
119 renderAzurePipelinesTutorial();
120 const user = userEvent.setup();
122 // No clickable steps.
124 screen.queryByRole('button', {
125 name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
127 ).not.toBeInTheDocument();
129 // Go to the next steps.
130 await goToNextStep(user);
131 await goToNextStep(user);
133 // The first 2 steps become clickable.
135 screen.getByRole('button', {
136 name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
138 ).toBeInTheDocument();
140 screen.getByRole('button', {
141 name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
143 ).toBeInTheDocument();
145 // Navigate back to the first step.
146 await clickButton(user, 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title');
148 // No more clickable steps.
150 screen.queryByRole('button', {
151 name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
153 ).not.toBeInTheDocument();
156 it('should not offer CFamily analysis if the language is not available', async () => {
157 renderAzurePipelinesTutorial(undefined, { languages: {} });
158 const user = userEvent.setup();
160 // Go to the analysis step.
161 await goToNextStep(user);
162 await goToNextStep(user);
164 expect(getTutorialBuildButtons().dotnetBuildButton.get()).toBeInTheDocument();
165 expect(getTutorialBuildButtons().cFamilyBuildButton.query()).not.toBeInTheDocument();
168 function assertDefaultStepIsCorrectlyRendered() {
170 screen.getByRole('heading', {
171 name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
173 ).toBeInTheDocument();
176 function assertServiceEndpointStepIsCorrectlyRendered() {
178 screen.getByRole('heading', {
179 name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
181 ).toBeInTheDocument();
182 expect(getCopyToClipboardValue()).toBe('https://sonarqube.example.com/');
184 screen.getByRole('button', { name: 'onboarding.token.generate.long' })
185 ).toBeInTheDocument();
188 function assertDotNetStepIsCorrectlyRendered() {
190 screen.getByRole('heading', {
191 name: 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title',
193 ).toBeInTheDocument();
194 expect(getCopyToClipboardValue()).toBe('foo');
197 function assertMavenStepIsCorrectlyRendered() {
198 expect(getCopyToClipboardValue()).toMatchSnapshot('maven, copy additional properties');
201 function assertGradleStepIsCorrectlyRendered() {
202 expect(getCopyToClipboardValue()).toMatchSnapshot('gradle, copy additional properties');
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`);
212 function assertOtherStepIsCorrectlyRendered() {
213 expect(getCopyToClipboardValue()).toBe('foo');
216 function assertFinishStepIsCorrectlyRendered() {
218 screen.getByRole('heading', {
219 name: 'onboarding.tutorial.ci_outro.all_set.title',
221 ).toBeInTheDocument();
224 function renderAzurePipelinesTutorial(
225 props: Partial<AzurePipelinesTutorialProps> = {},
226 { languages = { c: mockLanguage({ key: 'c' }) } }: RenderContext = {}
230 <AzurePipelinesTutorial
231 baseUrl="https://sonarqube.example.com/"
232 component={mockComponent({ key: 'foo' })}
233 currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Scan] } })}
234 willRefreshAutomatically
241 async function clickButton(user: UserEvent, name: string, context?: HTMLElement) {
243 await user.click(within(context).getByRole('button', { name }));
245 await user.click(screen.getByRole('button', { name }));
249 async function goToNextStep(user: UserEvent) {
250 await clickButton(user, 'continue');