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 } 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 clickButton(user, 'onboarding.build.dotnet');
86 assertDotNetStepIsCorrectlyRendered();
88 //// Analysis step: Maven
89 await clickButton(user, 'onboarding.build.maven');
90 assertMavenStepIsCorrectlyRendered();
92 //// Analysis step: Gradle
93 await clickButton(user, 'onboarding.build.gradle');
94 assertGradleStepIsCorrectlyRendered();
96 //// Analysis step: C Family
97 await clickButton(user, 'onboarding.build.cfamily');
100 await clickButton(user, `onboarding.build.other.os.${OSs.Linux}`);
101 assertCFamilyStepIsCorrectlyRendered(OSs.Linux);
103 await clickButton(user, `onboarding.build.other.os.${OSs.Windows}`);
104 assertCFamilyStepIsCorrectlyRendered(OSs.Windows);
106 await clickButton(user, `onboarding.build.other.os.${OSs.MacOS}`);
107 assertCFamilyStepIsCorrectlyRendered(OSs.MacOS);
109 //// Analysis step: Other
110 await clickButton(user, 'onboarding.build.other');
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: '1 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: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
138 ).toBeInTheDocument();
140 screen.getByRole('button', {
141 name: '2 onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
143 ).toBeInTheDocument();
145 // Navigate back to the first step.
146 await clickButton(user, '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title');
148 // No more clickable steps.
150 screen.queryByRole('button', {
151 name: '1 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(screen.getByRole('button', { name: 'onboarding.build.dotnet' })).toBeInTheDocument();
166 screen.queryByRole('button', { name: 'onboarding.build.cfamily' })
167 ).not.toBeInTheDocument();
170 function assertDefaultStepIsCorrectlyRendered() {
172 screen.getByRole('heading', {
173 name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title',
175 ).toBeInTheDocument();
178 function assertServiceEndpointStepIsCorrectlyRendered() {
180 screen.getByRole('heading', {
181 name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title',
183 ).toBeInTheDocument();
184 expect(getCopyToClipboardValue()).toBe('https://sonarqube.example.com/');
186 screen.getByRole('button', { name: 'onboarding.token.generate.long' })
187 ).toBeInTheDocument();
190 function assertDotNetStepIsCorrectlyRendered() {
192 screen.getByRole('heading', {
193 name: 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title',
195 ).toBeInTheDocument();
196 expect(getCopyToClipboardValue()).toBe('foo');
199 function assertMavenStepIsCorrectlyRendered() {
200 expect(getCopyToClipboardValue()).toMatchSnapshot('maven, copy additional properties');
203 function assertGradleStepIsCorrectlyRendered() {
204 expect(getCopyToClipboardValue()).toMatchSnapshot('gradle, copy additional properties');
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`);
214 function assertOtherStepIsCorrectlyRendered() {
215 expect(getCopyToClipboardValue()).toBe('foo');
218 function assertFinishStepIsCorrectlyRendered() {
220 screen.getByRole('heading', {
221 name: 'onboarding.tutorial.ci_outro.all_set.title',
223 ).toBeInTheDocument();
226 function renderAzurePipelinesTutorial(
227 props: Partial<AzurePipelinesTutorialProps> = {},
228 { languages = { c: mockLanguage({ key: 'c' }) } }: RenderContext = {}
232 <AzurePipelinesTutorial
233 baseUrl="https://sonarqube.example.com/"
234 component={mockComponent({ key: 'foo' })}
235 currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Scan] } })}
236 willRefreshAutomatically
243 async function clickButton(user: UserEvent, name: string, context?: HTMLElement) {
245 await user.click(within(context).getByRole('button', { name }));
247 await user.click(screen.getByRole('button', { name }));
251 async function goToNextStep(user: UserEvent) {
252 await clickButton(user, 'continue');