]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14934 Improve the manual tutorial
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Tue, 15 Jun 2021 07:43:43 +0000 (09:43 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 17 Jun 2021 20:03:07 +0000 (20:03 +0000)
45 files changed:
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavProjectBindingErrorNotif.tsx
server/sonar-web/src/main/js/app/styles/init/type.css
server/sonar-web/src/main/js/apps/create/project/WrongBindingCountAlert.tsx
server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx
server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/DoneNextSteps.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/DoneNextSteps-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/DoneNextSteps-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/AnalysisCommand.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/ClangGCCCommand.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNet.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNetCore.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNetExecute.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNetFramework.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/ExecScanner.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaGradle.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaMaven.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/Other.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/CLangGCCCommand-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotNet-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotNetExecute-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotNetFramework-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotnetCore-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/ExecScanner-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaGradle-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaMaven-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/Other-test.tsx
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/CLangGCCCommand-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotNetExecute-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotNetFramework-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotnetCore-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/ExecScanner-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/Other-test.tsx.snap
server/sonar-web/src/main/js/components/tutorials/types.ts
server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts
server/sonar-web/src/main/js/helpers/urls.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index e420188b893526af823edd6c435dee255ee77cb4..0308b9ede487b72e67c356f699c073af8ac190fc 100644 (file)
@@ -23,6 +23,7 @@ import { Link } from 'react-router';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import { PULL_REQUEST_DECORATION_BINDING_CATEGORY } from '../../../../apps/settings/components/AdditionalCategoryKeys';
+import { getProjectSettingsUrl } from '../../../../helpers/urls';
 
 export interface ComponentNavProjectBindingErrorNotifProps {
   component: T.Component;
@@ -36,11 +37,7 @@ export function ComponentNavProjectBindingErrorNotif(
 
   if (component.configuration?.showSettings) {
     action = (
-      <Link
-        to={{
-          pathname: '/project/settings',
-          query: { category: PULL_REQUEST_DECORATION_BINDING_CATEGORY, id: component.key }
-        }}>
+      <Link to={getProjectSettingsUrl(component.key, PULL_REQUEST_DECORATION_BINDING_CATEGORY)}>
         {translate('component_navigation.pr_deco.action.check_project_settings')}
       </Link>
     );
index b03a9fcd4df5d260ba9ef999f230e896b0fbe419..0ce0bb1a6473b7639072a397d4c1da05d7aa17bb 100644 (file)
@@ -179,6 +179,10 @@ small,
   font-size: var(--hugeFontSize);
 }
 
+.gigantic {
+  font-size: var(--giganticFontSize);
+}
+
 .zero-font-size {
   font-size: 0 !important;
 }
index 52ff4f36c772ec37f5893acf4379c8f595d3ef00..670a81e7d424e7d817fee8b8e4f348a293e83f55 100644 (file)
@@ -22,6 +22,7 @@ import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getGlobalSettingsUrl } from '../../../helpers/urls';
 import { AlmKeys } from '../../../types/alm-settings';
 import { ALM_INTEGRATION } from '../../settings/components/AdditionalCategoryKeys';
 
@@ -42,13 +43,7 @@ export default function WrongBindingCountAlert(props: WrongBindingCountAlertProp
           values={{
             alm: translate('onboarding.alm', alm),
             url: (
-              <Link
-                to={{
-                  pathname: '/admin/settings',
-                  query: { category: ALM_INTEGRATION }
-                }}>
-                {translate('settings.page')}
-              </Link>
+              <Link to={getGlobalSettingsUrl(ALM_INTEGRATION)}>{translate('settings.page')}</Link>
             )
           }}
         />
index b3044c5f82e6928306d11a8c41935f8108b400cf..00d96a02c90e611ed027a6981b38d42221276303 100644 (file)
@@ -22,6 +22,7 @@ import { sortBy } from 'lodash';
 import * as React from 'react';
 import { connect } from 'react-redux';
 import { IndexLink } from 'react-router';
+import { getGlobalSettingsUrl, getProjectSettingsUrl } from '../../../helpers/urls';
 import { getAppState, getSettingsAppAllCategories, Store } from '../../../store/rootReducer';
 import { getCategoryName } from '../utils';
 import { ADDITIONAL_CATEGORIES } from './AdditionalCategories';
@@ -37,7 +38,6 @@ export interface CategoriesListProps {
 
 export function CategoriesList(props: CategoriesListProps) {
   const { branchesEnabled, categories, component, defaultCategory, selectedCategory } = props;
-  const pathname = component ? '/project/settings' : '/settings';
 
   const categoriesWithName = categories
     .filter(key => !CATEGORY_OVERRIDES[key.toLowerCase()])
@@ -60,24 +60,25 @@ export function CategoriesList(props: CategoriesListProps) {
 
   return (
     <ul className="side-tabs-menu">
-      {sortedCategories.map(category => (
-        <li key={category.key}>
-          <IndexLink
-            className={classNames({
-              active: category.key.toLowerCase() === selectedCategory.toLowerCase()
-            })}
-            title={category.name}
-            to={{
-              pathname,
-              query: {
-                category: category.key !== defaultCategory ? category.key.toLowerCase() : undefined,
-                id: component && component.key
-              }
-            }}>
-            {category.name}
-          </IndexLink>
-        </li>
-      ))}
+      {sortedCategories.map(c => {
+        const category = c.key !== defaultCategory ? c.key.toLowerCase() : undefined;
+        return (
+          <li key={c.key}>
+            <IndexLink
+              className={classNames({
+                active: c.key.toLowerCase() === selectedCategory.toLowerCase()
+              })}
+              title={c.name}
+              to={
+                component
+                  ? getProjectSettingsUrl(component.key, category)
+                  : getGlobalSettingsUrl(category)
+              }>
+              {c.name}
+            </IndexLink>
+          </li>
+        );
+      })}
     </ul>
   );
 }
index 161cb0517828e343bb9f191ff0afd0a8a223566e..bc199c6498d031e7cfacd57c502225f484a91bbf 100644 (file)
@@ -12,10 +12,9 @@ exports[`should render correctly: branches disabled 1`] = `
       title="CAT_2_NAME"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": "cat_2",
-            "id": undefined,
           },
         }
       }
@@ -31,10 +30,9 @@ exports[`should render correctly: branches disabled 1`] = `
       title="general"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": undefined,
-            "id": undefined,
           },
         }
       }
@@ -57,10 +55,9 @@ exports[`should render correctly: global mode 1`] = `
       title="CAT_1_NAME"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": "cat_1",
-            "id": undefined,
           },
         }
       }
@@ -76,10 +73,9 @@ exports[`should render correctly: global mode 1`] = `
       title="CAT_2_NAME"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": "cat_2",
-            "id": undefined,
           },
         }
       }
@@ -95,10 +91,9 @@ exports[`should render correctly: global mode 1`] = `
       title="general"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": undefined,
-            "id": undefined,
           },
         }
       }
@@ -185,10 +180,9 @@ exports[`should render correctly: selected category 1`] = `
       title="CAT_1_NAME"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": "cat_1",
-            "id": undefined,
           },
         }
       }
@@ -204,10 +198,9 @@ exports[`should render correctly: selected category 1`] = `
       title="CAT_2_NAME"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": "cat_2",
-            "id": undefined,
           },
         }
       }
@@ -223,10 +216,9 @@ exports[`should render correctly: selected category 1`] = `
       title="general"
       to={
         Object {
-          "pathname": "/settings",
+          "pathname": "/admin/settings",
           "query": Object {
             "category": undefined,
-            "id": undefined,
           },
         }
       }
