]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13888 SONAR-13890 SONAR-13889 Small improvements on Jenkins tutorial
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Tue, 10 Aug 2021 06:18:26 +0000 (08:18 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 11 Aug 2021 20:08:07 +0000 (20:08 +0000)
- Make next step buttons easier to spot
- Correct label for Webhook creation in GitHub
- Make tutorial ending a stand-alone step for increased clarity
- Make pre-requisites more prominent

24 files changed:
server/sonar-web/src/main/js/app/theme.js
server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx
server/sonar-web/src/main/js/components/tutorials/components/AllSetStep.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSetStep-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSetStep-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/PipelineStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepBitbucket.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStepGithub.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/MultiBranchPipelineStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PipelineStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStep-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStepBitbucket-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/WebhookStepGithub-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 6c2a97a00d3ee55bca3a2c3635e45bea712238f9..21528195573bd8a48c290c874dfb731ef187bd1d 100644 (file)
@@ -19,6 +19,7 @@
  */
 // IMPORTANT: any change in this file requires restart of the dev server
 const grid = 8;
+const baseFontSizeRaw = 13;
 
 module.exports = {
   colors: {
@@ -143,7 +144,7 @@ module.exports = {
   sizes: {
     gridSize: `${grid}px`,
 
-    baseFontSize: '13px',
+    baseFontSize: `${baseFontSizeRaw}px`,
     verySmallFontSize: '10px',
     smallFontSize: '12px',
     mediumFontSize: '14px',
@@ -168,6 +169,7 @@ module.exports = {
 
   rawSizes: {
     grid,
+    baseFontSizeRaw,
     globalNavHeightRaw: 6 * grid,
     globalNavContentHeightRaw: 4 * grid,
     contextNavHeightRaw: 9 * grid
index 2e0c1e3d81876c2cf5a240e47f0659e1df8311f5..ad3e794c589d9b3ba24abe197f8039692c008bdb 100644 (file)
@@ -36,50 +36,56 @@ export function AllSet(props: AllSetProps) {
   } = props;
 
   return (
-    <div className="abs-width-600">
-      <p className="big-spacer-bottom">
-        <SentenceWithHighlights
-          highlightKeys={['all_set']}
-          translationKey="onboarding.tutorial.ci_outro.all_set"
-        />
-      </p>
-      <div className="display-flex-row big-spacer-bottom">
-        <div>
-          <img
-            alt="" // Should be ignored by screen readers
-            className="big-spacer-right"
-            width={30}
-            src={`${getBaseUrl()}/images/tutorials/commit.svg`}
+    <>
+      <div className="abs-width-600">
+        <p className="big-spacer-bottom">
+          <SentenceWithHighlights
+            highlightKeys={['all_set']}
+            translationKey="onboarding.tutorial.ci_outro.all_set"
           />
+        </p>
+        <div className="display-flex-row big-spacer-bottom">
+          <div>
+            <img
+              alt="" // Should be ignored by screen readers
+              className="big-spacer-right"
+              width={30}
+              src={`${getBaseUrl()}/images/tutorials/commit.svg`}
+            />
+          </div>
+          <div>
+            <p className="little-spacer-bottom">
+              <strong>{translate('onboarding.tutorial.ci_outro.commit')}</strong>
+            </p>
+            <p>
+              {branchesEnabled
+                ? translate('onboarding.tutorial.ci_outro.commit.why', alm)
+                : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')}
+            </p>
+          </div>
         </div>
-        <div>
-          <p className="little-spacer-bottom">
-            <strong>{translate('onboarding.tutorial.ci_outro.commit')}</strong>
-          </p>
-          <p>
-            {branchesEnabled
-              ? translate('onboarding.tutorial.ci_outro.commit.why', alm)
-              : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')}
-          </p>
+        <div className="display-flex-row">
+          <div>
+            <img
+              alt="" // Should be ignored by screen readers
+              className="big-spacer-right"
+              width={30}
+              src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
+            />
+          </div>
+          <div>
+            <p className="little-spacer-bottom">
+              <strong>{translate('onboarding.tutorial.ci_outro.refresh')}</strong>
+            </p>
+            <p>{translate('onboarding.tutorial.ci_outro.refresh.why')}</p>
+          </div>
         </div>
       </div>
-      <div className="display-flex-row huge-spacer-bottom">
-        <div>
-          <img
-            alt="" // Should be ignored by screen readers
-            className="big-spacer-right"
-            width={30}
-            src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
-          />
-        </div>
-        <div>
-          <p className="little-spacer-bottom">
-            <strong>{translate('onboarding.tutorial.ci_outro.refresh')}</strong>
-          </p>
-          <p>{translate('onboarding.tutorial.ci_outro.refresh.why')}</p>
-        </div>
+      <div className="huge-spacer-bottom huge-spacer-top big-padded-top text-muted display-flex-center display-flex-justify-center">
+        <i className="spinner spacer-right" />
+        {translate('onboarding.tutorial.ci_outro.waiting_for_fist_analysis')}
       </div>
-    </div>
+    </>
   );
 }
 
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/AllSetStep.tsx b/server/sonar-web/src/main/js/components/tutorials/components/AllSetStep.tsx
new file mode 100644 (file)
index 0000000..b18fcbf
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { AlmKeys } from '../../../types/alm-settings';
+import AllSet from './AllSet';
+import Step from './Step';
+
+export interface AllSetStepProps {
+  alm: AlmKeys;
+  open: boolean;
+  stepNumber: number;
+}
+
+export default function AllSetStep(props: AllSetStepProps) {
+  const { alm, open, stepNumber } = props;
+  return (
+    <Step
+      finished={false}
+      open={open}
+      renderForm={() => (
+        <div className="boxed-group-inner">
+          <AllSet alm={alm} />
+        </div>
+      )}
+      stepNumber={stepNumber}
+      stepTitle={translate('onboarding.tutorial.ci_outro.all_set.title')}
+    />
+  );
+}
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
new file mode 100644 (file)
index 0000000..9982316
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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} {...props} />
+  );
+}
index 977d4d7c6792acaa47f6ee942ce77b1a3e27c220..5b3d13d45d1069753aaea9baab15419af0e07250 100644 (file)
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should render correctly 1`] = `
-<div
-  className="abs-width-600"
->
-  <p
-    className="big-spacer-bottom"
-  >
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "all_set",
-        ]
-      }
-      translationKey="onboarding.tutorial.ci_outro.all_set"
-    />
-  </p>
+<Fragment>
   <div
-    className="display-flex-row big-spacer-bottom"
+    className="abs-width-600"
   >
-    <div>
-      <img
-        alt=""
-        className="big-spacer-right"
-        src="/images/tutorials/commit.svg"
-        width={30}
+    <p
+      className="big-spacer-bottom"
+    >
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "all_set",
+          ]
+        }
+        translationKey="onboarding.tutorial.ci_outro.all_set"
       />
+    </p>
+    <div
+      className="display-flex-row big-spacer-bottom"
+    >
+      <div>
+        <img
+          alt=""
+          className="big-spacer-right"
+          src="/images/tutorials/commit.svg"
+          width={30}
+        />
+      </div>
+      <div>
+        <p
+          className="little-spacer-bottom"
+        >
+          <strong>
+            onboarding.tutorial.ci_outro.commit
+          </strong>
+        </p>
+        <p>
+          onboarding.tutorial.ci_outro.commit.why.github
+        </p>
+      </div>
     </div>
-    <div>
-      <p
-        className="little-spacer-bottom"
-      >
-        <strong>
-          onboarding.tutorial.ci_outro.commit
-        </strong>
-      </p>
-      <p>
-        onboarding.tutorial.ci_outro.commit.why.github
-      </p>
+    <div
+      className="display-flex-row"
+    >
+      <div>
+        <img
+          alt=""
+          className="big-spacer-right"
+          src="/images/tutorials/refresh.svg"
+          width={30}
+        />
+      </div>
+      <div>
+        <p
+          className="little-spacer-bottom"
+        >
+          <strong>
+            onboarding.tutorial.ci_outro.refresh
+          </strong>
+        </p>
+        <p>
+          onboarding.tutorial.ci_outro.refresh.why
+        </p>
+      </div>
     </div>
   </div>
   <div
-    className="display-flex-row huge-spacer-bottom"
+    className="huge-spacer-bottom huge-spacer-top big-padded-top text-muted display-flex-center display-flex-justify-center"
   >
-    <div>
-      <img
-        alt=""
-        className="big-spacer-right"
-        src="/images/tutorials/refresh.svg"
-        width={30}
-      />
-    </div>
-    <div>
-      <p
-        className="little-spacer-bottom"
-      >
-        <strong>
-          onboarding.tutorial.ci_outro.refresh
-        </strong>
-      </p>
-      <p>
-        onboarding.tutorial.ci_outro.refresh.why
-      </p>
-    </div>
+    <i
+      className="spinner spacer-right"
+    />
+    onboarding.tutorial.ci_outro.waiting_for_fist_analysis
   </div>
-</div>
+</Fragment>
 `;
 
 exports[`should render correctly: without branch 1`] = `
-<div
-  className="abs-width-600"
->
-  <p
-    className="big-spacer-bottom"
-  >
-    <SentenceWithHighlights
-      highlightKeys={
-        Array [
-          "all_set",
-        ]
-      }
-      translationKey="onboarding.tutorial.ci_outro.all_set"
-    />
-  </p>
+<Fragment>
   <div
-    className="display-flex-row big-spacer-bottom"
+    className="abs-width-600"
   >
-    <div>
-      <img
-        alt=""
-        className="big-spacer-right"
-        src="/images/tutorials/commit.svg"
-        width={30}
+    <p
+      className="big-spacer-bottom"
+    >
+      <SentenceWithHighlights
+        highlightKeys={
+          Array [
+            "all_set",
+          ]
+        }
+        translationKey="onboarding.tutorial.ci_outro.all_set"
       />
+    </p>
+    <div
+      className="display-flex-row big-spacer-bottom"
+    >
+      <div>
+        <img
+          alt=""
+          className="big-spacer-right"
+          src="/images/tutorials/commit.svg"
+          width={30}
+        />
+      </div>
+      <div>
+        <p
+          className="little-spacer-bottom"
+        >
+          <strong>
+            onboarding.tutorial.ci_outro.commit
+          </strong>
+        </p>
+        <p>
+          onboarding.tutorial.ci_outro.commit.why.no_branches
+        </p>
+      </div>
     </div>
-    <div>
-      <p
-        className="little-spacer-bottom"
-      >
-        <strong>
-          onboarding.tutorial.ci_outro.commit
-        </strong>
-      </p>
-      <p>
-        onboarding.tutorial.ci_outro.commit.why.no_branches
-      </p>
+    <div
+      className="display-flex-row"
+    >
+      <div>
+        <img
+          alt=""
+          className="big-spacer-right"
+          src="/images/tutorials/refresh.svg"
+          width={30}
+        />
+      </div>
+      <div>
+        <p
+          className="little-spacer-bottom"
+        >
+          <strong>
+            onboarding.tutorial.ci_outro.refresh
+          </strong>
+        </p>
+        <p>
+          onboarding.tutorial.ci_outro.refresh.why
+        </p>
+      </div>
     </div>
   </div>
   <div
-    className="display-flex-row huge-spacer-bottom"
+    className="huge-spacer-bottom huge-spacer-top big-padded-top text-muted display-flex-center display-flex-justify-center"
   >
-    <div>
-      <img
-        alt=""
-        className="big-spacer-right"
-        src="/images/tutorials/refresh.svg"
-        width={30}
-      />
-    </div>
-    <div>
-      <p
-        className="little-spacer-bottom"
-      >
-        <strong>
-          onboarding.tutorial.ci_outro.refresh
-        </strong>
-      </p>
-      <p>
-        onboarding.tutorial.ci_outro.refresh.why
-      </p>
-    </div>
+    <i
+      className="spinner spacer-right"
+    />
+    onboarding.tutorial.ci_outro.waiting_for_fist_analysis
   </div>
-</div>
+</Fragment>
 `;
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
new file mode 100644 (file)
index 0000000..12deb83
--- /dev/null
@@ -0,0 +1,21 @@
+// 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"
+>
+  <Connect(withAppState(AllSet))
+    alm="azure"
+  />
+</div>
+`;
index f88416c3d257bf340c4a0014f0413133b00bcffc..443bc98e516cc8a88796bb0bc426a2c4b7e0c3b1 100644 (file)
@@ -27,6 +27,7 @@ import {
   AlmSettingsInstance,
   ProjectAlmBindingResponse
 } from '../../../types/alm-settings';
+import AllSetStep from '../components/AllSetStep';
 import JenkinsfileStep from './JenkinsfileStep';
 import MultiBranchPipelineStep from './MultiBranchPipelineStep';
 import PipelineStep from './PipelineStep';
@@ -49,7 +50,8 @@ enum Steps {
   PreRequisites = 1,
   MultiBranchPipeline = 2,
   Webhook = 3,
-  Jenkinsfile = 4
+  Jenkinsfile = 4,
+  AllSet = 5
 }
 
 const USER_SETTING_SKIP_BITBUCKET_PREREQS = 'tutorials.jenkins.skipBitbucketPreReqs';
@@ -135,11 +137,15 @@ export function JenkinsTutorial(props: JenkinsTutorialProps) {
           />
 
           <JenkinsfileStep
-            alm={alm}
             component={component}
             baseUrl={baseUrl}
+            finished={step > Steps.Jenkinsfile}
+            onDone={() => setStep(Steps.AllSet)}
+            onOpen={() => setStep(Steps.Jenkinsfile)}
             open={step === Steps.Jenkinsfile}
           />
+
+          <AllSetStep alm={alm} open={step === Steps.AllSet} stepNumber={4} />
         </>
       )}
     </>
