]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17188 SONAR-17189 Generate a global analysis token when following the Azure...
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Tue, 27 Sep 2022 07:31:08 +0000 (09:31 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 29 Sep 2022 20:03:14 +0000 (20:03 +0000)
52 files changed:
server/sonar-web/__mocks__/react-intl.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/api/mocks/UserTokensMock.ts
server/sonar-web/src/main/js/apps/account/__tests__/Account-it.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ExtensionInstallationStepContent.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/JavaToolInstallation-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/JavaToolInstallation-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/AlertClassicEditor-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/AnalysisCommand-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/ClangGCC-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/DotNet-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/JavaGradle-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/JavaMaven-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/Other-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/PrepareAnalysisCommand-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/PublishSteps-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/AlertClassicEditor-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/PrepareAnalysisCommand-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/PublishSteps-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/components/AllSetStep.tsx
server/sonar-web/src/main/js/components/tutorials/components/EditTokenModal.tsx
server/sonar-web/src/main/js/components/tutorials/components/ProjectTokenScopeInfo.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/Step.tsx
server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx
server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSetStep-test.tsx [deleted file]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/EditTokenModal-test.tsx
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/EditTokenModal-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/other/TokenStep.tsx
server/sonar-web/src/main/js/components/tutorials/other/__tests__/__snapshots__/TokenStep-test.tsx.snap
server/sonar-web/src/main/js/helpers/testReactTestingUtils.tsx
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/__mocks__/react-intl.tsx b/server/sonar-web/__mocks__/react-intl.tsx
new file mode 100644 (file)
index 0000000..888162e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+
+module.exports = {
+  ...jest.requireActual('react-intl'),
+  FormattedMessage: ({ id, values }: { id: string; values: { [x: string]: React.ReactNode } }) => {
+    return (
+      <>
+        {id}
+        {Object.entries(values).map(([key, value]) => (
+          <React.Fragment key={key}>{value}</React.Fragment>
+        ))}
+      </>
+    );
+  }
+};
index a50e62582cef0b5ee03ff44ec0f4e13726bf9c54..9a10d6e29e02dbaeb733ba47adc02319656f7800 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { cloneDeep } from 'lodash';
+import { cloneDeep, last } from 'lodash';
 import { mockUserToken } from '../../helpers/mocks/token';
 import { NewUserToken, TokenType, UserToken } from '../../types/token';
 import { generateToken, getTokens, revokeToken } from '../user-tokens';
@@ -100,6 +100,10 @@ export default class UserTokensMock {
     return cloneDeep(this.tokens);
   };
 
+  getLastToken = () => {
+    return last(this.getTokens());
+  };
+
   reset = () => {
     this.tokens = cloneDeep(defaultTokens);
   };
index a9bec509b0f28c5eae6b7c4b493083207fed8af9..217f18dbbbf7f912f3b7bba8b260037dddfe6698 100644 (file)
@@ -306,9 +306,11 @@ describe('security page', () => {
       ).toBeInTheDocument();
       expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
 
-      const lastTokenCreated = tokenMock.getTokens().pop();
-      expect(lastTokenCreated).toBeDefined();
-      expect(screen.getByLabelText('users.new_token').textContent).toBe(lastTokenCreated!.token);
+      const lastTokenCreated = tokenMock.getLastToken();
+      if (lastTokenCreated === undefined) {
+        throw new Error("Couldn't find the latest generated token.");
+      }
+      expect(screen.getByLabelText('users.new_token').textContent).toBe(lastTokenCreated.token);
 
       expect(screen.getAllByRole('row')).toHaveLength(4); // 3 tokens + header
 
index 84d74c3840f33267936f983ee9fbe6c903e7fb28..bb8f74738d66a6a8f631722c511e8388adbd267b 100644 (file)
@@ -21,7 +21,6 @@ exports[`it should render correctly for azure 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
           id="settings.pr_decoration.binding.form.azure.project.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -69,7 +68,6 @@ exports[`it should render correctly for azure 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
           id="settings.pr_decoration.binding.form.azure.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -122,7 +120,6 @@ exports[`it should render correctly for bitbucket 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
           id="settings.pr_decoration.binding.form.bitbucket.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -172,7 +169,6 @@ exports[`it should render correctly for bitbucket 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
           id="settings.pr_decoration.binding.form.bitbucket.slug.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -227,7 +223,6 @@ exports[`it should render correctly for bitbucket if an instance URL is provided
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
           id="settings.pr_decoration.binding.form.bitbucket.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -277,7 +272,6 @@ exports[`it should render correctly for bitbucket if an instance URL is provided
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
           id="settings.pr_decoration.binding.form.bitbucket.slug.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -332,7 +326,6 @@ exports[`it should render correctly for bitbucketcloud 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
           id="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -386,7 +379,6 @@ exports[`it should render correctly for github 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
           id="settings.pr_decoration.binding.form.github.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -434,7 +426,6 @@ exports[`it should render correctly for github 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
           id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-          values={Object {}}
         />
       </div>
     </div>
@@ -480,7 +471,6 @@ exports[`it should render correctly for github if an instance URL is provided 1`
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
           id="settings.pr_decoration.binding.form.github.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -528,7 +518,6 @@ exports[`it should render correctly for github if an instance URL is provided 1`
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
           id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-          values={Object {}}
         />
       </div>
     </div>
@@ -574,7 +563,6 @@ exports[`it should render correctly for github if an instance URL is provided 2`
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
           id="settings.pr_decoration.binding.form.github.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -622,7 +610,6 @@ exports[`it should render correctly for github if an instance URL is provided 2`
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
           id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-          values={Object {}}
         />
       </div>
     </div>
@@ -668,7 +655,6 @@ exports[`it should render correctly for gitlab 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.gitlab.repository.help"
           id="settings.pr_decoration.binding.form.gitlab.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -721,7 +707,6 @@ exports[`should render the monorepo field when the feature is supported 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
           id="settings.pr_decoration.binding.form.azure.project.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
@@ -769,7 +754,6 @@ exports[`should render the monorepo field when the feature is supported 1`] = `
         <FormattedMessage
           defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
           id="settings.pr_decoration.binding.form.azure.repository.help"
-          values={Object {}}
         />
         <div
           className="spacer-top nowrap"
index 70200e4d080fb0287f31a2e399427dbca217b836..0579b592fa6c04332090197581c03410bb541e97 100644 (file)
@@ -58,11 +58,13 @@ export function BranchAnalysisStepContent(props: BranchesAnalysisStepProps) {
         optionLabelKey="onboarding.build"
         options={buildToolsList}
       />
-      <AnalysisCommand
-        onStepValidationChange={onStepValidationChange}
-        buildTool={buildTechnology}
-        projectKey={component.key}
-      />
+      <div data-testid="azure-tutorial__analysis-command">
+        <AnalysisCommand
+          onStepValidationChange={onStepValidationChange}
+          buildTool={buildTechnology}
+          projectKey={component.key}
+        />
+      </div>
     </>
   );
 }
index eff9bb3e535459b050135f3a98283d9113b649a7..2b6271bc5d8fa7b6a546bfbfb4e58a7bedf059a6 100644 (file)
@@ -24,7 +24,7 @@ import Link from '../../common/Link';
 
 export default function ExtensionInstallationStepContent() {
   return (
-    <span>
+    <span data-testid="azure-tutorial__extension">
       <FormattedMessage
         defaultMessage={translate(
           'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence'
index 00a9cd4e40e15f506045f78bbc7082d2c2c953fd..675744a3b6b525193284f53dccac4e989e1e759e 100644 (file)
@@ -22,6 +22,7 @@ import { FormattedMessage } from 'react-intl';
 import { Button } from '../../../components/controls/buttons';
 import { ClipboardIconButton } from '../../../components/controls/clipboard';
 import { translate } from '../../../helpers/l10n';
+import { TokenType } from '../../../types/token';
 import { Component } from '../../../types/types';
 import { LoggedInUser } from '../../../types/users';
 import EditTokenModal from '../components/EditTokenModal';
@@ -40,7 +41,7 @@ export default function ServiceEndpointStepContent(props: ServiceEndpointStepPro
 
   return (
     <>
-      <ol className="list-styled">
+      <ol className="list-styled" data-testid="azure-tutorial__service-endpoint">
         <li>
           <SentenceWithHighlights
             translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1"
@@ -70,7 +71,7 @@ export default function ServiceEndpointStepContent(props: ServiceEndpointStepPro
             {translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence')}:
           </span>
           <Button className="spacer-left" onClick={() => toggleModal(true)}>
-            {translate('onboarding.token.generate_token')}
+            {translate('onboarding.token.generate.long')}
           </Button>
         </li>
         <li>
@@ -86,6 +87,7 @@ export default function ServiceEndpointStepContent(props: ServiceEndpointStepPro
           component={component}
           currentUser={currentUser}
           onClose={() => toggleModal(false)}
+          preferredTokenType={TokenType.Global}
         />
       )}
     </>
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-it.tsx
new file mode 100644 (file)
index 0000000..365c2e1
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { screen, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { UserEvent } from '@testing-library/user-event/dist/types/setup';
+import * as React from 'react';
+import UserTokensMock from '../../../../api/mocks/UserTokensMock';
+import { mockComponent } from '../../../../helpers/mocks/component';
+import { mockAppState, mockLanguage, mockLoggedInUser } from '../../../../helpers/testMocks';
+import { renderApp, RenderContext } from '../../../../helpers/testReactTestingUtils';
+import { Permissions } from '../../../../types/permissions';
+import { TokenType } from '../../../../types/token';
+import AzurePipelinesTutorial, { AzurePipelinesTutorialProps } from '../AzurePipelinesTutorial';
+
+jest.mock('../../../../api/user-tokens');
+
+jest.mock('../../../../api/settings', () => ({
+  getAllValues: jest.fn().mockResolvedValue([])
+}));
+
+let tokenMock: UserTokensMock;
+
+beforeAll(() => {
+  tokenMock = new UserTokensMock();
+});
+
+afterEach(() => {
+  tokenMock.reset();
+});
+
+it('should render correctly and allow navigating between the different steps', async () => {
+  renderAzurePipelinesTutorial();
+  const user = userEvent.setup();
+
+  expect(
+    screen.getByRole('heading', { name: 'onboarding.tutorial.with.azure_pipelines.title' })
+  ).toBeInTheDocument();
+
+  //// Default step.
+  assertDefaultStepIsCorrectlyRendered();
+
+  // Continue.
+  await goToNextStep(user);
+
+  //// Token step.
+  assertServiceEndpointStepIsCorrectlyRendered();
+
+  // Generate a token.
+  await clickButton(user, 'onboarding.token.generate.long');
+  const modal = screen.getByRole('dialog');
+  await clickButton(user, 'onboarding.token.generate', modal);
+  const lastToken = tokenMock.getLastToken();
+  if (lastToken === undefined) {
+    throw new Error("Couldn't find the latest generated token.");
+  }
+  expect(lastToken.type).toBe(TokenType.Global);
+  expect(within(modal).getByRole('alert')).toHaveTextContent(
+    `users.tokens.new_token_created.${lastToken.token}`
+  );
+  await clickButton(user, 'continue', modal);
+
+  // Continue.
+  await goToNextStep(user);
+
+  //// Analysis step: .NET
+  await clickButton(user, 'onboarding.build.dotnet');
+  assertDotNetStepIsCorrectlyRendered();
+
+  //// Analysis step: Maven
+  await clickButton(user, 'onboarding.build.maven');
+  assertMavenStepIsCorrectlyRendered();
+
+  //// Analysis step: Gradle
+  await clickButton(user, 'onboarding.build.gradle');
+  assertGradleStepIsCorrectlyRendered();
+
+  //// Analysis step: Gradle
+  await clickButton(user, 'onboarding.build.gradle');
+  assertGradleStepIsCorrectlyRendered();
+
+  //// Analysis step: C Family
+  await clickButton(user, 'onboarding.build.cfamily');
+
+  // OS: Linux
+  await clickButton(user, 'onboarding.build.other.os.linux');
+  assertCFamilyLinuxStepIsCorrectlyRendered();
+
+  // OS: Windows
+  await clickButton(user, 'onboarding.build.other.os.win');
+  assertCFamilyWindowsStepIsCorrectlyRendered();
+
+  // OS: macOS
+  await clickButton(user, 'onboarding.build.other.os.mac');
+  assertCFamilyMacOSStepIsCorrectlyRendered();
+
+  //// Analysis step: Other
+  await clickButton(user, 'onboarding.build.other');
+  assertOtherStepIsCorrectlyRendered();
+
+  //// Finish tutorial
+  await clickButton(user, 'tutorials.finish');
+  assertFinishStepIsCorrectlyRendered();
+});
+
+it('allows to navigate back to a previous step', async () => {
+  renderAzurePipelinesTutorial();
+  const user = userEvent.setup();
+
+  // No clickable steps.
+  expect(
+    screen.queryByRole('button', {
+      name: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title'
+    })
+  ).not.toBeInTheDocument();
+
+  // Go to the next steps.
+  await goToNextStep(user);
+  await goToNextStep(user);
+
+  // The first 2 steps become clickable.
+  expect(
+    screen.getByRole('button', {
+      name: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title'
+    })
+  ).toBeInTheDocument();
+  expect(
+    screen.getByRole('button', {
+      name: '2 onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title'
+    })
+  ).toBeInTheDocument();
+
+  // Navigate back to the first step.
+  await clickButton(user, '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title');
+
+  // No more clickable steps.
+  expect(
+    screen.queryByRole('button', {
+      name: '1 onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title'
+    })
+  ).not.toBeInTheDocument();
+});
+
+it('should not offer CFamily analysis if the language is not available', async () => {
+  renderAzurePipelinesTutorial(undefined, { languages: {} });
+  const user = userEvent.setup();
+
+  // Go to the analysis step.
+  await goToNextStep(user);
+  await goToNextStep(user);
+
+  expect(screen.getByRole('button', { name: 'onboarding.build.dotnet' })).toBeInTheDocument();
+  expect(
+    screen.queryByRole('button', { name: 'onboarding.build.cfamily' })
+  ).not.toBeInTheDocument();
+});
+
+function renderAzurePipelinesTutorial(
+  props: Partial<AzurePipelinesTutorialProps> = {},
+  {
+    appState = mockAppState({ branchesEnabled: true }),
+    languages = { c: mockLanguage({ key: 'c' }) }
+  }: RenderContext = {}
+) {
+  return renderApp(
+    '/',
+    <AzurePipelinesTutorial
+      baseUrl="http://localhost:9000"
+      component={mockComponent()}
+      currentUser={mockLoggedInUser({ permissions: { global: [Permissions.Scan] } })}
+      willRefreshAutomatically={true}
+      {...props}
+    />,
+    { appState, languages }
+  );
+}
+
+async function clickButton(user: UserEvent, name: string, context?: HTMLElement) {
+  if (context) {
+    await user.click(within(context).getByRole('button', { name }));
+  } else {
+    await user.click(screen.getByRole('button', { name }));
+  }
+}
+
+async function goToNextStep(user: UserEvent) {
+  await clickButton(user, 'continue');
+}
+
+function assertDefaultStepIsCorrectlyRendered() {
+  expect(
+    screen.getByRole('heading', {
+      name: 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title'
+    })
+  ).toBeInTheDocument();
+  expect(screen.getByTestId('azure-tutorial__extension')).toMatchSnapshot('extension step');
+}
+
+function assertServiceEndpointStepIsCorrectlyRendered() {
+  expect(
+    screen.getByRole('heading', {
+      name: 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title'
+    })
+  ).toBeInTheDocument();
+  expect(screen.getByTestId('azure-tutorial__service-endpoint')).toMatchSnapshot(
+    'service endpoint step'
+  );
+  expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
+  expect(
+    screen.getByRole('button', { name: 'onboarding.token.generate.long' })
+  ).toBeInTheDocument();
+}
+
+function assertDotNetStepIsCorrectlyRendered() {
+  expect(
+    screen.getByRole('heading', {
+      name: 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title'
+    })
+  ).toBeInTheDocument();
+
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot('dotnet step');
+  expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
+}
+
+function assertMavenStepIsCorrectlyRendered() {
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot('maven step');
+  expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
+}
+
+function assertGradleStepIsCorrectlyRendered() {
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot('gradle step');
+  expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
+}
+
+function assertCFamilyLinuxStepIsCorrectlyRendered() {
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot(
+    'cfamily linux step'
+  );
+  expect(screen.getAllByRole('button', { name: 'copy_to_clipboard' })).toHaveLength(4);
+}
+
+function assertCFamilyWindowsStepIsCorrectlyRendered() {
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot(
+    'cfamily windows step'
+  );
+  expect(screen.getAllByRole('button', { name: 'copy_to_clipboard' })).toHaveLength(4);
+}
+
+function assertCFamilyMacOSStepIsCorrectlyRendered() {
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot(
+    'cfamily macos step'
+  );
+  expect(screen.getAllByRole('button', { name: 'copy_to_clipboard' })).toHaveLength(4);
+}
+
+function assertOtherStepIsCorrectlyRendered() {
+  expect(screen.getByTestId('azure-tutorial__analysis-command')).toMatchSnapshot('other step');
+  expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
+}
+
+function assertFinishStepIsCorrectlyRendered() {
+  expect(
+    screen.getByRole('heading', {
+      name: 'onboarding.tutorial.ci_outro.all_set.title'
+    })
+  ).toBeInTheDocument();
+  expect(screen.getByTestId('azure-tutorial__all-set')).toMatchSnapshot('all set step');
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx
deleted file mode 100644 (file)
index 495db2d..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { Button } from '../../../../components/controls/buttons';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockLoggedInUser } from '../../../../helpers/testMocks';
-import { click } from '../../../../helpers/testUtils';
-import Step from '../../components/Step';
-import AzurePipelinesTutorial, { AzurePipelinesTutorialProps } from '../AzurePipelinesTutorial';
-
-it('should render correctly', () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.find(Step).forEach(step => {
-    expect(step.dive()).toMatchSnapshot();
-  });
-});
-
-it('should display the next step when one is finished', () => {
-  const wrapper = shallowRender();
-  expect(
-    wrapper
-      .find(Step)
-      .filterWhere(elt => elt.props().open === true)
-      .props().stepNumber
-  ).toBe(1);
-
-  click(
-    wrapper
-      .find(Step)
-      .first()
-      .dive()
-      .find(Button)
-  );
-
-  expect(
-    wrapper
-      .find(Step)
-      .filterWhere(elt => elt.props().open === true)
-      .props().stepNumber
-  ).toBe(2);
-});
-
-it('should open a step when user click on it', () => {
-  const wrapper = shallowRender();
-  expect(
-    wrapper
-      .find(Step)
-      .filterWhere(elt => elt.props().open === true)
-      .props().stepNumber
-  ).toBe(1);
-
-  (
-    wrapper
-      .find(Step)
-      .filterWhere(elt => elt.props().stepNumber === 3)
-      .props().onOpen ?? (() => {})
-  )();
-
-  expect(
-    wrapper
-      .find(Step)
-      .filterWhere(elt => elt.props().open === true)
-      .props().stepNumber
-  ).toBe(3);
-});
-
-function shallowRender(props: Partial<AzurePipelinesTutorialProps> = {}) {
-  return shallow<AzurePipelinesTutorialProps>(
-    <AzurePipelinesTutorial
-      baseUrl="http://localhost:9000"
-      component={mockComponent()}
-      currentUser={mockLoggedInUser()}
-      willRefreshAutomatically={true}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx
deleted file mode 100644 (file)
index 551ab13..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import RenderOptions from '../../components/RenderOptions';
-import { BuildTools } from '../../types';
-import { BranchAnalysisStepContent, BranchesAnalysisStepProps } from '../BranchAnalysisStepContent';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ languages: {} })).toMatchSnapshot('without C');
-});
-
-it.each([BuildTools.DotNet, BuildTools.Gradle, BuildTools.Maven, BuildTools.Other])(
-  'should render correctly',
-  (buildTech: BuildTools) => {
-    const wrapper = shallowRender();
-    wrapper
-      .find(RenderOptions)
-      .props()
-      .onCheck(buildTech);
-
-    expect(wrapper).toMatchSnapshot(buildTech);
-  }
-);
-
-function shallowRender(props: Partial<BranchesAnalysisStepProps> = {}) {
-  return shallow(
-    <BranchAnalysisStepContent
-      languages={{ c: { key: 'c', name: 'test' } }}
-      component={mockComponent()}
-      onStepValidationChange={jest.fn()}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx
deleted file mode 100644 (file)
index 06a39f1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import ExtensionInstallationStepContent from '../ExtensionInstallationStepContent';
-
-it('should render correctly', () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender() {
-  return shallow(<ExtensionInstallationStepContent />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/JavaToolInstallation-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/JavaToolInstallation-test.tsx
deleted file mode 100644 (file)
index c10228d..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import JavaToolInstallation from '../JavaToolInstallation';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
-  return shallow(<JavaToolInstallation />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx
deleted file mode 100644 (file)
index ba0b4bb..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { Button } from '../../../../components/controls/buttons';
-import { mockComponent } from '../../../../helpers/mocks/component';
-import { mockLoggedInUser } from '../../../../helpers/testMocks';
-import { click } from '../../../../helpers/testUtils';
-import EditTokenModal from '../../components/EditTokenModal';
-import ServiceEndpointStepContent from '../ServiceEndpointStepContent';
-
-it('should render correctly', () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should render open/close the token modal when asked to', () => {
-  const wrapper = shallowRender();
-  expect(wrapper.exists(EditTokenModal)).toBe(false);
-
-  click(wrapper.find(Button));
-  expect(wrapper.exists(EditTokenModal)).toBe(true);
-
-  wrapper
-    .find(EditTokenModal)
-    .props()
-    .onClose();
-  expect(wrapper.exists(EditTokenModal)).toBe(false);
-});
-
-function shallowRender() {
-  return shallow(
-    <ServiceEndpointStepContent
-      baseUrl="http://localhost:9000"
-      component={mockComponent()}
-      currentUser={mockLoggedInUser()}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-it.tsx.snap
new file mode 100644 (file)
index 0000000..8d91963
--- /dev/null
@@ -0,0 +1,3340 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly and allow navigating between the different steps: all set step 1`] = `
+<div
+  class="boxed-group-inner"
+  data-testid="azure-tutorial__all-set"
+>
+  <div
+    class="abs-width-600"
+  >
+    <p
+      class="big-spacer-bottom"
+    >
+      onboarding.tutorial.ci_outro.all_set.sentence
+      <strong>
+        onboarding.tutorial.ci_outro.all_set.sentence.all_set
+      </strong>
+    </p>
+    <div
+      class="display-flex-row big-spacer-bottom"
+    >
+      <div>
+        <img
+          alt=""
+          class="big-spacer-right"
+          src="/images/tutorials/commit.svg"
+          width="30"
+        />
+      </div>
+      <div>
+        <p
+          class="little-spacer-bottom"
+        >
+          <strong>
+            onboarding.tutorial.ci_outro.commit
+          </strong>
+        </p>
+        <p>
+          onboarding.tutorial.ci_outro.commit.why.azure
+        </p>
+      </div>
+    </div>
+    <div
+      class="display-flex-row"
+    >
+      <div>
+        <img
+          alt=""
+          class="big-spacer-right"
+          src="/images/tutorials/refresh.svg"
+          width="30"
+        />
+      </div>
+      <div>
+        <p
+          class="little-spacer-bottom"
+        >
+          <strong>
+            onboarding.tutorial.ci_outro.refresh
+          </strong>
+        </p>
+        <p>
+          onboarding.tutorial.ci_outro.refresh.why
+        </p>
+      </div>
+    </div>
+  </div>
+  <div
+    class="huge-spacer-bottom huge-spacer-top big-padded-top text-muted display-flex-center display-flex-justify-center"
+  >
+    <i
+      class="spinner spacer-right"
+    />
+    onboarding.tutorial.ci_outro.waiting_for_fist_analysis
+  </div>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: cfamily linux step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <span
+    class="big-spacer-top display-block"
+  >
+    onboarding.tutorial.with.azure_pipelines.os
+  </span>
+  <div
+    class="big-spacer-top"
+  >
+    <ul
+      aria-label="onboarding.tutorial.with.azure_pipelines.os"
+      class="button-toggle"
+      role="group"
+    >
+      <li>
+        <button
+          aria-current="true"
+          class="button selected"
+          data-value="linux"
+          type="button"
+        >
+          onboarding.build.other.os.linux
+        </button>
+      </li>
+      <li>
+        <button
+          aria-current="false"
+          class="button"
+          data-value="win"
+          type="button"
+        >
+          onboarding.build.other.os.win
+        </button>
+      </li>
+      <li>
+        <button
+          aria-current="false"
+          class="button"
+          data-value="mac"
+          type="button"
+        >
+          onboarding.build.other.os.mac
+        </button>
+      </li>
+    </ul>
+  </div>
+  <div
+    class="github-cfamily-example-repositories-box big-padded boxed-group big-spacer-top abs-width-600"
+  >
+    <div
+      class="display-flex-center"
+    >
+      <img
+        alt=""
+        class="spacer-right"
+        height="20"
+        src="/images/alm/github.svg"
+      />
+      <a
+        class="spacer-left big"
+        href="https://github.com/orgs/sonarsource-cfamily-examples/repositories?q=sq+linux+azure"
+        rel="noopener noreferrer"
+        target="_blank"
+      >
+        <svg
+          aria-label="opens_in_new_window"
+          class="little-spacer-right"
+          height="14"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="14"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M12 9.25v2.5A2.25 2.25 0 0 1 9.75 14h-6.5A2.25 2.25 0 0 1 1 11.75v-6.5A2.25 2.25 0 0 1 3.25 3h5.5c.14 0 .25.11.25.25v.5c0 .14-.11.25-.25.25h-5.5C2.562 4 2 4.563 2 5.25v6.5c0 .688.563 1.25 1.25 1.25h6.5c.688 0 1.25-.563 1.25-1.25v-2.5c0-.14.11-.25.25-.25h.5c.14 0 .25.11.25.25zm3-6.75v4c0 .273-.227.5-.5.5a.497.497 0 0 1-.352-.148l-1.375-1.375L7.68 10.57a.27.27 0 0 1-.18.078.27.27 0 0 1-.18-.078l-.89-.89a.27.27 0 0 1-.078-.18.27.27 0 0 1 .078-.18l5.093-5.093-1.375-1.375A.497.497 0 0 1 10 2.5c0-.273.227-.5.5-.5h4c.273 0 .5.227.5.5z"
+            style="fill: currentColor;"
+          />
+        </svg>
+        sonarsource-cfamily-examples
+      </a>
+    </div>
+    <p
+      class="spacer-top"
+    >
+      onboarding.tutorial.cfamily.examples_repositories_description
+    </p>
+  </div>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.nix.sentence.task
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.nix.sentence.inline
+        </strong>
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            curl 'http://localhost/static/cpp/build-wrapper-linux-x86.zip' --output build-wrapper.zip
+unzip build-wrapper.zip
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="curl 'http://localhost/static/cpp/build-wrapper-linux-x86.zip' --output build-wrapper.zip
+unzip build-wrapper.zip"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.cfamily
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence.mode
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
+        </b>
+        <code
+          class="rule"
+        >
+          my-project
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="my-project"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.advanced
+        </b>
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.additional
+        </b>
+        <code
+          class="rule"
+        >
+          sonar.cfamily.build-wrapper-output=bw-output
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="sonar.cfamily.build-wrapper-output=bw-output"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp.sentence.task
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp.sentence.build_wrapper
+        </strong>
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            ./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output &lt;your build command here&gt;
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output <your build command here>"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+        <div
+          aria-label="alert.tooltip.info"
+          class="alert spacer-top spacer-bottom emotion-3"
+          role="status"
+        >
+          <div
+            class="emotion-2"
+          >
+            <div
+              class="emotion-0"
+            >
+              <svg
+                height="16"
+                style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+                version="1.1"
+                viewBox="0 0 16 16"
+                width="16"
+                xml:space="preserve"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+              >
+                <path
+                  d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                  style="fill: #0271b9;"
+                />
+              </svg>
+            </div>
+            <div
+              class="alert-content emotion-1"
+            >
+              <p
+                class="spacer-bottom"
+              >
+                onboarding.tutorial.cfamilly.compilation_database_info
+                <a
+                  href="/documentation/analysis/languages/cfamily/"
+                  rel="noopener noreferrer"
+                  target="_blank"
+                >
+                  onboarding.tutorial.cfamilly.compilation_database_info.link
+                </a>
+              </p>
+              <p>
+                onboarding.tutorial.cfamilly.speed_caching
+                <a
+                  href="/documentation/analysis/languages/cfamily/#analysis-cache"
+                  rel="noopener noreferrer"
+                  target="_blank"
+                >
+                  onboarding.tutorial.cfamilly.speed_caching.link
+                </a>
+              </p>
+            </div>
+          </div>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after
+      </strong>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: cfamily macos step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <span
+    class="big-spacer-top display-block"
+  >
+    onboarding.tutorial.with.azure_pipelines.os
+  </span>
+  <div
+    class="big-spacer-top"
+  >
+    <ul
+      aria-label="onboarding.tutorial.with.azure_pipelines.os"
+      class="button-toggle"
+      role="group"
+    >
+      <li>
+        <button
+          aria-current="false"
+          class="button"
+          data-value="linux"
+          type="button"
+        >
+          onboarding.build.other.os.linux
+        </button>
+      </li>
+      <li>
+        <button
+          aria-current="false"
+          class="button"
+          data-value="win"
+          type="button"
+        >
+          onboarding.build.other.os.win
+        </button>
+      </li>
+      <li>
+        <button
+          aria-current="true"
+          class="button selected"
+          data-value="mac"
+          type="button"
+        >
+          onboarding.build.other.os.mac
+        </button>
+      </li>
+    </ul>
+  </div>
+  <div
+    class="github-cfamily-example-repositories-box big-padded boxed-group big-spacer-top abs-width-600"
+  >
+    <div
+      class="display-flex-center"
+    >
+      <img
+        alt=""
+        class="spacer-right"
+        height="20"
+        src="/images/alm/github.svg"
+      />
+      <a
+        class="spacer-left big"
+        href="https://github.com/orgs/sonarsource-cfamily-examples/repositories?q=sq+macos+azure"
+        rel="noopener noreferrer"
+        target="_blank"
+      >
+        <svg
+          aria-label="opens_in_new_window"
+          class="little-spacer-right"
+          height="14"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="14"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M12 9.25v2.5A2.25 2.25 0 0 1 9.75 14h-6.5A2.25 2.25 0 0 1 1 11.75v-6.5A2.25 2.25 0 0 1 3.25 3h5.5c.14 0 .25.11.25.25v.5c0 .14-.11.25-.25.25h-5.5C2.562 4 2 4.563 2 5.25v6.5c0 .688.563 1.25 1.25 1.25h6.5c.688 0 1.25-.563 1.25-1.25v-2.5c0-.14.11-.25.25-.25h.5c.14 0 .25.11.25.25zm3-6.75v4c0 .273-.227.5-.5.5a.497.497 0 0 1-.352-.148l-1.375-1.375L7.68 10.57a.27.27 0 0 1-.18.078.27.27 0 0 1-.18-.078l-.89-.89a.27.27 0 0 1-.078-.18.27.27 0 0 1 .078-.18l5.093-5.093-1.375-1.375A.497.497 0 0 1 10 2.5c0-.273.227-.5.5-.5h4c.273 0 .5.227.5.5z"
+            style="fill: currentColor;"
+          />
+        </svg>
+        sonarsource-cfamily-examples
+      </a>
+    </div>
+    <p
+      class="spacer-top"
+    >
+      onboarding.tutorial.cfamily.examples_repositories_description
+    </p>
+  </div>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.nix.sentence.task
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.nix.sentence.inline
+        </strong>
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            curl 'http://localhost/static/cpp/build-wrapper-macosx-x86.zip' --output build-wrapper.zip
+unzip build-wrapper.zip
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="curl 'http://localhost/static/cpp/build-wrapper-macosx-x86.zip' --output build-wrapper.zip
+unzip build-wrapper.zip"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.cfamily
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence.mode
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
+        </b>
+        <code
+          class="rule"
+        >
+          my-project
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="my-project"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.advanced
+        </b>
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.additional
+        </b>
+        <code
+          class="rule"
+        >
+          sonar.cfamily.build-wrapper-output=bw-output
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="sonar.cfamily.build-wrapper-output=bw-output"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp.sentence.task
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp.sentence.build_wrapper
+        </strong>
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            ./build-wrapper-macos-x86/build-wrapper-macos-x86 --out-dir bw-output &lt;your build command here&gt;
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="./build-wrapper-macos-x86/build-wrapper-macos-x86 --out-dir bw-output <your build command here>"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+        <div
+          aria-label="alert.tooltip.info"
+          class="alert spacer-top spacer-bottom emotion-3"
+          role="status"
+        >
+          <div
+            class="emotion-2"
+          >
+            <div
+              class="emotion-0"
+            >
+              <svg
+                height="16"
+                style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+                version="1.1"
+                viewBox="0 0 16 16"
+                width="16"
+                xml:space="preserve"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+              >
+                <path
+                  d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                  style="fill: #0271b9;"
+                />
+              </svg>
+            </div>
+            <div
+              class="alert-content emotion-1"
+            >
+              <p
+                class="spacer-bottom"
+              >
+                onboarding.tutorial.cfamilly.compilation_database_info
+                <a
+                  href="/documentation/analysis/languages/cfamily/"
+                  rel="noopener noreferrer"
+                  target="_blank"
+                >
+                  onboarding.tutorial.cfamilly.compilation_database_info.link
+                </a>
+              </p>
+              <p>
+                onboarding.tutorial.cfamilly.speed_caching
+                <a
+                  href="/documentation/analysis/languages/cfamily/#analysis-cache"
+                  rel="noopener noreferrer"
+                  target="_blank"
+                >
+                  onboarding.tutorial.cfamilly.speed_caching.link
+                </a>
+              </p>
+            </div>
+          </div>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after
+      </strong>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: cfamily windows step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <span
+    class="big-spacer-top display-block"
+  >
+    onboarding.tutorial.with.azure_pipelines.os
+  </span>
+  <div
+    class="big-spacer-top"
+  >
+    <ul
+      aria-label="onboarding.tutorial.with.azure_pipelines.os"
+      class="button-toggle"
+      role="group"
+    >
+      <li>
+        <button
+          aria-current="false"
+          class="button"
+          data-value="linux"
+          type="button"
+        >
+          onboarding.build.other.os.linux
+        </button>
+      </li>
+      <li>
+        <button
+          aria-current="true"
+          class="button selected"
+          data-value="win"
+          type="button"
+        >
+          onboarding.build.other.os.win
+        </button>
+      </li>
+      <li>
+        <button
+          aria-current="false"
+          class="button"
+          data-value="mac"
+          type="button"
+        >
+          onboarding.build.other.os.mac
+        </button>
+      </li>
+    </ul>
+  </div>
+  <div
+    class="github-cfamily-example-repositories-box big-padded boxed-group big-spacer-top abs-width-600"
+  >
+    <div
+      class="display-flex-center"
+    >
+      <img
+        alt=""
+        class="spacer-right"
+        height="20"
+        src="/images/alm/github.svg"
+      />
+      <a
+        class="spacer-left big"
+        href="https://github.com/orgs/sonarsource-cfamily-examples/repositories?q=sq+windows+azure"
+        rel="noopener noreferrer"
+        target="_blank"
+      >
+        <svg
+          aria-label="opens_in_new_window"
+          class="little-spacer-right"
+          height="14"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="14"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M12 9.25v2.5A2.25 2.25 0 0 1 9.75 14h-6.5A2.25 2.25 0 0 1 1 11.75v-6.5A2.25 2.25 0 0 1 3.25 3h5.5c.14 0 .25.11.25.25v.5c0 .14-.11.25-.25.25h-5.5C2.562 4 2 4.563 2 5.25v6.5c0 .688.563 1.25 1.25 1.25h6.5c.688 0 1.25-.563 1.25-1.25v-2.5c0-.14.11-.25.25-.25h.5c.14 0 .25.11.25.25zm3-6.75v4c0 .273-.227.5-.5.5a.497.497 0 0 1-.352-.148l-1.375-1.375L7.68 10.57a.27.27 0 0 1-.18.078.27.27 0 0 1-.18-.078l-.89-.89a.27.27 0 0 1-.078-.18.27.27 0 0 1 .078-.18l5.093-5.093-1.375-1.375A.497.497 0 0 1 10 2.5c0-.273.227-.5.5-.5h4c.273 0 .5.227.5.5z"
+            style="fill: currentColor;"
+          />
+        </svg>
+        sonarsource-cfamily-examples
+      </a>
+    </div>
+    <p
+      class="spacer-top"
+    >
+      onboarding.tutorial.cfamily.examples_repositories_description
+    </p>
+  </div>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.win.sentence.task
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.win.sentence.inline
+        </strong>
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            Invoke-WebRequest -Uri 'http://localhost/static/cpp/build-wrapper-win-x86.zip' -OutFile 'build-wrapper.zip'
+Expand-Archive -Path 'build-wrapper.zip' -DestinationPath '.'
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="Invoke-WebRequest -Uri 'http://localhost/static/cpp/build-wrapper-win-x86.zip' -OutFile 'build-wrapper.zip'
+Expand-Archive -Path 'build-wrapper.zip' -DestinationPath '.'"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.cfamily
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence.mode
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
+        </b>
+        <code
+          class="rule"
+        >
+          my-project
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="my-project"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.advanced
+        </b>
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.additional
+        </b>
+        <code
+          class="rule"
+        >
+          sonar.cfamily.build-wrapper-output=bw-output
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="sonar.cfamily.build-wrapper-output=bw-output"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp.sentence.task
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp.sentence.build_wrapper
+        </strong>
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            build-wrapper-win-x86/build-wrapper-win-x86-64.exe --out-dir bw-output &lt;your build command here&gt;
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="build-wrapper-win-x86/build-wrapper-win-x86-64.exe --out-dir bw-output <your build command here>"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+        <div
+          aria-label="alert.tooltip.info"
+          class="alert spacer-top spacer-bottom emotion-3"
+          role="status"
+        >
+          <div
+            class="emotion-2"
+          >
+            <div
+              class="emotion-0"
+            >
+              <svg
+                height="16"
+                style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+                version="1.1"
+                viewBox="0 0 16 16"
+                width="16"
+                xml:space="preserve"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+              >
+                <path
+                  d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                  style="fill: #0271b9;"
+                />
+              </svg>
+            </div>
+            <div
+              class="alert-content emotion-1"
+            >
+              <p
+                class="spacer-bottom"
+              >
+                onboarding.tutorial.cfamilly.compilation_database_info
+                <a
+                  href="/documentation/analysis/languages/cfamily/"
+                  rel="noopener noreferrer"
+                  target="_blank"
+                >
+                  onboarding.tutorial.cfamilly.compilation_database_info.link
+                </a>
+              </p>
+              <p>
+                onboarding.tutorial.cfamilly.speed_caching
+                <a
+                  href="/documentation/analysis/languages/cfamily/#analysis-cache"
+                  rel="noopener noreferrer"
+                  target="_blank"
+                >
+                  onboarding.tutorial.cfamilly.speed_caching.link
+                </a>
+              </p>
+            </div>
+          </div>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after
+      </strong>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: dotnet step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
+        </b>
+        <code
+          class="rule"
+        >
+          my-project
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="my-project"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after
+      </strong>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: extension step 1`] = `
+<span
+  data-testid="azure-tutorial__extension"
+>
+  onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence
+  <a
+    href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube"
+    rel="noopener noreferrer"
+    target="_blank"
+  >
+    <svg
+      aria-label="opens_in_new_window"
+      class="little-spacer-right"
+      height="14"
+      style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+      version="1.1"
+      viewBox="0 0 16 16"
+      width="14"
+      xml:space="preserve"
+      xmlns:xlink="http://www.w3.org/1999/xlink"
+    >
+      <path
+        d="M12 9.25v2.5A2.25 2.25 0 0 1 9.75 14h-6.5A2.25 2.25 0 0 1 1 11.75v-6.5A2.25 2.25 0 0 1 3.25 3h5.5c.14 0 .25.11.25.25v.5c0 .14-.11.25-.25.25h-5.5C2.562 4 2 4.563 2 5.25v6.5c0 .688.563 1.25 1.25 1.25h6.5c.688 0 1.25-.563 1.25-1.25v-2.5c0-.14.11-.25.25-.25h.5c.14 0 .25.11.25.25zm3-6.75v4c0 .273-.227.5-.5.5a.497.497 0 0 1-.352-.148l-1.375-1.375L7.68 10.57a.27.27 0 0 1-.18.078.27.27 0 0 1-.18-.078l-.89-.89a.27.27 0 0 1-.078-.18.27.27 0 0 1 .078-.18l5.093-5.093-1.375-1.375A.497.497 0 0 1 10 2.5c0-.273.227-.5.5-.5h4c.273 0 .5.227.5.5z"
+        style="fill: currentColor;"
+      />
+    </svg>
+    onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link
+  </a>
+  <strong>
+    onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button
+  </strong>
+</span>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: gradle step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.properties
+        </strong>
+        :
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            # Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="# Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.title
+      <ul
+        class="list-styled"
+      >
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_version
+          </strong>
+          <strong>
+            11
+          </strong>
+           
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.or_higher
+        </li>
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_architecture
+          </strong>
+          <strong>
+            x64
+          </strong>
+        </li>
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_source
+          </strong>
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.pre-installed
+          </strong>
+        </li>
+      </ul>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.gradle
+    </li>
+    <ul
+      class="list-styled big-spacer-bottom"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option
+        </strong>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: gradle step 2`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.properties
+        </strong>
+        :
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            # Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="# Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.title
+      <ul
+        class="list-styled"
+      >
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_version
+          </strong>
+          <strong>
+            11
+          </strong>
+           
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.or_higher
+        </li>
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_architecture
+          </strong>
+          <strong>
+            x64
+          </strong>
+        </li>
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_source
+          </strong>
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.pre-installed
+          </strong>
+        </li>
+      </ul>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.gradle
+    </li>
+    <ul
+      class="list-styled big-spacer-bottom"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option
+        </strong>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: maven step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.properties
+        </strong>
+        :
+        <div
+          class="code-snippet spacer-top spacer-bottom display-flex-row"
+        >
+          <pre
+            class="flex-1"
+            tabindex="0"
+          >
+            # Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project
+          </pre>
+          <button
+            aria-label="copy_to_clipboard"
+            class="button no-select"
+            data-clipboard-text="# Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project"
+            tabindex="0"
+            type="button"
+          >
+            <svg
+              class="little-spacer-right"
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <g
+                fill="currentColor"
+                fill-rule="nonzero"
+              >
+                <path
+                  d="M2.931 15.005V3H2v13h9v-.995z"
+                />
+                <path
+                  d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+                />
+                <path
+                  d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+                />
+              </g>
+            </svg>
+            copy
+          </button>
+        </div>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.title
+      <ul
+        class="list-styled"
+      >
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_version
+          </strong>
+          <strong>
+            11
+          </strong>
+           
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.or_higher
+        </li>
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_architecture
+          </strong>
+          <strong>
+            x64
+          </strong>
+        </li>
+        <li>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_source
+          </strong>
+          <strong>
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.pre-installed
+          </strong>
+        </li>
+      </ul>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.maven
+    </li>
+    <ul
+      class="list-styled big-spacer-bottom"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option
+        </strong>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: other step 1`] = `
+.emotion-2 {
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-align-items: stretch;
+  -webkit-box-align: stretch;
+  -ms-flex-align: stretch;
+  align-items: stretch;
+}
+
+.emotion-1 {
+  -webkit-flex: 1 1 auto;
+  -ms-flex: 1 1 auto;
+  flex: 1 1 auto;
+  overflow: auto;
+  text-align: left;
+  padding: 8px calc(2 * 8px);
+}
+
+.emotion-3 {
+  border: 1px solid;
+  border-radius: 2px;
+  margin-bottom: 8px;
+  border-color: #b1dff3;
+  background-color: #d9edf7;
+  color: #0e516f;
+  display: block;
+}
+
+.emotion-3:empty {
+  display: none;
+}
+
+.emotion-3 a,
+.emotion-3 .button-link {
+  border-color: #236a97;
+}
+
+.emotion-0 {
+  -webkit-flex: 0 0 auto;
+  -ms-flex: 0 0 auto;
+  flex: 0 0 auto;
+  display: -webkit-box;
+  display: -webkit-flex;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-pack: center;
+  -ms-flex-pack: center;
+  -webkit-justify-content: center;
+  justify-content: center;
+  -webkit-align-items: center;
+  -webkit-box-align: center;
+  -ms-flex-align: center;
+  align-items: center;
+  width: calc(4 * 8px);
+  border-right: 1px solid;
+  border-color: #b1dff3;
+}
+
+<div
+  data-testid="azure-tutorial__analysis-command"
+>
+  <div
+    aria-label="alert.tooltip.info"
+    class="alert big-spacer-top emotion-3"
+    role="status"
+  >
+    <div
+      class="emotion-2"
+    >
+      <div
+        class="emotion-0"
+      >
+        <svg
+          height="16"
+          style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+          version="1.1"
+          viewBox="0 0 16 16"
+          width="16"
+          xml:space="preserve"
+          xmlns:xlink="http://www.w3.org/1999/xlink"
+        >
+          <path
+            d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+            style="fill: #0271b9;"
+          />
+        </svg>
+      </div>
+      <div
+        class="alert-content emotion-1"
+      >
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info
+        <a
+          href="/documentation/analysis/azuredevops-integration/"
+          rel="noopener noreferrer"
+          target="_blank"
+        >
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
+        </a>
+      </div>
+    </div>
+  </div>
+  <ol
+    class="list-styled big-spacer-top"
+  >
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before
+      </strong>
+    </li>
+    <ul
+      class="list-styled"
+    >
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+        </strong>
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.other
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence
+        <strong>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence.mode
+        </strong>
+      </li>
+      <li>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence
+        <b>
+          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
+        </b>
+        <code
+          class="rule"
+        >
+          my-project
+        </code>
+        <button
+          aria-label="copy_to_clipboard"
+          class="button no-select button-icon"
+          data-clipboard-text="my-project"
+          style="color: rgb(35, 106, 151);"
+          tabindex="0"
+          type="button"
+        >
+          <svg
+            height="16"
+            style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+            version="1.1"
+            viewBox="0 0 16 16"
+            width="16"
+            xml:space="preserve"
+            xmlns:xlink="http://www.w3.org/1999/xlink"
+          >
+            <g
+              fill="currentColor"
+              fill-rule="nonzero"
+            >
+              <path
+                d="M2.931 15.005V3H2v13h9v-.995z"
+              />
+              <path
+                d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+              />
+              <path
+                d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+              />
+            </g>
+          </svg>
+        </button>
+      </li>
+    </ul>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after
+      </strong>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task
+      </strong>
+      <div
+        aria-label="alert.tooltip.info"
+        class="alert spacer-top emotion-3"
+        role="status"
+      >
+        <div
+          class="emotion-2"
+        >
+          <div
+            class="emotion-0"
+          >
+            <svg
+              height="16"
+              style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+              version="1.1"
+              viewBox="0 0 16 16"
+              width="16"
+              xml:space="preserve"
+              xmlns:xlink="http://www.w3.org/1999/xlink"
+            >
+              <path
+                d="M10.333 12.375v-1.458a.288.288 0 0 0-.291-.292h-.875V5.958a.288.288 0 0 0-.292-.291H5.958a.288.288 0 0 0-.291.291v1.459c0 .164.127.291.291.291h.875v2.917h-.875a.288.288 0 0 0-.291.292v1.458c0 .164.127.292.291.292h4.084a.288.288 0 0 0 .291-.292zM9.167 4.208V2.75a.288.288 0 0 0-.292-.292h-1.75a.288.288 0 0 0-.292.292v1.458c0 .164.128.292.292.292h1.75a.288.288 0 0 0 .292-.292zM15 8c0 3.865-3.135 7-7 7s-7-3.135-7-7 3.135-7 7-7 7 3.135 7 7z"
+                style="fill: #0271b9;"
+              />
+            </svg>
+          </div>
+          <div
+            class="alert-content emotion-1"
+          >
+            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+          </div>
+        </div>
+      </div>
+    </li>
+    <li>
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab
+      </strong>
+      <strong>
+        onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration
+      </strong>
+    </li>
+    <hr />
+    onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection
+    <a
+      href="/documentation/analysis/azuredevops-integration/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+    </a>
+  </ol>
+</div>
+`;
+
+exports[`should render correctly and allow navigating between the different steps: service endpoint step 1`] = `
+<ol
+  class="list-styled"
+  data-testid="azure-tutorial__service-endpoint"
+>
+  <li>
+    onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence
+    <strong>
+      onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence.menu
+    </strong>
+  </li>
+  <li>
+    onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence
+    <strong>
+      onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence.type
+    </strong>
+  </li>
+  <li>
+    onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence
+    <code
+      class="rule"
+    >
+      http://localhost:9000
+    </code>
+    <button
+      aria-label="copy_to_clipboard"
+      class="button no-select button-icon"
+      data-clipboard-text="http://localhost:9000"
+      style="color: rgb(35, 106, 151);"
+      tabindex="0"
+      type="button"
+    >
+      <svg
+        height="16"
+        style="fill-rule: evenodd; clip-rule: evenodd; stroke-linejoin: round; stroke-miterlimit: 1.41421;"
+        version="1.1"
+        viewBox="0 0 16 16"
+        width="16"
+        xml:space="preserve"
+        xmlns:xlink="http://www.w3.org/1999/xlink"
+      >
+        <g
+          fill="currentColor"
+          fill-rule="nonzero"
+        >
+          <path
+            d="M2.931 15.005V3H2v13h9v-.995z"
+          />
+          <path
+            d="M10 4.015h3V14H4V1h6v3.015zM9 8V6H8v2H6v1h2v2h1V9h2V8H9z"
+          />
+          <path
+            d="M11 1v2h2a2.151 2.151 0 0 0-2-2z"
+          />
+        </g>
+      </svg>
+    </button>
+  </li>
+  <li>
+    <span>
+      onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence
+      :
+    </span>
+    <button
+      class="button spacer-left"
+      type="button"
+    >
+      onboarding.token.generate.long
+    </button>
+  </li>
+  <li>
+    onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence
+  </li>
+  <li>
+    onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence
+  </li>
+</ol>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap
deleted file mode 100644 (file)
index c180cb8..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <div
-    className="page-header big-spacer-bottom"
-  >
-    <h2
-      className="page-title"
-    >
-      onboarding.tutorial.with.azure_pipelines.title
-    </h2>
-  </div>
-  <Step
-    finished={false}
-    key="0"
-    onOpen={[Function]}
-    open={true}
-    renderForm={[Function]}
-    stepNumber={1}
-    stepTitle="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title"
-  />
-  <Step
-    finished={false}
-    key="1"
-    onOpen={[Function]}
-    open={false}
-    renderForm={[Function]}
-    stepNumber={2}
-    stepTitle="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title"
-  />
-  <Step
-    finished={false}
-    key="2"
-    onOpen={[Function]}
-    open={false}
-    renderForm={[Function]}
-    stepNumber={3}
-    stepTitle="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title"
-  />
-  <AllSetStep
-    alm="azure"
-    open={false}
-    stepNumber={4}
-    willRefreshAutomatically={true}
-  />
-</Fragment>
-`;
-
-exports[`should render correctly 2`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <div>
-        <ExtensionInstallationStepContent />
-      </div>
-      <Button
-        className="big-spacer-top spacer-bottom"
-        onClick={[Function]}
-      >
-        continue
-      </Button>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`should render correctly 3`] = `
-<div
-  className="boxed-group onboarding-step"
->
-  <div
-    className="onboarding-step-number"
-  >
-    2
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title
-    </h2>
-  </div>
-  <div
-    className="boxed-group-inner"
-  />
-  <div
-    className="hidden"
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <div>
-        <ServiceEndpointStepContent
-          baseUrl="http://localhost:9000"
-          component={
-            Object {
-              "breadcrumbs": Array [],
-              "key": "my-project",
-              "name": "MyProject",
-              "qualifier": "TRK",
-              "qualityGate": Object {
-                "isDefault": true,
-                "key": "30",
-                "name": "Sonar way",
-              },
-              "qualityProfiles": Array [
-                Object {
-                  "deleted": false,
-                  "key": "my-qp",
-                  "language": "ts",
-                  "name": "Sonar way",
-                },
-              ],
-              "tags": Array [],
-            }
-          }
-          currentUser={
-            Object {
-              "dismissedNotices": Object {
-                "educationPrinciples": false,
-              },
-              "groups": Array [],
-              "isLoggedIn": true,
-              "login": "luke",
-              "name": "Skywalker",
-              "scmAccounts": Array [],
-            }
-          }
-        />
-      </div>
-      <Button
-        className="big-spacer-top spacer-bottom"
-        onClick={[Function]}
-      >
-        continue
-      </Button>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`should render correctly 4`] = `
-<div
-  className="boxed-group onboarding-step"
->
-  <div
-    className="onboarding-step-number"
-  >
-    3
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title
-    </h2>
-  </div>
-  <div
-    className="boxed-group-inner"
-  />
-  <div
-    className="hidden"
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <div>
-        <withLanguagesContext(BranchAnalysisStepContent)
-          component={
-            Object {
-              "breadcrumbs": Array [],
-              "key": "my-project",
-              "name": "MyProject",
-              "qualifier": "TRK",
-              "qualityGate": Object {
-                "isDefault": true,
-                "key": "30",
-                "name": "Sonar way",
-              },
-              "qualityProfiles": Array [
-                Object {
-                  "deleted": false,
-                  "key": "my-qp",
-                  "language": "ts",
-                  "name": "Sonar way",
-                },
-              ],
-              "tags": Array [],
-            }
-          }
-          onStepValidationChange={[Function]}
-        />
-      </div>
-    </div>
-  </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap
deleted file mode 100644 (file)
index 1baea0c..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <span>
-    onboarding.build
-  </span>
-  <RenderOptions
-    label="onboarding.build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build"
-    options={
-      Array [
-        "dotnet",
-        "maven",
-        "gradle",
-        "cfamily",
-        "other",
-      ]
-    }
-  />
-  <AnalysisCommand
-    onStepValidationChange={[MockFunction]}
-    projectKey="my-project"
-  />
-</Fragment>
-`;
-
-exports[`should render correctly: dotnet 1`] = `
-<Fragment>
-  <span>
-    onboarding.build
-  </span>
-  <RenderOptions
-    checked="dotnet"
-    label="onboarding.build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build"
-    options={
-      Array [
-        "dotnet",
-        "maven",
-        "gradle",
-        "cfamily",
-        "other",
-      ]
-    }
-  />
-  <AnalysisCommand
-    buildTool="dotnet"
-    onStepValidationChange={[MockFunction]}
-    projectKey="my-project"
-  />
-</Fragment>
-`;
-
-exports[`should render correctly: gradle 1`] = `
-<Fragment>
-  <span>
-    onboarding.build
-  </span>
-  <RenderOptions
-    checked="gradle"
-    label="onboarding.build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build"
-    options={
-      Array [
-        "dotnet",
-        "maven",
-        "gradle",
-        "cfamily",
-        "other",
-      ]
-    }
-  />
-  <AnalysisCommand
-    buildTool="gradle"
-    onStepValidationChange={[MockFunction]}
-    projectKey="my-project"
-  />
-</Fragment>
-`;
-
-exports[`should render correctly: maven 1`] = `
-<Fragment>
-  <span>
-    onboarding.build
-  </span>
-  <RenderOptions
-    checked="maven"
-    label="onboarding.build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build"
-    options={
-      Array [
-        "dotnet",
-        "maven",
-        "gradle",
-        "cfamily",
-        "other",
-      ]
-    }
-  />
-  <AnalysisCommand
-    buildTool="maven"
-    onStepValidationChange={[MockFunction]}
-    projectKey="my-project"
-  />
-</Fragment>
-`;
-
-exports[`should render correctly: other 1`] = `
-<Fragment>
-  <span>
-    onboarding.build
-  </span>
-  <RenderOptions
-    checked="other"
-    label="onboarding.build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build"
-    options={
-      Array [
-        "dotnet",
-        "maven",
-        "gradle",
-        "cfamily",
-        "other",
-      ]
-    }
-  />
-  <AnalysisCommand
-    buildTool="other"
-    onStepValidationChange={[MockFunction]}
-    projectKey="my-project"
-  />
-</Fragment>
-`;
-
-exports[`should render correctly: without C 1`] = `
-<Fragment>
-  <span>
-    onboarding.build
-  </span>
-  <RenderOptions
-    label="onboarding.build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build"
-    options={
-      Array [
-        "dotnet",
-        "maven",
-        "gradle",
-        "other",
-      ]
-    }
-  />
-  <AnalysisCommand
-    onStepValidationChange={[MockFunction]}
-    projectKey="my-project"
-  />
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap
deleted file mode 100644 (file)
index e8b50f4..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<span>
-  <FormattedMessage
-    defaultMessage="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
-    id="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
-    values={
-      Object {
-        "button": <strong>
-          onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button
-        </strong>,
-        "link": <ForwardRef(Link)
-          target="_blank"
-          to="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube"
-        >
-          onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link
-        </ForwardRef(Link)>,
-      }
-    }
-  />
-</span>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/JavaToolInstallation-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/JavaToolInstallation-test.tsx.snap
deleted file mode 100644 (file)
index fc74464..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<li>
-  onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.title
-  <ul
-    className="list-styled"
-  >
-    <li>
-      <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence"
-        id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence"
-        values={
-          Object {
-            "field": <strong>
-              onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_version
-            </strong>,
-            "value": <strong>
-              11
-            </strong>,
-          }
-        }
-      />
-       
-      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.or_higher
-    </li>
-    <li>
-      <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence"
-        id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence"
-        values={
-          Object {
-            "field": <strong>
-              onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_architecture
-            </strong>,
-            "value": <strong>
-              x64
-            </strong>,
-          }
-        }
-      />
-    </li>
-    <li>
-      <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence"
-        id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.sentence"
-        values={
-          Object {
-            "field": <strong>
-              onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.java_source
-            </strong>,
-            "value": <strong>
-              onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.pre-installed
-            </strong>,
-          }
-        }
-      />
-    </li>
-  </ul>
-</li>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap
deleted file mode 100644 (file)
index 468ec3d..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <ol
-    className="list-styled"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "menu",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1"
-      />
-    </li>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "type",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2"
-      />
-    </li>
-    <li>
-      <FormattedMessage
-        defaultMessage="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence"
-        id="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence"
-        values={
-          Object {
-            "button": <ClipboardIconButton
-              copyValue="http://localhost:9000"
-            />,
-            "url": <code
-              className="rule"
-            >
-              http://localhost:9000
-            </code>,
-          }
-        }
-      />
-    </li>
-    <li>
-      <span>
-        onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence
-        :
-      </span>
-      <Button
-        className="spacer-left"
-        onClick={[Function]}
-      >
-        onboarding.token.generate_token
-      </Button>
-    </li>
-    <li>
-      onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence
-    </li>
-    <li>
-      onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence
-    </li>
-  </ol>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/AlertClassicEditor-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/AlertClassicEditor-test.tsx
deleted file mode 100644 (file)
index 1ad0bbb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import AlertClassicEditor from '../AlertClassicEditor';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
-  return shallow(<AlertClassicEditor />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/AnalysisCommand-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/AnalysisCommand-test.tsx
deleted file mode 100644 (file)
index fcf77e3..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { BuildTools } from '../../../types';
-import AnalysisCommand, { AnalysisCommandProps } from '../AnalysisCommand';
-
-it.each([
-  undefined,
-  BuildTools.CFamily,
-  BuildTools.DotNet,
-  BuildTools.Gradle,
-  BuildTools.Maven,
-  BuildTools.Other
-])('should render correctly for %p', buildTool => {
-  expect(shallowRender({ buildTool })).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<AnalysisCommandProps> = {}) {
-  return shallow<AnalysisCommandProps>(
-    <AnalysisCommand onStepValidationChange={jest.fn()} projectKey="projectKey" {...props} />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/ClangGCC-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/ClangGCC-test.tsx
deleted file mode 100644 (file)
index 7f83989..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import RenderOptions from '../../../components/RenderOptions';
-import { OSs } from '../../../types';
-import ClangGCC, { ClangGCCProps } from '../ClangGCC';
-
-it.each([OSs.Linux, OSs.Windows, OSs.MacOS])('should render correctly for %p', buildTool => {
-  const wrapper = shallowRender();
-
-  wrapper.find(RenderOptions).simulate('check', buildTool);
-  expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ClangGCCProps> = {}) {
-  return shallow<ClangGCCProps>(
-    <ClangGCC onStepValidationChange={jest.fn()} projectKey="projectKey" {...props} />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/DotNet-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/DotNet-test.tsx
deleted file mode 100644 (file)
index 38353ae..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import DotNet, { DotNetProps } from '../DotNet';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<DotNetProps> = {}) {
-  return shallow<DotNetProps>(<DotNet projectKey="projectKey" {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/JavaGradle-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/JavaGradle-test.tsx
deleted file mode 100644 (file)
index 51402f9..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import JavaGradle, { JavaGradleProps } from '../JavaGradle';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<JavaGradleProps> = {}) {
-  return shallow<JavaGradleProps>(<JavaGradle projectKey="projectKey" {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/JavaMaven-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/JavaMaven-test.tsx
deleted file mode 100644 (file)
index 7f864fb..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import JavaMaven, { JavaMavenProps } from '../JavaMaven';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<JavaMavenProps> = {}) {
-  return shallow<JavaMavenProps>(<JavaMaven projectKey="projectKey" {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/Other-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/Other-test.tsx
deleted file mode 100644 (file)
index cb7cd6d..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import Other, { OtherProps } from '../Other';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<OtherProps> = {}) {
-  return shallow<OtherProps>(<Other projectKey="projectKey" {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/PrepareAnalysisCommand-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/PrepareAnalysisCommand-test.tsx
deleted file mode 100644 (file)
index 9f387e6..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { BuildTools } from '../../../types';
-import PrepareAnalysisCommand, {
-  PrepareAnalysisCommandProps,
-  PrepareType
-} from '../PrepareAnalysisCommand';
-
-it.each([
-  [PrepareType.JavaMavenGradle, BuildTools.Gradle],
-  [PrepareType.JavaMavenGradle, BuildTools.Maven],
-  [PrepareType.StandAlone, BuildTools.Other],
-  [PrepareType.StandAlone, BuildTools.CFamily],
-  [PrepareType.MSBuild, BuildTools.DotNet]
-])('should render correctly', (kind, buildTool) => {
-  expect(shallowRender({ kind, buildTool })).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<PrepareAnalysisCommandProps> = {}) {
-  return shallow<PrepareAnalysisCommandProps>(
-    <PrepareAnalysisCommand
-      kind={PrepareType.JavaMavenGradle}
-      buildTool={BuildTools.CFamily}
-      projectKey="projectKey"
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/PublishSteps-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/PublishSteps-test.tsx
deleted file mode 100644 (file)
index 31d95cf..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockAppState } from '../../../../../helpers/testMocks';
-import { PublishSteps, PublishStepsProps } from '../PublishSteps';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ appState: mockAppState({ branchesEnabled: true }) })).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<PublishStepsProps> = {}) {
-  return shallow<PublishStepsProps>(<PublishSteps appState={mockAppState()} {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/AlertClassicEditor-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/AlertClassicEditor-test.tsx.snap
deleted file mode 100644 (file)
index 21628b7..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Alert
-  className="big-spacer-top"
-  variant="info"
->
-  <FormattedMessage
-    defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info"
-    id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info"
-    values={
-      Object {
-        "doc_link": <ForwardRef(Link)
-          target="_blank"
-          to="/documentation/analysis/azuredevops-integration/"
-        >
-          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link
-        </ForwardRef(Link)>,
-      }
-    }
-  />
-</Alert>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap
deleted file mode 100644 (file)
index af15f04..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly for "cfamily" 1`] = `
-<ClangGCC
-  onStepValidationChange={[MockFunction]}
-  projectKey="projectKey"
-/>
-`;
-
-exports[`should render correctly for "dotnet" 1`] = `
-<DotNet
-  projectKey="projectKey"
-/>
-`;
-
-exports[`should render correctly for "gradle" 1`] = `
-<JavaGradle
-  projectKey="projectKey"
-/>
-`;
-
-exports[`should render correctly for "maven" 1`] = `
-<JavaMaven
-  projectKey="projectKey"
-/>
-`;
-
-exports[`should render correctly for "other" 1`] = `
-<Other
-  projectKey="projectKey"
-/>
-`;
-
-exports[`should render correctly for undefined 1`] = `""`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/ClangGCC-test.tsx.snap
deleted file mode 100644 (file)
index 7c5167f..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly for "linux" 1`] = `
-<Fragment>
-  <span
-    className="big-spacer-top display-block"
-  >
-    onboarding.tutorial.with.azure_pipelines.os
-  </span>
-  <RenderOptions
-    checked="linux"
-    label="onboarding.tutorial.with.azure_pipelines.os"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build.other.os"
-    options={
-      Array [
-        "linux",
-        "win",
-        "mac",
-      ]
-    }
-  />
-  <GithubCFamilyExampleRepositories
-    ci="azure-pipelines"
-    className="big-spacer-top abs-width-600"
-    os="linux"
-  />
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp"
-      />
-    </li>
-    <ul
-      className="list-styled"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "task",
-              "inline",
-            ]
-          }
-          highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.nix"
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script"
-        />
-        <CodeSnippet
-          snippet="curl 'http://localhost/static/cpp/build-wrapper-linux-x86.zip' --output build-wrapper.zip
-unzip build-wrapper.zip"
-        />
-      </li>
-    </ul>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "before",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="cfamily"
-      kind={1}
-      projectKey="projectKey"
-    />
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp"
-      />
-    </li>
-    <ul
-      className="list-styled"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "build_wrapper",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp"
-        />
-        <CodeSnippet
-          snippet="./build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw-output <your build command here>"
-        />
-        <CompilationInfo />
-      </li>
-    </ul>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "after",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
-      />
-    </li>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
-
-exports[`should render correctly for "mac" 1`] = `
-<Fragment>
-  <span
-    className="big-spacer-top display-block"
-  >
-    onboarding.tutorial.with.azure_pipelines.os
-  </span>
-  <RenderOptions
-    checked="mac"
-    label="onboarding.tutorial.with.azure_pipelines.os"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build.other.os"
-    options={
-      Array [
-        "linux",
-        "win",
-        "mac",
-      ]
-    }
-  />
-  <GithubCFamilyExampleRepositories
-    ci="azure-pipelines"
-    className="big-spacer-top abs-width-600"
-    os="mac"
-  />
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp"
-      />
-    </li>
-    <ul
-      className="list-styled"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "task",
-              "inline",
-            ]
-          }
-          highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.nix"
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script"
-        />
-        <CodeSnippet
-          snippet="curl 'http://localhost/static/cpp/build-wrapper-macosx-x86.zip' --output build-wrapper.zip
-unzip build-wrapper.zip"
-        />
-      </li>
-    </ul>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "before",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="cfamily"
-      kind={1}
-      projectKey="projectKey"
-    />
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp"
-      />
-    </li>
-    <ul
-      className="list-styled"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "build_wrapper",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp"
-        />
-        <CodeSnippet
-          snippet="./build-wrapper-macos-x86/build-wrapper-macos-x86 --out-dir bw-output <your build command here>"
-        />
-        <CompilationInfo />
-      </li>
-    </ul>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "after",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
-      />
-    </li>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
-
-exports[`should render correctly for "win" 1`] = `
-<Fragment>
-  <span
-    className="big-spacer-top display-block"
-  >
-    onboarding.tutorial.with.azure_pipelines.os
-  </span>
-  <RenderOptions
-    checked="win"
-    label="onboarding.tutorial.with.azure_pipelines.os"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.build.other.os"
-    options={
-      Array [
-        "linux",
-        "win",
-        "mac",
-      ]
-    }
-  />
-  <GithubCFamilyExampleRepositories
-    ci="azure-pipelines"
-    className="big-spacer-top abs-width-600"
-    os="win"
-  />
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp"
-      />
-    </li>
-    <ul
-      className="list-styled"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "task",
-              "inline",
-            ]
-          }
-          highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.win"
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.script"
-        />
-        <CodeSnippet
-          snippet="Invoke-WebRequest -Uri 'http://localhost/static/cpp/build-wrapper-win-x86.zip' -OutFile 'build-wrapper.zip'
-Expand-Archive -Path 'build-wrapper.zip' -DestinationPath '.'"
-        />
-      </li>
-    </ul>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "before",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.ccpp"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="cfamily"
-      kind={1}
-      projectKey="projectKey"
-    />
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build.ccpp"
-      />
-    </li>
-    <ul
-      className="list-styled"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "build_wrapper",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_script.ccpp"
-        />
-        <CodeSnippet
-          snippet="build-wrapper-win-x86/build-wrapper-win-x86-64.exe --out-dir bw-output <your build command here>"
-        />
-        <CompilationInfo />
-      </li>
-    </ul>
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "after",
-          ]
-        }
-        highlightPrefixKeys="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.ccpp"
-      />
-    </li>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
deleted file mode 100644 (file)
index ab15dae..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-            "task",
-            "before",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="dotnet"
-      kind={2}
-      projectKey="projectKey"
-    />
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "after",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
-      />
-    </li>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
deleted file mode 100644 (file)
index 47f592f..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-            "task",
-            "before",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="gradle"
-      kind={0}
-      projectKey="projectKey"
-    />
-    <JavaToolInstallation />
-    <li>
-      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.gradle
-    </li>
-    <ul
-      className="list-styled big-spacer-bottom"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "section",
-              "option",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
-        />
-      </li>
-    </ul>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
deleted file mode 100644 (file)
index 3eaf0af..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-            "task",
-            "before",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="gradle"
-      kind={0}
-      projectKey="projectKey"
-    />
-    <JavaToolInstallation />
-    <li>
-      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.maven
-    </li>
-    <ul
-      className="list-styled big-spacer-bottom"
-    >
-      <li>
-        <SentenceWithHighlights
-          highlightKeys={
-            Array [
-              "section",
-              "option",
-            ]
-          }
-          translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
-        />
-      </li>
-    </ul>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/Other-test.tsx.snap
deleted file mode 100644 (file)
index 87f7bf3..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <AlertClassicEditor />
-  <ol
-    className="list-styled big-spacer-top"
-  >
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "pipeline",
-            "task",
-            "before",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
-      />
-    </li>
-    <PrepareAnalysisCommand
-      buildTool="other"
-      kind={1}
-      projectKey="projectKey"
-    />
-    <li>
-      <SentenceWithHighlights
-        highlightKeys={
-          Array [
-            "task",
-            "after",
-          ]
-        }
-        translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
-      />
-    </li>
-    <withAppStateContext(PublishSteps) />
-  </ol>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/PrepareAnalysisCommand-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/PrepareAnalysisCommand-test.tsx.snap
deleted file mode 100644 (file)
index 170210c..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<ul
-  className="list-styled"
->
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "endpoint",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      values={
-        Object {
-          "run_analysis_value": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle
-          </strong>,
-          "section": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
-          </strong>,
-        }
-      }
-    />
-  </li>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "section",
-          "properties",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
-    />
-    :
-    <CodeSnippet
-      snippet="# Additional properties that will be passed to the scanner,
-# Put one key=value per line, example:
-# sonar.exclusions=**/*.bin
-sonar.projectKey=projectKey"
-    />
-  </li>
-</ul>
-`;
-
-exports[`should render correctly 2`] = `
-<ul
-  className="list-styled"
->
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "endpoint",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      values={
-        Object {
-          "run_analysis_value": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.maven
-          </strong>,
-          "section": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
-          </strong>,
-        }
-      }
-    />
-  </li>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "section",
-          "properties",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
-    />
-    :
-    <CodeSnippet
-      snippet="# Additional properties that will be passed to the scanner,
-# Put one key=value per line, example:
-# sonar.exclusions=**/*.bin
-sonar.projectKey=projectKey"
-    />
-  </li>
-</ul>
-`;
-
-exports[`should render correctly 3`] = `
-<ul
-  className="list-styled"
->
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "endpoint",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      values={
-        Object {
-          "run_analysis_value": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.other
-          </strong>,
-          "section": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
-          </strong>,
-        }
-      }
-    />
-  </li>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "mode",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
-      values={
-        Object {
-          "button": <ClipboardIconButton
-            copyValue="projectKey"
-          />,
-          "key": <code
-            className="rule"
-          >
-            projectKey
-          </code>,
-          "project_key": <b>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
-          </b>,
-        }
-      }
-    />
-  </li>
-</ul>
-`;
-
-exports[`should render correctly 4`] = `
-<ul
-  className="list-styled"
->
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "endpoint",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      values={
-        Object {
-          "run_analysis_value": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.cfamily
-          </strong>,
-          "section": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
-          </strong>,
-        }
-      }
-    />
-  </li>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "mode",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
-      values={
-        Object {
-          "button": <ClipboardIconButton
-            copyValue="projectKey"
-          />,
-          "key": <code
-            className="rule"
-          >
-            projectKey
-          </code>,
-          "project_key": <b>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
-          </b>,
-        }
-      }
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp"
-      values={
-        Object {
-          "additional": <b>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.additional
-          </b>,
-          "advanced": <b>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare_additional.ccpp.advanced
-          </b>,
-          "button": <ClipboardIconButton
-            copyValue="sonar.cfamily.build-wrapper-output=bw-output"
-          />,
-          "property": <code
-            className="rule"
-          >
-            sonar.cfamily.build-wrapper-output=bw-output
-          </code>,
-        }
-      }
-    />
-  </li>
-</ul>
-`;
-
-exports[`should render correctly 5`] = `
-<ul
-  className="list-styled"
->
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "endpoint",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
-      values={
-        Object {
-          "run_analysis_value": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet
-          </strong>,
-          "section": <strong>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
-          </strong>,
-        }
-      }
-    />
-  </li>
-  <li>
-    <FormattedMessage
-      defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
-      id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
-      values={
-        Object {
-          "button": <ClipboardIconButton
-            copyValue="projectKey"
-          />,
-          "key": <code
-            className="rule"
-          >
-            projectKey
-          </code>,
-          "project_key": <b>
-            onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence.project_key
-          </b>,
-        }
-      }
-    />
-  </li>
-</ul>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/PublishSteps-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/commands/__tests__/__snapshots__/PublishSteps-test.tsx.snap
deleted file mode 100644 (file)
index 091a7fb..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Fragment>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "task",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
-    />
-    <Alert
-      className="spacer-top"
-      variant="info"
-    >
-      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
-    </Alert>
-  </li>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "tab",
-          "continuous_integration",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.no_branches"
-    />
-  </li>
-</Fragment>
-`;
-
-exports[`should render correctly 2`] = `
-<Fragment>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "task",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
-    />
-    <Alert
-      className="spacer-top"
-      variant="info"
-    >
-      onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
-    </Alert>
-  </li>
-  <li>
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "tab",
-          "continuous_integration",
-        ]
-      }
-      translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
-    />
-  </li>
-  <hr />
-  <FormattedMessage
-    defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
-    id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
-    values={
-      Object {
-        "link": <ForwardRef(Link)
-          target="_blank"
-          to="/documentation/analysis/azuredevops-integration/"
-        >
-          onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
-        </ForwardRef(Link)>,
-      }
-    }
-  />
-</Fragment>
-`;
index 58c3805e34d93bc0833f9cb74b374c217ffd21f8..dbf457dd86197dace3501a3859821b2d07b52ba9 100644 (file)
@@ -37,7 +37,7 @@ export default function AllSetStep(props: AllSetStepProps) {
       finished={false}
       open={open}
       renderForm={() => (
-        <div className="boxed-group-inner">
+        <div data-testid="azure-tutorial__all-set" className="boxed-group-inner">
           <AllSet alm={alm} willRefreshAutomatically={willRefreshAutomatically} />
         </div>
       )}
index 622f0a2b48643a339791ba6eba78f4d7323e78d0..29dd00f8ec94d45578e4934aa6ee20f5ac68d02e 100644 (file)
@@ -31,12 +31,15 @@ import {
   EXPIRATION_OPTIONS,
   getAvailableExpirationOptions
 } from '../../../helpers/tokens';
+import { hasGlobalPermission } from '../../../helpers/users';
+import { Permissions } from '../../../types/permissions';
 import { TokenExpiration, TokenType } from '../../../types/token';
 import { Component } from '../../../types/types';
 import { LoggedInUser } from '../../../types/users';
 import Link from '../../common/Link';
 import Select from '../../controls/Select';
 import { getUniqueTokenName } from '../utils';
+import ProjectTokenScopeInfo from './ProjectTokenScopeInfo';
 
 interface State {
   loading: boolean;
@@ -50,6 +53,7 @@ interface Props {
   component: Component;
   currentUser: LoggedInUser;
   onClose: (token?: string) => void;
+  preferredTokenType?: TokenType.Global | TokenType.Project;
 }
 
 export default class EditTokenModal extends React.PureComponent<Props, State> {
@@ -97,9 +101,11 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
     } = this.props;
     const { tokenName, tokenExpiration } = this.state;
 
+    const type = this.getTokenType();
+
     const { token } = await generateToken({
       name: tokenName,
-      type: TokenType.Project,
+      type,
       projectKey: key,
       ...(tokenExpiration !== TokenExpiration.NoExpiration && {
         expirationDate: computeTokenExpirationDate(tokenExpiration)
@@ -114,6 +120,15 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
     }
   };
 
+  getTokenType = () => {
+    const { currentUser, preferredTokenType } = this.props;
+
+    return preferredTokenType === TokenType.Global &&
+      hasGlobalPermission(currentUser, Permissions.Scan)
+      ? TokenType.Global
+      : TokenType.Project;
+  };
+
   handleTokenNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
     this.setState({
       tokenName: event.target.value
@@ -142,7 +157,9 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
   render() {
     const { loading, token, tokenName, tokenExpiration, tokenExpirationOptions } = this.state;
 
-    const header = translate('onboarding.token.generate_project_token');
+    const type = this.getTokenType();
+    const header = translate('onboarding.token.generate', type);
+    const intro = translate('onboarding.token.text', type);
 
     return (
       <SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.props.onClose}>
@@ -155,8 +172,8 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
             <div className="modal-body">
               <p className="spacer-bottom">
                 <FormattedMessage
-                  defaultMessage={translate('onboarding.project_token.text')}
-                  id="onboarding.project_token.text"
+                  defaultMessage={intro}
+                  id={intro}
                   values={{
                     link: (
                       <Link target="_blank" to="/account/security">
@@ -178,7 +195,10 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
 
                     <ClipboardIconButton copyValue={token} />
 
-                    <DeleteButton onClick={this.handleTokenRevoke} />
+                    <DeleteButton
+                      aria-label={translate('users.tokens.revoke_token')}
+                      onClick={this.handleTokenRevoke}
+                    />
                   </div>
 
                   <Alert className="big-spacer-top" variant="warning">
@@ -186,52 +206,56 @@ export default class EditTokenModal extends React.PureComponent<Props, State> {
                   </Alert>
                 </>
               ) : (
-                <div className="big-spacer-top display-flex-center">
-                  {loading ? (
-                    <DeferredSpinner />
-                  ) : (
-                    <>
-                      <div className="display-flex-column">
-                        <label className="text-bold little-spacer-bottom" htmlFor="token-name">
-                          {translate('onboarding.token.generate_token.placeholder')}
-                        </label>
-                        <input
-                          className="input-large spacer-right text-middle"
-                          onChange={this.handleTokenNameChange}
-                          required={true}
-                          id="token-name"
-                          type="text"
-                          value={tokenName}
-                        />
-                      </div>
-                      <div className="display-flex-column">
-                        <label
-                          className="text-bold little-spacer-bottom"
-                          htmlFor="token-expiration">
-                          {translate('users.tokens.expires_in')}
-                        </label>
-                        <div className="display-flex-center">
-                          <Select
-                            id="token-expiration"
-                            className="abs-width-100 spacer-right"
-                            isSearchable={false}
-                            onChange={this.handleTokenExpirationChange}
-                            options={tokenExpirationOptions}
-                            value={tokenExpirationOptions.find(
-                              option => option.value === tokenExpiration
-                            )}
+                <>
+                  <div className="big-spacer-top display-flex-center">
+                    {loading ? (
+                      <DeferredSpinner />
+                    ) : (
+                      <>
+                        <div className="display-flex-column">
+                          <label className="text-bold little-spacer-bottom" htmlFor="token-name">
+                            {translate('onboarding.token.name.label')}
+                          </label>
+                          <input
+                            className="input-large spacer-right text-middle"
+                            onChange={this.handleTokenNameChange}
+                            required={true}
+                            id="token-name"
+                            type="text"
+                            placeholder={translate('onboarding.token.name.placeholder')}
+                            value={tokenName}
                           />
-                          <Button
-                            className="text-middle"
-                            disabled={!tokenName}
-                            onClick={this.getNewToken}>
-                            {translate('onboarding.token.generate')}
-                          </Button>
                         </div>
-                      </div>
-                    </>
-                  )}
-                </div>
+                        <div className="display-flex-column">
+                          <label
+                            className="text-bold little-spacer-bottom"
+                            htmlFor="token-expiration">
+                            {translate('users.tokens.expires_in')}
+                          </label>
+                          <div className="display-flex-center">
+                            <Select
+                              id="token-expiration"
+                              className="abs-width-100 spacer-right"
+                              isSearchable={false}
+                              onChange={this.handleTokenExpirationChange}
+                              options={tokenExpirationOptions}
+                              value={tokenExpirationOptions.find(
+                                option => option.value === tokenExpiration
+                              )}
+                            />
+                            <Button
+                              className="text-middle"
+                              disabled={!tokenName}
+                              onClick={this.getNewToken}>
+                              {translate('onboarding.token.generate')}
+                            </Button>
+                          </div>
+                        </div>
+                      </>
+                    )}
+                  </div>
+                  {type === TokenType.Project && <ProjectTokenScopeInfo />}
+                </>
               )}
             </div>
             <div className="modal-foot">
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/ProjectTokenScopeInfo.tsx b/server/sonar-web/src/main/js/components/tutorials/components/ProjectTokenScopeInfo.tsx
new file mode 100644 (file)
index 0000000..202d297
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import classNames from 'classnames';
+import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { translate } from '../../../helpers/l10n';
+import Link from '../../common/Link';
+import { Alert } from '../../ui/Alert';
+
+export interface ProjectTokenScopeInfoProps {
+  className?: string;
+}
+
+export default function ProjectTokenScopeInfo({ className }: ProjectTokenScopeInfoProps) {
+  return (
+    <Alert variant="info" className={classNames('spacer-top', className)}>
+      <FormattedMessage
+        defaultMessage={translate('onboarding.token.warning_project_token_scope')}
+        id="onboarding.token.warning_project_token_scope"
+        values={{
+          link: (
+            <Link target="_blank" to="/account/security">
+              {translate('onboarding.token.text.user_account')}
+            </Link>
+          ),
+          doc_link: (
+            <Link target="_blank" to="/documentation/user-guide/user-token/">
+              {translate('documentation')}
+            </Link>
+          )
+        }}
+      />
+    </Alert>
+  );
+}
index e4d534aa20b006b24d7752751c0c08141f71cde8..5b43bab425f658845dbdb51ac5b024a9f451f746 100644 (file)
@@ -60,8 +60,7 @@ export default function Step(props: Props) {
       <div className="boxed-group-header">
         <h2>{stepTitle}</h2>
       </div>
-      {!open && <div className="boxed-group-inner" />}
-      <div className={classNames({ hidden: !open })}>{props.renderForm()}</div>
+      {open ? <div>{props.renderForm()}</div> : <div className="boxed-group-inner" />}
     </div>
   );
 }
index 265a04b16365b6bde9c4ab5d5d9d8968944396d6..7529a06b49742fd227534e68eee204f71219dba1 100644 (file)
@@ -46,7 +46,7 @@ export default function TokenStepGenerator(props: TokenStepGeneratorProps) {
           values={{
             extra: (
               <Button className="spacer-left" onClick={toggleTokenModal}>
-                {translate('onboarding.token.generate_token')}
+                {translate('onboarding.token.generate.long')}
               </Button>
             ),
             field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>,
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSetStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSetStep-test.tsx
deleted file mode 100644 (file)
index 749f450..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { AlmKeys } from '../../../../types/alm-settings';
-import { renderStepContent } from '../../test-utils';
-import AllSetStep, { AllSetStepProps } from '../AllSetStep';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot('default');
-  expect(renderStepContent(shallowRender())).toMatchSnapshot('step content');
-});
-
-function shallowRender(props: Partial<AllSetStepProps> = {}) {
-  return shallow<AllSetStepProps>(
-    <AllSetStep
-      alm={AlmKeys.Azure}
-      open={true}
-      stepNumber={1}
-      willRefreshAutomatically={true}
-      {...props}
-    />
-  );
-}
index 8ad792ee98104c68909fd339dcd61836fdf5edbd..aef45fe07383f7fb914fcb594849f1e21109bd8d 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-import { shallow } from 'enzyme';
+import { screen, waitFor } from '@testing-library/dom';
+import userEvent from '@testing-library/user-event';
+import { UserEvent } from '@testing-library/user-event/dist/types/setup';
 import * as React from 'react';
-import { generateToken, getTokens, revokeToken } from '../../../../api/user-tokens';
+import UserTokensMock from '../../../../api/mocks/UserTokensMock';
 import { mockComponent } from '../../../../helpers/mocks/component';
+import { mockUserToken } from '../../../../helpers/mocks/token';
 import { mockLoggedInUser } from '../../../../helpers/testMocks';
-import { mockEvent, waitAndUpdate } from '../../../../helpers/testUtils';
-import { getUniqueTokenName } from '../../utils';
+import { renderComponent } from '../../../../helpers/testReactTestingUtils';
+import { computeTokenExpirationDate } from '../../../../helpers/tokens';
+import { Permissions } from '../../../../types/permissions';
+import { TokenType } from '../../../../types/token';
 import EditTokenModal from '../EditTokenModal';
 
-jest.mock('../../../../api/user-tokens', () => ({
-  generateToken: jest.fn().mockResolvedValue({
-    name: 'baz',
-    createdAt: '2019-01-21T08:06:00+0100',
-    login: 'luke',
-    token: 'token_value'
-  }),
-  getTokens: jest.fn().mockResolvedValue([
-    {
-      name: 'foo',
-      createdAt: '2019-01-15T15:06:33+0100',
-      lastConnectionDate: '2019-01-18T15:06:33+0100'
-    },
-    { name: 'bar', createdAt: '2019-01-18T15:06:33+0100' }
-  ]),
-  revokeToken: jest.fn().mockResolvedValue(Promise.resolve())
-}));
+jest.mock('../../../../api/user-tokens');
 
-jest.mock('../../utils', () => ({
-  getUniqueTokenName: jest.fn().mockReturnValue('lightsaber-9000')
+jest.mock('../../../../api/settings', () => ({
+  getAllValues: jest.fn().mockResolvedValue([])
 }));
 
-jest.mock('../../../../api/settings', () => {
-  return {
-    ...jest.requireActual('../../../../api/settings'),
-    getAllValues: jest.fn().mockResolvedValue([
-      {
-        key: 'sonar.auth.token.max.allowed.lifetime',
-        value: 'No expiration'
-      }
-    ])
-  };
-});
+let tokenMock: UserTokensMock;
 
-beforeEach(() => {
-  jest.clearAllMocks();
+beforeAll(() => {
+  tokenMock = new UserTokensMock();
+  tokenMock.tokens.push(mockUserToken({ name: 'Analyze "Foo"' }));
 });
 
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
+afterEach(() => {
+  tokenMock.reset();
 });
 
-it('should get tokens and unique name', async () => {
-  const wrapper = shallowRender();
-  const { getTokensAndName } = wrapper.instance();
+it('should behave correctly', async () => {
+  renderEditTokenModal();
+  const user = userEvent.setup();
 
-  getTokensAndName();
-  await waitAndUpdate(wrapper);
-
-  expect(getTokens).toHaveBeenCalled();
-  expect(getUniqueTokenName).toHaveBeenCalled();
-  expect(wrapper.state('tokenName')).toBe('lightsaber-9000');
-});
+  expect(
+    screen.getByRole('heading', { name: 'onboarding.token.generate.PROJECT_ANALYSIS_TOKEN' })
+  ).toBeInTheDocument();
+  expect(screen.getByText('onboarding.token.text.PROJECT_ANALYSIS_TOKEN')).toBeInTheDocument();
 
-it('should get a new token', async () => {
-  const wrapper = shallowRender();
-  const { getNewToken } = wrapper.instance();
+  // Renders form correctly.
+  await waitFor(() =>
+    expect(screen.getByLabelText('onboarding.token.name.label')).toBeInTheDocument()
+  );
+  // Should be getByLabelText(), but this is due to a limitation with React Select.
+  expect(screen.getByText('users.tokens.expires_in')).toBeInTheDocument();
+  expect(screen.getByRole('button', { name: 'continue' })).toBeInTheDocument();
+
+  // Sets a default token name.
+  const tokenNameInput = screen.getByLabelText('onboarding.token.name.label');
+  expect(tokenNameInput).toHaveValue('Analyze "Foo" 1');
+
+  // Change name and expiration date.
+  await typeInField(user, tokenNameInput, 'new token name');
+  await user.click(screen.getByText('users.tokens.expiration.30'));
+  await user.click(screen.getByText('users.tokens.expiration.365'));
+
+  // Generate token.
+  await clickButton(user, 'onboarding.token.generate');
+  let lastToken = tokenMock.getLastToken();
+  if (lastToken === undefined) {
+    throw new Error("Couldn't find the latest generated token.");
+  }
+
+  expect(lastToken.type).toBe(TokenType.Project);
+  expect(lastToken.expirationDate).toBe(computeTokenExpirationDate(365));
+  expect(screen.getByRole('alert')).toHaveTextContent(
+    `users.tokens.new_token_created.${lastToken.token}`
+  );
+  expect(screen.getByRole('button', { name: 'copy_to_clipboard' })).toBeInTheDocument();
 
-  getNewToken();
-  await waitAndUpdate(wrapper);
+  // Revoke token.
+  await clickButton(user, 'users.tokens.revoke_token');
+  expect(tokenMock.tokens.map(t => t.name)).not.toContain(lastToken.name);
 
-  expect(generateToken).toHaveBeenCalled();
-  expect(wrapper.state('token')).toBe('token_value');
+  // Generate a new token.
+  await typeInField(
+    user,
+    screen.getByLabelText('onboarding.token.name.label'),
+    'another token name'
+  );
+  await clickButton(user, 'onboarding.token.generate');
+
+  lastToken = tokenMock.getLastToken();
+  if (lastToken === undefined) {
+    throw new Error("Couldn't find the latest generated token.");
+  }
+  expect(lastToken.type).toBe(TokenType.Project);
+  expect(lastToken.expirationDate).toBe(computeTokenExpirationDate(365));
+  expect(screen.getByRole('alert')).toHaveTextContent(
+    `users.tokens.new_token_created.${lastToken.token}`
+  );
 });
 
-it('should handle token revocation', async () => {
-  const wrapper = shallowRender();
-  const { getTokensAndName, handleTokenRevoke } = wrapper.instance();
+it('should allow setting a preferred token type', async () => {
+  renderEditTokenModal({
+    preferredTokenType: TokenType.Global,
+    currentUser: mockLoggedInUser({ permissions: { global: [Permissions.Scan] } })
+  });
+  const user = userEvent.setup();
 
-  getTokensAndName();
-  await waitAndUpdate(wrapper);
-  handleTokenRevoke();
-  await waitAndUpdate(wrapper);
+  await waitFor(() =>
+    expect(screen.getByLabelText('onboarding.token.name.label')).toBeInTheDocument()
+  );
 
-  expect(revokeToken).toHaveBeenCalled();
-  expect(wrapper.state('token')).toBe('');
-  expect(wrapper.state('tokenName')).toBe('');
+  await clickButton(user, 'onboarding.token.generate');
+  const lastToken = tokenMock.getLastToken();
+  if (lastToken === undefined) {
+    throw new Error("Couldn't find the latest generated token.");
+  }
+  expect(lastToken.type).toBe(TokenType.Global);
 });
 
-it('should handle change on user input', () => {
-  const wrapper = shallowRender();
-  const instance = wrapper.instance();
+it('should fallback to project tokens if the user cannot generate global tokens', async () => {
+  renderEditTokenModal({
+    preferredTokenType: TokenType.Global
+  });
+  const user = userEvent.setup();
+
+  await waitFor(() =>
+    expect(screen.getByLabelText('onboarding.token.name.label')).toBeInTheDocument()
+  );
 
-  instance.handleTokenNameChange(mockEvent({ target: { value: 'my-token' } }));
-  expect(wrapper.state('tokenName')).toBe('my-token');
+  await clickButton(user, 'onboarding.token.generate');
+  const lastToken = tokenMock.getLastToken();
+  if (lastToken === undefined) {
+    throw new Error("Couldn't find the latest generated token.");
+  }
+  expect(lastToken.type).toBe(TokenType.Project);
 });
 
-function shallowRender(props: Partial<EditTokenModal['props']> = {}) {
-  return shallow<EditTokenModal>(
+function renderEditTokenModal(props: Partial<EditTokenModal['props']> = {}) {
+  return renderComponent(
     <EditTokenModal
-      component={mockComponent()}
+      component={mockComponent({ name: 'Foo' })}
       currentUser={mockLoggedInUser()}
       onClose={jest.fn()}
       {...props}
     />
   );
 }
+
+async function clickButton(user: UserEvent, name: string) {
+  await user.click(screen.getByRole('button', { name }));
+}
+
+async function typeInField(user: UserEvent, input: HTMLElement, value: string) {
+  await user.clear(input);
+  await user.type(input, value);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap
deleted file mode 100644 (file)
index 25a6c4c..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<Step
-  finished={false}
-  open={true}
-  renderForm={[Function]}
-  stepNumber={1}
-  stepTitle="onboarding.tutorial.ci_outro.all_set.title"
-/>
-`;
-
-exports[`should render correctly: step content 1`] = `
-<div
-  className="boxed-group-inner"
->
-  <withAppStateContext(AllSet)
-    alm="azure"
-    willRefreshAutomatically={true}
-  />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/EditTokenModal-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/EditTokenModal-test.tsx.snap
deleted file mode 100644 (file)
index 98ffd84..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<SimpleModal
-  header="onboarding.token.generate_project_token"
-  onClose={[MockFunction]}
-  onSubmit={[MockFunction]}
->
-  <Component />
-</SimpleModal>
-`;
index 1e188e0d498ee7372906deb2723bfab9b78c139c..e386e0596d6849b80a01e976d99ba6bd83334f53 100644 (file)
@@ -16,9 +16,7 @@ exports[`renders 1`] = `
       First Step
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div>
       form
     </div>
@@ -51,12 +49,5 @@ exports[`renders 2`] = `
   <div
     className="boxed-group-inner"
   />