index 92aba7659fa5a45ecbf718bc96bdaac97178ee5d..84567b02dbc33bf07bdf23709b180e3fc86e5721 100644 (file)
@@ -28,6 +28,7 @@ import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
 import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
 import MandatoryFieldsExplanation from 'sonar-ui-common/components/ui/MandatoryFieldsExplanation';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getGlobalSettingsUrl } from '../../../../helpers/urls';
 import {
   AlmSettingsInstance,
   ProjectAlmBindingConfigurationErrors,
@@ -216,14 +217,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
                     )}
                     values={{
                       link: (
-                        <Link
-                          to={{
-                            pathname: '/admin/settings',
-                            query: {
-                              category: ALM_INTEGRATION,
-                              alm
-                            }
-                          }}>
+                        <Link to={getGlobalSettingsUrl(ALM_INTEGRATION, { alm })}>
                           {translate(
                             'settings.pr_decoration.binding.check_configuration.failure.check_global_settings.link'
                           )}
index b0c4eeacdfcceccd8ac8541f7fb848276ac9765e..5d0847a1b69e94ea562a0b7e35d37b5a0e7e7327 100644 (file)
@@ -40,6 +40,29 @@ export interface TutorialSelectionRendererProps {
   selectedTutorial?: TutorialModes;
 }
 
+const DEFAULT_ICON_SIZE = 80;
+const GH_ACTION_ICON_SIZE = 64;
+
+function renderButton(
+  mode: TutorialModes,
+  onSelectTutorial: (mode: TutorialModes) => void,
+  icon: React.ReactNode
+) {
+  return (
+    <button
+      className={`button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-${mode}`}
+      // Currently, OtherCI is the same tutorial as Manual. We might update it to its own stand-alone
+      // tutorial in the future.
+      onClick={() => onSelectTutorial(mode === TutorialModes.OtherCI ? TutorialModes.Manual : mode)}
+      type="button">
+      {icon}
+      <div className="medium big-spacer-top">
+        {translate('onboarding.tutorial.choose_method', mode)}
+      </div>
+    </button>
+  );
+}
+
 export default function TutorialSelectionRenderer(props: TutorialSelectionRendererProps) {
   const {
     almBinding,
@@ -84,100 +107,82 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender
             </h1>
           </header>
 
-          <div className="display-flex-justify-center">
-            <button
-              className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
-              onClick={() => props.onSelectTutorial(TutorialModes.Manual)}
-              type="button">
+          <div className="display-flex-justify-center display-flex-wrap">
+            {renderButton(
+              TutorialModes.Manual,
+              props.onSelectTutorial,
               <img
                 alt="" // Should be ignored by screen readers
-                height={80}
+                height={DEFAULT_ICON_SIZE}
                 src={`${getBaseUrl()}/images/tutorials/manual.svg`}
               />
-              <div className="medium big-spacer-top">
-                {translate('onboarding.tutorial.choose_method.manual')}
-              </div>
-            </button>
-
-            {showAzurePipelines && (
-              <button
-                className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
-                onClick={() => props.onSelectTutorial(TutorialModes.AzurePipelines)}
-                type="button">
+            )}
+
+            {showAzurePipelines &&
+              renderButton(
+                TutorialModes.AzurePipelines,
+                props.onSelectTutorial,
                 <img
                   alt="" // Should be ignored by screen readers
-                  height={80}
+                  height={DEFAULT_ICON_SIZE}
                   src={`${getBaseUrl()}/images/tutorials/azure-pipelines.svg`}
                 />
-                <div className="medium big-spacer-top">
-                  {translate('onboarding.tutorial.choose_method.azure_pipelines')}
-                </div>
-              </button>
-            )}
+              )}
 
-            {showBitbucketPipelines && (
-              <button
-                className="button button-huge display-flex-column spacer-left spacer-right bitbucket-pipelines"
-                onClick={() => props.onSelectTutorial(TutorialModes.BitbucketPipelines)}
-                type="button">
+            {showBitbucketPipelines &&
+              renderButton(
+                TutorialModes.BitbucketPipelines,
+                props.onSelectTutorial,
                 <img
                   alt="" // Should be ignored by screen readers
-                  height={80}
+                  height={DEFAULT_ICON_SIZE}
                   src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
                 />
-                <div className="medium big-spacer-top">
-                  {translate('onboarding.tutorial.choose_method.bitbucket_pipelines')}
-                </div>
-              </button>
-            )}
+              )}
 
-            {showGitHubActions && (
-              <button
-                className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github"
-                onClick={() => props.onSelectTutorial(TutorialModes.GitHubActions)}
-                type="button">
+            {showGitHubActions &&
+              renderButton(
+                TutorialModes.GitHubActions,
+                props.onSelectTutorial,
                 <img
                   alt="" // Should be ignored by screen readers
-                  height={64}
+                  height={GH_ACTION_ICON_SIZE}
                   className="spacer-bottom spacer-top"
                   src={`${getBaseUrl()}/images/tutorials/github-actions.svg`}
                 />
-                <div className="medium big-spacer-top">
-                  {translate('onboarding.tutorial.choose_method.github_action')}
-                </div>
-              </button>
-            )}
+              )}
 
-            {showGitLabCICD && (
-              <button
-                className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab"
-                onClick={() => props.onSelectTutorial(TutorialModes.GitLabCI)}
-                type="button">
+            {showGitLabCICD &&
+              renderButton(
+                TutorialModes.GitLabCI,
+                props.onSelectTutorial,
                 <img
                   alt="" // Should be ignored by screen readers
-                  height={80}
+                  height={DEFAULT_ICON_SIZE}
                   src={`${getBaseUrl()}/images/alm/gitlab.svg`}
                 />
-                <div className="medium big-spacer-top">
-                  {translate('onboarding.tutorial.choose_method.gitlab_ci')}
-                </div>
-              </button>
-            )}
+              )}
 
-            {showJenkins && (
-              <button
-                className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
-                onClick={() => props.onSelectTutorial(TutorialModes.Jenkins)}
-                type="button">
+            {showJenkins &&
+              renderButton(
+                TutorialModes.Jenkins,
+                props.onSelectTutorial,
                 <img
                   alt="" // Should be ignored by screen readers
-                  height={80}
+                  height={DEFAULT_ICON_SIZE}
                   src={`${getBaseUrl()}/images/tutorials/jenkins.svg`}
                 />
-                <div className="medium big-spacer-top">
-                  {translate('onboarding.tutorial.choose_method.jenkins')}
-                </div>
-              </button>
+              )}
+
+            {renderButton(
+              TutorialModes.OtherCI,
+              props.onSelectTutorial,
+              <span
+                aria-disabled={true}
+                className="display-flex-center gigantic"
+                style={{ height: DEFAULT_ICON_SIZE }}>
+                &hellip;
+              </span>
             )}
           </div>
         </div>
index 74460e6a4a4339c7100ec89d277ea72b29b29ece..70c2829493ad5b148cef4af8bc52dd0e0ff87ed7 100644 (file)
@@ -102,10 +102,10 @@ it('should allow mode selection for Github', () => {
   click(wrapper.find('button.tutorial-mode-manual'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Manual);
 
-  click(wrapper.find('button.tutorial-mode-github'));
+  click(wrapper.find('button.tutorial-mode-github-actions'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitHubActions);
 
-  click(wrapper.find('button.azure-pipelines'));
+  click(wrapper.find('button.tutorial-mode-azure-pipelines'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.AzurePipelines);
 });
 
@@ -119,7 +119,7 @@ it('should allow mode selection for GitLab', () => {
   click(wrapper.find('button.tutorial-mode-jenkins'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Jenkins);
 
-  click(wrapper.find('button.tutorial-mode-gitlab'));
+  click(wrapper.find('button.tutorial-mode-gitlab-ci'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitLabCI);
 
   click(wrapper.find('button.tutorial-mode-manual'));
@@ -136,7 +136,7 @@ it('should allow mode selection for Bitbucket pipepline', () => {
   click(wrapper.find('button.tutorial-mode-jenkins'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Jenkins);
 
-  click(wrapper.find('button.bitbucket-pipelines'));
+  click(wrapper.find('button.tutorial-mode-bitbucket-pipelines'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.BitbucketPipelines);
 
   click(wrapper.find('button.tutorial-mode-manual'));
@@ -150,7 +150,7 @@ it('should allow mode selection for Azure DevOps', () => {
     projectBinding: mockProjectAzureBindingResponse()
   });
 
-  click(wrapper.find('button.azure-pipelines'));
+  click(wrapper.find('button.tutorial-mode-azure-pipelines'));
   expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.AzurePipelines);
 
   click(wrapper.find('button.tutorial-mode-manual'));
index 5fe73834477e5d78ea948443f22451cc96799fef..0422dc2c2ae40dcf6088157a3bbbb194a588fb18 100644 (file)
@@ -15,10 +15,10 @@ exports[`should render correctly for azure 1`] = `
       </h1>
     </header>
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-center display-flex-wrap"
     >
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-manual"
         onClick={[Function]}
         type="button"
       >
@@ -34,7 +34,7 @@ exports[`should render correctly for azure 1`] = `
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-azure-pipelines"
         onClick={[Function]}
         type="button"
       >
@@ -46,7 +46,29 @@ exports[`should render correctly for azure 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.azure_pipelines
+          onboarding.tutorial.choose_method.azure-pipelines
+        </div>
+      </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-other-ci"
+        onClick={[Function]}
+        type="button"
+      >
+        <span
+          aria-disabled={true}
+          className="display-flex-center gigantic"
+          style={
+            Object {
+              "height": 80,
+            }
+          }
+        >
+          â€¦
+        </span>
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.other-ci
         </div>
       </button>
     </div>
@@ -69,10 +91,10 @@ exports[`should render correctly for bitbucket server 1`] = `
       </h1>
     </header>
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-center display-flex-wrap"
     >
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-manual"
         onClick={[Function]}
         type="button"
       >
@@ -88,7 +110,7 @@ exports[`should render correctly for bitbucket server 1`] = `
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-jenkins"
         onClick={[Function]}
         type="button"
       >
@@ -103,6 +125,28 @@ exports[`should render correctly for bitbucket server 1`] = `
           onboarding.tutorial.choose_method.jenkins
         </div>
       </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-other-ci"
+        onClick={[Function]}
+        type="button"
+      >
+        <span
+          aria-disabled={true}
+          className="display-flex-center gigantic"
+          style={
+            Object {
+              "height": 80,
+            }
+          }
+        >
+          â€¦
+        </span>
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.other-ci
+        </div>
+      </button>
     </div>
   </div>
 </Fragment>
@@ -123,10 +167,10 @@ exports[`should render correctly for github 1`] = `
       </h1>
     </header>
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-center display-flex-wrap"
     >
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-manual"
         onClick={[Function]}
         type="button"
       >
@@ -142,7 +186,7 @@ exports[`should render correctly for github 1`] = `
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-azure-pipelines"
         onClick={[Function]}
         type="button"
       >
@@ -154,11 +198,11 @@ exports[`should render correctly for github 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.azure_pipelines
+          onboarding.tutorial.choose_method.azure-pipelines
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-github-actions"
         onClick={[Function]}
         type="button"
       >
@@ -171,11 +215,11 @@ exports[`should render correctly for github 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.github_action
+          onboarding.tutorial.choose_method.github-actions
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-jenkins"
         onClick={[Function]}
         type="button"
       >
@@ -190,6 +234,28 @@ exports[`should render correctly for github 1`] = `
           onboarding.tutorial.choose_method.jenkins
         </div>
       </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-other-ci"
+        onClick={[Function]}
+        type="button"
+      >
+        <span
+          aria-disabled={true}
+          className="display-flex-center gigantic"
+          style={
+            Object {
+              "height": 80,
+            }
+          }
+        >
+          â€¦
+        </span>
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.other-ci
+        </div>
+      </button>
     </div>
   </div>
 </Fragment>
@@ -210,10 +276,10 @@ exports[`should render correctly for gitlab 1`] = `
       </h1>
     </header>
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-center display-flex-wrap"
     >
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-manual"
         onClick={[Function]}
         type="button"
       >
@@ -229,7 +295,7 @@ exports[`should render correctly for gitlab 1`] = `
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-gitlab-ci"
         onClick={[Function]}
         type="button"
       >
@@ -241,11 +307,11 @@ exports[`should render correctly for gitlab 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.gitlab_ci
+          onboarding.tutorial.choose_method.gitlab-ci
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-jenkins"
         onClick={[Function]}
         type="button"
       >
@@ -260,6 +326,28 @@ exports[`should render correctly for gitlab 1`] = `
           onboarding.tutorial.choose_method.jenkins
         </div>
       </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-other-ci"
+        onClick={[Function]}
+        type="button"
+      >
+        <span
+          aria-disabled={true}
+          className="display-flex-center gigantic"
+          style={
+            Object {
+              "height": 80,
+            }
+          }
+        >
+          â€¦
+        </span>
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.other-ci
+        </div>
+      </button>
     </div>
   </div>
 </Fragment>
@@ -499,10 +587,10 @@ exports[`should render correctly: selection 1`] = `
       </h1>
     </header>
     <div
-      className="display-flex-justify-center"
+      className="display-flex-justify-center display-flex-wrap"
     >
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-manual"
         onClick={[Function]}
         type="button"
       >
@@ -518,7 +606,7 @@ exports[`should render correctly: selection 1`] = `
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-azure-pipelines"
         onClick={[Function]}
         type="button"
       >
@@ -530,11 +618,11 @@ exports[`should render correctly: selection 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.azure_pipelines
+          onboarding.tutorial.choose_method.azure-pipelines
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right bitbucket-pipelines"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-bitbucket-pipelines"
         onClick={[Function]}
         type="button"
       >
@@ -546,11 +634,11 @@ exports[`should render correctly: selection 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.bitbucket_pipelines
+          onboarding.tutorial.choose_method.bitbucket-pipelines
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-github-actions"
         onClick={[Function]}
         type="button"
       >
@@ -563,11 +651,11 @@ exports[`should render correctly: selection 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.github_action
+          onboarding.tutorial.choose_method.github-actions
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-gitlab-ci"
         onClick={[Function]}
         type="button"
       >
@@ -579,11 +667,11 @@ exports[`should render correctly: selection 1`] = `
         <div
           className="medium big-spacer-top"
         >
-          onboarding.tutorial.choose_method.gitlab_ci
+          onboarding.tutorial.choose_method.gitlab-ci
         </div>
       </button>
       <button
-        className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-jenkins"
         onClick={[Function]}
         type="button"
       >
@@ -598,6 +686,28 @@ exports[`should render correctly: selection 1`] = `
           onboarding.tutorial.choose_method.jenkins
         </div>
       </button>
+      <button
+        className="button button-huge display-flex-column spacer-left spacer-right huge-spacer-bottom tutorial-mode-other-ci"
+        onClick={[Function]}
+        type="button"
+      >
+        <span
+          aria-disabled={true}
+          className="display-flex-center gigantic"
+          style={
+            Object {
+              "height": 80,
+            }
+          }
+        >
+          â€¦
+        </span>
+        <div
+          className="medium big-spacer-top"
+        >
+          onboarding.tutorial.choose_method.other-ci
+        </div>
+      </button>
     </div>
   </div>
 </Fragment>
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/DoneNextSteps.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/DoneNextSteps.tsx
new file mode 100644 (file)
index 0000000..729bd9c
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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 { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { PULL_REQUEST_DECORATION_BINDING_CATEGORY } from '../../../apps/settings/components/AdditionalCategoryKeys';
+import { getProjectSettingsUrl } from '../../../helpers/urls';
+
+export interface DoneNextStepsProps {
+  component: T.Component;
+}
+
+export default function DoneNextSteps({ component }: DoneNextStepsProps) {
+  const tutorialsLink = (
+    <Link to={{ pathname: '/tutorials', query: { id: component.key } }}>
+      {translate(
+        'onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.see_tutorials'
+      )}
+    </Link>
+  );
+  const isProjectAdmin = component.configuration?.showSettings;
+
+  return (
+    <>
+      <hr className="big-spacer-top big-spacer-bottom" />
+
+      <p>
+        <strong>{translate('onboarding.analysis.auto_refresh_after_analysis.done')}</strong>{' '}
+        {translate('onboarding.analysis.auto_refresh_after_analysis.auto_refresh')}
+      </p>
+      <p className="big-spacer-top">
+        {isProjectAdmin ? (
+          <FormattedMessage
+            defaultMessage={translate(
+              'onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.admin'
+            )}
+            id="onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.admin"
+            values={{
+              link_project_settings: (
+                <Link
+                  to={getProjectSettingsUrl(
+                    component.key,
+                    PULL_REQUEST_DECORATION_BINDING_CATEGORY
+                  )}>
+                  {translate(
+                    'onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.project_settings'
+                  )}
+                </Link>
+              ),
+              link_see_tutorials: tutorialsLink
+            }}
+          />
+        ) : (
+          <FormattedMessage
+            defaultMessage={translate(
+              'onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci'
+            )}
+            id="onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci"
+            values={{
+              link_see_tutorials: tutorialsLink
+            }}
+          />
+        )}
+      </p>
+      <p className="big-spacer-top">
+        <FormattedMessage
+          defaultMessage={translate(
+            'onboarding.analysis.auto_refresh_after_analysis.check_these_links'
+          )}
+          id="onboarding.analysis.auto_refresh_after_analysis.check_these_links"
+          values={{
+            link_branches: (
+              <Link
+                to="/documentation/branches/overview/"
+                target="_blank"
+                rel="noopener noreferrer">
+                {translate(
+                  'onboarding.analysis.auto_refresh_after_analysis.check_these_links.branches'
+                )}
+              </Link>
+            ),
+            link_pr_analysis: (
+              <Link
+                to="/documentation/analysis/pull-request/"
+                target="_blank"
+                rel="noopener noreferrer">
+                {translate(
+                  'onboarding.analysis.auto_refresh_after_analysis.check_these_links.pr_analysis'
+                )}
+              </Link>
+            )
+          }}
+        />
+      </p>
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/DoneNextSteps-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/DoneNextSteps-test.tsx
new file mode 100644 (file)
index 0000000..9c39138
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 { mockComponent } from '../../../../helpers/testMocks';
+import DoneNextSteps, { DoneNextStepsProps } from '../DoneNextSteps';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(
+    shallowRender({ component: mockComponent({ configuration: { showSettings: true } }) })
+  ).toMatchSnapshot('project admin');
+});
+
+function shallowRender(props: Partial<DoneNextStepsProps> = {}) {
+  return shallow<DoneNextStepsProps>(<DoneNextSteps component={mockComponent()} {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/DoneNextSteps-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/DoneNextSteps-test.tsx.snap
new file mode 100644 (file)
index 0000000..0b6489a
--- /dev/null
@@ -0,0 +1,158 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<Fragment>
+  <hr
+    className="big-spacer-top big-spacer-bottom"
+  />
+  <p>
+    <strong>
+      onboarding.analysis.auto_refresh_after_analysis.done
+    </strong>
+     
+    onboarding.analysis.auto_refresh_after_analysis.auto_refresh
+  </p>
+  <p
+    className="big-spacer-top"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci"
+      id="onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci"
+      values={
+        Object {
+          "link_see_tutorials": <Link
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/tutorials",
+                "query": Object {
+                  "id": "my-project",
+                },
+              }
+            }
+          >
+            onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.see_tutorials
+          </Link>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.auto_refresh_after_analysis.check_these_links"
+      id="onboarding.analysis.auto_refresh_after_analysis.check_these_links"
+      values={
+        Object {
+          "link_branches": <Link
+            onlyActiveOnIndex={false}
+            rel="noopener noreferrer"
+            style={Object {}}
+            target="_blank"
+            to="/documentation/branches/overview/"
+          >
+            onboarding.analysis.auto_refresh_after_analysis.check_these_links.branches
+          </Link>,
+          "link_pr_analysis": <Link
+            onlyActiveOnIndex={false}
+            rel="noopener noreferrer"
+            style={Object {}}
+            target="_blank"
+            to="/documentation/analysis/pull-request/"
+          >
+            onboarding.analysis.auto_refresh_after_analysis.check_these_links.pr_analysis
+          </Link>,
+        }
+      }
+    />
+  </p>
+</Fragment>
+`;
+
+exports[`should render correctly: project admin 1`] = `
+<Fragment>
+  <hr
+    className="big-spacer-top big-spacer-bottom"
+  />
+  <p>
+    <strong>
+      onboarding.analysis.auto_refresh_after_analysis.done
+    </strong>
+     
+    onboarding.analysis.auto_refresh_after_analysis.auto_refresh
+  </p>
+  <p
+    className="big-spacer-top"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.admin"
+      id="onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.admin"
+      values={
+        Object {
+          "link_project_settings": <Link
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/project/settings",
+                "query": Object {
+                  "category": "pull_request_decoration_binding",
+                  "id": "my-project",
+                },
+              }
+            }
+          >
+            onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.project_settings
+          </Link>,
+          "link_see_tutorials": <Link
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/tutorials",
+                "query": Object {
+                  "id": "my-project",
+                },
+              }
+            }
+          >
+            onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.see_tutorials
+          </Link>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.auto_refresh_after_analysis.check_these_links"
+      id="onboarding.analysis.auto_refresh_after_analysis.check_these_links"
+      values={
+        Object {
+          "link_branches": <Link
+            onlyActiveOnIndex={false}
+            rel="noopener noreferrer"
+            style={Object {}}
+            target="_blank"
+            to="/documentation/branches/overview/"
+          >
+            onboarding.analysis.auto_refresh_after_analysis.check_these_links.branches
+          </Link>,
+          "link_pr_analysis": <Link
+            onlyActiveOnIndex={false}
+            rel="noopener noreferrer"
+            style={Object {}}
+            target="_blank"
+            to="/documentation/analysis/pull-request/"
+          >
+            onboarding.analysis.auto_refresh_after_analysis.check_these_links.pr_analysis
+          </Link>,
+        }
+      }
+    />
+  </p>
+</Fragment>
+`;
index e679a08c2fce1dc767d2de4c0d93ca369923a0c4..1cb28a011d19046347ea3374f6510179217d2851 100644 (file)
@@ -40,26 +40,25 @@ export default function AnalysisCommand(props: AnalysisCommandProps) {
   }
 
   const host = getHostUrl();
-  const projectKey = component.key;
 
   switch (languageConfig.buildTool) {
     case BuildTools.Maven:
-      return <JavaMaven host={host} projectKey={projectKey} token={token} />;
+      return <JavaMaven host={host} component={component} token={token} />;
 
     case BuildTools.Gradle:
-      return <JavaGradle host={host} projectKey={projectKey} token={token} />;
+      return <JavaGradle host={host} component={component} token={token} />;
 
     case BuildTools.DotNet:
-      return <DotNet host={host} projectKey={projectKey} token={token} />;
+      return <DotNet host={host} component={component} token={token} />;
 
     case BuildTools.CFamily:
       return languageConfig.os !== undefined ? (
-        <ClangGCCCustom os={languageConfig.os} host={host} projectKey={projectKey} token={token} />
+        <ClangGCCCustom os={languageConfig.os} host={host} component={component} token={token} />
       ) : null;
 
     case BuildTools.Other:
       return languageConfig.os !== undefined ? (
-        <Other host={host} os={languageConfig.os} projectKey={projectKey} token={token} />
+        <Other host={host} os={languageConfig.os} component={component} token={token} />
       ) : null;
 
     default:
index 1fc693c4cc0d745daffd6d1b28da1cc96e344ba2..a49fed27b0ddbcd94be7dde49b0159a195d3d306 100644 (file)
@@ -25,21 +25,21 @@ import ExecBuildWrapper from './ExecBuildWrapper';
 import ExecScanner from './ExecScanner';
 
 export interface ClangGCCCustomProps {
+  component: T.Component;
   host: string;
   os: OSs;
-  projectKey: string;
   token: string;
 }
 
 export default function ClangGCCCustom(props: ClangGCCCustomProps) {
-  const { os, host, projectKey, token } = props;
+  const { os, host, component, token } = props;
 
   return (
     <div>
       <DownloadBuildWrapper os={os} />
       <DownloadScanner os={os} />
       <ExecBuildWrapper os={os} />
-      <ExecScanner host={host} projectKey={projectKey} os={os} token={token} cfamily={true} />
+      <ExecScanner host={host} component={component} os={os} token={token} cfamily={true} />
     </div>
   );
 }
index 03956c08f93cfa317cb4e833bd2570d53a652a12..1ba53cf62250df2c26659619aaadd25af69b7045 100644 (file)
@@ -23,8 +23,8 @@ import DotNetCore from './DotNetCore';
 import DotNetFramework from './DotNetFramework';
 
 export interface DotNetProps {
+  component: T.Component;
   host: string;
-  projectKey: string;
   token: string;
 }
 
index d667ced97e20d7ba18bcb3b9fd4c6ad6d6015263..9d74fe62b43580f681ceee96828399802daec7a8 100644 (file)
@@ -26,10 +26,10 @@ import { DotNetProps } from './DotNet';
 import DotNetExecute from './DotNetExecute';
 
 export default function DotNetCore(props: DotNetProps) {
-  const { host, projectKey, token } = props;
+  const { host, component, token } = props;
 
   const commands = [
-    `dotnet sonarscanner begin /k:"${projectKey}" /d:sonar.host.url="${host}"  /d:sonar.login="${token}"`,
+    `dotnet sonarscanner begin /k:"${component.key}" /d:sonar.host.url="${host}"  /d:sonar.login="${token}"`,
     'dotnet build',
     `dotnet sonarscanner end /d:sonar.login="${token}"`
   ];
@@ -46,7 +46,7 @@ export default function DotNetCore(props: DotNetProps) {
       <Alert className="spacer-top" variant="info">
         {translate('onboarding.analysis.dotnetcore.global.text.path')}
       </Alert>
-      <DotNetExecute commands={commands} />
+      <DotNetExecute commands={commands} component={component} />
     </div>
   );
 }
index ec2e272d8a8404665043ff3bfbf627d6fde85668..56091251a9a7f7c222b88c68db116c640fbfc0e7 100644 (file)
@@ -24,12 +24,14 @@ import { Link } from 'react-router';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import CodeSnippet from '../../../common/CodeSnippet';
 import InstanceMessage from '../../../common/InstanceMessage';
+import DoneNextSteps from '../DoneNextSteps';
 
 export interface DotNetExecuteProps {
   commands: string[];
+  component: T.Component;
 }
 
-export default function DotNetExecute({ commands }: DotNetExecuteProps) {
+export default function DotNetExecute({ commands, component }: DotNetExecuteProps) {
   return (
     <>
       <h4 className="huge-spacer-top spacer-bottom">
@@ -55,6 +57,7 @@ export default function DotNetExecute({ commands }: DotNetExecuteProps) {
           }}
         />
       </p>
+      <DoneNextSteps component={component} />
     </>
   );
 }
index 0ead421f64b58645f9abddd059737ba72a43034e..2542db69702d043750bbb5018a4ae30b9026d770 100644 (file)
@@ -24,10 +24,10 @@ import { DotNetProps } from './DotNet';
 import DotNetExecute from './DotNetExecute';
 
 export default function DotNetFramework(props: DotNetProps) {
-  const { host, projectKey, token } = props;
+  const { host, component, token } = props;
 
   const commands = [
-    `SonarScanner.MSBuild.exe begin /k:"${projectKey}" /d:sonar.host.url="${host}" /d:sonar.login="${token}"`,
+    `SonarScanner.MSBuild.exe begin /k:"${component.key}" /d:sonar.host.url="${host}" /d:sonar.login="${token}"`,
     'MsBuild.exe /t:Rebuild',
     `SonarScanner.MSBuild.exe end /d:sonar.login="${token}"`
   ];
@@ -57,7 +57,7 @@ export default function DotNetFramework(props: DotNetProps) {
         </p>
       </div>
 
-      <DotNetExecute commands={commands} />
+      <DotNetExecute commands={commands} component={component} />
     </div>
   );
 }
index cc595bb0d03931b5736a2b63d33d60cb3fc9309d..a3c2083ec34e4b0f1a6365f003197c297b1680b2 100644 (file)
@@ -25,22 +25,23 @@ import CodeSnippet from '../../../common/CodeSnippet';
 import InstanceMessage from '../../../common/InstanceMessage';
 import { OSs } from '../../types';
 import { quote } from '../../utils';
+import DoneNextSteps from '../DoneNextSteps';
 
 export interface ExecScannerProps {
+  component: T.Component;
   host: string;
   os: OSs;
-  projectKey: string;
   token: string;
   cfamily?: boolean;
 }
 
 export default function ExecScanner(props: ExecScannerProps) {
-  const { host, os, projectKey, token, cfamily } = props;
+  const { host, os, component, token, cfamily } = props;
 
   const q = quote(os);
   const command = [
     os === OSs.Windows ? 'sonar-scanner.bat' : 'sonar-scanner',
-    '-D' + q(`sonar.projectKey=${projectKey}`),
+    '-D' + q(`sonar.projectKey=${component.key}`),
     '-D' + q('sonar.sources=.'),
     cfamily ? '-D' + q('sonar.cfamily.build-wrapper-output=bw-output') : undefined,
     '-D' + q(`sonar.host.url=${host}`),
@@ -69,9 +70,7 @@ export default function ExecScanner(props: ExecScannerProps) {
           }}
         />
       </p>
-      <p className="big-spacer-top markdown">
-        {translate('onboarding.analysis.auto_refresh_after_analysis')}
-      </p>
+      <DoneNextSteps component={component} />
     </div>
   );
 }
index 16fc994a8f498b1f147a87336094906098954718..773148047c2d5aaa242015a6c34258811e23d3ca 100644 (file)
@@ -23,20 +23,21 @@ import { Link } from 'react-router';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import CodeSnippet from '../../../common/CodeSnippet';
 import InstanceMessage from '../../../common/InstanceMessage';
+import DoneNextSteps from '../DoneNextSteps';
 
 export interface JavaGradleProps {
+  component: T.Component;
   host: string;
-  projectKey: string;
   token: string;
 }
 
 export default function JavaGradle(props: JavaGradleProps) {
-  const { host, projectKey, token } = props;
+  const { host, component, token } = props;
   const config = 'plugins {\n  id "org.sonarqube" version "3.3"\n}';
 
   const command = [
     './gradlew sonarqube',
-    `-Dsonar.projectKey=${projectKey}`,
+    `-Dsonar.projectKey=${component.key}`,
     `-Dsonar.host.url=${host}`,
     `-Dsonar.login=${token}`
   ];
@@ -91,9 +92,7 @@ export default function JavaGradle(props: JavaGradleProps) {
           }}
         />
       </p>
-      <p className="big-spacer-top markdown">
-        {translate('onboarding.analysis.auto_refresh_after_analysis')}
-      </p>
+      <DoneNextSteps component={component} />
     </div>
   );
 }
index 7a9b4b6849d241a447e9d1055fbbbcb6321f7827..d6e00ac2fbffc3306c694a330b6350a8496eac42 100644 (file)
@@ -23,18 +23,19 @@ import { Link } from 'react-router';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import CodeSnippet from '../../../common/CodeSnippet';
 import InstanceMessage from '../../../common/InstanceMessage';
+import DoneNextSteps from '../DoneNextSteps';
 
 export interface JavaMavenProps {
+  component: T.Component;
   host: string;
-  projectKey: string;
   token: string;
 }
 
 export default function JavaMaven(props: JavaMavenProps) {
-  const { host, projectKey, token } = props;
+  const { host, component, token } = props;
   const command = [
     'mvn sonar:sonar',
-    `-Dsonar.projectKey=${projectKey}`,
+    `-Dsonar.projectKey=${component.key}`,
     `-Dsonar.host.url=${host}`,
     `-Dsonar.login=${token}`
   ];
@@ -59,9 +60,7 @@ export default function JavaMaven(props: JavaMavenProps) {
           }}
         />
       </p>
-      <p className="big-spacer-top markdown">
-        {translate('onboarding.analysis.auto_refresh_after_analysis')}
-      </p>
+      <DoneNextSteps component={component} />
     </div>
   );
 }
index 7db9c3880bbc9704382ced90edf9140109b35aa7..ac53a410e21e1465ab2b85fc20870f398793a99e 100644 (file)
@@ -23,19 +23,19 @@ import DownloadScanner from './DownloadScanner';
 import ExecScanner from './ExecScanner';
 
 export interface OtherProps {
+  component: T.Component;
   host: string;
   os: OSs;
-  projectKey: string;
   token: string;
 }
 
 export default function Other(props: OtherProps) {
-  const { host, os, projectKey, token } = props;
+  const { host, os, component, token } = props;
 
   return (
     <div>
       <DownloadScanner os={os} />
-      <ExecScanner host={host} os={os} projectKey={projectKey} token={token} />
+      <ExecScanner host={host} os={os} component={component} token={token} />
     </div>
   );
 }
index d35ea6ba31efdb5260971b1497c77bd6826b31bb..574c5fbb785d1eb61a47ad58a85610f99e51941b 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import { OSs } from '../../../types';
 import ClangGCCCommand from '../ClangGCCCommand';
 
-it('Shoud renders correctly', () => {
+it('should render correctly', () => {
   expect(
-    shallow(<ClangGCCCommand os={OSs.Linux} host="host" projectKey="projectKey" token="token" />)
+    shallow(
+      <ClangGCCCommand
+        os={OSs.Linux}
+        host="host"
+        component={mockComponent({ key: 'projectKey' })}
+        token="token"
+      />
+    )
   ).toMatchSnapshot();
 });
index dd5408d54eb13e53f4c55520c5f9410696c8ae16..86e50f914bae4f0804647c213aabd192bafe97b2 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import DotNet from '../DotNet';
 
-it('Should renders correctly', () => {
-  expect(shallow(<DotNet host="host" projectKey="projectKey" token="token" />)).toMatchSnapshot();
+it('should render correctly', () => {
+  expect(
+    shallow(<DotNet host="host" component={mockComponent({ key: 'projectKey' })} token="token" />)
+  ).toMatchSnapshot();
 });
index 5a3693177d5f40039f1c5fc11f497dfe48f523e2..563cae7b1abab7f974d64a2788edc6e380652c68 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import DotNetExecute from '../DotNetExecute';
 
-it('Should renders correctly', () => {
-  expect(shallow(<DotNetExecute commands={['command1', 'command2']} />)).toMatchSnapshot();
+it('should render correctly', () => {
+  expect(
+    shallow(<DotNetExecute commands={['command1', 'command2']} component={mockComponent()} />)
+  ).toMatchSnapshot();
 });
index a6c1d5c3dbfb4ff31caca23be4559238bb35326a..e2cfce486d8436376434fd575410748cc26871a7 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import DotNetFramework from '../DotNetFramework';
 
-it('Should renders correctly', () => {
+it('should render correctly', () => {
   expect(
-    shallow(<DotNetFramework host="host" projectKey="projectKey" token="token" />)
+    shallow(
+      <DotNetFramework host="host" component={mockComponent({ key: 'projectKey' })} token="token" />
+    )
   ).toMatchSnapshot();
 });
index 1de261193b9d6266f5abe41bd31f7a8864b126d3..ecf1c268ceee9b04e8a895b01d4db3421ebdcbf4 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import DotNetCore from '../DotNetCore';
 
-it('Should renders correctly', () => {
+it('should render correctly', () => {
   expect(
-    shallow(<DotNetCore host="host" projectKey="projectKey" token="token" />)
+    shallow(
+      <DotNetCore host="host" component={mockComponent({ key: 'projectKey' })} token="token" />
+    )
   ).toMatchSnapshot();
 });
index 02c3feefe19ab9859438f9e9fdda30d90851e514..9f9ccd1d23b6cd1c28a58cfd113aa27a6a1fc6d9 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import { OSs } from '../../../types';
 import ExecScanner, { ExecScannerProps } from '../ExecScanner';
 
-it.each([OSs.Linux, OSs.Windows, OSs.MacOS])('Shoud renders for %p correctly', os => {
+it.each([OSs.Linux, OSs.Windows, OSs.MacOS])('should render correctly for %p', os => {
   expect(shallowRender({ os })).toMatchSnapshot();
 });
 
-it('Should render for cfamily', () => {
+it('should render correctly for cfamily', () => {
   expect(shallowRender({ cfamily: true })).toMatchSnapshot();
 });
 
 function shallowRender(props: Partial<ExecScannerProps> = {}) {
   return shallow<ExecScannerProps>(
-    <ExecScanner host="host" os={OSs.Linux} projectKey="projectKey" token="token" {...props} />
+    <ExecScanner
+      host="host"
+      os={OSs.Linux}
+      component={mockComponent({ key: 'projectKey' })}
+      token="token"
+      {...props}
+    />
   );
 }
index 917f279493b162c83dda4899c224e322344f5c9f..e9f868da12affa12fc4200229e88be9bbff3f7b5 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import JavaGradle from '../JavaGradle';
 
 it('renders correctly', () => {
   expect(
-    shallow(<JavaGradle host="host" projectKey="projectKey" token="token" />)
+    shallow(
+      <JavaGradle host="host" component={mockComponent({ key: 'projectKey' })} token="token" />
+    )
   ).toMatchSnapshot();
 });
index eddbb1df1e8b35a8e1e151818de4b87b6ea35f9e..054fa94df61749e10937a28890bc4e42fc3e112e 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import JavaMaven from '../JavaMaven';
 
 it('renders correctly', () => {
   expect(
-    shallow(<JavaMaven host="host" projectKey="projectKey" token="token" />)
+    shallow(
+      <JavaMaven host="host" component={mockComponent({ key: 'projectKey' })} token="token" />
+    )
   ).toMatchSnapshot();
 });
index 751a95f8cd59437f59528f7664f57c3ba7d73621..db6f85490edbb1550dc5a2de3703d9755f404602 100644 (file)
@@ -19,6 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { mockComponent } from '../../../../../helpers/testMocks';
 import { OSs } from '../../../types';
 import Other, { OtherProps } from '../Other';
 
@@ -28,6 +29,12 @@ it('renders correctly', () => {
 
 function shallowRender(props: Partial<OtherProps> = {}) {
   return shallow<OtherProps>(
-    <Other host="host" os={OSs.Linux} projectKey="projectKey" token="token" {...props} />
+    <Other
+      host="host"
+      os={OSs.Linux}
+      component={mockComponent({ key: 'projectKey' })}
+      token="token"
+      {...props}
+    />
   );
 }
index 2df0e23b2b8b7fcbfc9e98bf99a7ee44990ab9b8..c33c2b7b1401eb1330b144b38c8928f81fdc119a 100644 (file)
@@ -2,17 +2,59 @@
 
 exports[`renders correctly: .NET 1`] = `
 <DotNet
+  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 [],
+    }
+  }
   host="HOST"
-  projectKey="my-project"
   token="myToken"
 />
 `;
 
 exports[`renders correctly: CFamily 1`] = `
 <ClangGCCCustom
+  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 [],
+    }
+  }
   host="HOST"
   os="linux"
-  projectKey="my-project"
   token="myToken"
 />
 `;
@@ -21,25 +63,88 @@ exports[`renders correctly: Empty CFamily 1`] = `""`;
 
 exports[`renders correctly: gradle 1`] = `
 <JavaGradle
+  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 [],
+    }
+  }
   host="HOST"
-  projectKey="my-project"
   token="myToken"
 />
 `;
 
 exports[`renders correctly: maven 1`] = `
 <JavaMaven
+  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 [],
+    }
+  }
   host="HOST"
-  projectKey="my-project"
   token="myToken"
 />
 `;
 
 exports[`renders correctly: other 1`] = `
 <Other
+  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 [],
+    }
+  }
   host="HOST"
   os="win"
-  projectKey="my-project"
   token="myToken"
 />
 `;
index 7539b8cf22dfdcdc23c47e98acbaccae58992d2d..b4cb5128d1be1ac57f4eee8e50c6732d1004805f 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Shoud renders correctly 1`] = `
+exports[`should render correctly 1`] = `
 <div>
   <DownloadBuildWrapper
     os="linux"
@@ -13,9 +13,30 @@ exports[`Shoud renders correctly 1`] = `
   />
   <ExecScanner
     cfamily={true}
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
     host="host"
     os="linux"
-    projectKey="projectKey"
     token="token"
   />
 </div>
index a322725aec2ae53a762be089c7113068d16d6bf2..b88033c45f5e7cf1ebc8cd1ee79451406c283f6a 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Should renders correctly 1`] = `
+exports[`should render correctly 1`] = `
 <Fragment>
   <RenderOptions
     checked="dotnet_core"
@@ -16,8 +16,29 @@ exports[`Should renders correctly 1`] = `
     titleLabelKey="onboarding.build.dotnet.variant"
   />
   <DotNetCore
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
     host="host"
-    projectKey="projectKey"
     token="token"
   />
 </Fragment>
index bc623435f0185b2ca0f509c31cce522f102a1c1e..7ff51cbcb674d0446feb209c7360deb2ec769150 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Should renders correctly 1`] = `
+exports[`should render correctly 1`] = `
 <Fragment>
   <h4
     className="huge-spacer-top spacer-bottom"
@@ -40,5 +40,29 @@ exports[`Should renders correctly 1`] = `
       }
     />
   </p>
+  <DoneNextSteps
+    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 [],
+      }
+    }
+  />
 </Fragment>
 `;
index 986272b490de02a36bd413918d90f1328936142a..13edb7d737070660a8587f5e24d1bfd081dba2bc 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Should renders correctly 1`] = `
+exports[`should render correctly 1`] = `
 <div>
   <div>
     <h4
@@ -39,6 +39,28 @@ exports[`Should renders correctly 1`] = `
         "SonarScanner.MSBuild.exe end /d:sonar.login=\\"token\\"",
       ]
     }
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
   />
 </div>
 `;
index 5621eab6cad2fd98b113ccab6f3e2a895f3cef1b..73c108fd1a2701d70c566cb56670e45cffe6a3bd 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Should renders correctly 1`] = `
+exports[`should render correctly 1`] = `
 <div>
   <h4
     className="huge-spacer-top spacer-bottom"
@@ -29,6 +29,28 @@ exports[`Should renders correctly 1`] = `
         "dotnet sonarscanner end /d:sonar.login=\\"token\\"",
       ]
     }
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
   />
 </div>
 `;
index 140fe68ea6a46e03538a5963229fe8a7d84a4d7a..b48bd7b66ebc6bae3c89f57961b90adb3e4427ce 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Shoud renders for "linux" correctly 1`] = `
+exports[`should render correctly for "linux" 1`] = `
 <div>
   <h4
     className="huge-spacer-top spacer-bottom"
@@ -45,15 +45,34 @@ exports[`Shoud renders for "linux" correctly 1`] = `
       }
     />
   </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
+  <DoneNextSteps
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
+  />
 </div>
 `;
 
-exports[`Shoud renders for "mac" correctly 1`] = `
+exports[`should render correctly for "mac" 1`] = `
 <div>
   <h4
     className="huge-spacer-top spacer-bottom"
@@ -98,15 +117,34 @@ exports[`Shoud renders for "mac" correctly 1`] = `
       }
     />
   </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
+  <DoneNextSteps
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
+  />
 </div>
 `;
 
-exports[`Shoud renders for "win" correctly 1`] = `
+exports[`should render correctly for "win" 1`] = `
 <div>
   <h4
     className="huge-spacer-top spacer-bottom"
@@ -151,15 +189,34 @@ exports[`Shoud renders for "win" correctly 1`] = `
       }
     />
   </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
+  <DoneNextSteps
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
+  />
 </div>
 `;
 
-exports[`Should render for cfamily 1`] = `
+exports[`should render correctly for cfamily 1`] = `
 <div>
   <h4
     className="huge-spacer-top spacer-bottom"
@@ -204,10 +261,29 @@ exports[`Should render for cfamily 1`] = `
       }
     />
   </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
+  <DoneNextSteps
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
+  />
 </div>
 `;
index b93775bcc24a0159c1382c9013ade862754098d8..9737f0a58df75b4fc35db0ea13807bc3caa1fef6 100644 (file)
@@ -76,10 +76,29 @@ exports[`renders correctly 1`] = `
       }
     />
   </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
+  <DoneNextSteps
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
+  />
 </div>
 `;
index 2482e1d3457cfd5663fafd29f8b4611dfe9ff10c..aafc6e3517e3763e892fb7eee661044eae29a067 100644 (file)
@@ -44,10 +44,29 @@ exports[`renders correctly 1`] = `
       }
     />
   </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