index c52cbde566709e3597add1c3f6a1696234373878..6e8e84e2b0b1c1aac0e08595953d4050127e6119 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { Button } from 'sonar-ui-common/components/controls/buttons';
+import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate } from 'sonar-ui-common/helpers/l10n';
-import { AlmKeys } from '../../../types/alm-settings';
+import { rawSizes } from '../../../app/theme';
 import { withCLanguageFeature } from '../../hoc/withCLanguageFeature';
-import AllSet from '../components/AllSet';
 import RenderOptions from '../components/RenderOptions';
 import Step from '../components/Step';
 import { BuildTools } from '../types';
@@ -33,10 +34,12 @@ import Maven from './buildtool-steps/Maven';
 import Other from './buildtool-steps/Other';
 
 export interface JenkinsfileStepProps {
-  alm: AlmKeys;
   baseUrl: string;
   component: T.Component;
   hasCLanguageFeature: boolean;
+  finished: boolean;
+  onDone: () => void;
+  onOpen: () => void;
   open: boolean;
 }
 
@@ -56,7 +59,7 @@ const BUILDTOOL_COMPONENT_MAP: {
 };
 
 export function JenkinsfileStep(props: JenkinsfileStepProps) {
-  const { alm, component, hasCLanguageFeature, baseUrl, open } = props;
+  const { component, hasCLanguageFeature, baseUrl, finished, open } = props;
   const [buildTool, setBuildTool] = React.useState<BuildTools>();
   const buildToolOrder = Object.keys(BUILDTOOL_COMPONENT_MAP);
   if (!hasCLanguageFeature) {
@@ -64,7 +67,8 @@ export function JenkinsfileStep(props: JenkinsfileStepProps) {
   }
   return (
     <Step
-      finished={false}
+      finished={finished}
+      onOpen={props.onOpen}
       open={open}
       renderForm={() => (
         <div className="boxed-group-inner">
@@ -88,10 +92,10 @@ export function JenkinsfileStep(props: JenkinsfileStepProps) {
               React.createElement(BUILDTOOL_COMPONENT_MAP[buildTool], { component, baseUrl })}
           </ol>
           {buildTool !== undefined && (
-            <>
-              <hr className="huge-spacer-top huge-spacer-bottom" />
-              <AllSet alm={alm} />
-            </>
+            <Button className="big-spacer-top" onClick={props.onDone}>
+              {translate('tutorials.finish')}
+              <ChevronRightIcon size={rawSizes.baseFontSizeRaw} />
+            </Button>
           )}
         </div>
       )}
index 4cea006cd28eb5abde9545ff96c4ddcb668df1d4..13c866859e257289a5792f467a8cb7c3f3ddeb13 100644 (file)
@@ -19,7 +19,9 @@
  */
 import * as React from 'react';
 import { Button } from 'sonar-ui-common/components/controls/buttons';
+import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { rawSizes } from '../../../app/theme';
 import {
   AlmKeys,
   AlmSettingsInstance,
@@ -205,7 +207,10 @@ export default function MultiBranchPipelineStep(props: MultiBranchPipelineStepPr
               />
             </li>
           </ol>
-          <Button onClick={props.onDone}>{translate('continue')}</Button>
+          <Button className="big-spacer-top" onClick={props.onDone}>
+            {translate('continue')}
+            <ChevronRightIcon size={rawSizes.baseFontSizeRaw} />
+          </Button>
         </div>
       )}
       stepNumber={1}
index 3fdbd0d319374534cdd1558e86b7e832151fc977..7123047dd6bf76c1b36137dea243d7ea60cd1cea 100644 (file)
@@ -19,7 +19,9 @@
  */
 import * as React from 'react';
 import { Button } from 'sonar-ui-common/components/controls/buttons';
+import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { rawSizes } from '../../../app/theme';
 import { AlmKeys } from '../../../types/alm-settings';
 import LabelActionPair from '../components/LabelActionPair';
 import SentenceWithHighlights from '../components/SentenceWithHighlights';
@@ -114,7 +116,10 @@ export default function PipelineStep(props: PipelineStepProps) {
               />
             </li>
           </ol>
-          <Button onClick={props.onDone}>{translate('continue')}</Button>
+          <Button className="big-spacer-top" onClick={props.onDone}>
+            {translate('continue')}
+            <ChevronRightIcon size={rawSizes.baseFontSizeRaw} />
+          </Button>
         </div>
       )}
       stepNumber={1}
index 89179a987b65fce6dfc4250c5e7df346db79da04..c488fb4b526654a1baec8159a6146aa5009ed936 100644 (file)
@@ -22,7 +22,10 @@ import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
 import { Button } from 'sonar-ui-common/components/controls/buttons';
 import Checkbox from 'sonar-ui-common/components/controls/Checkbox';
+import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { rawSizes } from '../../../app/theme';
 import { AlmKeys } from '../../../types/alm-settings';
 import SentenceWithHighlights from '../components/SentenceWithHighlights';
 import Step from '../components/Step';
@@ -47,12 +50,12 @@ export default function PreRequisitesStep(props: PreRequisitesStepProps) {
       open={open}
       renderForm={() => (
         <div className="boxed-group-inner">
-          <p className="big-spacer-bottom">
+          <Alert className="big-spacer-bottom" variant="warning">
             <SentenceWithHighlights
-              highlightKeys={['must_have']}
+              highlightKeys={['installed', 'configured']}
               translationKey="onboarding.tutorial.with.jenkins.prereqs.intro"
             />
-          </p>
+          </Alert>
           <ul className="list-styled big-spacer-bottom">
             {branchesEnabled && (
               <li>
@@ -98,8 +101,9 @@ export default function PreRequisitesStep(props: PreRequisitesStepProps) {
               onCheck={props.onChangeSkipNextTime}
             />
           </p>
-          <Button onClick={props.onDone}>
+          <Button className="big-spacer-top" onClick={props.onDone}>
             {translate('onboarding.tutorial.with.jenkins.prereqs.done')}
+            <ChevronRightIcon size={rawSizes.baseFontSizeRaw} />
           </Button>
         </div>
       )}
index 4c3d76ec4ec7243f7f9e0ad4682fb7b3b728ee8b..e0a9a5cdb2d8aaadd659bbb202e40bee4e606a28 100644 (file)
@@ -20,7 +20,9 @@
 import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import { Button, ButtonLink } from 'sonar-ui-common/components/controls/buttons';
+import ChevronRightIcon from 'sonar-ui-common/components/icons/ChevronRightIcon';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { rawSizes } from '../../../app/theme';
 import {
   AlmKeys,
   AlmSettingsInstance,
@@ -98,7 +100,10 @@ export default function WebhookStep(props: WebhookStepProps) {
             />
           </p>
           <ol className="list-styled">{renderAlmSpecificInstructions(props)}</ol>
-          <Button onClick={props.onDone}>{translate('continue')}</Button>
+          <Button className="big-spacer-top" onClick={props.onDone}>
+            {translate('continue')}
+            <ChevronRightIcon size={rawSizes.baseFontSizeRaw} />
+          </Button>
         </div>
       )}
       stepNumber={2}
index 7da826014b12913a76e6218099745218bd941a61..b5bdfb0f7acebaac77dd8957d1309503468b6188 100644 (file)
@@ -157,7 +157,7 @@ export default function WebhookStepBitbucket(props: WebhookStepBitbucketProps) {
         ) : (
           <SentenceWithHighlights
             highlightKeys={['create']}
-            translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+            translationKey="onboarding.tutorial.with.jenkins.webhook.bitbucket.step3"
           />
         )}
       </li>
index 1315cd0611743b0b7319a7b5bacc26deb5ecbc58..64d20fd3ed727320a36e369ee4c1ca1fb7ec63c9 100644 (file)
@@ -91,8 +91,8 @@ export default function WebhookStepGithub(props: WebhookStepGithubProps) {
       </li>
       <li>
         <SentenceWithHighlights
-          highlightKeys={['create']}
-          translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+          highlightKeys={['add_webhook']}
+          translationKey="onboarding.tutorial.with.jenkins.webhook.github.step3"
         />
       </li>
     </>
index be6fc8508a77b5a3771f60b1b54f6aef2648c6a5..8238457e61997d2af78ecdebd2782f1701474a63 100644 (file)
@@ -20,7 +20,6 @@
 import { shallow, ShallowWrapper } from 'enzyme';
 import * as React from 'react';
 import { mockComponent } from '../../../../helpers/testMocks';
-import { AlmKeys } from '../../../../types/alm-settings';
 import RenderOptions from '../../components/RenderOptions';
 import Step from '../../components/Step';
 import { renderStepContent } from '../../test-utils';
@@ -72,10 +71,12 @@ function selectBuildTool(wrapper: ShallowWrapper<JenkinsfileStepProps>, tool: Bu
 function shallowRender(props: Partial<JenkinsfileStepProps> = {}) {
   return shallow<JenkinsfileStepProps>(
     <JenkinsfileStep
-      alm={AlmKeys.BitbucketCloud}
       baseUrl="nice_url"
       component={mockComponent()}
       hasCLanguageFeature={false}
+      finished={false}
+      onDone={jest.fn()}
+      onOpen={jest.fn()}
       open={true}
       {...props}
     />
index 57eb0d2e22bcc8956f8c642796055bfb31deba0a..e8c3dfb49ce4c39758fc4051836afc231d5800e1 100644 (file)
@@ -46,7 +46,6 @@ exports[`should render correctly: branches not enabled 1`] = `
     }
   />
   <Connect(withCLanguageFeature(JenkinsfileStep))
-    alm="bitbucket"
     baseUrl=""
     component={
       Object {
@@ -70,7 +69,15 @@ exports[`should render correctly: branches not enabled 1`] = `
         "tags": Array [],
       }
     }
+    finished={false}
+    onDone={[Function]}
+    onOpen={[Function]}
+    open={false}
+  />
+  <AllSetStep
+    alm="bitbucket"
     open={false}
+    stepNumber={4}
   />
 </Fragment>
 `;
@@ -130,7 +137,6 @@ exports[`should render correctly: default 1`] = `
     }
   />
   <Connect(withCLanguageFeature(JenkinsfileStep))
-    alm="bitbucket"
     baseUrl=""
     component={
       Object {
@@ -154,7 +160,15 @@ exports[`should render correctly: default 1`] = `
         "tags": Array [],
       }
     }
+    finished={false}
+    onDone={[Function]}
+    onOpen={[Function]}
+    open={false}
+  />
+  <AllSetStep
+    alm="bitbucket"
     open={false}
+    stepNumber={4}
   />
 </Fragment>
 `;
index 8ab1bb204200963b7011239ae3265117a6c96a30..62c25169a1a2d66c96e081aefa4bb2dc36b846e5 100644 (file)
@@ -50,14 +50,15 @@ exports[`should render correctly for .NET 1`] = `
       }
     />
   </ol>
-  <React.Fragment>
-    <hr
-      className="huge-spacer-top huge-spacer-bottom"
-    />
-    <Connect(withAppState(AllSet))
-      alm="bitbucketcloud"
+  <Button
+    className="big-spacer-top"
+    onClick={[MockFunction]}
+  >
+    tutorials.finish
+    <ChevronRightIcon
+      size={13}
     />
-  </React.Fragment>
+  </Button>
 </div>
 `;
 
@@ -111,14 +112,15 @@ exports[`should render correctly for Gradle 1`] = `
       }
     />
   </ol>
-  <React.Fragment>
-    <hr
-      className="huge-spacer-top huge-spacer-bottom"
-    />
-    <Connect(withAppState(AllSet))
-      alm="bitbucketcloud"
+  <Button
+    className="big-spacer-top"
+    onClick={[MockFunction]}
+  >
+    tutorials.finish
+    <ChevronRightIcon
+      size={13}
     />
-  </React.Fragment>
+  </Button>
 </div>
 `;
 
@@ -172,14 +174,15 @@ exports[`should render correctly for Maven 1`] = `
       }
     />
   </ol>
-  <React.Fragment>
-    <hr
-      className="huge-spacer-top huge-spacer-bottom"
-    />
-    <Connect(withAppState(AllSet))
-      alm="bitbucketcloud"
+  <Button
+    className="big-spacer-top"
+    onClick={[MockFunction]}
+  >
+    tutorials.finish
+    <ChevronRightIcon
+      size={13}
     />
-  </React.Fragment>
+  </Button>
 </div>
 `;
 
@@ -233,20 +236,22 @@ exports[`should render correctly for Other 1`] = `
       }
     />
   </ol>
-  <React.Fragment>
-    <hr
-      className="huge-spacer-top huge-spacer-bottom"
-    />
-    <Connect(withAppState(AllSet))
-      alm="bitbucketcloud"
+  <Button
+    className="big-spacer-top"
+    onClick={[MockFunction]}
+  >
+    tutorials.finish
+    <ChevronRightIcon
+      size={13}
     />
-  </React.Fragment>
+  </Button>
 </div>
 `;
 
 exports[`should render correctly: Step wrapper 1`] = `
 <Step
   finished={false}
+  onOpen={[MockFunction]}
   open={true}
   renderForm={[Function]}
   stepNumber={3}
@@ -257,6 +262,7 @@ exports[`should render correctly: Step wrapper 1`] = `
 exports[`should render correctly: Step wrapper with C 1`] = `
 <Step
   finished={false}
+  onOpen={[MockFunction]}
   open={true}
   renderForm={[Function]}
   stepNumber={3}
index f23756305d4e0be71b1814487fa016f4d5d5e5c3..8364d95b281a418982b95731cdc6efd39d917218 100644 (file)
@@ -119,9 +119,13 @@ exports[`should render correctly: content for bitbucket 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -234,9 +238,13 @@ exports[`should render correctly: content for bitbucket cloud 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -347,9 +355,13 @@ exports[`should render correctly: content for bitbucket cloud, no binding 1`] =
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -460,9 +472,13 @@ exports[`should render correctly: content for bitbucket, no binding 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -564,9 +580,13 @@ exports[`should render correctly: content for github 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -667,9 +687,13 @@ exports[`should render correctly: content for github, no binding 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -775,9 +799,13 @@ exports[`should render correctly: content for gitlab 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
index 6527408f3d8d415909a6c3a427398f978270f93a..3e9317b8ccb4a4d05fe544e8ddcc1aa49da38361 100644 (file)
@@ -92,9 +92,13 @@ exports[`should render correctly: content 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -218,9 +222,13 @@ exports[`should render correctly: gitlab content 1`] = `
     </li>
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
index 9f3b6834df1b8e86f080083e7b4272b1469947df..e3c6038f47aaddd239fc5dad26e0e9345c89ce30 100644 (file)
@@ -14,18 +14,20 @@ exports[`should render correctly: content 1`] = `
 <div
   className="boxed-group-inner"
 >
-  <p
+  <Alert
     className="big-spacer-bottom"
+    variant="warning"
   >
     <SentenceWithHighlights
       highlightKeys={
         Array [
-          "must_have",
+          "installed",
+          "configured",
         ]
       }
       translationKey="onboarding.tutorial.with.jenkins.prereqs.intro"
     />
-  </p>
+  </Alert>
   <ul
     className="list-styled big-spacer-bottom"
   >
@@ -80,9 +82,13 @@ exports[`should render correctly: content 1`] = `
     />
   </p>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     onboarding.tutorial.with.jenkins.prereqs.done
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -91,18 +97,20 @@ exports[`should render correctly: content for branches disabled 1`] = `
 <div
   className="boxed-group-inner"
 >
-  <p
+  <Alert
     className="big-spacer-bottom"
+    variant="warning"
   >
     <SentenceWithHighlights
       highlightKeys={
         Array [
-          "must_have",
+          "installed",
+          "configured",
         ]
       }
       translationKey="onboarding.tutorial.with.jenkins.prereqs.intro"
     />
-  </p>
+  </Alert>
   <ul
     className="list-styled big-spacer-bottom"
   >
@@ -154,9 +162,13 @@ exports[`should render correctly: content for branches disabled 1`] = `
     />
   </p>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     onboarding.tutorial.with.jenkins.prereqs.done
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -165,18 +177,20 @@ exports[`should render correctly: content for branches disabled, gitlab 1`] = `
 <div
   className="boxed-group-inner"
 >
-  <p
+  <Alert
     className="big-spacer-bottom"
+    variant="warning"
   >
     <SentenceWithHighlights
       highlightKeys={
         Array [
-          "must_have",
+          "installed",
+          "configured",
         ]
       }
       translationKey="onboarding.tutorial.with.jenkins.prereqs.intro"
     />
-  </p>
+  </Alert>
   <ul
     className="list-styled big-spacer-bottom"
   >
@@ -231,9 +245,13 @@ exports[`should render correctly: content for branches disabled, gitlab 1`] = `
     />
   </p>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     onboarding.tutorial.with.jenkins.prereqs.done
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
index 644d1626f6b0faaf69f4f4b5a56a6486f8341dcc..6ab72065f9b1094a313a9494de9b914de29852e9 100644 (file)
@@ -25,9 +25,13 @@ exports[`it should render correctly for azure: content 1`] = `
     className="list-styled"
   />
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -88,9 +92,13 @@ exports[`it should render correctly for bitbucket: content 1`] = `
     />
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -150,9 +158,13 @@ exports[`it should render correctly for bitbucketcloud: content 1`] = `
     />
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -211,9 +223,13 @@ exports[`it should render correctly for github: content 1`] = `
     />
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
@@ -258,9 +274,13 @@ exports[`it should render correctly for gitlab: content 1`] = `
     />
   </ol>
   <Button
+    className="big-spacer-top"
     onClick={[MockFunction]}
   >
     continue
+    <ChevronRightIcon
+      size={13}
+    />
   </Button>
 </div>
 `;
index 7421b105cc4bdfb30abdc662f0956c749b445eb5..e12a985c465c0b08cfee529169d6d88814f673a5 100644 (file)
@@ -77,7 +77,7 @@ exports[`should render correctly for bitbucket 1`] = `
           "create",
         ]
       }
-      translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+      translationKey="onboarding.tutorial.with.jenkins.webhook.bitbucket.step3"
     />
   </li>
 </Fragment>
@@ -150,7 +150,7 @@ exports[`should render correctly for bitbucket: with branches disabled 1`] = `
           "create",
         ]
       }
-      translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+      translationKey="onboarding.tutorial.with.jenkins.webhook.bitbucket.step3"
     />
   </li>
 </Fragment>
@@ -229,7 +229,7 @@ exports[`should render correctly for bitbucket: with no alm binding 1`] = `
           "create",
         ]
       }
-      translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+      translationKey="onboarding.tutorial.with.jenkins.webhook.bitbucket.step3"
     />
   </li>
 </Fragment>
index 0e0a81581b4df29b1560099ea54f5aa04ecdf351..da3946e015cf24c3b6ba7ba9d922648a6cd9b7fc 100644 (file)
@@ -65,10 +65,10 @@ exports[`should render correctly 1`] = `
     <SentenceWithHighlights
       highlightKeys={
         Array [
-          "create",
+          "add_webhook",
         ]
       }
-      translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+      translationKey="onboarding.tutorial.with.jenkins.webhook.github.step3"
     />
   </li>
 </Fragment>
@@ -134,10 +134,10 @@ exports[`should render correctly: with branches disabled 1`] = `
     <SentenceWithHighlights
       highlightKeys={
         Array [
-          "create",
+          "add_webhook",
         ]
       }
-      translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+      translationKey="onboarding.tutorial.with.jenkins.webhook.github.step3"
     />
   </li>
 </Fragment>
@@ -204,10 +204,10 @@ exports[`should render correctly: with no alm binding 1`] = `
     <SentenceWithHighlights
       highlightKeys={
         Array [
-          "create",
+          "add_webhook",
         ]
       }
-      translationKey="onboarding.tutorial.with.jenkins.webhook.step3"
+      translationKey="onboarding.tutorial.with.jenkins.webhook.github.step3"
     />
   </li>
 </Fragment>
index fbb789fd9983408849ff887e0916d6e44ed093e1..859905b76d5f5bf046f7496d198a8e83840bc8a7 100644 (file)
@@ -3477,6 +3477,7 @@ onboarding.analysis.dotnetcore.global.text=As a prerequisite you need to have th
 onboarding.analysis.dotnetcore.global.text.path=Make sure dotnet tools folder is in your path. See dotnet global tools documentation for more information.
 
 onboarding.tutorial.return_to_list=Choose another option
+onboarding.tutorial.ci_outro.all_set.title=You're all set!
 onboarding.tutorial.ci_outro.all_set.sentence={all_set} and ready to improve the quality and security of your code!
 onboarding.tutorial.ci_outro.all_set.sentence.all_set=You're all set
 onboarding.tutorial.ci_outro.commit=Commit and push your code to start the analysis.
@@ -3487,7 +3488,8 @@ onboarding.tutorial.ci_outro.commit.why.bitbucketcloud=Each new push you make on
 onboarding.tutorial.ci_outro.commit.why.azure=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on Azure for you.
 onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube.
 onboarding.tutorial.ci_outro.refresh=This page will then refresh with your analysis results.
-onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration.
+onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration, and check your logs.
+onboarding.tutorial.ci_outro.waiting_for_fist_analysis=Waiting for the first analysis to come in...
 onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code:
 onboarding.tutorial.cfamilly.compilation_database_info=If you have trouble using the build wrapper, you can try using a {link}
 onboarding.tutorial.cfamilly.compilation_database_info.link=compilation database
@@ -3569,8 +3571,9 @@ onboarding.tutorial.with.gitlab_ci.yml.done.links.QG=What is a Quality Gate?
 onboarding.tutorial.with.jenkins.title=Analyze your project with Jenkins
 onboarding.tutorial.with.jenkins.alm_selection.title=Select your DevOps platform
 onboarding.tutorial.with.jenkins.prereqs.title=Prerequisites
-onboarding.tutorial.with.jenkins.prereqs.intro.sentence=To run your project analyses with Jenkins, the following plugins {must_have}
-onboarding.tutorial.with.jenkins.prereqs.intro.sentence.must_have=must be installed and configured:
+onboarding.tutorial.with.jenkins.prereqs.intro.sentence=To run your project analyses with Jenkins, the following plugins must be {installed} and {configured}.
+onboarding.tutorial.with.jenkins.prereqs.intro.sentence.installed=installed
+onboarding.tutorial.with.jenkins.prereqs.intro.sentence.configured=configured
 onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source.bitbucket=Bitbucket Branch Source plugin for Jenkins - version 2.7 or later
 onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source.bitbucketcloud=Bitbucket Branch Source plugin for Jenkins - version 2.7 or later
 onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source.github=GitHub Branch Source plugin for Jenkins - version 2.7.1 or later
@@ -3735,10 +3738,12 @@ onboarding.tutorial.with.jenkins.webhook.github.step2.repo=Pushes
 onboarding.tutorial.with.jenkins.webhook.github.step2.pr=Pull Requests
 onboarding.tutorial.with.jenkins.webhook.gitlab.step2.repo=Push events
 onboarding.tutorial.with.jenkins.webhook.gitlab.step2.mr=Merge request events
-onboarding.tutorial.with.jenkins.webhook.step3.sentence=Click {create}.
-onboarding.tutorial.with.jenkins.webhook.step3.sentence.create=Create
+onboarding.tutorial.with.jenkins.webhook.github.step3.sentence=Click {add_webhook}.
+onboarding.tutorial.with.jenkins.webhook.github.step3.sentence.add_webhook=Add webhook
 onboarding.tutorial.with.jenkins.webhook.bitbucketcloud.step3.sentence=Click {save}.
 onboarding.tutorial.with.jenkins.webhook.bitbucketcloud.step3.sentence.save=Save
+onboarding.tutorial.with.jenkins.webhook.bitbucket.step3.sentence=Click {create}.
+onboarding.tutorial.with.jenkins.webhook.bitbucket.step3.sentence.create=Create
 onboarding.tutorial.with.jenkins.webhook.gitlab.step3.sentence=Click {add_webhook}.
 onboarding.tutorial.with.jenkins.webhook.gitlab.step3.sentence.add_webhook=Add webhook
 onboarding.tutorial.with.jenkins.jenkinsfile.title=Create a Jenkinsfile