-  <div
-    className="hidden"
-  >
-    <div>
-      form
-    </div>
-  </div>
 </div>
 `;
index f7a17687227f1be638f3742f440424147bd746e4..3119466d5df1946c3860011e464df681e1121d0b 100644 (file)
@@ -14,7 +14,7 @@ exports[`should render correctly 1`] = `
             className="spacer-left"
             onClick={[Function]}
           >
-            onboarding.token.generate_token
+            onboarding.token.generate.long
           </Button>,
           "field": <strong>
             onboarding.tutorial.env_variables.field
index 63f9725f011872377812fa9441b289214f23a90d..d4ae7963b23c66ae92254879305305c59d742a81 100644 (file)
@@ -35,6 +35,7 @@ import Radio from '../../controls/Radio';
 import Select from '../../controls/Select';
 import AlertErrorIcon from '../../icons/AlertErrorIcon';
 import AlertSuccessIcon from '../../icons/AlertSuccessIcon';
+import ProjectTokenScopeInfo from '../components/ProjectTokenScopeInfo';
 import Step from '../components/Step';
 import { getUniqueTokenName } from '../utils';
 
@@ -197,20 +198,20 @@ export default class TokenStep extends React.PureComponent<Props, State> {
             checked={selection === 'generate'}
             onCheck={this.handleModeChange}
             value="generate">
-            {translate('onboarding.token.generate_project_token')}
+            {translate('onboarding.token.generate', TokenType.Project)}
           </Radio>
         ) : (
-          translate('onboarding.token.generate_project_token')
+          translate('onboarding.token.generate', TokenType.Project)
         )}
         {selection === 'generate' && (
           <div className="big-spacer-top">
             <form className="display-flex-center" onSubmit={this.handleTokenGenerate}>
               <div className="display-flex-column">
                 <label className="h3" htmlFor="generate-token-input">
-                  {translate('onboarding.token.generate_project_token.label')}
+                  {translate('onboarding.token.name.label')}
                   <DocumentationTooltip
                     className="spacer-left"
-                    content={translate('onboarding.token.generate_project_token.help')}
+                    content={translate('onboarding.token.name.help')}
                     links={[
                       {
                         href: '/documentation/user-guide/user-token/',
@@ -253,6 +254,7 @@ export default class TokenStep extends React.PureComponent<Props, State> {
                 </div>
               </div>
             </form>
+            <ProjectTokenScopeInfo className="width-50" />
           </div>
         )}
       </div>
index caeabbe63ae99d93672061b2a0a3b1212a63b5e2..e0696eebd2880f15c8644ee62ff2293aa3f495e6 100644 (file)
@@ -16,9 +16,7 @@ exports[`generates token 1`] = `
       onboarding.token.header
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div
       className="boxed-group-inner"
     >
@@ -29,7 +27,7 @@ exports[`generates token 1`] = `
             onCheck={[Function]}
             value="generate"
           >
-            onboarding.token.generate_project_token
+            onboarding.token.generate.PROJECT_ANALYSIS_TOKEN
           </Radio>
           <div
             className="big-spacer-top"
@@ -45,10 +43,10 @@ exports[`generates token 1`] = `
                   className="h3"
                   htmlFor="generate-token-input"
                 >
-                  onboarding.token.generate_project_token.label
+                  onboarding.token.name.label
                   <DocumentationTooltip
                     className="spacer-left"
-                    content="onboarding.token.generate_project_token.help"
+                    content="onboarding.token.name.help"
                     links={
                       Array [
                         Object {
@@ -122,6 +120,9 @@ exports[`generates token 1`] = `
                 </div>
               </div>
             </form>
+            <ProjectTokenScopeInfo
+              className="width-50"
+            />
           </div>
         </div>
         <div
@@ -175,9 +176,7 @@ exports[`generates token 2`] = `
       onboarding.token.header
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div
       className="boxed-group-inner"
     >
@@ -188,7 +187,7 @@ exports[`generates token 2`] = `
             onCheck={[Function]}
             value="generate"
           >
-            onboarding.token.generate_project_token
+            onboarding.token.generate.PROJECT_ANALYSIS_TOKEN
           </Radio>
           <div
             className="big-spacer-top"
@@ -204,10 +203,10 @@ exports[`generates token 2`] = `
                   className="h3"
                   htmlFor="generate-token-input"
                 >
-                  onboarding.token.generate_project_token.label
+                  onboarding.token.name.label
                   <DocumentationTooltip
                     className="spacer-left"
-                    content="onboarding.token.generate_project_token.help"
+                    content="onboarding.token.name.help"
                     links={
                       Array [
                         Object {
@@ -278,6 +277,9 @@ exports[`generates token 2`] = `
                 </div>
               </div>
             </form>
+            <ProjectTokenScopeInfo
+              className="width-50"
+            />
           </div>
         </div>
         <div
@@ -331,9 +333,7 @@ exports[`generates token 3`] = `
       onboarding.token.header
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div
       className="boxed-group-inner"
     >
@@ -405,9 +405,7 @@ exports[`revokes token 1`] = `
       onboarding.token.header
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div
       className="boxed-group-inner"
     >
@@ -479,9 +477,7 @@ exports[`revokes token 2`] = `
       onboarding.token.header
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div
       className="boxed-group-inner"
     >
@@ -552,9 +548,7 @@ exports[`revokes token 3`] = `
       onboarding.token.header
     </h2>
   </div>
-  <div
-    className=""
-  >
+  <div>
     <div
       className="boxed-group-inner"
     >
@@ -565,7 +559,7 @@ exports[`revokes token 3`] = `
             onCheck={[Function]}
             value="generate"
           >
-            onboarding.token.generate_project_token
+            onboarding.token.generate.PROJECT_ANALYSIS_TOKEN
           </Radio>
           <div
             className="big-spacer-top"
@@ -581,10 +575,10 @@ exports[`revokes token 3`] = `
                   className="h3"
                   htmlFor="generate-token-input"
                 >
-                  onboarding.token.generate_project_token.label
+                  onboarding.token.name.label
                   <DocumentationTooltip
                     className="spacer-left"
-                    content="onboarding.token.generate_project_token.help"
+                    content="onboarding.token.name.help"
                     links={
                       Array [
                         Object {
@@ -658,6 +652,9 @@ exports[`revokes token 3`] = `
                 </div>
               </div>
             </form>
+            <ProjectTokenScopeInfo
+              className="width-50"
+            />
           </div>
         </div>
         <div
index 597d2a798021e3a6319ef4c13ffe8d0996c824f2..0ba3c484d6d6d65445ad6de4880ec4ab7c397956 100644 (file)
@@ -21,7 +21,6 @@ import { render, RenderResult } from '@testing-library/react';
 import * as React from 'react';
 import { HelmetProvider } from 'react-helmet-async';
 import { IntlProvider } from 'react-intl';
-
 import { MemoryRouter, Outlet, parsePath, Route, Routes } from 'react-router-dom';
 import AdminContext from '../app/components/AdminContext';
 import AppStateContextProvider from '../app/components/app-state/AppStateContextProvider';
@@ -39,7 +38,7 @@ import { CurrentUser } from '../types/users';
 import { DEFAULT_METRICS } from './mocks/metrics';
 import { mockAppState, mockCurrentUser } from './testMocks';
 
-interface RenderContext {
+export interface RenderContext {
   metrics?: Dict<Metric>;
   appState?: AppState;
   languages?: Languages;
index 99e9a317cd640570ffb798d162462f8602da2be7..3bacb7f7a63636f218c871f74e41d6a21d14b46d 100644 (file)
@@ -69,6 +69,7 @@ directory=Directory
 dismiss=Dismiss
 dismiss_permanently=Dismiss permanently
 display=Display
+documentation=documentation
 download_verb=Download
 duplications=Duplications
 end_date=End Date
@@ -3561,19 +3562,21 @@ onboarding.create_project.gitlab.link=See on GitLab
 
 onboarding.token.header=Provide a token
 onboarding.token.text=The token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point in time in your {link}.
-onboarding.project_token.text=The project token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point in time in your {link}.
+onboarding.token.text.PROJECT_ANALYSIS_TOKEN=The project token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point in time in your {link}.
+onboarding.token.text.GLOBAL_ANALYSIS_TOKEN=The global token is used to identify you when an analysis is performed. If it has been compromised, you can revoke it at any point in time in your {link}.
 onboarding.token.text.user_account=user account
 onboarding.token.generate=Generate
-onboarding.token.placeholder=Enter a name for your token
-onboarding.token.generate_token=Generate a token
-onboarding.token.generate_token.placeholder=Token name
-onboarding.token.generate_project_token=Generate a project token
-onboarding.token.generate_project_token.label=Token name
+onboarding.token.generate.long=Generate a token
+onboarding.token.generate.PROJECT_ANALYSIS_TOKEN=Generate a project token
+onboarding.token.generate.GLOBAL_ANALYSIS_TOKEN=Generate a global token
 onboarding.token.use_existing_token=Use existing token
 onboarding.token.use_existing_token.label=Existing token value
-onboarding.token.invalid_format=The token you have entered has invalid format.
 onboarding.token.use_existing_token.help=Paste an existing token value into the input field.
-onboarding.token.generate_project_token.help=Enter a name for your project token
+onboarding.token.warning_project_token_scope=Please note that this token will only allow you to analyze the current project. If you want to use the same token to analyze multiple projects, you need to generate a global token in your {link}. See the {doc_link} for more information.
+onboarding.token.name.label=Token name
+onboarding.token.name.placeholder=Enter a name for your token
+onboarding.token.name.help=Enter a name for your project token
+onboarding.token.invalid_format=The token you have entered has invalid format.
 
 onboarding.tutorial.env_variables=In the {field} field, enter {value} {extra}
 onboarding.tutorial.env_variables.field=Value