+  <DoneNextSteps
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
+  />
 </div>
 `;
index 2d3b3086434b064f04c2414c502b30dc3b55dee5..bea3152d2d9bebfdeae4c34919f8ace94725cd9e 100644 (file)
@@ -6,9 +6,30 @@ exports[`renders correctly 1`] = `
     os="linux"
   />
   <ExecScanner
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "projectKey",
+        "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 [],
+      }
+    }
     host="host"
     os="linux"
-    projectKey="projectKey"
     token="token"
   />
 </div>
index 364d8886ab5f7851d1f78f301bbf7e7e36a06233..cd79f043667e5f4569286fc70d3dbe8335402d10 100644 (file)
@@ -23,7 +23,8 @@ export enum TutorialModes {
   BitbucketPipelines = 'bitbucket-pipelines',
   GitLabCI = 'gitlab-ci',
   GitHubActions = 'github-actions',
-  AzurePipelines = 'azure-pipelines'
+  AzurePipelines = 'azure-pipelines',
+  OtherCI = 'other-ci'
 }
 
 export enum BuildTools {
index bed9b1df8fb20d47ed1145c58f608d6c9b487f9c..de610e43f4e61ee882a9a726e9da5c7bb28480f5 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { AlmKeys } from '../../types/alm-settings';
 import { ComponentQualifier } from '../../types/component';
 import { IssueType } from '../../types/issues';
 import {
@@ -25,7 +26,9 @@ import {
   getComponentIssuesUrl,
   getComponentOverviewUrl,
   getComponentSecurityHotspotsUrl,
+  getGlobalSettingsUrl,
   getIssuesUrl,
+  getProjectSettingsUrl,
   getQualityGatesUrl,
   getQualityGateUrl,
   stripTrailingSlash
@@ -67,7 +70,7 @@ describe('#getComponentIssuesUrl', () => {
   });
 });
 
-describe('getComponentSecurityHotspotsUrl', () => {
+describe('#getComponentSecurityHotspotsUrl', () => {
   it('should work with no extra parameters', () => {
     expect(getComponentSecurityHotspotsUrl(SIMPLE_COMPONENT_KEY, {})).toEqual({
       pathname: '/security_hotspots',
@@ -88,7 +91,7 @@ describe('getComponentSecurityHotspotsUrl', () => {
   });
 });
 
-describe('getComponentOverviewUrl', () => {
+describe('#getComponentOverviewUrl', () => {
   it('should return a portfolio url for a portfolio', () => {
     expect(getComponentOverviewUrl(SIMPLE_COMPONENT_KEY, ComponentQualifier.Portfolio)).toEqual({
       pathname: '/portfolio',
@@ -142,7 +145,7 @@ describe('#getQualityGate(s)Url', () => {
   });
 });
 
-describe('getIssuesUrl', () => {
+describe('#getIssuesUrl', () => {
   it('should work as expected', () => {
     const type = IssueType.Bug;
     expect(getIssuesUrl({ type })).toEqual({
@@ -151,3 +154,29 @@ describe('getIssuesUrl', () => {
     });
   });
 });
+
+describe('#getGlobalSettingsUrl', () => {
+  it('should work as expected', () => {
+    expect(getGlobalSettingsUrl('foo')).toEqual({
+      pathname: '/admin/settings',
+      query: { category: 'foo' }
+    });
+    expect(getGlobalSettingsUrl('foo', { alm: AlmKeys.GitHub })).toEqual({
+      pathname: '/admin/settings',
+      query: { category: 'foo', alm: AlmKeys.GitHub }
+    });
+  });
+});
+
+describe('#getProjectSettingsUrl', () => {
+  it('should work as expected', () => {
+    expect(getProjectSettingsUrl('foo')).toEqual({
+      pathname: '/project/settings',
+      query: { id: 'foo' }
+    });
+    expect(getProjectSettingsUrl('foo', 'bar')).toEqual({
+      pathname: '/project/settings',
+      query: { id: 'foo', category: 'bar' }
+    });
+  });
+});
index 1adb6ee8ec67f9019e5fbcf976a3da8e7942c608..992c0f76a906e6c6049587719619704b6297a3ff 100644 (file)
@@ -221,6 +221,23 @@ export function getQualityGatesUrl(): Location {
   };
 }
 
+export function getGlobalSettingsUrl(
+  category?: string,
+  query?: T.Dict<string | undefined | number>
+): Location {
+  return {
+    pathname: '/admin/settings',
+    query: { category, ...query }
+  };
+}
+
+export function getProjectSettingsUrl(id: string, category?: string): Location {
+  return {
+    pathname: '/project/settings',
+    query: { id, category }
+  };
+}
+
 /**
  * Generate URL for the rules page
  */
index 8653a01041407ea39b2a5a72db3e17b89d82de32..496f4b491c45b34192abb8a0048023215175f95f 100644 (file)
@@ -3366,7 +3366,15 @@ onboarding.tutorial.env_variables.field=Value
 onboarding.tutorial.env_variables.token_generator.value=an existing token, or a newly generated one:
 
 onboarding.analysis.header=Run analysis on your project
-onboarding.analysis.auto_refresh_after_analysis=Once the analysis is completed, this page will automatically refresh and you will be able to browse the analysis results.
+onboarding.analysis.auto_refresh_after_analysis.done=Is my analysis done?
+onboarding.analysis.auto_refresh_after_analysis.auto_refresh=If your analysis is successful, this page will automatically refresh in a few moments.
+onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.admin=You can set up Pull Request Decoration under the {link_project_settings}. To set up analysis with your favorite CI tool, {link_see_tutorials}.
+onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci=You can request from a project administrator to set up Pull Request Decoration. To set up analysis with your favorite CI tool, {link_see_tutorials}.
+onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.project_settings=project settings
+onboarding.analysis.auto_refresh_after_analysis.set_up_pr_deco_and_ci.see_tutorials=see the tutorials
+onboarding.analysis.auto_refresh_after_analysis.check_these_links=Check these useful links while you wait: {link_branches}, {link_pr_analysis}.
+onboarding.analysis.auto_refresh_after_analysis.check_these_links.pr_analysis=Pull Request Analysis
+onboarding.analysis.auto_refresh_after_analysis.check_these_links.branches=Branch Analysis
 
 onboarding.build=What option best describes your build?
 onboarding.build.maven=Maven
@@ -3400,7 +3408,7 @@ onboarding.analysis.build_wrapper.docs=Please visit the {link}  of the Build Wra
 onboarding.analysis.build_wrapper.docs_link=official documentation
 
 
-onboarding.analysis.java.maven.header=Execute the Scanner for Maven from your computer
+onboarding.analysis.java.maven.header=Execute the Scanner for Maven
 onboarding.analysis.java.maven.header.ci=Execute the Scanner for Maven from your CI
 onboarding.analysis.java.maven.text=Running a {instance} analysis with Maven is straighforward. You just need to run the following command in your project's folder.
 onboarding.analysis.java.maven.docs_link=official documentation of the Scanner for Maven
@@ -3409,7 +3417,7 @@ onboarding.analysis.java.maven.text.custom.ci=Run the following command in the p
 onboarding.analysis.java.maven.header.sonarcloud=Execute the Scanner for Maven from Travis
 onboarding.analysis.java.maven.text.sonarcloud=Add the following lines to your <code>.travis.yml</code> file:
 
-onboarding.analysis.java.gradle.header=Execute the Scanner for Gradle from your computer
+onboarding.analysis.java.gradle.header=Execute the Scanner for Gradle
 onboarding.analysis.java.gradle.header.ci=Execute the Scanner for Gradle from your CI
 onboarding.analysis.java.gradle.text.1=Running an analysis with Gradle is straighforward. You just need to declare the {plugin_code} plugin in your {filename} file:
 onboarding.analysis.java.gradle.text.2=and run the following command:
@@ -3421,7 +3429,7 @@ onboarding.analysis.java.gradle.text.2.sonarcloud=and add the following lines to
 
 onboarding.analysis.msbuild.header=Download and unzip the Scanner for .NET
 onboarding.analysis.msbuild.text=Visit the {link} to download the latest version, and add the executable's directory to the {code} environment variable
-onboarding.analysis.msbuild.execute=Execute the Scanner for .NET from your computer
+onboarding.analysis.msbuild.execute=Execute the Scanner for .NET
 onboarding.analysis.msbuild.execute.text=Running a {instance} analysis is straighforward. You just need to execute the following commands at the root of your solution.
 onboarding.analysis.msbuild.docs_link=official documentation of the Scanner for .NET
 
@@ -3429,7 +3437,7 @@ onboarding.analysis.sq_scanner.header.linux=Download and unzip the Scanner for L
 onboarding.analysis.sq_scanner.header.win=Download and unzip the Scanner for Windows
 onboarding.analysis.sq_scanner.header.mac=Download and unzip the Scanner for macOS
 onboarding.analysis.sq_scanner.text=Visit the {link} to download the latest version, and add the {dir} directory to the {env_var} environment variable
-onboarding.analysis.sq_scanner.execute=Execute the Scanner from your computer
+onboarding.analysis.sq_scanner.execute=Execute the Scanner
 onboarding.analysis.sq_scanner.execute.text=Running a {instance} analysis is straighforward. You just need to execute the following commands in your project's folder.
 onboarding.analysis.sq_scanner.execute.text.custom=Run the following commands in your project's folder.
 onboarding.analysis.sq_scanner.docs=Please visit the {link} for more details.
@@ -3453,12 +3461,13 @@ onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a whi
 onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code:
 
 onboarding.tutorial.choose_method=How do you want to analyze your repository?
-onboarding.tutorial.choose_method.manual=Manually
+onboarding.tutorial.choose_method.manual=Locally
+onboarding.tutorial.choose_method.other-ci=Other CI
 onboarding.tutorial.choose_method.jenkins=With Jenkins
-onboarding.tutorial.choose_method.github_action=With GitHub Actions
-onboarding.tutorial.choose_method.gitlab_ci=With GitLab CI
-onboarding.tutorial.choose_method.azure_pipelines=With Azure Pipelines
-onboarding.tutorial.choose_method.bitbucket_pipelines=With Bitbucket Pipelines
+onboarding.tutorial.choose_method.github-actions=With GitHub Actions
+onboarding.tutorial.choose_method.gitlab-ci=With GitLab CI
+onboarding.tutorial.choose_method.azure-pipelines=With Azure Pipelines
+onboarding.tutorial.choose_method.bitbucket-pipelines=With Bitbucket Pipelines
 
 
 onboarding.tutorial.with.yaml.maven.pom=Update your {pom} file with the following properties: