]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13296 Re-organize tutorial components and ALM-related types
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Fri, 17 Apr 2020 15:54:50 +0000 (17:54 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 4 May 2020 20:03:53 +0000 (20:03 +0000)
103 files changed:
server/sonar-web/src/main/js/api/alm-settings.ts
server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
server/sonar-web/src/main/js/apps/create/organization/OrganizationDetailsStep.tsx
server/sonar-web/src/main/js/apps/create/organization/PlanStep.tsx
server/sonar-web/src/main/js/apps/organizations/components/OrganizationEmpty.tsx
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
server/sonar-web/src/main/js/apps/tutorials/__tests__/utils-test.ts [deleted file]
server/sonar-web/src/main/js/apps/tutorials/analyzeProject/AnalyzeTutorial.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/AnalyzeTutorial-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/__snapshots__/AnalyzeTutorial-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/LanguageForm.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/ProjectAnalysisStep.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/RenderOptions.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/Step.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/TokenStep.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/LanguageForm-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/ProjectAnalysisStep-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/Step-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/LanguageForm-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/ProjectAnalysisStep-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TokenStep-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/AnalysisCommand.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/AnalysisCommand-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/DotNet-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/JavaGradle-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/JavaMaven-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/MSBuildScanner-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/Other-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/SQScanner-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/tutorials/styles.css [deleted file]
server/sonar-web/src/main/js/apps/tutorials/utils.ts [deleted file]
server/sonar-web/src/main/js/components/tutorials/__tests__/utils-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/Step.css [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/Step.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/RenderOptions-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/Step-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/RenderOptions-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/LanguageForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/TokenStep.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/LanguageForm-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ProjectAnalysisStep-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/TokenStep-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/LanguageForm-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ProjectAnalysisStep-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/TokenStep-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/AnalysisCommand.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNet.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaGradle.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaMaven.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/MSBuildScanner.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/Other.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/SQScanner.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/AnalysisCommand-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotNet-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaGradle-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaMaven-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/MSBuildScanner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/Other-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/SQScanner-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotNet-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/Other-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/tutorials/utils.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts
server/sonar-web/src/main/js/types/alm-settings.ts

index 6ac6b1c09634a29c6f6c34751a360cd68c26286e..5b70298a7c425b0378a435c1ad981e710ab7856f 100644 (file)
@@ -23,14 +23,14 @@ import {
   AlmSettingsBindingDefinitions,
   AlmSettingsInstance,
   AzureBindingDefinition,
-  AzureProjectAlmBinding,
+  AzureProjectAlmBindingParams,
   BitbucketBindingDefinition,
-  BitbucketProjectAlmBinding,
+  BitbucketProjectAlmBindingParams,
   GithubBindingDefinition,
-  GithubProjectAlmBinding,
+  GithubProjectAlmBindingParams,
   GitlabBindingDefinition,
-  GitlabProjectAlmBinding,
-  ProjectAlmBinding
+  GitlabProjectAlmBindingParams,
+  ProjectAlmBindingResponse
 } from '../types/alm-settings';
 
 export function getAlmDefinitions(): Promise<AlmSettingsBindingDefinitions> {
@@ -87,7 +87,7 @@ export function countBindedProjects(almSetting: string) {
     .catch(throwGlobalError);
 }
 
-export function getProjectAlmBinding(project: string): Promise<ProjectAlmBinding> {
+export function getProjectAlmBinding(project: string): Promise<ProjectAlmBindingResponse> {
   return getJSON('/api/alm_settings/get_binding', { project });
 }
 
@@ -95,18 +95,18 @@ export function deleteProjectAlmBinding(project: string): Promise<void> {
   return post('/api/alm_settings/delete_binding', { project }).catch(throwGlobalError);
 }
 
-export function setProjectAzureBinding(data: AzureProjectAlmBinding) {
+export function setProjectAzureBinding(data: AzureProjectAlmBindingParams) {
   return post('/api/alm_settings/set_azure_binding', data).catch(throwGlobalError);
 }
 
-export function setProjectBitbucketBinding(data: BitbucketProjectAlmBinding) {
+export function setProjectBitbucketBinding(data: BitbucketProjectAlmBindingParams) {
   return post('/api/alm_settings/set_bitbucket_binding', data).catch(throwGlobalError);
 }
 
-export function setProjectGithubBinding(data: GithubProjectAlmBinding) {
+export function setProjectGithubBinding(data: GithubProjectAlmBindingParams) {
   return post('/api/alm_settings/set_github_binding', data).catch(throwGlobalError);
 }
 
-export function setProjectGitlabBinding(data: GitlabProjectAlmBinding) {
+export function setProjectGitlabBinding(data: GitlabProjectAlmBindingParams) {
   return post('/api/alm_settings/set_gitlab_binding', data).catch(throwGlobalError);
 }
index f074bde4aaa37acb2f24128e2bc63b0d0d17d57d..ac6a60056f879334be93fb10209bcec13c51b37b 100644 (file)
@@ -47,7 +47,6 @@ import { hasAdvancedALMIntegration, sanitizeAlmId } from '../../../helpers/almIn
 import { getOrganizationUrl } from '../../../helpers/urls';
 import { skipOnboarding } from '../../../store/users';
 import { deleteOrganization } from '../../organizations/actions';
-import '../../tutorials/styles.css'; // TODO remove me
 import { createOrganization } from './actions';
 import AlmApplicationInstalling from './AlmApplicationInstalling';
 import AutoOrganizationCreate from './AutoOrganizationCreate';
index bc1e5972dd72e149f559972a37379598b0862063..91b45c1e44f006447fa271a44bc4334da5c24e38 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon';
 import { translate } from 'sonar-ui-common/helpers/l10n';
-import Step from '../../tutorials/components/Step';
+import Step from '../../../components/tutorials/components/Step';
 
 interface Props {
   children: React.ReactNode;
index 8c15bee7ab5850ecacd0fe2e4cc4f4d723de3a2e..042210cee1e3433e23ba34419994b4b5fbbdf18f 100644 (file)
@@ -22,8 +22,8 @@ import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
 import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
+import Step from '../../../components/tutorials/components/Step';
 import { getExtensionStart } from '../../../helpers/extensions';
-import Step from '../../tutorials/components/Step';
 import BillingFormShim from '../components/BillingFormShim';
 import PlanSelect, { Plan } from './PlanSelect';
 
index 1b7d6cc8994233f64074a233cd45222c9131d920..ec09e6c5e31ad72346f0c1298425ce88d0888b84 100644 (file)
@@ -22,7 +22,7 @@ import { Button } from 'sonar-ui-common/components/controls/buttons';
 import OnboardingAddMembersIcon from 'sonar-ui-common/components/icons/OnboardingAddMembersIcon';
 import { translate } from 'sonar-ui-common/helpers/l10n';
 import { Router, withRouter } from '../../../components/hoc/withRouter';
-import '../../tutorials/styles.css';
+import '../../../components/tutorials/styles.css';
 import './OrganizationEmpty.css';
 
 interface Props {
index 633b7587789ecb373d16c8c8db56cf314417b066..c60873f126c5af0d67412359475a3a75ad09cc52 100644 (file)
@@ -22,12 +22,12 @@ import { FormattedMessage } from 'react-intl';
 import { connect } from 'react-redux';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import AnalyzeTutorial from '../../../components/tutorials/manual/AnalyzeTutorial';
 import { getBranchLikeDisplayName, isBranch, isMainBranch } from '../../../helpers/branch-like';
 import { isLoggedIn } from '../../../helpers/users';
 import { getCurrentUser, Store } from '../../../store/rootReducer';
 import { BranchLike } from '../../../types/branch-like';
 import { ComponentQualifier } from '../../../types/component';
-import AnalyzeTutorial from '../../tutorials/analyzeProject/AnalyzeTutorial';
 
 interface Props {
   branchLike?: BranchLike;
index 0a2b19abd9a86693d1ffffb6bafae21569d6da66..e532eb6775940794335d5a08600847077c06402a 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import { GithubBindingDefinition } from '../../../../../types/alm-settings';
 import AlmBindingDefinitionForm from '../AlmBindingDefinitionForm';
 
@@ -29,14 +29,14 @@ it('should render correctly', () => {
 });
 
 it('should reset if the props change', () => {
-  const bindingDefinition = mockGithubDefinition();
+  const bindingDefinition = mockGithubBindingDefinition();
   const wrapper = shallowRender({ bindingDefinition });
 
   wrapper.setState({ formData: { ...bindingDefinition, appId: 'newAppId' }, touched: true });
   wrapper.setProps({ bindingDefinition: { ...bindingDefinition } });
   expect(wrapper.state('touched')).toBe(true);
 
-  wrapper.setProps({ bindingDefinition: mockGithubDefinition({ key: 'diffKey' }) });
+  wrapper.setProps({ bindingDefinition: mockGithubBindingDefinition({ key: 'diffKey' }) });
   expect(wrapper.state('touched')).toBe(false);
 });
 
@@ -90,7 +90,7 @@ it('should handle cancelling', () => {
     onCancel
   });
 
-  wrapper.setState({ formData: mockGithubDefinition() });
+  wrapper.setState({ formData: mockGithubBindingDefinition() });
   wrapper.instance().handleCancel();
 
   expect(wrapper.state().formData).toBe(bindingDefinition);
@@ -99,7 +99,7 @@ it('should handle cancelling', () => {
 
 it('should handle deleting', () => {
   const onDelete = jest.fn();
-  const bindingDefinition = mockGithubDefinition();
+  const bindingDefinition = mockGithubBindingDefinition();
   const wrapper = shallowRender({
     bindingDefinition,
     onDelete
@@ -113,14 +113,14 @@ it('should (dis)allow submit by validating its state', () => {
   const wrapper = shallowRender();
   expect(wrapper.instance().canSubmit()).toBe(false);
 
-  wrapper.setState({ formData: mockGithubDefinition(), touched: true });
+  wrapper.setState({ formData: mockGithubBindingDefinition(), touched: true });
   expect(wrapper.instance().canSubmit()).toBe(true);
 
-  wrapper.setState({ formData: mockGithubDefinition({ key: '' }), touched: true });
+  wrapper.setState({ formData: mockGithubBindingDefinition({ key: '' }), touched: true });
   wrapper.setProps({ hideKeyField: true });
   expect(wrapper.instance().canSubmit()).toBe(true);
 
-  wrapper.setState({ formData: mockGithubDefinition({ url: '' }), touched: true });
+  wrapper.setState({ formData: mockGithubBindingDefinition({ url: '' }), touched: true });
   wrapper.setProps({ optionalFields: ['url'] });
   expect(wrapper.instance().canSubmit()).toBe(true);
 });
index 4e6fb6289d0c123083bcab9a7c132793788cc5c8..a6172bb85bb2934138fe556f4e80f6433bb14fc3 100644 (file)
@@ -20,7 +20,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockAzureBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import { AlmKeys, AzureBindingDefinition } from '../../../../../types/alm-settings';
 import AlmTab from '../AlmTab';
 
@@ -37,7 +37,7 @@ it('should handle cancel', async () => {
   const wrapper = shallowRender();
 
   wrapper.setState({
-    editedDefinition: mockAzureDefinition()
+    editedDefinition: mockAzureBindingDefinition()
   });
 
   wrapper.instance().handleCancel();
@@ -48,7 +48,7 @@ it('should handle cancel', async () => {
 });
 
 it('should handle edit', async () => {
-  const config = mockAzureDefinition();
+  const config = mockAzureBindingDefinition();
   const wrapper = shallowRender({ definitions: [config] });
   wrapper.instance().handleEdit(config.key);
   await waitAndUpdate(wrapper);
@@ -58,7 +58,7 @@ it('should handle edit', async () => {
 it('should create config', async () => {
   const onUpdateDefinitions = jest.fn();
   const createConfiguration = jest.fn(() => Promise.resolve());
-  const config = mockAzureDefinition();
+  const config = mockAzureBindingDefinition();
   const wrapper = shallowRender({ createConfiguration, onUpdateDefinitions });
 
   wrapper.instance().handleCreate();
@@ -75,7 +75,7 @@ it('should create config', async () => {
 it('should update config', async () => {
   const onUpdateDefinitions = jest.fn();
   const updateConfiguration = jest.fn(() => Promise.resolve());
-  const config = mockAzureDefinition();
+  const config = mockAzureBindingDefinition();
   const wrapper = shallowRender({ onUpdateDefinitions, updateConfiguration });
   wrapper.setState({ editedDefinition: config });
 
@@ -96,7 +96,7 @@ function shallowRender(props: Partial<AlmTab<AzureBindingDefinition>['props']> =
       alm={AlmKeys.Azure}
       createConfiguration={jest.fn()}
       defaultBinding={DEFAULT_BINDING}
-      definitions={[mockAzureDefinition()]}
+      definitions={[mockAzureBindingDefinition()]}
       form={jest.fn()}
       loading={false}
       multipleAlmEnabled={true}
index d0fab91181d692757a5e44e1c3c252f4c171c5b3..4514ccf263fe91b39e270f55847e320bf595cd9a 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import { AlmKeys, GithubBindingDefinition } from '../../../../../types/alm-settings';
 import AlmTabRenderer, { AlmTabRendererProps } from '../AlmTabRenderer';
 
 it('should render correctly for multi-ALM binding', () => {
   expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
   expect(shallowRender()).toMatchSnapshot('loaded');
-  expect(shallowRender({ editedDefinition: mockGithubDefinition() })).toMatchSnapshot(
+  expect(shallowRender({ editedDefinition: mockGithubBindingDefinition() })).toMatchSnapshot(
     'editing a definition'
   );
   expect(
@@ -51,7 +51,7 @@ it('should render correctly for single-ALM binding', () => {
   expect(shallowRender({ loading: true, multipleAlmEnabled: false })).toMatchSnapshot();
   expect(shallowRender({ multipleAlmEnabled: false })).toMatchSnapshot();
   expect(
-    shallowRender({ definitions: [mockGithubDefinition()], multipleAlmEnabled: false })
+    shallowRender({ definitions: [mockGithubBindingDefinition()], multipleAlmEnabled: false })
   ).toMatchSnapshot();
 });
 
@@ -61,8 +61,8 @@ function shallowRender(props: Partial<AlmTabRendererProps<GithubBindingDefinitio
       additionalColumnsHeaders={['url', 'app_id']}
       additionalColumnsKeys={['url', 'appId']}
       alm={AlmKeys.GitHub}
-      defaultBinding={mockGithubDefinition()}
-      definitions={[mockGithubDefinition()]}
+      defaultBinding={mockGithubBindingDefinition()}
+      definitions={[mockGithubBindingDefinition()]}
       form={jest.fn()}
       loading={false}
       multipleAlmEnabled={true}
index 1d5fbb7472b5939c88c13a8c9ddac4c2575659da..fe4a99102a0bd08623a035a9ec6898b0c6a96eb2 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockAzureBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import AzureForm, { AzureFormProps } from '../AzureForm';
 
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ formData: mockAzureDefinition() })).toMatchSnapshot();
+  expect(shallowRender({ formData: mockAzureBindingDefinition() })).toMatchSnapshot();
 });
 
 function shallowRender(props: Partial<AzureFormProps> = {}) {
index 16dba8d81cd272fcb088068e7ea72de01a22177b..5b2389dda7ca94b9e9c4f52211ee994693909ee5 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockAzureBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import AzureTab, { AzureTabProps } from '../AzureTab';
 
 it('should render correctly', () => {
@@ -29,7 +29,7 @@ it('should render correctly', () => {
 function shallowRender(props: Partial<AzureTabProps> = {}) {
   return shallow(
     <AzureTab
-      definitions={[mockAzureDefinition()]}
+      definitions={[mockAzureBindingDefinition()]}
       loading={false}
       multipleAlmEnabled={true}
       onDelete={jest.fn()}
index de69381684497b661158c1ed913f266cd4fa414f..af5548d793d503c9376a6f60bcfd4e61e714c400 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockBitbucketDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockBitbucketBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import BitbucketForm, { BitbucketFormProps } from '../BitbucketForm';
 
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ formData: mockBitbucketDefinition() })).toMatchSnapshot();
+  expect(shallowRender({ formData: mockBitbucketBindingDefinition() })).toMatchSnapshot();
 });
 
 function shallowRender(props: Partial<BitbucketFormProps> = {}) {
index 1cdb3162ad92f1269660ba95c6dfc34815567b40..d1bfdcc5d499cac77b76d307c896918a5c812b25 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockBitbucketDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockBitbucketBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import BitbucketTab, { BitbucketTabProps } from '../BitbucketTab';
 
 it('should render correctly', () => {
@@ -29,7 +29,7 @@ it('should render correctly', () => {
 function shallowRender(props: Partial<BitbucketTabProps> = {}) {
   return shallow(
     <BitbucketTab
-      definitions={[mockBitbucketDefinition()]}
+      definitions={[mockBitbucketBindingDefinition()]}
       loading={false}
       multipleAlmEnabled={true}
       onDelete={jest.fn()}
index b279b01ea22a3a2cedb5584e4ce9a2037b8b3a6d..e67edb9e294dcf1fe90c984ab8955b498f48bd8d 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GithubForm, { GithubFormProps } from '../GithubForm';
 
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ formData: mockGithubDefinition() })).toMatchSnapshot();
+  expect(shallowRender({ formData: mockGithubBindingDefinition() })).toMatchSnapshot();
 });
 
 function shallowRender(props: Partial<GithubFormProps> = {}) {
index 15d6d36638d8b0f4fdbd149068a14a13391bd91c..51f3ec73ee0c97359921aa0721f964b7c775740e 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockGithubBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GithubTab, { GithubTabProps } from '../GithubTab';
 
 it('should render correctly', () => {
@@ -31,7 +31,7 @@ function shallowRender(props: Partial<GithubTabProps> = {}) {
   return shallow(
     <GithubTab
       branchesEnabled={true}
-      definitions={[mockGithubDefinition()]}
+      definitions={[mockGithubBindingDefinition()]}
       loading={false}
       multipleAlmEnabled={true}
       onDelete={jest.fn()}
index 038331bd641d8c2e57abc1d9bb2ba2aa5f8b8218..06c7b18a948c740c88c198a36e0e7b50768e0f92 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGitlabDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockGitlabBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GitlabForm, { GitlabFormProps } from '../GitlabForm';
 
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot();
-  expect(shallowRender({ formData: mockGitlabDefinition() })).toMatchSnapshot();
+  expect(shallowRender({ formData: mockGitlabBindingDefinition() })).toMatchSnapshot();
 });
 
 function shallowRender(props: Partial<GitlabFormProps> = {}) {
index 45cc16ed97bc65bdad59804da70c0e1db0ca932b..7d8d70c3b3cdb6052a416e77fdea60c36f71ed5e 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGitlabDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { mockGitlabBindingDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GitlabTab, { GitlabTabProps } from '../GitlabTab';
 
 it('should render correctly', () => {
@@ -31,7 +31,7 @@ function shallowRender(props: Partial<GitlabTabProps> = {}) {
   return shallow(
     <GitlabTab
       branchesEnabled={true}
-      definitions={[mockGitlabDefinition()]}
+      definitions={[mockGitlabBindingDefinition()]}
       loading={false}
       multipleAlmEnabled={true}
       onDelete={jest.fn()}
index c311e6a0a3380285ce2937ea43e82f53be91120d..9d5e2fa471f366dd172302c4a458abeb9dff8c6f 100644 (file)
@@ -28,27 +28,33 @@ import {
   setProjectGitlabBinding
 } from '../../../../api/alm-settings';
 import throwGlobalError from '../../../../app/utils/throwGlobalError';
-import { AlmKeys, AlmSettingsInstance, ProjectAlmBinding } from '../../../../types/alm-settings';
+import {
+  AlmKeys,
+  AlmSettingsInstance,
+  ProjectAlmBindingResponse
+} from '../../../../types/alm-settings';
 import PRDecorationBindingRenderer from './PRDecorationBindingRenderer';
 
+type FormData = T.Omit<ProjectAlmBindingResponse, 'alm'>;
+
 interface Props {
   component: T.Component;
 }
 
 interface State {
-  formData: ProjectAlmBinding;
+  formData: FormData;
   instances: AlmSettingsInstance[];
   isChanged: boolean;
   isConfigured: boolean;
   isValid: boolean;
   loading: boolean;
-  orignalData?: ProjectAlmBinding;
+  orignalData?: FormData;
   saving: boolean;
   success: boolean;
 }
 
 const REQUIRED_FIELDS_BY_ALM: {
-  [almKey in AlmKeys]: Array<keyof T.Omit<ProjectAlmBinding, 'key'>>;
+  [almKey in AlmKeys]: Array<keyof T.Omit<FormData, 'key'>>;
 } = {
   [AlmKeys.Azure]: [],
   [AlmKeys.Bitbucket]: ['repository', 'slug'],
@@ -104,7 +110,7 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat
       });
   };
 
-  getProjectBinding(project: string): Promise<ProjectAlmBinding | undefined> {
+  getProjectBinding(project: string): Promise<ProjectAlmBindingResponse | undefined> {
     return getProjectAlmBinding(project).catch((response: Response) => {
       if (response && response.status === 404) {
         return undefined;
@@ -144,7 +150,7 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat
   submitProjectAlmBinding(
     alm: AlmKeys,
     key: string,
-    almSpecificFields?: T.Omit<ProjectAlmBinding, 'key'>
+    almSpecificFields?: T.Omit<FormData, 'key'>
   ): Promise<void> {
     const almSetting = key;
     const project = this.props.component.key;
@@ -225,13 +231,13 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat
   };
 
   isDataSame(
-    { key, repository = '', slug = '', summaryCommentEnabled = false }: ProjectAlmBinding,
+    { key, repository = '', slug = '', summaryCommentEnabled = false }: FormData,
     {
       key: oKey = '',
       repository: oRepository = '',
       slug: oSlug = '',
       summaryCommentEnabled: osummaryCommentEnabled = false
-    }: ProjectAlmBinding
+    }: FormData
   ) {
     return (
       key === oKey &&
@@ -241,7 +247,7 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat
     );
   }
 
-  handleFieldChange = (id: keyof ProjectAlmBinding, value: string | boolean) => {
+  handleFieldChange = (id: keyof ProjectAlmBindingResponse, value: string | boolean) => {
     this.setState(({ formData, orignalData }) => {
       const newFormData = {
         ...formData,
index 91b8a78af1f9ed5c8978b6b6f5986d1eed1fb7d5..bebbde1d0202a0edd8bcad862d539d9ebdb4fae5 100644 (file)
@@ -27,17 +27,21 @@ import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon'
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
 import { translate } from 'sonar-ui-common/helpers/l10n';
-import { AlmKeys, AlmSettingsInstance, ProjectAlmBinding } from '../../../../types/alm-settings';
+import {
+  AlmKeys,
+  AlmSettingsInstance,
+  ProjectAlmBindingResponse
+} from '../../../../types/alm-settings';
 import InputForBoolean from '../inputs/InputForBoolean';
 
 export interface PRDecorationBindingRendererProps {
-  formData: ProjectAlmBinding;
+  formData: T.Omit<ProjectAlmBindingResponse, 'alm'>;
   instances: AlmSettingsInstance[];
   isChanged: boolean;
   isConfigured: boolean;
   isValid: boolean;
   loading: boolean;
-  onFieldChange: (id: keyof ProjectAlmBinding, value: string | boolean) => void;
+  onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
   onReset: () => void;
   onSubmit: () => void;
   saving: boolean;
@@ -52,8 +56,8 @@ interface LabelProps {
 }
 
 interface CommonFieldProps extends LabelProps {
-  onFieldChange: (id: keyof ProjectAlmBinding, value: string | boolean) => void;
-  propKey: keyof ProjectAlmBinding;
+  onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
+  propKey: keyof ProjectAlmBindingResponse;
 }
 
 function optionRenderer(instance: AlmSettingsInstance) {
diff --git a/server/sonar-web/src/main/js/apps/tutorials/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/tutorials/__tests__/utils-test.ts
deleted file mode 100644 (file)
index f16f4b8..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { getUniqueTokenName } from '../utils';
-
-const initialTokenName = 'Analyze "lightsaber"';
-
-it('should return the given name when the user has no token', () => {
-  const userTokens: T.UserToken[] = [];
-
-  expect(getUniqueTokenName(userTokens, initialTokenName)).toBe(initialTokenName);
-});
-
-it('should generate a token with the given name', () => {
-  const userTokens = [{ name: initialTokenName, createdAt: '2019-06-14T09:45:52+0200' }];
-
-  expect(getUniqueTokenName(userTokens, 'Analyze "project"')).toBe('Analyze "project"');
-});
-
-it('should generate a unique token when the name already exists', () => {
-  const userTokens = [
-    { name: initialTokenName, createdAt: '2019-06-14T09:45:52+0200' },
-    { name: `${initialTokenName} 1`, createdAt: '2019-06-14T09:45:52+0200' }
-  ];
-
-  expect(getUniqueTokenName(userTokens, initialTokenName)).toBe('Analyze "lightsaber" 2');
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/analyzeProject/AnalyzeTutorial.tsx b/server/sonar-web/src/main/js/apps/tutorials/analyzeProject/AnalyzeTutorial.tsx
deleted file mode 100644 (file)
index e873d68..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 InstanceMessage from '../../../components/common/InstanceMessage';
-import { isVSTS } from '../../../helpers/almIntegrations';
-import ProjectAnalysisStep from '../components/ProjectAnalysisStep';
-import TokenStep from '../components/TokenStep';
-import '../styles.css';
-
-export enum Steps {
-  ANALYSIS,
-  TOKEN
-}
-
-interface Props {
-  component: T.Component;
-  currentUser: T.LoggedInUser;
-}
-
-interface State {
-  step: Steps;
-  token?: string;
-}
-
-export default class AnalyzeTutorial extends React.PureComponent<Props, State> {
-  state: State = { step: Steps.TOKEN };
-
-  handleTokenDone = (token: string) => {
-    this.setState({ step: Steps.ANALYSIS, token });
-  };
-
-  handleTokenOpen = () => {
-    this.setState({ step: Steps.TOKEN });
-  };
-
-  render() {
-    const { component, currentUser } = this.props;
-    const { step, token } = this.state;
-
-    const almKey = (component.alm && component.alm.key) || currentUser.externalProvider;
-    return (
-      <>
-        <div className="page-header big-spacer-bottom">
-          <h1 className="page-title">{translate('onboarding.project_analysis.header')}</h1>
-          <p className="page-description">
-            <InstanceMessage message={translate('onboarding.project_analysis.description')} />
-          </p>
-        </div>
-
-        {!isVSTS(almKey) && (
-          <>
-            <TokenStep
-              currentUser={currentUser}
-              finished={Boolean(this.state.token)}
-              initialTokenName={`Analyze "${component.name}"`}
-              onContinue={this.handleTokenDone}
-              onOpen={this.handleTokenOpen}
-              open={step === Steps.TOKEN}
-              stepNumber={1}
-            />
-
-            <ProjectAnalysisStep
-              component={component}
-              displayRowLayout={true}
-              open={step === Steps.ANALYSIS}
-              stepNumber={2}
-              token={token}
-            />
-          </>
-        )}
-      </>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/AnalyzeTutorial-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/AnalyzeTutorial-test.tsx
deleted file mode 100644 (file)
index 4c4344f..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 AnalyzeTutorial from '../AnalyzeTutorial';
-
-Date.now = jest.fn().mockReturnValue(1540457859031);
-
-const component = {
-  key: 'foo',
-  analysisDate: '2016-01-01',
-  breadcrumbs: [],
-  name: 'Foo',
-  organization: 'org',
-  qualifier: 'TRK',
-  version: '0.0.1'
-};
-
-const loggedInUser: T.LoggedInUser = {
-  groups: [],
-  isLoggedIn: true,
-  login: 'luke',
-  name: 'Skywalker',
-  scmAccounts: []
-};
-
-it('renders correctly', () => {
-  expect(getWrapper()).toMatchSnapshot();
-});
-
-function getWrapper(props = {}) {
-  return shallow(<AnalyzeTutorial component={component} currentUser={loggedInUser} {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/__snapshots__/AnalyzeTutorial-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/analyzeProject/__tests__/__snapshots__/AnalyzeTutorial-test.tsx.snap
deleted file mode 100644 (file)
index c4b2f7a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<Fragment>
-  <div
-    className="page-header big-spacer-bottom"
-  >
-    <h1
-      className="page-title"
-    >
-      onboarding.project_analysis.header
-    </h1>
-    <p
-      className="page-description"
-    >
-      <InstanceMessage
-        message="onboarding.project_analysis.description"
-      />
-    </p>
-  </div>
-  <TokenStep
-    currentUser={
-      Object {
-        "groups": Array [],
-        "isLoggedIn": true,
-        "login": "luke",
-        "name": "Skywalker",
-        "scmAccounts": Array [],
-      }
-    }
-    finished={false}
-    initialTokenName="Analyze \\"Foo\\""
-    onContinue={[Function]}
-    onOpen={[Function]}
-    open={true}
-    stepNumber={1}
-  />
-  <ProjectAnalysisStep
-    component={
-      Object {
-        "analysisDate": "2016-01-01",
-        "breadcrumbs": Array [],
-        "key": "foo",
-        "name": "Foo",
-        "organization": "org",
-        "qualifier": "TRK",
-        "version": "0.0.1",
-      }
-    }
-    displayRowLayout={true}
-    open={false}
-    stepNumber={2}
-  />
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/LanguageForm.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/LanguageForm.tsx
deleted file mode 100644 (file)
index d289f09..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 RadioToggle from 'sonar-ui-common/components/controls/RadioToggle';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { isLanguageConfigured, LanguageConfig } from '../utils';
-import { RenderOptions } from './RenderOptions';
-
-interface Props {
-  component: T.Component;
-  config?: LanguageConfig;
-  onDone: (config: LanguageConfig) => void;
-  onReset: VoidFunction;
-  organization?: string;
-}
-
-type State = LanguageConfig;
-
-export interface RenderOSProps {
-  os: string | undefined;
-  setOS: (os: string) => void;
-}
-
-export function RenderOS(props: RenderOSProps) {
-  return (
-    <RenderOptions
-      checked={props.os}
-      name="os"
-      onCheck={props.setOS}
-      optionLabelKey="onboarding.language.os"
-      options={['linux', 'win', 'mac']}
-      titleLabelKey="onboarding.language.os"
-    />
-  );
-}
-
-export default class LanguageForm extends React.PureComponent<Props, State> {
-  constructor(props: Props) {
-    super(props);
-    this.state = {
-      ...(this.props.config || {}),
-      projectKey: props.component ? props.component.key : undefined
-    };
-  }
-
-  handleChange = () => {
-    if (isLanguageConfigured(this.state)) {
-      this.props.onDone(this.state);
-    } else {
-      this.props.onReset();
-    }
-  };
-
-  handleLanguageChange = (language: string) => {
-    this.setState({ language }, this.handleChange);
-  };
-
-  handleJavaBuildChange = (javaBuild: string) => {
-    this.setState({ javaBuild }, this.handleChange);
-  };
-
-  handleOSChange = (os: string) => {
-    this.setState({ os }, this.handleChange);
-  };
-
-  renderJavaBuild = () => (
-    <RenderOptions
-      checked={this.state.javaBuild}
-      name="java-build"
-      onCheck={this.handleJavaBuildChange}
-      optionLabelKey="onboarding.language.java.build_technology"
-      options={['maven', 'gradle']}
-      titleLabelKey="onboarding.language.java.build_technology"
-    />
-  );
-
-  render() {
-    const { language } = this.state;
-    const languages = ['java', 'dotnet', 'other'];
-
-    return (
-      <>
-        <div>
-          <h4 className="spacer-bottom">{translate('onboarding.language')}</h4>
-          <RadioToggle
-            name="language"
-            onCheck={this.handleLanguageChange}
-            options={languages.map(language => ({
-              label: translate('onboarding.language', language),
-              value: language
-            }))}
-            value={language}
-          />
-        </div>
-        {language === 'java' && this.renderJavaBuild()}
-        {language === 'other' && <RenderOS os={this.state.os} setOS={this.handleOSChange} />}
-      </>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/ProjectAnalysisStep.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/ProjectAnalysisStep.tsx
deleted file mode 100644 (file)
index 710938d..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { LanguageConfig } from '../utils';
-import AnalysisCommand from './commands/AnalysisCommand';
-import LanguageForm from './LanguageForm';
-import Step from './Step';
-
-interface Props {
-  component: T.Component;
-  displayRowLayout?: boolean;
-  onFinish?: (projectKey?: string) => void;
-  onReset?: VoidFunction;
-  open: boolean;
-  organization?: string;
-  stepNumber: number;
-  token?: string;
-}
-
-interface State {
-  config?: LanguageConfig;
-}
-
-export function getProjectKey(config?: LanguageConfig, component?: T.Component) {
-  return (component && component.key) || (config && config.projectKey);
-}
-
-export default class ProjectAnalysisStep extends React.PureComponent<Props, State> {
-  state: State = {};
-
-  handleLanguageSelect = (config: LanguageConfig) => {
-    this.setState({ config });
-    if (this.props.onFinish) {
-      const projectKey = config.language !== 'java' ? getProjectKey(config) : undefined;
-      this.props.onFinish(projectKey);
-    }
-  };
-
-  handleLanguageReset = () => {
-    this.setState({ config: undefined });
-    if (this.props.onReset) {
-      this.props.onReset();
-    }
-  };
-
-  renderForm = () => {
-    const languageComponent = (
-      <LanguageForm
-        component={this.props.component}
-        onDone={this.handleLanguageSelect}
-        onReset={this.handleLanguageReset}
-        organization={this.props.organization}
-      />
-    );
-    const analysisComponent = this.state.config && (
-      <AnalysisCommand
-        component={this.props.component}
-        languageConfig={this.state.config}
-        organization={this.props.organization}
-        small={true}
-        token={this.props.token}
-      />
-    );
-
-    if (this.props.displayRowLayout) {
-      return (
-        <div className="boxed-group-inner">
-          <div className="display-flex-column">
-            {languageComponent}
-            {analysisComponent && <div className="huge-spacer-top">{analysisComponent}</div>}
-          </div>
-        </div>
-      );
-    }
-
-    return (
-      <div className="boxed-group-inner">
-        <div className="flex-columns">
-          <div className="flex-column flex-column-half bordered-right">{languageComponent}</div>
-          <div className="flex-column flex-column-half">{analysisComponent}</div>
-        </div>
-      </div>
-    );
-  };
-
-  renderResult = () => null;
-
-  render() {
-    return (
-      <Step
-        finished={false}
-        onOpen={() => {}}
-        open={this.props.open}
-        renderForm={this.renderForm}
-        renderResult={this.renderResult}
-        stepNumber={this.props.stepNumber}
-        stepTitle={translate('onboarding.analysis.header')}
-      />
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/RenderOptions.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/RenderOptions.tsx
deleted file mode 100644 (file)
index 377727a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 RadioToggle from 'sonar-ui-common/components/controls/RadioToggle';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-
-interface RenderOptionsProps {
-  checked: string | undefined;
-  name: string;
-  onCheck: (checked: string) => void;
-  optionLabelKey: string;
-  options: string[];
-  titleLabelKey?: string;
-}
-
-export function RenderOptions({
-  checked,
-  onCheck,
-  optionLabelKey,
-  options,
-  titleLabelKey
-}: RenderOptionsProps) {
-  return (
-    <div className="big-spacer-top">
-      {titleLabelKey && <h4 className="spacer-bottom">{translate(titleLabelKey)}</h4>}
-
-      <RadioToggle
-        name={name}
-        onCheck={onCheck}
-        options={options.map(build => ({
-          label: translate(optionLabelKey, build),
-          value: build
-        }))}
-        value={checked}
-      />
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/Step.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/Step.tsx
deleted file mode 100644 (file)
index 190edda..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.
- */
-/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/no-noninteractive-tabindex */
-import * as classNames from 'classnames';
-import * as React from 'react';
-
-interface Props {
-  finished?: boolean;
-  onOpen: VoidFunction;
-  open: boolean;
-  renderForm: () => React.ReactNode;
-  renderResult: () => React.ReactNode;
-  stepNumber: number;
-  stepTitle: React.ReactNode;
-}
-
-export default function Step(props: Props) {
-  const className = classNames('boxed-group', 'onboarding-step', {
-    'is-open': props.open,
-    'is-finished': props.finished
-  });
-
-  const clickable = !props.open && props.finished;
-
-  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
-    event.preventDefault();
-    props.onOpen();
-  };
-
-  return (
-    <div
-      className={className}
-      onClick={clickable ? handleClick : undefined}
-      role={clickable ? 'button' : undefined}
-      tabIndex={clickable ? 0 : undefined}>
-      <div className="onboarding-step-number">{props.stepNumber}</div>
-      {!props.open && props.renderResult()}
-      <div className="boxed-group-header">
-        <h2>{props.stepTitle}</h2>
-      </div>
-      {!props.open && <div className="boxed-group-inner" />}
-      <div className={classNames({ hidden: !props.open })}>{props.renderForm()}</div>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/TokenStep.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/TokenStep.tsx
deleted file mode 100644 (file)
index 32da736..0000000
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { Button, DeleteButton, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
-import Radio from 'sonar-ui-common/components/controls/Radio';
-import AlertErrorIcon from 'sonar-ui-common/components/icons/AlertErrorIcon';
-import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { generateToken, getTokens, revokeToken } from '../../../api/user-tokens';
-import { getUniqueTokenName } from '../utils';
-import Step from './Step';
-
-interface Props {
-  currentUser: Pick<T.LoggedInUser, 'login'>;
-  finished: boolean;
-  initialTokenName?: string;
-  open: boolean;
-  onContinue: (token: string) => void;
-  onOpen: VoidFunction;
-  stepNumber: number;
-}
-
-interface State {
-  existingToken: string;
-  loading: boolean;
-  selection: string;
-  tokenName?: string;
-  token?: string;
-  tokens?: T.UserToken[];
-}
-
-export default class TokenStep extends React.PureComponent<Props, State> {
-  mounted = false;
-
-  constructor(props: Props) {
-    super(props);
-    this.state = {
-      existingToken: '',
-      loading: false,
-      selection: 'generate',
-      tokenName: props.initialTokenName
-    };
-  }
-
-  componentDidMount() {
-    this.mounted = true;
-    getTokens(this.props.currentUser.login).then(
-      tokens => {
-        if (this.mounted) {
-          this.setState({ tokens });
-          if (
-            this.props.initialTokenName !== undefined &&
-            this.props.initialTokenName === this.state.tokenName
-          ) {
-            this.setState({ tokenName: getUniqueTokenName(tokens) });
-          }
-        }
-      },
-      () => {}
-    );
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  getToken = () =>
-    this.state.selection === 'generate' ? this.state.token : this.state.existingToken;
-
-  canContinue = () => {
-    const { existingToken, selection, token } = this.state;
-    const validExistingToken = existingToken.match(/^[a-z0-9]+$/) != null;
-    return (
-      (selection === 'generate' && token != null) ||
-      (selection === 'use-existing' && existingToken && validExistingToken)
-    );
-  };
-
-  handleTokenNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-    this.setState({ tokenName: event.target.value });
-  };
-
-  handleTokenGenerate = (event: React.FormEvent<HTMLFormElement>) => {
-    event.preventDefault();
-    const { tokenName } = this.state;
-    if (tokenName) {
-      this.setState({ loading: true });
-      generateToken({ name: tokenName }).then(({ token }) => {
-        if (this.mounted) {
-          this.setState({ loading: false, token });
-        }
-      }, this.stopLoading);
-    }
-  };
-
-  handleTokenRevoke = () => {
-    const { tokenName } = this.state;
-    if (tokenName) {
-      this.setState({ loading: true });
-      revokeToken({ name: tokenName }).then(() => {
-        if (this.mounted) {
-          this.setState({ loading: false, token: undefined, tokenName: undefined });
-        }
-      }, this.stopLoading);
-    }
-  };
-
-  handleContinueClick = () => {
-    const token = this.getToken();
-    if (token) {
-      this.props.onContinue(token);
-    }
-  };
-
-  handleGenerateClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.setState({ selection: 'generate' });
-  };
-
-  handleUseExistingClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    this.setState({ selection: 'use-existing' });
-  };
-
-  handleModeChange = (mode: string) => {
-    this.setState({ selection: mode });
-  };
-
-  handleExisingTokenChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-    this.setState({ existingToken: event.currentTarget.value });
-  };
-
-  stopLoading = () => {
-    if (this.mounted) {
-      this.setState({ loading: false });
-    }
-  };
-
-  renderGenerateOption = () => (
-    <div>
-      {this.state.tokens !== undefined && this.state.tokens.length > 0 ? (
-        <Radio
-          checked={this.state.selection === 'generate'}
-          onCheck={this.handleModeChange}
-          value="generate">
-          {translate('onboarding.token.generate_token')}
-        </Radio>
-      ) : (
-        translate('onboarding.token.generate_token')
-      )}
-      {this.state.selection === 'generate' && (
-        <div className="big-spacer-top">
-          <form onSubmit={this.handleTokenGenerate}>
-            <input
-              autoFocus={true}
-              className="input-super-large spacer-right text-middle"
-              onChange={this.handleTokenNameChange}
-              placeholder={translate('onboarding.token.generate_token.placeholder')}
-              required={true}
-              type="text"
-              value={this.state.tokenName || ''}
-            />
-            {this.state.loading ? (
-              <i className="spinner text-middle" />
-            ) : (
-              <SubmitButton className="text-middle" disabled={!this.state.tokenName}>
-                {translate('onboarding.token.generate')}
-              </SubmitButton>
-            )}
-          </form>
-        </div>
-      )}
-    </div>
-  );
-
-  renderUseExistingOption = () => {
-    const { existingToken } = this.state;
-    const validInput = !existingToken || existingToken.match(/^[a-z0-9]+$/) != null;
-
-    return (
-      <div className="big-spacer-top">
-        <Radio
-          checked={this.state.selection === 'use-existing'}
-          onCheck={this.handleModeChange}
-          value="use-existing">
-          {translate('onboarding.token.use_existing_token')}
-        </Radio>
-        {this.state.selection === 'use-existing' && (
-          <div className="big-spacer-top">
-            <input
-              autoFocus={true}
-              className="input-super-large spacer-right text-middle"
-              onChange={this.handleExisingTokenChange}
-              placeholder={translate('onboarding.token.use_existing_token.placeholder')}
-              required={true}
-              type="text"
-              value={this.state.existingToken}
-            />
-            {!validInput && (
-              <span className="text-danger">
-                <AlertErrorIcon className="little-spacer-right text-text-top" />
-                {translate('onboarding.token.invalid_format')}
-              </span>
-            )}
-          </div>
-        )}
-      </div>
-    );
-  };
-
-  renderForm = () => {
-    const { loading, token, tokenName, tokens } = this.state;
-    const canUseExisting = tokens !== undefined && tokens.length > 0;
-
-    return (
-      <div className="boxed-group-inner">
-        {token != null ? (
-          <form onSubmit={this.handleTokenRevoke}>
-            <span className="text-middle">
-              {tokenName}
-              {': '}
-            </span>
-            <strong className="spacer-right text-middle">{token}</strong>
-            {loading ? (
-              <i className="spinner text-middle" />
-            ) : (
-              <DeleteButton className="button-small text-middle" onClick={this.handleTokenRevoke} />
-            )}
-          </form>
-        ) : (
-          <div>
-            {this.renderGenerateOption()}
-            {canUseExisting && this.renderUseExistingOption()}
-          </div>
-        )}
-
-        <div className="note big-spacer-top width-50">
-          <FormattedMessage
-            defaultMessage={translate('onboarding.token.text')}
-            id="onboarding.token.text"
-            values={{
-              link: (
-                <Link target="_blank" to="/account/security">
-                  {translate('onboarding.token.text.user_account')}
-                </Link>
-              )
-            }}
-          />
-        </div>
-
-        {this.canContinue() && (
-          <div className="big-spacer-top">
-            <Button className="js-continue" onClick={this.handleContinueClick}>
-              {translate('continue')}
-            </Button>
-          </div>
-        )}
-      </div>
-    );
-  };
-
-  renderResult = () => {
-    const { selection, tokenName } = this.state;
-    const token = this.getToken();
-
-    if (!token) {
-      return null;
-    }
-
-    return (
-      <div className="boxed-group-actions display-flex-center">
-        <AlertSuccessIcon className="spacer-right" />
-        {selection === 'generate' && tokenName && `${tokenName}: `}
-        <strong>{token}</strong>
-      </div>
-    );
-  };
-
-  render() {
-    return (
-      <Step
-        finished={this.props.finished}
-        onOpen={this.props.onOpen}
-        open={this.props.open}
-        renderForm={this.renderForm}
-        renderResult={this.renderResult}
-        stepNumber={this.props.stepNumber}
-        stepTitle={translate('onboarding.token.header')}
-      />
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/LanguageForm-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/LanguageForm-test.tsx
deleted file mode 100644 (file)
index 24dd2af..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 LanguageForm from '../LanguageForm';
-
-it('selects java', () => {
-  const onDone = jest.fn();
-  const wrapper = shallow(
-    <LanguageForm component={mockComponent()} onDone={onDone} onReset={jest.fn()} />
-  );
-
-  (wrapper.find('RadioToggle').prop('onCheck') as Function)('java');
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('selects c#', () => {
-  const onDone = jest.fn();
-  const wrapper = shallow(
-    <LanguageForm component={mockComponent()} onDone={onDone} onReset={jest.fn()} />
-  );
-
-  (wrapper.find('RadioToggle').prop('onCheck') as Function)('dotnet');
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('selects other', () => {
-  const onDone = jest.fn();
-  const wrapper = shallow(
-    <LanguageForm component={mockComponent()} onDone={onDone} onReset={jest.fn()} />
-  );
-
-  (wrapper.find('RadioToggle').prop('onCheck') as Function)('other');
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/ProjectAnalysisStep-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/ProjectAnalysisStep-test.tsx
deleted file mode 100644 (file)
index 4b6ecf8..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 ProjectAnalysisStep from '../ProjectAnalysisStep';
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender() {
-  return shallow(<ProjectAnalysisStep component={mockComponent()} open={true} stepNumber={1} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/Step-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/Step-test.tsx
deleted file mode 100644 (file)
index bda5bed..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { click } from 'sonar-ui-common/helpers/testUtils';
-import Step from '../Step';
-
-it('renders', () => {
-  const wrapper = shallow(
-    <Step
-      finished={true}
-      onOpen={jest.fn()}
-      open={true}
-      renderForm={() => <div>form</div>}
-      renderResult={() => <div>result</div>}
-      stepNumber={1}
-      stepTitle="First Step"
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-  wrapper.setProps({ open: false });
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('re-opens', () => {
-  const onOpen = jest.fn();
-  const wrapper = shallow(
-    <Step
-      finished={true}
-      onOpen={onOpen}
-      open={false}
-      renderForm={() => <div>form</div>}
-      renderResult={() => <div>result</div>}
-      stepNumber={1}
-      stepTitle="First Step"
-    />
-  );
-  click(wrapper);
-  expect(onOpen).toBeCalled();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/TokenStep-test.tsx
deleted file mode 100644 (file)
index 8a9f52d..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { change, click, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import TokenStep from '../TokenStep';
-
-jest.mock('../../../../api/user-tokens', () => ({
-  getTokens: () => Promise.resolve([{ name: 'foo' }]),
-  generateToken: () => Promise.resolve({ token: 'abcd1234' }),
-  revokeToken: () => Promise.resolve()
-}));
-
-const currentUser: Pick<T.LoggedInUser, 'login'> = { login: 'user' };
-
-it('generates token', async () => {
-  const wrapper = shallow(
-    <TokenStep
-      currentUser={currentUser}
-      finished={false}
-      onContinue={jest.fn()}
-      onOpen={jest.fn()}
-      open={true}
-      stepNumber={1}
-    />
-  );
-  await waitAndUpdate(wrapper);
-  expect(wrapper.dive()).toMatchSnapshot();
-  change(wrapper.dive().find('input'), 'my token');
-  submit(wrapper.dive().find('form'));
-  expect(wrapper.dive()).toMatchSnapshot(); // spinner
-  await waitAndUpdate(wrapper);
-  expect(wrapper.dive()).toMatchSnapshot();
-});
-
-it('revokes token', async () => {
-  const wrapper = shallow(
-    <TokenStep
-      currentUser={currentUser}
-      finished={false}
-      onContinue={jest.fn()}
-      onOpen={jest.fn()}
-      open={true}
-      stepNumber={1}
-    />
-  );
-  await new Promise(setImmediate);
-  wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
-  expect(wrapper.dive()).toMatchSnapshot();
-  (wrapper
-    .dive()
-    .find('DeleteButton')
-    .prop('onClick') as Function)();
-  wrapper.update();
-  expect(wrapper.dive()).toMatchSnapshot(); // spinner
-  await waitAndUpdate(wrapper);
-  expect(wrapper.dive()).toMatchSnapshot();
-});
-
-it('continues', async () => {
-  const onContinue = jest.fn();
-  const wrapper = shallow(
-    <TokenStep
-      currentUser={currentUser}
-      finished={false}
-      onContinue={onContinue}
-      onOpen={jest.fn()}
-      open={true}
-      stepNumber={1}
-    />
-  );
-  await new Promise(setImmediate);
-  wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
-  click(wrapper.dive().find('[className="js-continue"]'));
-  expect(onContinue).toBeCalledWith('abcd1234');
-});
-
-it('uses existing token', async () => {
-  const onContinue = jest.fn();
-  const wrapper = shallow(
-    <TokenStep
-      currentUser={currentUser}
-      finished={false}
-      onContinue={onContinue}
-      onOpen={jest.fn()}
-      open={true}
-      stepNumber={1}
-    />
-  );
-  await new Promise(setImmediate);
-  wrapper.setState({ existingToken: 'abcd1234', selection: 'use-existing' });
-  click(wrapper.dive().find('[className="js-continue"]'));
-  expect(onContinue).toBeCalledWith('abcd1234');
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/LanguageForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/LanguageForm-test.tsx.snap
deleted file mode 100644 (file)
index 04745f2..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`selects c# 1`] = `
-<Fragment>
-  <div>
-    <h4
-      className="spacer-bottom"
-    >
-      onboarding.language
-    </h4>
-    <RadioToggle
-      disabled={false}
-      name="language"
-      onCheck={[Function]}
-      options={
-        Array [
-          Object {
-            "label": "onboarding.language.java",
-            "value": "java",
-          },
-          Object {
-            "label": "onboarding.language.dotnet",
-            "value": "dotnet",
-          },
-          Object {
-            "label": "onboarding.language.other",
-            "value": "other",
-          },
-        ]
-      }
-      value="dotnet"
-    />
-  </div>
-</Fragment>
-`;
-
-exports[`selects java 1`] = `
-<Fragment>
-  <div>
-    <h4
-      className="spacer-bottom"
-    >
-      onboarding.language
-    </h4>
-    <RadioToggle
-      disabled={false}
-      name="language"
-      onCheck={[Function]}
-      options={
-        Array [
-          Object {
-            "label": "onboarding.language.java",
-            "value": "java",
-          },
-          Object {
-            "label": "onboarding.language.dotnet",
-            "value": "dotnet",
-          },
-          Object {
-            "label": "onboarding.language.other",
-            "value": "other",
-          },
-        ]
-      }
-      value="java"
-    />
-  </div>
-  <RenderOptions
-    name="java-build"
-    onCheck={[Function]}
-    optionLabelKey="onboarding.language.java.build_technology"
-    options={
-      Array [
-        "maven",
-        "gradle",
-      ]
-    }
-    titleLabelKey="onboarding.language.java.build_technology"
-  />
-</Fragment>
-`;
-
-exports[`selects other 1`] = `
-<Fragment>
-  <div>
-    <h4
-      className="spacer-bottom"
-    >
-      onboarding.language
-    </h4>
-    <RadioToggle
-      disabled={false}
-      name="language"
-      onCheck={[Function]}
-      options={
-        Array [
-          Object {
-            "label": "onboarding.language.java",
-            "value": "java",
-          },
-          Object {
-            "label": "onboarding.language.dotnet",
-            "value": "dotnet",
-          },
-          Object {
-            "label": "onboarding.language.other",
-            "value": "other",
-          },
-        ]
-      }
-      value="other"
-    />
-  </div>
-  <RenderOS
-    setOS={[Function]}
-  />
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/ProjectAnalysisStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/ProjectAnalysisStep-test.tsx.snap
deleted file mode 100644 (file)
index 0380c57..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<Step
-  finished={false}
-  onOpen={[Function]}
-  open={true}
-  renderForm={[Function]}
-  renderResult={[Function]}
-  stepNumber={1}
-  stepTitle="onboarding.analysis.header"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap
deleted file mode 100644 (file)
index 1e188e0..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<div
-  className="boxed-group onboarding-step is-open is-finished"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      First Step
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div>
-      form
-    </div>
-  </div>
-</div>
-`;
-
-exports[`renders 2`] = `
-<div
-  className="boxed-group onboarding-step is-finished"
-  onClick={[Function]}
-  role="button"
-  tabIndex={0}
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div>
-    result
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      First Step
-    </h2>
-  </div>
-  <div
-    className="boxed-group-inner"
-  />
-  <div
-    className="hidden"
-  >
-    <div>
-      form
-    </div>
-  </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TokenStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/__tests__/__snapshots__/TokenStep-test.tsx.snap
deleted file mode 100644 (file)
index 6ad2bfc..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`generates token 1`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.token.header
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <div>
-        <div>
-          <Radio
-            checked={true}
-            onCheck={[Function]}
-            value="generate"
-          >
-            onboarding.token.generate_token
-          </Radio>
-          <div
-            className="big-spacer-top"
-          >
-            <form
-              onSubmit={[Function]}
-            >
-              <input
-                autoFocus={true}
-                className="input-super-large spacer-right text-middle"
-                onChange={[Function]}
-                placeholder="onboarding.token.generate_token.placeholder"
-                required={true}
-                type="text"
-                value=""
-              />
-              <SubmitButton
-                className="text-middle"
-                disabled={true}
-              >
-                onboarding.token.generate
-              </SubmitButton>
-            </form>
-          </div>
-        </div>
-        <div
-          className="big-spacer-top"
-        >
-          <Radio
-            checked={false}
-            onCheck={[Function]}
-            value="use-existing"
-          >
-            onboarding.token.use_existing_token
-          </Radio>
-        </div>
-      </div>
-      <div
-        className="note big-spacer-top width-50"
-      >
-        <FormattedMessage
-          defaultMessage="onboarding.token.text"
-          id="onboarding.token.text"
-          values={
-            Object {
-              "link": <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                target="_blank"
-                to="/account/security"
-              >
-                onboarding.token.text.user_account
-              </Link>,
-            }
-          }
-        />
-      </div>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`generates token 2`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.token.header
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <div>
-        <div>
-          <Radio
-            checked={true}
-            onCheck={[Function]}
-            value="generate"
-          >
-            onboarding.token.generate_token
-          </Radio>
-          <div
-            className="big-spacer-top"
-          >
-            <form
-              onSubmit={[Function]}
-            >
-              <input
-                autoFocus={true}
-                className="input-super-large spacer-right text-middle"
-                onChange={[Function]}
-                placeholder="onboarding.token.generate_token.placeholder"
-                required={true}
-                type="text"
-                value="my token"
-              />
-              <i
-                className="spinner text-middle"
-              />
-            </form>
-          </div>
-        </div>
-        <div
-          className="big-spacer-top"
-        >
-          <Radio
-            checked={false}
-            onCheck={[Function]}
-            value="use-existing"
-          >
-            onboarding.token.use_existing_token
-          </Radio>
-        </div>
-      </div>
-      <div
-        className="note big-spacer-top width-50"
-      >
-        <FormattedMessage
-          defaultMessage="onboarding.token.text"
-          id="onboarding.token.text"
-          values={
-            Object {
-              "link": <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                target="_blank"
-                to="/account/security"
-              >
-                onboarding.token.text.user_account
-              </Link>,
-            }
-          }
-        />
-      </div>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`generates token 3`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.token.header
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <form
-        onSubmit={[Function]}
-      >
-        <span
-          className="text-middle"
-        >
-          my token
-          : 
-        </span>
-        <strong
-          className="spacer-right text-middle"
-        >
-          abcd1234
-        </strong>
-        <DeleteButton
-          className="button-small text-middle"
-          onClick={[Function]}
-        />
-      </form>
-      <div
-        className="note big-spacer-top width-50"
-      >
-        <FormattedMessage
-          defaultMessage="onboarding.token.text"
-          id="onboarding.token.text"
-          values={
-            Object {
-              "link": <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                target="_blank"
-                to="/account/security"
-              >
-                onboarding.token.text.user_account
-              </Link>,
-            }
-          }
-        />
-      </div>
-      <div
-        className="big-spacer-top"
-      >
-        <Button
-          className="js-continue"
-          onClick={[Function]}
-        >
-          continue
-        </Button>
-      </div>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`revokes token 1`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.token.header
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <form
-        onSubmit={[Function]}
-      >
-        <span
-          className="text-middle"
-        >
-          my token
-          : 
-        </span>
-        <strong
-          className="spacer-right text-middle"
-        >
-          abcd1234
-        </strong>
-        <DeleteButton
-          className="button-small text-middle"
-          onClick={[Function]}
-        />
-      </form>
-      <div
-        className="note big-spacer-top width-50"
-      >
-        <FormattedMessage
-          defaultMessage="onboarding.token.text"
-          id="onboarding.token.text"
-          values={
-            Object {
-              "link": <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                target="_blank"
-                to="/account/security"
-              >
-                onboarding.token.text.user_account
-              </Link>,
-            }
-          }
-        />
-      </div>
-      <div
-        className="big-spacer-top"
-      >
-        <Button
-          className="js-continue"
-          onClick={[Function]}
-        >
-          continue
-        </Button>
-      </div>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`revokes token 2`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.token.header
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <form
-        onSubmit={[Function]}
-      >
-        <span
-          className="text-middle"
-        >
-          my token
-          : 
-        </span>
-        <strong
-          className="spacer-right text-middle"
-        >
-          abcd1234
-        </strong>
-        <i
-          className="spinner text-middle"
-        />
-      </form>
-      <div
-        className="note big-spacer-top width-50"
-      >
-        <FormattedMessage
-          defaultMessage="onboarding.token.text"
-          id="onboarding.token.text"
-          values={
-            Object {
-              "link": <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                target="_blank"
-                to="/account/security"
-              >
-                onboarding.token.text.user_account
-              </Link>,
-            }
-          }
-        />
-      </div>
-      <div
-        className="big-spacer-top"
-      >
-        <Button
-          className="js-continue"
-          onClick={[Function]}
-        >
-          continue
-        </Button>
-      </div>
-    </div>
-  </div>
-</div>
-`;
-
-exports[`revokes token 3`] = `
-<div
-  className="boxed-group onboarding-step is-open"
->
-  <div
-    className="onboarding-step-number"
-  >
-    1
-  </div>
-  <div
-    className="boxed-group-header"
-  >
-    <h2>
-      onboarding.token.header
-    </h2>
-  </div>
-  <div
-    className=""
-  >
-    <div
-      className="boxed-group-inner"
-    >
-      <div>
-        <div>
-          <Radio
-            checked={true}
-            onCheck={[Function]}
-            value="generate"
-          >
-            onboarding.token.generate_token
-          </Radio>
-          <div
-            className="big-spacer-top"
-          >
-            <form
-              onSubmit={[Function]}
-            >
-              <input
-                autoFocus={true}
-                className="input-super-large spacer-right text-middle"
-                onChange={[Function]}
-                placeholder="onboarding.token.generate_token.placeholder"
-                required={true}
-                type="text"
-                value=""
-              />
-              <SubmitButton
-                className="text-middle"
-                disabled={true}
-              >
-                onboarding.token.generate
-              </SubmitButton>
-            </form>
-          </div>
-        </div>
-        <div
-          className="big-spacer-top"
-        >
-          <Radio
-            checked={false}
-            onCheck={[Function]}
-            value="use-existing"
-          >
-            onboarding.token.use_existing_token
-          </Radio>
-        </div>
-      </div>
-      <div
-        className="note big-spacer-top width-50"
-      >
-        <FormattedMessage
-          defaultMessage="onboarding.token.text"
-          id="onboarding.token.text"
-          values={
-            Object {
-              "link": <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                target="_blank"
-                to="/account/security"
-              >
-                onboarding.token.text.user_account
-              </Link>,
-            }
-          }
-        />
-      </div>
-    </div>
-  </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/AnalysisCommand.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/AnalysisCommand.tsx
deleted file mode 100644 (file)
index 6fc3994..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { getHostUrl } from 'sonar-ui-common/helpers/urls';
-import { LanguageConfig } from '../../utils';
-import { getProjectKey } from '../ProjectAnalysisStep';
-import DotNet from './DotNet';
-import JavaGradle from './JavaGradle';
-import JavaMaven from './JavaMaven';
-import Other from './Other';
-
-interface Props {
-  component?: T.Component;
-  organization?: string;
-  languageConfig: LanguageConfig;
-  small?: boolean;
-  token?: string;
-}
-
-export default class AnalysisCommand extends React.PureComponent<Props> {
-  renderCommandForMaven = () => {
-    const { component, token } = this.props;
-    if (!token) {
-      return null;
-    }
-    return (
-      <JavaMaven
-        host={getHostUrl()}
-        organization={this.props.organization}
-        projectKey={component && component.key}
-        token={token}
-      />
-    );
-  };
-
-  renderCommandForGradle = () => {
-    const { component, token } = this.props;
-    if (!token) {
-      return null;
-    }
-    return (
-      <JavaGradle
-        host={getHostUrl()}
-        organization={this.props.organization}
-        projectKey={component && component.key}
-        token={token}
-      />
-    );
-  };
-
-  renderCommandForDotNet = () => {
-    const { component, languageConfig, small, token } = this.props;
-    const projectKey = getProjectKey(languageConfig, component);
-    if (!projectKey || !token) {
-      return null;
-    }
-    return (
-      <DotNet
-        host={getHostUrl()}
-        organization={this.props.organization}
-        projectKey={projectKey}
-        small={small}
-        token={token}
-      />
-    );
-  };
-
-  renderCommandForOther = () => {
-    const { component, languageConfig, token } = this.props;
-    const projectKey = getProjectKey(languageConfig, component);
-    if (!languageConfig || !projectKey || !languageConfig.os || !token) {
-      return null;
-    }
-    return (
-      <Other
-        host={getHostUrl()}
-        organization={this.props.organization}
-        os={languageConfig.os}
-        projectKey={projectKey}
-        token={token}
-      />
-    );
-  };
-
-  render() {
-    const { languageConfig } = this.props;
-
-    if (languageConfig.language === 'java') {
-      return languageConfig.javaBuild === 'maven'
-        ? this.renderCommandForMaven()
-        : this.renderCommandForGradle();
-    } else if (languageConfig.language === 'dotnet') {
-      return this.renderCommandForDotNet();
-    } else {
-      return this.renderCommandForOther();
-    }
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/DotNet.tsx
deleted file mode 100644 (file)
index 99b8206..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-import CodeSnippet from '../../../../components/common/CodeSnippet';
-import InstanceMessage from '../../../../components/common/InstanceMessage';
-import MSBuildScanner from './MSBuildScanner';
-
-export interface Props {
-  host: string;
-  organization?: string;
-  projectKey: string;
-  small?: boolean;
-  token: string;
-}
-
-export default function DotNet(props: Props) {
-  const command1 = [
-    'SonarScanner.MSBuild.exe begin',
-    `/k:"${props.projectKey}"`,
-    props.organization && `/d:sonar.organization="${props.organization}"`,
-    `/d:sonar.host.url="${props.host}"`,
-    `/d:sonar.login="${props.token}"`
-  ];
-
-  const command2 = 'MsBuild.exe /t:Rebuild';
-
-  const command3 = ['SonarScanner.MSBuild.exe end', `/d:sonar.login="${props.token}"`];
-
-  return (
-    <div>
-      <MSBuildScanner />
-
-      <h4 className="huge-spacer-top spacer-bottom">
-        {translate('onboarding.analysis.msbuild.execute')}
-      </h4>
-      <InstanceMessage message={translate('onboarding.analysis.msbuild.execute.text')}>
-        {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
-      </InstanceMessage>
-      <CodeSnippet isOneLine={true} snippet={command1} />
-      <CodeSnippet isOneLine={false} snippet={command2} />
-      <CodeSnippet isOneLine={props.small} snippet={command3} />
-      <p className="big-spacer-top markdown">
-        <FormattedMessage
-          defaultMessage={translate('onboarding.analysis.docs')}
-          id="onboarding.analysis.docs"
-          values={{
-            link: (
-              <a
-                href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
-                rel="noopener noreferrer"
-                target="_blank">
-                {translate('onboarding.analysis.msbuild.docs_link')}
-              </a>
-            )
-          }}
-        />
-      </p>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaGradle.tsx
deleted file mode 100644 (file)
index d6a127b..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-import CodeSnippet from '../../../../components/common/CodeSnippet';
-import InstanceMessage from '../../../../components/common/InstanceMessage';
-
-export interface Props {
-  host: string;
-  organization?: string;
-  projectKey?: string;
-  token: string;
-}
-
-export default function JavaGradle(props: Props) {
-  const config = 'plugins {\n  id "org.sonarqube" version "2.7"\n}';
-
-  const command = [
-    './gradlew sonarqube',
-    props.projectKey && `-Dsonar.projectKey=${props.projectKey}`,
-    props.organization && `-Dsonar.organization=${props.organization}`,
-    `-Dsonar.host.url=${props.host}`,
-    `-Dsonar.login=${props.token}`
-  ];
-
-  return (
-    <div>
-      <h4 className="spacer-bottom">{translate('onboarding.analysis.java.gradle.header')}</h4>
-      <InstanceMessage message={translate('onboarding.analysis.java.gradle.text.1')}>
-        {transformedMessage => (
-          <p className="spacer-bottom markdown">
-            <FormattedMessage
-              defaultMessage={transformedMessage}
-              id="onboarding.analysis.java.gradle.text.1"
-              values={{
-                plugin_code: <code>org.sonarqube</code>,
-                filename: <code>build.gradle</code>
-              }}
-            />
-          </p>
-        )}
-      </InstanceMessage>
-      <CodeSnippet snippet={config} />
-      <p className="spacer-top spacer-bottom markdown">
-        {translate('onboarding.analysis.java.gradle.text.2')}
-      </p>
-      <CodeSnippet snippet={command} />
-      <p className="big-spacer-top markdown">
-        <FormattedMessage
-          defaultMessage={translate('onboarding.analysis.docs')}
-          id="onboarding.analysis.docs"
-          values={{
-            link: (
-              <a
-                href="http://redirect.sonarsource.com/doc/gradle.html"
-                rel="noopener noreferrer"
-                target="_blank">
-                {translate('onboarding.analysis.java.gradle.docs_link')}
-              </a>
-            )
-          }}
-        />
-      </p>
-      <p className="big-spacer-top markdown">
-        {props.projectKey
-          ? translate('onboarding.analysis.auto_refresh_after_analysis')
-          : translate('onboarding.analysis.browse_url_after_analysis')}
-      </p>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/JavaMaven.tsx
deleted file mode 100644 (file)
index d7a9779..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-import CodeSnippet from '../../../../components/common/CodeSnippet';
-import InstanceMessage from '../../../../components/common/InstanceMessage';
-
-export interface Props {
-  host: string;
-  organization?: string;
-  projectKey?: string;
-  token: string;
-}
-
-export default function JavaMaven(props: Props) {
-  const command = [
-    'mvn sonar:sonar',
-    props.projectKey && `-Dsonar.projectKey=${props.projectKey}`,
-    props.organization && `-Dsonar.organization=${props.organization}`,
-    `-Dsonar.host.url=${props.host}`,
-    `-Dsonar.login=${props.token}`
-  ];
-
-  return (
-    <div>
-      <h4 className="spacer-bottom">{translate('onboarding.analysis.java.maven.header')}</h4>
-      <p className="spacer-bottom markdown">
-        <InstanceMessage message={translate('onboarding.analysis.java.maven.text')} />
-      </p>
-      <CodeSnippet snippet={command} />
-      <p className="big-spacer-top markdown">
-        <FormattedMessage
-          defaultMessage={translate('onboarding.analysis.docs')}
-          id="onboarding.analysis.docs"
-          values={{
-            link: (
-              <a
-                href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
-                rel="noopener noreferrer"
-                target="_blank">
-                {translate('onboarding.analysis.java.maven.docs_link')}
-              </a>
-            )
-          }}
-        />
-      </p>
-      <p className="big-spacer-top markdown">
-        {props.projectKey
-          ? translate('onboarding.analysis.auto_refresh_after_analysis')
-          : translate('onboarding.analysis.browse_url_after_analysis')}
-      </p>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/MSBuildScanner.tsx
deleted file mode 100644 (file)
index 9ff8c9a..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-
-interface Props {
-  className?: string;
-}
-
-export default function MSBuildScanner(props: Props) {
-  return (
-    <div className={props.className}>
-      <h4 className="spacer-bottom">{translate('onboarding.analysis.msbuild.header')}</h4>
-      <p className="spacer-bottom markdown">
-        <FormattedMessage
-          defaultMessage={translate('onboarding.analysis.msbuild.text')}
-          id="onboarding.analysis.msbuild.text"
-          values={{ code: <code>%PATH%</code> }}
-        />
-      </p>
-      <p>
-        <a
-          className="button"
-          href="https://sonarcloud.io/documentation/analysis/scan/sonarscanner-for-msbuild/"
-          rel="noopener noreferrer"
-          target="_blank">
-          {translate('download_verb')}
-        </a>
-      </p>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/Other.tsx
deleted file mode 100644 (file)
index 3de5fd6..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-import CodeSnippet from '../../../../components/common/CodeSnippet';
-import InstanceMessage from '../../../../components/common/InstanceMessage';
-import { quote } from '../../utils';
-import SQScanner from './SQScanner';
-
-export interface Props {
-  host: string;
-  organization?: string;
-  os: string;
-  projectKey: string;
-  token: string;
-}
-
-export default function Other(props: Props) {
-  const q = quote(props.os);
-  const command = [
-    props.os === 'win' ? 'sonar-scanner.bat' : 'sonar-scanner',
-    '-D' + q(`sonar.projectKey=${props.projectKey}`),
-    props.organization && '-D' + q(`sonar.organization=${props.organization}`),
-    '-D' + q('sonar.sources=.'),
-    '-D' + q(`sonar.host.url=${props.host}`),
-    '-D' + q(`sonar.login=${props.token}`)
-  ];
-
-  return (
-    <div>
-      <SQScanner os={props.os} />
-
-      <h4 className="huge-spacer-top spacer-bottom">
-        {translate('onboarding.analysis.sq_scanner.execute')}
-      </h4>
-      <InstanceMessage message={translate('onboarding.analysis.sq_scanner.execute.text')}>
-        {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
-      </InstanceMessage>
-      <CodeSnippet isOneLine={props.os === 'win'} snippet={command} />
-      <p className="big-spacer-top markdown">
-        <FormattedMessage
-          defaultMessage={translate('onboarding.analysis.sq_scanner.docs')}
-          id="onboarding.analysis.sq_scanner.docs"
-          values={{
-            link: (
-              <a
-                href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-                rel="noopener noreferrer"
-                target="_blank">
-                {translate('onboarding.analysis.sq_scanner.docs_link')}
-              </a>
-            )
-          }}
-        />
-      </p>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/SQScanner.tsx
deleted file mode 100644 (file)
index 663fda6..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-
-interface Props {
-  className?: string;
-  os: string;
-}
-
-export default function SQScanner(props: Props) {
-  return (
-    <div className={props.className}>
-      <h4 className="spacer-bottom">
-        {translate('onboarding.analysis.sq_scanner.header', props.os)}
-      </h4>
-      <p className="spacer-bottom markdown">
-        <FormattedMessage
-          defaultMessage={translate('onboarding.analysis.sq_scanner.text')}
-          id="onboarding.analysis.sq_scanner.text"
-          values={{
-            dir: <code>bin</code>,
-            env_var: <code>{props.os === 'win' ? '%PATH%' : 'PATH'}</code>
-          }}
-        />
-      </p>
-      <p>
-        <a
-          className="button"
-          href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-          rel="noopener noreferrer"
-          target="_blank">
-          {translate('download_verb')}
-        </a>
-      </p>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/AnalysisCommand-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/AnalysisCommand-test.tsx
deleted file mode 100644 (file)
index 6f8c57e..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 AnalysisCommand from '../AnalysisCommand';
-
-jest.mock('sonar-ui-common/helpers/urls', () => ({
-  getHostUrl: () => 'HOST'
-}));
-
-it('display java command', () => {
-  expect(
-    getWrapper({ languageConfig: { language: 'java', javaBuild: 'gradle' } })
-  ).toMatchSnapshot();
-  expect(
-    getWrapper({ languageConfig: { language: 'java', javaBuild: 'maven' } })
-  ).toMatchSnapshot();
-});
-
-it('display c# command', () => {
-  expect(
-    getWrapper({ languageConfig: { language: 'dotnet', projectKey: 'project-foo' } })
-  ).toMatchSnapshot();
-});
-
-it('display others command', () => {
-  expect(
-    getWrapper({
-      languageConfig: { language: 'other', os: 'window', projectKey: 'project-foo' }
-    })
-  ).toMatchSnapshot();
-});
-
-function getWrapper(props = {}) {
-  return shallow(<AnalysisCommand languageConfig={{}} token="myToken" {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/DotNet-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/DotNet-test.tsx
deleted file mode 100644 (file)
index 8f00456..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import DotNet, { Props } from '../DotNet';
-
-it('DotNet renders correctly', () => {
-  expect(shallowRender).toMatchSnapshot();
-
-  expect(
-    shallowRender({
-      organization: 'organization',
-      small: true
-    })
-  ).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<Props> = {}) {
-  return shallow(<DotNet host="host" projectKey="projectKey" token="token" {...props} />);
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/JavaGradle-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/JavaGradle-test.tsx
deleted file mode 100644 (file)
index 0fc13a7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import JavaGradle from '../JavaGradle';
-
-it('renders correctly', () => {
-  expect(shallow(<JavaGradle host="host" token="token" />)).toMatchSnapshot();
-  expect(
-    shallow(<JavaGradle host="host" organization="organization" token="token" />)
-  ).toMatchSnapshot();
-});
-
-it('renders with projectKey', () => {
-  expect(
-    shallow(<JavaGradle host="host" organization="organization" projectKey="foo" token="token" />)
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/JavaMaven-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/JavaMaven-test.tsx
deleted file mode 100644 (file)
index 7304894..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import JavaMaven from '../JavaMaven';
-
-it('renders correctly', () => {
-  expect(shallow(<JavaMaven host="host" token="token" />)).toMatchSnapshot();
-  expect(
-    shallow(<JavaMaven host="host" organization="organization" token="token" />)
-  ).toMatchSnapshot();
-});
-
-it('renders with projectKey', () => {
-  expect(
-    shallow(<JavaMaven host="host" organization="organization" projectKey="foo" token="token" />)
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/MSBuildScanner-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/MSBuildScanner-test.tsx
deleted file mode 100644 (file)
index 8da96cd..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 MSBuildScanner from '../MSBuildScanner';
-
-it('renders correctly', () => {
-  expect(shallow(<MSBuildScanner />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/Other-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/Other-test.tsx
deleted file mode 100644 (file)
index f4ab19c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import Other from '../Other';
-
-it('renders correctly', () => {
-  expect(
-    shallow(<Other host="host" os="win" projectKey="projectKey" token="token" />)
-  ).toMatchSnapshot();
-
-  expect(
-    shallow(<Other host="host" os="linux" projectKey="projectKey" token="token" />)
-  ).toMatchSnapshot();
-
-  expect(
-    shallow(<Other host="host" os="linux" projectKey="projectKey" token="token" />)
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/SQScanner-test.tsx b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/SQScanner-test.tsx
deleted file mode 100644 (file)
index 26a51ae..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 SQScanner from '../SQScanner';
-
-it('renders correctly', () => {
-  expect(shallow(<SQScanner os="win" />)).toMatchSnapshot();
-  expect(shallow(<SQScanner os="linux" />)).toMatchSnapshot();
-  expect(shallow(<SQScanner os="mac" />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap
deleted file mode 100644 (file)
index 6afb099..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`display c# command 1`] = `
-<DotNet
-  host="HOST"
-  projectKey="project-foo"
-  token="myToken"
-/>
-`;
-
-exports[`display java command 1`] = `
-<JavaGradle
-  host="HOST"
-  token="myToken"
-/>
-`;
-
-exports[`display java command 2`] = `
-<JavaMaven
-  host="HOST"
-  token="myToken"
-/>
-`;
-
-exports[`display others command 1`] = `
-<Other
-  host="HOST"
-  os="window"
-  projectKey="project-foo"
-  token="myToken"
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
deleted file mode 100644 (file)
index 19a2937..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`DotNet renders correctly 1`] = `[Function]`;
-
-exports[`DotNet renders correctly 2`] = `
-<div>
-  <MSBuildScanner />
-  <h4
-    className="huge-spacer-top spacer-bottom"
-  >
-    onboarding.analysis.msbuild.execute
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.msbuild.execute.text"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    isOneLine={true}
-    snippet={
-      Array [
-        "SonarScanner.MSBuild.exe begin",
-        "/k:\\"projectKey\\"",
-        "/d:sonar.organization=\\"organization\\"",
-        "/d:sonar.host.url=\\"host\\"",
-        "/d:sonar.login=\\"token\\"",
-      ]
-    }
-  />
-  <CodeSnippet
-    isOneLine={false}
-    snippet="MsBuild.exe /t:Rebuild"
-  />
-  <CodeSnippet
-    isOneLine={true}
-    snippet={
-      Array [
-        "SonarScanner.MSBuild.exe end",
-        "/d:sonar.login=\\"token\\"",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.msbuild.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
deleted file mode 100644 (file)
index 7e1208e..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.java.gradle.header
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.java.gradle.text.1"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    snippet="plugins {
-  id \\"org.sonarqube\\" version \\"2.7\\"
-}"
-  />
-  <p
-    className="spacer-top spacer-bottom markdown"
-  >
-    onboarding.analysis.java.gradle.text.2
-  </p>
-  <CodeSnippet
-    snippet={
-      Array [
-        "./gradlew sonarqube",
-        undefined,
-        undefined,
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/gradle.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.java.gradle.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.browse_url_after_analysis
-  </p>
-</div>
-`;
-
-exports[`renders correctly 2`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.java.gradle.header
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.java.gradle.text.1"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    snippet="plugins {
-  id \\"org.sonarqube\\" version \\"2.7\\"
-}"
-  />
-  <p
-    className="spacer-top spacer-bottom markdown"
-  >
-    onboarding.analysis.java.gradle.text.2
-  </p>
-  <CodeSnippet
-    snippet={
-      Array [
-        "./gradlew sonarqube",
-        undefined,
-        "-Dsonar.organization=organization",
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/gradle.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.java.gradle.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.browse_url_after_analysis
-  </p>
-</div>
-`;
-
-exports[`renders with projectKey 1`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.java.gradle.header
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.java.gradle.text.1"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    snippet="plugins {
-  id \\"org.sonarqube\\" version \\"2.7\\"
-}"
-  />
-  <p
-    className="spacer-top spacer-bottom markdown"
-  >
-    onboarding.analysis.java.gradle.text.2
-  </p>
-  <CodeSnippet
-    snippet={
-      Array [
-        "./gradlew sonarqube",
-        "-Dsonar.projectKey=foo",
-        "-Dsonar.organization=organization",
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/gradle.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.java.gradle.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
deleted file mode 100644 (file)
index 7ee4d3b..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.java.maven.header
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <InstanceMessage
-      message="onboarding.analysis.java.maven.text"
-    />
-  </p>
-  <CodeSnippet
-    snippet={
-      Array [
-        "mvn sonar:sonar",
-        undefined,
-        undefined,
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.java.maven.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.browse_url_after_analysis
-  </p>
-</div>
-`;
-
-exports[`renders correctly 2`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.java.maven.header
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <InstanceMessage
-      message="onboarding.analysis.java.maven.text"
-    />
-  </p>
-  <CodeSnippet
-    snippet={
-      Array [
-        "mvn sonar:sonar",
-        undefined,
-        "-Dsonar.organization=organization",
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.java.maven.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.browse_url_after_analysis
-  </p>
-</div>
-`;
-
-exports[`renders with projectKey 1`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.java.maven.header
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <InstanceMessage
-      message="onboarding.analysis.java.maven.text"
-    />
-  </p>
-  <CodeSnippet
-    snippet={
-      Array [
-        "mvn sonar:sonar",
-        "-Dsonar.projectKey=foo",
-        "-Dsonar.organization=organization",
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.docs"
-      id="onboarding.analysis.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.java.maven.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-  <p
-    className="big-spacer-top markdown"
-  >
-    onboarding.analysis.auto_refresh_after_analysis
-  </p>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap
deleted file mode 100644 (file)
index f1d82f5..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.msbuild.header
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.msbuild.text"
-      id="onboarding.analysis.msbuild.text"
-      values={
-        Object {
-          "code": <code>
-            %PATH%
-          </code>,
-        }
-      }
-    />
-  </p>
-  <p>
-    <a
-      className="button"
-      href="https://sonarcloud.io/documentation/analysis/scan/sonarscanner-for-msbuild/"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      download_verb
-    </a>
-  </p>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/Other-test.tsx.snap
deleted file mode 100644 (file)
index 51b1479..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<div>
-  <SQScanner
-    os="win"
-  />
-  <h4
-    className="huge-spacer-top spacer-bottom"
-  >
-    onboarding.analysis.sq_scanner.execute
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.sq_scanner.execute.text"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    isOneLine={true}
-    snippet={
-      Array [
-        "sonar-scanner.bat",
-        "-D\\"sonar.projectKey=projectKey\\"",
-        undefined,
-        "-D\\"sonar.sources=.\\"",
-        "-D\\"sonar.host.url=host\\"",
-        "-D\\"sonar.login=token\\"",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.sq_scanner.docs"
-      id="onboarding.analysis.sq_scanner.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.sq_scanner.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-</div>
-`;
-
-exports[`renders correctly 2`] = `
-<div>
-  <SQScanner
-    os="linux"
-  />
-  <h4
-    className="huge-spacer-top spacer-bottom"
-  >
-    onboarding.analysis.sq_scanner.execute
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.sq_scanner.execute.text"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    isOneLine={false}
-    snippet={
-      Array [
-        "sonar-scanner",
-        "-Dsonar.projectKey=projectKey",
-        undefined,
-        "-Dsonar.sources=.",
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.sq_scanner.docs"
-      id="onboarding.analysis.sq_scanner.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.sq_scanner.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-</div>
-`;
-
-exports[`renders correctly 3`] = `
-<div>
-  <SQScanner
-    os="linux"
-  />
-  <h4
-    className="huge-spacer-top spacer-bottom"
-  >
-    onboarding.analysis.sq_scanner.execute
-  </h4>
-  <InstanceMessage
-    message="onboarding.analysis.sq_scanner.execute.text"
-  >
-    <Component />
-  </InstanceMessage>
-  <CodeSnippet
-    isOneLine={false}
-    snippet={
-      Array [
-        "sonar-scanner",
-        "-Dsonar.projectKey=projectKey",
-        undefined,
-        "-Dsonar.sources=.",
-        "-Dsonar.host.url=host",
-        "-Dsonar.login=token",
-      ]
-    }
-  />
-  <p
-    className="big-spacer-top markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.sq_scanner.docs"
-      id="onboarding.analysis.sq_scanner.docs"
-      values={
-        Object {
-          "link": <a
-            href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            onboarding.analysis.sq_scanner.docs_link
-          </a>,
-        }
-      }
-    />
-  </p>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap b/server/sonar-web/src/main/js/apps/tutorials/components/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap
deleted file mode 100644 (file)
index 6ad4425..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders correctly 1`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.sq_scanner.header.win
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.sq_scanner.text"
-      id="onboarding.analysis.sq_scanner.text"
-      values={
-        Object {
-          "dir": <code>
-            bin
-          </code>,
-          "env_var": <code>
-            %PATH%
-          </code>,
-        }
-      }
-    />
-  </p>
-  <p>
-    <a
-      className="button"
-      href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      download_verb
-    </a>
-  </p>
-</div>
-`;
-
-exports[`renders correctly 2`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.sq_scanner.header.linux
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.sq_scanner.text"
-      id="onboarding.analysis.sq_scanner.text"
-      values={
-        Object {
-          "dir": <code>
-            bin
-          </code>,
-          "env_var": <code>
-            PATH
-          </code>,
-        }
-      }
-    />
-  </p>
-  <p>
-    <a
-      className="button"
-      href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      download_verb
-    </a>
-  </p>
-</div>
-`;
-
-exports[`renders correctly 3`] = `
-<div>
-  <h4
-    className="spacer-bottom"
-  >
-    onboarding.analysis.sq_scanner.header.mac
-  </h4>
-  <p
-    className="spacer-bottom markdown"
-  >
-    <FormattedMessage
-      defaultMessage="onboarding.analysis.sq_scanner.text"
-      id="onboarding.analysis.sq_scanner.text"
-      values={
-        Object {
-          "dir": <code>
-            bin
-          </code>,
-          "env_var": <code>
-            PATH
-          </code>,
-        }
-      }
-    />
-  </p>
-  <p>
-    <a
-      className="button"
-      href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
-      rel="noopener noreferrer"
-      target="_blank"
-    >
-      download_verb
-    </a>
-  </p>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/tutorials/styles.css b/server/sonar-web/src/main/js/apps/tutorials/styles.css
deleted file mode 100644 (file)
index ec206ba..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.
- */
-.onboarding-step {
-  position: relative;
-  padding-left: 34px;
-}
-
-.onboarding-step:not(.is-open):not(.is-finished) {
-  opacity: 0.4;
-}
-
-.onboarding-step .boxed-group-actions {
-  height: var(--controlHeight);
-  line-height: var(--controlHeight);
-}
-
-.onboarding-step hr {
-  margin-left: -54px;
-}
-
-.onboarding-step-number {
-  position: absolute;
-  top: 15px;
-  left: 15px;
-  width: 24px;
-  height: 24px;
-  line-height: 24px;
-  border-radius: 24px;
-  background-color: #b9b9b9;
-  color: #fff;
-  font-size: var(--mediumFontSize);
-  text-align: center;
-}
-
-.onboarding-step.is-open .onboarding-step-number {
-  background-color: var(--darkBlue);
-}
-
-.onboarding-step.is-finished {
-  cursor: pointer;
-  outline: none;
-}
diff --git a/server/sonar-web/src/main/js/apps/tutorials/utils.ts b/server/sonar-web/src/main/js/apps/tutorials/utils.ts
deleted file mode 100644 (file)
index a1a28ed..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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.
- */
-export interface LanguageConfig {
-  language?: string;
-  javaBuild?: string;
-  cFamilyCompiler?: string;
-  os?: string;
-  projectKey?: string;
-}
-
-export function isLanguageConfigured(config?: LanguageConfig) {
-  if (!config) {
-    return false;
-  }
-  const { language, javaBuild, cFamilyCompiler, os, projectKey } = config;
-  const isJavaConfigured = language === 'java' && javaBuild != null;
-  const isDotNetConfigured = language === 'dotnet' && projectKey != null;
-  const isCFamilyConfigured =
-    language === 'c-family' && (cFamilyCompiler === 'msvc' || os != null) && projectKey != null;
-  const isOtherConfigured = language === 'other' && projectKey != null;
-
-  return isJavaConfigured || isDotNetConfigured || isCFamilyConfigured || isOtherConfigured;
-}
-
-export function quote(os: string): (s: string) => string {
-  return os === 'win' ? (s: string) => `"${s}"` : (s: string) => s;
-}
-
-export function getUniqueTokenName(tokens: T.UserToken[], initialTokenName = '') {
-  const hasToken = (name: string) => tokens.find(token => token.name === name) !== undefined;
-
-  if (!hasToken(initialTokenName)) {
-    return initialTokenName;
-  }
-
-  let i = 1;
-  while (hasToken(`${initialTokenName} ${i}`)) {
-    i++;
-  }
-  return `${initialTokenName} ${i}`;
-}
diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/utils-test.ts b/server/sonar-web/src/main/js/components/tutorials/__tests__/utils-test.ts
new file mode 100644 (file)
index 0000000..f16f4b8
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { getUniqueTokenName } from '../utils';
+
+const initialTokenName = 'Analyze "lightsaber"';
+
+it('should return the given name when the user has no token', () => {
+  const userTokens: T.UserToken[] = [];
+
+  expect(getUniqueTokenName(userTokens, initialTokenName)).toBe(initialTokenName);
+});
+
+it('should generate a token with the given name', () => {
+  const userTokens = [{ name: initialTokenName, createdAt: '2019-06-14T09:45:52+0200' }];
+
+  expect(getUniqueTokenName(userTokens, 'Analyze "project"')).toBe('Analyze "project"');
+});
+
+it('should generate a unique token when the name already exists', () => {
+  const userTokens = [
+    { name: initialTokenName, createdAt: '2019-06-14T09:45:52+0200' },
+    { name: `${initialTokenName} 1`, createdAt: '2019-06-14T09:45:52+0200' }
+  ];
+
+  expect(getUniqueTokenName(userTokens, initialTokenName)).toBe('Analyze "lightsaber" 2');
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx b/server/sonar-web/src/main/js/components/tutorials/components/RenderOptions.tsx
new file mode 100644 (file)
index 0000000..488289c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 RadioToggle from 'sonar-ui-common/components/controls/RadioToggle';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+
+export interface RenderOptionsProps {
+  checked: string | undefined;
+  name: string;
+  onCheck: (checked: string) => void;
+  optionLabelKey: string;
+  options: string[];
+  titleLabelKey?: string;
+}
+
+export default function RenderOptions({
+  checked,
+  onCheck,
+  optionLabelKey,
+  options,
+  titleLabelKey
+}: RenderOptionsProps) {
+  return (
+    <div className="big-spacer-top">
+      {titleLabelKey && <h4 className="spacer-bottom">{translate(titleLabelKey)}</h4>}
+
+      <RadioToggle
+        name={name}
+        onCheck={onCheck}
+        options={options.map(build => ({
+          label: translate(optionLabelKey, build),
+          value: build
+        }))}
+        value={checked}
+      />
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/Step.css b/server/sonar-web/src/main/js/components/tutorials/components/Step.css
new file mode 100644 (file)
index 0000000..ec206ba
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.
+ */
+.onboarding-step {
+  position: relative;
+  padding-left: 34px;
+}
+
+.onboarding-step:not(.is-open):not(.is-finished) {
+  opacity: 0.4;
+}
+
+.onboarding-step .boxed-group-actions {
+  height: var(--controlHeight);
+  line-height: var(--controlHeight);
+}
+
+.onboarding-step hr {
+  margin-left: -54px;
+}
+
+.onboarding-step-number {
+  position: absolute;
+  top: 15px;
+  left: 15px;
+  width: 24px;
+  height: 24px;
+  line-height: 24px;
+  border-radius: 24px;
+  background-color: #b9b9b9;
+  color: #fff;
+  font-size: var(--mediumFontSize);
+  text-align: center;
+}
+
+.onboarding-step.is-open .onboarding-step-number {
+  background-color: var(--darkBlue);
+}
+
+.onboarding-step.is-finished {
+  cursor: pointer;
+  outline: none;
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/Step.tsx b/server/sonar-web/src/main/js/components/tutorials/components/Step.tsx
new file mode 100644 (file)
index 0000000..106d8c3
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.
+ */
+/* eslint-disable jsx-a11y/no-static-element-interactions, jsx-a11y/no-noninteractive-tabindex */
+import * as classNames from 'classnames';
+import * as React from 'react';
+import './Step.css';
+
+interface Props {
+  finished?: boolean;
+  onOpen: VoidFunction;
+  open: boolean;
+  renderForm: () => React.ReactNode;
+  renderResult: () => React.ReactNode;
+  stepNumber: number;
+  stepTitle: React.ReactNode;
+}
+
+export default function Step(props: Props) {
+  const className = classNames('boxed-group', 'onboarding-step', {
+    'is-open': props.open,
+    'is-finished': props.finished
+  });
+
+  const clickable = !props.open && props.finished;
+
+  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
+    event.preventDefault();
+    props.onOpen();
+  };
+
+  return (
+    <div
+      className={className}
+      onClick={clickable ? handleClick : undefined}
+      role={clickable ? 'button' : undefined}
+      tabIndex={clickable ? 0 : undefined}>
+      <div className="onboarding-step-number">{props.stepNumber}</div>
+      {!props.open && props.renderResult()}
+      <div className="boxed-group-header">
+        <h2>{props.stepTitle}</h2>
+      </div>
+      {!props.open && <div className="boxed-group-inner" />}
+      <div className={classNames({ hidden: !props.open })}>{props.renderForm()}</div>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/RenderOptions-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/RenderOptions-test.tsx
new file mode 100644 (file)
index 0000000..c2ad687
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import RenderOptions, { RenderOptionsProps } from '../RenderOptions';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(shallowRender({ checked: 'baz' })).toMatchSnapshot('option checked');
+  expect(shallowRender({ titleLabelKey: 'title.key' })).toMatchSnapshot('with title');
+});
+
+function shallowRender(props: Partial<RenderOptionsProps> = {}) {
+  return shallow<RenderOptionsProps>(
+    <RenderOptions
+      checked={undefined}
+      name="bar"
+      onCheck={jest.fn()}
+      optionLabelKey="foo.bar"
+      options={['foo', 'baz']}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/Step-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/Step-test.tsx
new file mode 100644 (file)
index 0000000..bda5bed
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { click } from 'sonar-ui-common/helpers/testUtils';
+import Step from '../Step';
+
+it('renders', () => {
+  const wrapper = shallow(
+    <Step
+      finished={true}
+      onOpen={jest.fn()}
+      open={true}
+      renderForm={() => <div>form</div>}
+      renderResult={() => <div>result</div>}
+      stepNumber={1}
+      stepTitle="First Step"
+    />
+  );
+  expect(wrapper).toMatchSnapshot();
+  wrapper.setProps({ open: false });
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('re-opens', () => {
+  const onOpen = jest.fn();
+  const wrapper = shallow(
+    <Step
+      finished={true}
+      onOpen={onOpen}
+      open={false}
+      renderForm={() => <div>form</div>}
+      renderResult={() => <div>result</div>}
+      stepNumber={1}
+      stepTitle="First Step"
+    />
+  );
+  click(wrapper);
+  expect(onOpen).toBeCalled();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/RenderOptions-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/RenderOptions-test.tsx.snap
new file mode 100644 (file)
index 0000000..535f9f5
--- /dev/null
@@ -0,0 +1,81 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<div
+  className="big-spacer-top"
+>
+  <RadioToggle
+    disabled={false}
+    name=""
+    onCheck={[MockFunction]}
+    options={
+      Array [
+        Object {
+          "label": "foo.bar.foo",
+          "value": "foo",
+        },
+        Object {
+          "label": "foo.bar.baz",
+          "value": "baz",
+        },
+      ]
+    }
+    value={null}
+  />
+</div>
+`;
+
+exports[`should render correctly: option checked 1`] = `
+<div
+  className="big-spacer-top"
+>
+  <RadioToggle
+    disabled={false}
+    name=""
+    onCheck={[MockFunction]}
+    options={
+      Array [
+        Object {
+          "label": "foo.bar.foo",
+          "value": "foo",
+        },
+        Object {
+          "label": "foo.bar.baz",
+          "value": "baz",
+        },
+      ]
+    }
+    value="baz"
+  />
+</div>
+`;
+
+exports[`should render correctly: with title 1`] = `
+<div
+  className="big-spacer-top"
+>
+  <h4
+    className="spacer-bottom"
+  >
+    title.key
+  </h4>
+  <RadioToggle
+    disabled={false}
+    name=""
+    onCheck={[MockFunction]}
+    options={
+      Array [
+        Object {
+          "label": "foo.bar.foo",
+          "value": "foo",
+        },
+        Object {
+          "label": "foo.bar.baz",
+          "value": "baz",
+        },
+      ]
+    }
+    value={null}
+  />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/Step-test.tsx.snap
new file mode 100644 (file)
index 0000000..1e188e0
--- /dev/null
@@ -0,0 +1,62 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+  className="boxed-group onboarding-step is-open is-finished"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      First Step
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div>
+      form
+    </div>
+  </div>
+</div>
+`;
+
+exports[`renders 2`] = `
+<div
+  className="boxed-group onboarding-step is-finished"
+  onClick={[Function]}
+  role="button"
+  tabIndex={0}
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div>
+    result
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      First Step
+    </h2>
+  </div>
+  <div
+    className="boxed-group-inner"
+  />
+  <div
+    className="hidden"
+  >
+    <div>
+      form
+    </div>
+  </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/LanguageForm.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/LanguageForm.tsx
new file mode 100644 (file)
index 0000000..30701bc
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 RadioToggle from 'sonar-ui-common/components/controls/RadioToggle';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import RenderOptions from '../components/RenderOptions';
+import { LanguageConfig } from '../types';
+import { isLanguageConfigured } from '../utils';
+
+interface Props {
+  component: T.Component;
+  config?: LanguageConfig;
+  onDone: (config: LanguageConfig) => void;
+  onReset: VoidFunction;
+  organization?: string;
+}
+
+type State = LanguageConfig;
+
+export interface RenderOSProps {
+  os: string | undefined;
+  setOS: (os: string) => void;
+}
+
+export function RenderOS(props: RenderOSProps) {
+  return (
+    <RenderOptions
+      checked={props.os}
+      name="os"
+      onCheck={props.setOS}
+      optionLabelKey="onboarding.language.os"
+      options={['linux', 'win', 'mac']}
+      titleLabelKey="onboarding.language.os"
+    />
+  );
+}
+
+export default class LanguageForm extends React.PureComponent<Props, State> {
+  constructor(props: Props) {
+    super(props);
+    this.state = {
+      ...(this.props.config || {}),
+      projectKey: props.component ? props.component.key : undefined
+    };
+  }
+
+  handleChange = () => {
+    if (isLanguageConfigured(this.state)) {
+      this.props.onDone(this.state);
+    } else {
+      this.props.onReset();
+    }
+  };
+
+  handleLanguageChange = (language: string) => {
+    this.setState({ language }, this.handleChange);
+  };
+
+  handleJavaBuildChange = (javaBuild: string) => {
+    this.setState({ javaBuild }, this.handleChange);
+  };
+
+  handleOSChange = (os: string) => {
+    this.setState({ os }, this.handleChange);
+  };
+
+  renderJavaBuild = () => (
+    <RenderOptions
+      checked={this.state.javaBuild}
+      name="java-build"
+      onCheck={this.handleJavaBuildChange}
+      optionLabelKey="onboarding.language.java.build_technology"
+      options={['maven', 'gradle']}
+      titleLabelKey="onboarding.language.java.build_technology"
+    />
+  );
+
+  render() {
+    const { language } = this.state;
+    const languages = ['java', 'dotnet', 'other'];
+
+    return (
+      <>
+        <div>
+          <h4 className="spacer-bottom">{translate('onboarding.language')}</h4>
+          <RadioToggle
+            name="language"
+            onCheck={this.handleLanguageChange}
+            options={languages.map(language => ({
+              label: translate('onboarding.language', language),
+              value: language
+            }))}
+            value={language}
+          />
+        </div>
+        {language === 'java' && this.renderJavaBuild()}
+        {language === 'other' && <RenderOS os={this.state.os} setOS={this.handleOSChange} />}
+      </>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx
new file mode 100644 (file)
index 0000000..f1b4c1d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 Tooltip from 'sonar-ui-common/components/controls/Tooltip';
+import BackIcon from 'sonar-ui-common/components/icons/BackIcon';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { isVSTS } from '../../../helpers/almIntegrations';
+import InstanceMessage from '../../common/InstanceMessage';
+import ProjectAnalysisStep from './ProjectAnalysisStep';
+import TokenStep from './TokenStep';
+
+export enum Steps {
+  ANALYSIS,
+  TOKEN
+}
+
+interface Props {
+  component: T.Component;
+  currentUser: T.LoggedInUser;
+  onBack?: () => void;
+}
+
+interface State {
+  step: Steps;
+  token?: string;
+}
+
+export default class ManualTutorial extends React.PureComponent<Props, State> {
+  state: State = { step: Steps.TOKEN };
+
+  handleTokenDone = (token: string) => {
+    this.setState({ step: Steps.ANALYSIS, token });
+  };
+
+  handleTokenOpen = () => {
+    this.setState({ step: Steps.TOKEN });
+  };
+
+  render() {
+    const { component, currentUser } = this.props;
+    const { step, token } = this.state;
+
+    const almKey = (component.alm && component.alm.key) || currentUser.externalProvider;
+    return (
+      <>
+        <div className="page-header big-spacer-bottom">
+          <h1 className="page-title">
+            {this.props.onBack !== undefined && (
+              <Tooltip overlay={translate('onboarding.tutorial.return_to_list')}>
+                <a
+                  aria-label={translate('onboarding.tutorial.return_to_list')}
+                  className="link-no-underline big-spacer-right"
+                  onClick={this.props.onBack}>
+                  <BackIcon />
+                </a>
+              </Tooltip>
+            )}
+            {translate('onboarding.project_analysis.header')}
+          </h1>
+          <p className="page-description">
+            <InstanceMessage message={translate('onboarding.project_analysis.description')} />
+          </p>
+        </div>
+
+        {!isVSTS(almKey) && (
+          <>
+            <TokenStep
+              currentUser={currentUser}
+              finished={Boolean(this.state.token)}
+              initialTokenName={`Analyze "${component.name}"`}
+              onContinue={this.handleTokenDone}
+              onOpen={this.handleTokenOpen}
+              open={step === Steps.TOKEN}
+              stepNumber={1}
+            />
+
+            <ProjectAnalysisStep
+              component={component}
+              displayRowLayout={true}
+              open={step === Steps.ANALYSIS}
+              stepNumber={2}
+              token={token}
+            />
+          </>
+        )}
+      </>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/ProjectAnalysisStep.tsx
new file mode 100644 (file)
index 0000000..c3acaff
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 Step from '../components/Step';
+import { LanguageConfig } from '../types';
+import AnalysisCommand from './commands/AnalysisCommand';
+import LanguageForm from './LanguageForm';
+
+interface Props {
+  component: T.Component;
+  displayRowLayout?: boolean;
+  onFinish?: (projectKey?: string) => void;
+  onReset?: VoidFunction;
+  open: boolean;
+  organization?: string;
+  stepNumber: number;
+  token?: string;
+}
+
+interface State {
+  config?: LanguageConfig;
+}
+
+export function getProjectKey(config?: LanguageConfig, component?: T.Component) {
+  return (component && component.key) || (config && config.projectKey);
+}
+
+export default class ProjectAnalysisStep extends React.PureComponent<Props, State> {
+  state: State = {};
+
+  handleLanguageSelect = (config: LanguageConfig) => {
+    this.setState({ config });
+    if (this.props.onFinish) {
+      const projectKey = config.language !== 'java' ? getProjectKey(config) : undefined;
+      this.props.onFinish(projectKey);
+    }
+  };
+
+  handleLanguageReset = () => {
+    this.setState({ config: undefined });
+    if (this.props.onReset) {
+      this.props.onReset();
+    }
+  };
+
+  renderForm = () => {
+    const languageComponent = (
+      <LanguageForm
+        component={this.props.component}
+        onDone={this.handleLanguageSelect}
+        onReset={this.handleLanguageReset}
+        organization={this.props.organization}
+      />
+    );
+    const analysisComponent = this.state.config && (
+      <AnalysisCommand
+        component={this.props.component}
+        languageConfig={this.state.config}
+        organization={this.props.organization}
+        small={true}
+        token={this.props.token}
+      />
+    );
+
+    if (this.props.displayRowLayout) {
+      return (
+        <div className="boxed-group-inner">
+          <div className="display-flex-column">
+            {languageComponent}
+            {analysisComponent && <div className="huge-spacer-top">{analysisComponent}</div>}
+          </div>
+        </div>
+      );
+    }
+
+    return (
+      <div className="boxed-group-inner">
+        <div className="flex-columns">
+          <div className="flex-column flex-column-half bordered-right">{languageComponent}</div>
+          <div className="flex-column flex-column-half">{analysisComponent}</div>
+        </div>
+      </div>
+    );
+  };
+
+  renderResult = () => null;
+
+  render() {
+    return (
+      <Step
+        finished={false}
+        onOpen={() => {}}
+        open={this.props.open}
+        renderForm={this.renderForm}
+        renderResult={this.renderResult}
+        stepNumber={this.props.stepNumber}
+        stepTitle={translate('onboarding.analysis.header')}
+      />
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/TokenStep.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/TokenStep.tsx
new file mode 100644 (file)
index 0000000..4ce914d
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { Button, DeleteButton, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
+import Radio from 'sonar-ui-common/components/controls/Radio';
+import AlertErrorIcon from 'sonar-ui-common/components/icons/AlertErrorIcon';
+import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { generateToken, getTokens, revokeToken } from '../../../api/user-tokens';
+import Step from '../components/Step';
+import { getUniqueTokenName } from '../utils';
+
+interface Props {
+  currentUser: Pick<T.LoggedInUser, 'login'>;
+  finished: boolean;
+  initialTokenName?: string;
+  open: boolean;
+  onContinue: (token: string) => void;
+  onOpen: VoidFunction;
+  stepNumber: number;
+}
+
+interface State {
+  existingToken: string;
+  loading: boolean;
+  selection: string;
+  tokenName?: string;
+  token?: string;
+  tokens?: T.UserToken[];
+}
+
+export default class TokenStep extends React.PureComponent<Props, State> {
+  mounted = false;
+
+  constructor(props: Props) {
+    super(props);
+    this.state = {
+      existingToken: '',
+      loading: false,
+      selection: 'generate',
+      tokenName: props.initialTokenName
+    };
+  }
+
+  componentDidMount() {
+    this.mounted = true;
+    getTokens(this.props.currentUser.login).then(
+      tokens => {
+        if (this.mounted) {
+          this.setState({ tokens });
+          if (
+            this.props.initialTokenName !== undefined &&
+            this.props.initialTokenName === this.state.tokenName
+          ) {
+            this.setState({ tokenName: getUniqueTokenName(tokens) });
+          }
+        }
+      },
+      () => {}
+    );
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  getToken = () =>
+    this.state.selection === 'generate' ? this.state.token : this.state.existingToken;
+
+  canContinue = () => {
+    const { existingToken, selection, token } = this.state;
+    const validExistingToken = existingToken.match(/^[a-z0-9]+$/) != null;
+    return (
+      (selection === 'generate' && token != null) ||
+      (selection === 'use-existing' && existingToken && validExistingToken)
+    );
+  };
+
+  handleTokenNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    this.setState({ tokenName: event.target.value });
+  };
+
+  handleTokenGenerate = (event: React.FormEvent<HTMLFormElement>) => {
+    event.preventDefault();
+    const { tokenName } = this.state;
+    if (tokenName) {
+      this.setState({ loading: true });
+      generateToken({ name: tokenName }).then(({ token }) => {
+        if (this.mounted) {
+          this.setState({ loading: false, token });
+        }
+      }, this.stopLoading);
+    }
+  };
+
+  handleTokenRevoke = () => {
+    const { tokenName } = this.state;
+    if (tokenName) {
+      this.setState({ loading: true });
+      revokeToken({ name: tokenName }).then(() => {
+        if (this.mounted) {
+          this.setState({ loading: false, token: undefined, tokenName: undefined });
+        }
+      }, this.stopLoading);
+    }
+  };
+
+  handleContinueClick = () => {
+    const token = this.getToken();
+    if (token) {
+      this.props.onContinue(token);
+    }
+  };
+
+  handleGenerateClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    this.setState({ selection: 'generate' });
+  };
+
+  handleUseExistingClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    this.setState({ selection: 'use-existing' });
+  };
+
+  handleModeChange = (mode: string) => {
+    this.setState({ selection: mode });
+  };
+
+  handleExisingTokenChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    this.setState({ existingToken: event.currentTarget.value });
+  };
+
+  stopLoading = () => {
+    if (this.mounted) {
+      this.setState({ loading: false });
+    }
+  };
+
+  renderGenerateOption = () => (
+    <div>
+      {this.state.tokens !== undefined && this.state.tokens.length > 0 ? (
+        <Radio
+          checked={this.state.selection === 'generate'}
+          onCheck={this.handleModeChange}
+          value="generate">
+          {translate('onboarding.token.generate_token')}
+        </Radio>
+      ) : (
+        translate('onboarding.token.generate_token')
+      )}
+      {this.state.selection === 'generate' && (
+        <div className="big-spacer-top">
+          <form onSubmit={this.handleTokenGenerate}>
+            <input
+              autoFocus={true}
+              className="input-super-large spacer-right text-middle"
+              onChange={this.handleTokenNameChange}
+              placeholder={translate('onboarding.token.generate_token.placeholder')}
+              required={true}
+              type="text"
+              value={this.state.tokenName || ''}
+            />
+            {this.state.loading ? (
+              <i className="spinner text-middle" />
+            ) : (
+              <SubmitButton className="text-middle" disabled={!this.state.tokenName}>
+                {translate('onboarding.token.generate')}
+              </SubmitButton>
+            )}
+          </form>
+        </div>
+      )}
+    </div>
+  );
+
+  renderUseExistingOption = () => {
+    const { existingToken } = this.state;
+    const validInput = !existingToken || existingToken.match(/^[a-z0-9]+$/) != null;
+
+    return (
+      <div className="big-spacer-top">
+        <Radio
+          checked={this.state.selection === 'use-existing'}
+          onCheck={this.handleModeChange}
+          value="use-existing">
+          {translate('onboarding.token.use_existing_token')}
+        </Radio>
+        {this.state.selection === 'use-existing' && (
+          <div className="big-spacer-top">
+            <input
+              autoFocus={true}
+              className="input-super-large spacer-right text-middle"
+              onChange={this.handleExisingTokenChange}
+              placeholder={translate('onboarding.token.use_existing_token.placeholder')}
+              required={true}
+              type="text"
+              value={this.state.existingToken}
+            />
+            {!validInput && (
+              <span className="text-danger">
+                <AlertErrorIcon className="little-spacer-right text-text-top" />
+                {translate('onboarding.token.invalid_format')}
+              </span>
+            )}
+          </div>
+        )}
+      </div>
+    );
+  };
+
+  renderForm = () => {
+    const { loading, token, tokenName, tokens } = this.state;
+    const canUseExisting = tokens !== undefined && tokens.length > 0;
+
+    return (
+      <div className="boxed-group-inner">
+        {token != null ? (
+          <form onSubmit={this.handleTokenRevoke}>
+            <span className="text-middle">
+              {tokenName}
+              {': '}
+            </span>
+            <strong className="spacer-right text-middle">{token}</strong>
+            {loading ? (
+              <i className="spinner text-middle" />
+            ) : (
+              <DeleteButton className="button-small text-middle" onClick={this.handleTokenRevoke} />
+            )}
+          </form>
+        ) : (
+          <div>
+            {this.renderGenerateOption()}
+            {canUseExisting && this.renderUseExistingOption()}
+          </div>
+        )}
+
+        <div className="note big-spacer-top width-50">
+          <FormattedMessage
+            defaultMessage={translate('onboarding.token.text')}
+            id="onboarding.token.text"
+            values={{
+              link: (
+                <Link target="_blank" to="/account/security">
+                  {translate('onboarding.token.text.user_account')}
+                </Link>
+              )
+            }}
+          />
+        </div>
+
+        {this.canContinue() && (
+          <div className="big-spacer-top">
+            <Button className="js-continue" onClick={this.handleContinueClick}>
+              {translate('continue')}
+            </Button>
+          </div>
+        )}
+      </div>
+    );
+  };
+
+  renderResult = () => {
+    const { selection, tokenName } = this.state;
+    const token = this.getToken();
+
+    if (!token) {
+      return null;
+    }
+
+    return (
+      <div className="boxed-group-actions display-flex-center">
+        <AlertSuccessIcon className="spacer-right" />
+        {selection === 'generate' && tokenName && `${tokenName}: `}
+        <strong>{token}</strong>
+      </div>
+    );
+  };
+
+  render() {
+    return (
+      <Step
+        finished={this.props.finished}
+        onOpen={this.props.onOpen}
+        open={this.props.open}
+        renderForm={this.renderForm}
+        renderResult={this.renderResult}
+        stepNumber={this.props.stepNumber}
+        stepTitle={translate('onboarding.token.header')}
+      />
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/LanguageForm-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/LanguageForm-test.tsx
new file mode 100644 (file)
index 0000000..24dd2af
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 LanguageForm from '../LanguageForm';
+
+it('selects java', () => {
+  const onDone = jest.fn();
+  const wrapper = shallow(
+    <LanguageForm component={mockComponent()} onDone={onDone} onReset={jest.fn()} />
+  );
+
+  (wrapper.find('RadioToggle').prop('onCheck') as Function)('java');
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('selects c#', () => {
+  const onDone = jest.fn();
+  const wrapper = shallow(
+    <LanguageForm component={mockComponent()} onDone={onDone} onReset={jest.fn()} />
+  );
+
+  (wrapper.find('RadioToggle').prop('onCheck') as Function)('dotnet');
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('selects other', () => {
+  const onDone = jest.fn();
+  const wrapper = shallow(
+    <LanguageForm component={mockComponent()} onDone={onDone} onReset={jest.fn()} />
+  );
+
+  (wrapper.find('RadioToggle').prop('onCheck') as Function)('other');
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx
new file mode 100644 (file)
index 0000000..79a269a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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, mockLoggedInUser } from '../../../../helpers/testMocks';
+import ManualTutorial from '../ManualTutorial';
+import ProjectAnalysisStep from '../ProjectAnalysisStep';
+import TokenStep from '../TokenStep';
+
+it('renders correctly', () => {
+  expect(shallowRender()).toMatchSnapshot('default');
+  expect(shallowRender({ onBack: jest.fn() })).toMatchSnapshot('with back button');
+});
+
+it('allows to navigate between steps', () => {
+  const wrapper = shallowRender();
+  const instance = wrapper.instance();
+
+  expect(wrapper.find(TokenStep).props().open).toBe(true);
+
+  instance.handleTokenDone('foo');
+  expect(wrapper.find(TokenStep).props().open).toBe(false);
+  expect(wrapper.find(ProjectAnalysisStep).props().open).toBe(true);
+
+  instance.handleTokenOpen();
+  expect(wrapper.find(TokenStep).props().open).toBe(true);
+  expect(wrapper.find(ProjectAnalysisStep).props().open).toBe(false);
+});
+
+function shallowRender(props: Partial<ManualTutorial['props']> = {}) {
+  return shallow<ManualTutorial>(
+    <ManualTutorial component={mockComponent()} currentUser={mockLoggedInUser()} {...props} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ProjectAnalysisStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ProjectAnalysisStep-test.tsx
new file mode 100644 (file)
index 0000000..4b6ecf8
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 ProjectAnalysisStep from '../ProjectAnalysisStep';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+function shallowRender() {
+  return shallow(<ProjectAnalysisStep component={mockComponent()} open={true} stepNumber={1} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/TokenStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/TokenStep-test.tsx
new file mode 100644 (file)
index 0000000..8a9f52d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { change, click, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import TokenStep from '../TokenStep';
+
+jest.mock('../../../../api/user-tokens', () => ({
+  getTokens: () => Promise.resolve([{ name: 'foo' }]),
+  generateToken: () => Promise.resolve({ token: 'abcd1234' }),
+  revokeToken: () => Promise.resolve()
+}));
+
+const currentUser: Pick<T.LoggedInUser, 'login'> = { login: 'user' };
+
+it('generates token', async () => {
+  const wrapper = shallow(
+    <TokenStep
+      currentUser={currentUser}
+      finished={false}
+      onContinue={jest.fn()}
+      onOpen={jest.fn()}
+      open={true}
+      stepNumber={1}
+    />
+  );
+  await waitAndUpdate(wrapper);
+  expect(wrapper.dive()).toMatchSnapshot();
+  change(wrapper.dive().find('input'), 'my token');
+  submit(wrapper.dive().find('form'));
+  expect(wrapper.dive()).toMatchSnapshot(); // spinner
+  await waitAndUpdate(wrapper);
+  expect(wrapper.dive()).toMatchSnapshot();
+});
+
+it('revokes token', async () => {
+  const wrapper = shallow(
+    <TokenStep
+      currentUser={currentUser}
+      finished={false}
+      onContinue={jest.fn()}
+      onOpen={jest.fn()}
+      open={true}
+      stepNumber={1}
+    />
+  );
+  await new Promise(setImmediate);
+  wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
+  expect(wrapper.dive()).toMatchSnapshot();
+  (wrapper
+    .dive()
+    .find('DeleteButton')
+    .prop('onClick') as Function)();
+  wrapper.update();
+  expect(wrapper.dive()).toMatchSnapshot(); // spinner
+  await waitAndUpdate(wrapper);
+  expect(wrapper.dive()).toMatchSnapshot();
+});
+
+it('continues', async () => {
+  const onContinue = jest.fn();
+  const wrapper = shallow(
+    <TokenStep
+      currentUser={currentUser}
+      finished={false}
+      onContinue={onContinue}
+      onOpen={jest.fn()}
+      open={true}
+      stepNumber={1}
+    />
+  );
+  await new Promise(setImmediate);
+  wrapper.setState({ token: 'abcd1234', tokenName: 'my token' });
+  click(wrapper.dive().find('[className="js-continue"]'));
+  expect(onContinue).toBeCalledWith('abcd1234');
+});
+
+it('uses existing token', async () => {
+  const onContinue = jest.fn();
+  const wrapper = shallow(
+    <TokenStep
+      currentUser={currentUser}
+      finished={false}
+      onContinue={onContinue}
+      onOpen={jest.fn()}
+      open={true}
+      stepNumber={1}
+    />
+  );
+  await new Promise(setImmediate);
+  wrapper.setState({ existingToken: 'abcd1234', selection: 'use-existing' });
+  click(wrapper.dive().find('[className="js-continue"]'));
+  expect(onContinue).toBeCalledWith('abcd1234');
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/LanguageForm-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/LanguageForm-test.tsx.snap
new file mode 100644 (file)
index 0000000..04745f2
--- /dev/null
@@ -0,0 +1,118 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`selects c# 1`] = `
+<Fragment>
+  <div>
+    <h4
+      className="spacer-bottom"
+    >
+      onboarding.language
+    </h4>
+    <RadioToggle
+      disabled={false}
+      name="language"
+      onCheck={[Function]}
+      options={
+        Array [
+          Object {
+            "label": "onboarding.language.java",
+            "value": "java",
+          },
+          Object {
+            "label": "onboarding.language.dotnet",
+            "value": "dotnet",
+          },
+          Object {
+            "label": "onboarding.language.other",
+            "value": "other",
+          },
+        ]
+      }
+      value="dotnet"
+    />
+  </div>
+</Fragment>
+`;
+
+exports[`selects java 1`] = `
+<Fragment>
+  <div>
+    <h4
+      className="spacer-bottom"
+    >
+      onboarding.language
+    </h4>
+    <RadioToggle
+      disabled={false}
+      name="language"
+      onCheck={[Function]}
+      options={
+        Array [
+          Object {
+            "label": "onboarding.language.java",
+            "value": "java",
+          },
+          Object {
+            "label": "onboarding.language.dotnet",
+            "value": "dotnet",
+          },
+          Object {
+            "label": "onboarding.language.other",
+            "value": "other",
+          },
+        ]
+      }
+      value="java"
+    />
+  </div>
+  <RenderOptions
+    name="java-build"
+    onCheck={[Function]}
+    optionLabelKey="onboarding.language.java.build_technology"
+    options={
+      Array [
+        "maven",
+        "gradle",
+      ]
+    }
+    titleLabelKey="onboarding.language.java.build_technology"
+  />
+</Fragment>
+`;
+
+exports[`selects other 1`] = `
+<Fragment>
+  <div>
+    <h4
+      className="spacer-bottom"
+    >
+      onboarding.language
+    </h4>
+    <RadioToggle
+      disabled={false}
+      name="language"
+      onCheck={[Function]}
+      options={
+        Array [
+          Object {
+            "label": "onboarding.language.java",
+            "value": "java",
+          },
+          Object {
+            "label": "onboarding.language.dotnet",
+            "value": "dotnet",
+          },
+          Object {
+            "label": "onboarding.language.other",
+            "value": "other",
+          },
+        ]
+      }
+      value="other"
+    />
+  </div>
+  <RenderOS
+    setOS={[Function]}
+  />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap
new file mode 100644 (file)
index 0000000..cc1cf2f
--- /dev/null
@@ -0,0 +1,144 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly: default 1`] = `
+<Fragment>
+  <div
+    className="page-header big-spacer-bottom"
+  >
+    <h1
+      className="page-title"
+    >
+      onboarding.project_analysis.header
+    </h1>
+    <p
+      className="page-description"
+    >
+      <InstanceMessage
+        message="onboarding.project_analysis.description"
+      />
+    </p>
+  </div>
+  <TokenStep
+    currentUser={
+      Object {
+        "groups": Array [],
+        "isLoggedIn": true,
+        "login": "luke",
+        "name": "Skywalker",
+        "scmAccounts": Array [],
+      }
+    }
+    finished={false}
+    initialTokenName="Analyze \\"MyProject\\""
+    onContinue={[Function]}
+    onOpen={[Function]}
+    open={true}
+    stepNumber={1}
+  />
+  <ProjectAnalysisStep
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "my-project",
+        "name": "MyProject",
+        "organization": "foo",
+        "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 [],
+      }
+    }
+    displayRowLayout={true}
+    open={false}
+    stepNumber={2}
+  />
+</Fragment>
+`;
+
+exports[`renders correctly: with back button 1`] = `
+<Fragment>
+  <div
+    className="page-header big-spacer-bottom"
+  >
+    <h1
+      className="page-title"
+    >
+      <Tooltip
+        overlay="onboarding.tutorial.return_to_list"
+      >
+        <a
+          aria-label="onboarding.tutorial.return_to_list"
+          className="link-no-underline big-spacer-right"
+          onClick={[MockFunction]}
+        >
+          <BackIcon />
+        </a>
+      </Tooltip>
+      onboarding.project_analysis.header
+    </h1>
+    <p
+      className="page-description"
+    >
+      <InstanceMessage
+        message="onboarding.project_analysis.description"
+      />
+    </p>
+  </div>
+  <TokenStep
+    currentUser={
+      Object {
+        "groups": Array [],
+        "isLoggedIn": true,
+        "login": "luke",
+        "name": "Skywalker",
+        "scmAccounts": Array [],
+      }
+    }
+    finished={false}
+    initialTokenName="Analyze \\"MyProject\\""
+    onContinue={[Function]}
+    onOpen={[Function]}
+    open={true}
+    stepNumber={1}
+  />
+  <ProjectAnalysisStep
+    component={
+      Object {
+        "breadcrumbs": Array [],
+        "key": "my-project",
+        "name": "MyProject",
+        "organization": "foo",
+        "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 [],
+      }
+    }
+    displayRowLayout={true}
+    open={false}
+    stepNumber={2}
+  />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ProjectAnalysisStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ProjectAnalysisStep-test.tsx.snap
new file mode 100644 (file)
index 0000000..0380c57
--- /dev/null
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Step
+  finished={false}
+  onOpen={[Function]}
+  open={true}
+  renderForm={[Function]}
+  renderResult={[Function]}
+  stepNumber={1}
+  stepTitle="onboarding.analysis.header"
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/TokenStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/TokenStep-test.tsx.snap
new file mode 100644 (file)
index 0000000..6ad2bfc
--- /dev/null
@@ -0,0 +1,501 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generates token 1`] = `
+<div
+  className="boxed-group onboarding-step is-open"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      onboarding.token.header
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <div>
+        <div>
+          <Radio
+            checked={true}
+            onCheck={[Function]}
+            value="generate"
+          >
+            onboarding.token.generate_token
+          </Radio>
+          <div
+            className="big-spacer-top"
+          >
+            <form
+              onSubmit={[Function]}
+            >
+              <input
+                autoFocus={true}
+                className="input-super-large spacer-right text-middle"
+                onChange={[Function]}
+                placeholder="onboarding.token.generate_token.placeholder"
+                required={true}
+                type="text"
+                value=""
+              />
+              <SubmitButton
+                className="text-middle"
+                disabled={true}
+              >
+                onboarding.token.generate
+              </SubmitButton>
+            </form>
+          </div>
+        </div>
+        <div
+          className="big-spacer-top"
+        >
+          <Radio
+            checked={false}
+            onCheck={[Function]}
+            value="use-existing"
+          >
+            onboarding.token.use_existing_token
+          </Radio>
+        </div>
+      </div>
+      <div
+        className="note big-spacer-top width-50"
+      >
+        <FormattedMessage
+          defaultMessage="onboarding.token.text"
+          id="onboarding.token.text"
+          values={
+            Object {
+              "link": <Link
+                onlyActiveOnIndex={false}
+                style={Object {}}
+                target="_blank"
+                to="/account/security"
+              >
+                onboarding.token.text.user_account
+              </Link>,
+            }
+          }
+        />
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`generates token 2`] = `
+<div
+  className="boxed-group onboarding-step is-open"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      onboarding.token.header
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <div>
+        <div>
+          <Radio
+            checked={true}
+            onCheck={[Function]}
+            value="generate"
+          >
+            onboarding.token.generate_token
+          </Radio>
+          <div
+            className="big-spacer-top"
+          >
+            <form
+              onSubmit={[Function]}
+            >
+              <input
+                autoFocus={true}
+                className="input-super-large spacer-right text-middle"
+                onChange={[Function]}
+                placeholder="onboarding.token.generate_token.placeholder"
+                required={true}
+                type="text"
+                value="my token"
+              />
+              <i
+                className="spinner text-middle"
+              />
+            </form>
+          </div>
+        </div>
+        <div
+          className="big-spacer-top"
+        >
+          <Radio
+            checked={false}
+            onCheck={[Function]}
+            value="use-existing"
+          >
+            onboarding.token.use_existing_token
+          </Radio>
+        </div>
+      </div>
+      <div
+        className="note big-spacer-top width-50"
+      >
+        <FormattedMessage
+          defaultMessage="onboarding.token.text"
+          id="onboarding.token.text"
+          values={
+            Object {
+              "link": <Link
+                onlyActiveOnIndex={false}
+                style={Object {}}
+                target="_blank"
+                to="/account/security"
+              >
+                onboarding.token.text.user_account
+              </Link>,
+            }
+          }
+        />
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`generates token 3`] = `
+<div
+  className="boxed-group onboarding-step is-open"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      onboarding.token.header
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <form
+        onSubmit={[Function]}
+      >
+        <span
+          className="text-middle"
+        >
+          my token
+          : 
+        </span>
+        <strong
+          className="spacer-right text-middle"
+        >
+          abcd1234
+        </strong>
+        <DeleteButton
+          className="button-small text-middle"
+          onClick={[Function]}
+        />
+      </form>
+      <div
+        className="note big-spacer-top width-50"
+      >
+        <FormattedMessage
+          defaultMessage="onboarding.token.text"
+          id="onboarding.token.text"
+          values={
+            Object {
+              "link": <Link
+                onlyActiveOnIndex={false}
+                style={Object {}}
+                target="_blank"
+                to="/account/security"
+              >
+                onboarding.token.text.user_account
+              </Link>,
+            }
+          }
+        />
+      </div>
+      <div
+        className="big-spacer-top"
+      >
+        <Button
+          className="js-continue"
+          onClick={[Function]}
+        >
+          continue
+        </Button>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`revokes token 1`] = `
+<div
+  className="boxed-group onboarding-step is-open"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      onboarding.token.header
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <form
+        onSubmit={[Function]}
+      >
+        <span
+          className="text-middle"
+        >
+          my token
+          : 
+        </span>
+        <strong
+          className="spacer-right text-middle"
+        >
+          abcd1234
+        </strong>
+        <DeleteButton
+          className="button-small text-middle"
+          onClick={[Function]}
+        />
+      </form>
+      <div
+        className="note big-spacer-top width-50"
+      >
+        <FormattedMessage
+          defaultMessage="onboarding.token.text"
+          id="onboarding.token.text"
+          values={
+            Object {
+              "link": <Link
+                onlyActiveOnIndex={false}
+                style={Object {}}
+                target="_blank"
+                to="/account/security"
+              >
+                onboarding.token.text.user_account
+              </Link>,
+            }
+          }
+        />
+      </div>
+      <div
+        className="big-spacer-top"
+      >
+        <Button
+          className="js-continue"
+          onClick={[Function]}
+        >
+          continue
+        </Button>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`revokes token 2`] = `
+<div
+  className="boxed-group onboarding-step is-open"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      onboarding.token.header
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <form
+        onSubmit={[Function]}
+      >
+        <span
+          className="text-middle"
+        >
+          my token
+          : 
+        </span>
+        <strong
+          className="spacer-right text-middle"
+        >
+          abcd1234
+        </strong>
+        <i
+          className="spinner text-middle"
+        />
+      </form>
+      <div
+        className="note big-spacer-top width-50"
+      >
+        <FormattedMessage
+          defaultMessage="onboarding.token.text"
+          id="onboarding.token.text"
+          values={
+            Object {
+              "link": <Link
+                onlyActiveOnIndex={false}
+                style={Object {}}
+                target="_blank"
+                to="/account/security"
+              >
+                onboarding.token.text.user_account
+              </Link>,
+            }
+          }
+        />
+      </div>
+      <div
+        className="big-spacer-top"
+      >
+        <Button
+          className="js-continue"
+          onClick={[Function]}
+        >
+          continue
+        </Button>
+      </div>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`revokes token 3`] = `
+<div
+  className="boxed-group onboarding-step is-open"
+>
+  <div
+    className="onboarding-step-number"
+  >
+    1
+  </div>
+  <div
+    className="boxed-group-header"
+  >
+    <h2>
+      onboarding.token.header
+    </h2>
+  </div>
+  <div
+    className=""
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <div>
+        <div>
+          <Radio
+            checked={true}
+            onCheck={[Function]}
+            value="generate"
+          >
+            onboarding.token.generate_token
+          </Radio>
+          <div
+            className="big-spacer-top"
+          >
+            <form
+              onSubmit={[Function]}
+            >
+              <input
+                autoFocus={true}
+                className="input-super-large spacer-right text-middle"
+                onChange={[Function]}
+                placeholder="onboarding.token.generate_token.placeholder"
+                required={true}
+                type="text"
+                value=""
+              />
+              <SubmitButton
+                className="text-middle"
+                disabled={true}
+              >
+                onboarding.token.generate
+              </SubmitButton>
+            </form>
+          </div>
+        </div>
+        <div
+          className="big-spacer-top"
+        >
+          <Radio
+            checked={false}
+            onCheck={[Function]}
+            value="use-existing"
+          >
+            onboarding.token.use_existing_token
+          </Radio>
+        </div>
+      </div>
+      <div
+        className="note big-spacer-top width-50"
+      >
+        <FormattedMessage
+          defaultMessage="onboarding.token.text"
+          id="onboarding.token.text"
+          values={
+            Object {
+              "link": <Link
+                onlyActiveOnIndex={false}
+                style={Object {}}
+                target="_blank"
+                to="/account/security"
+              >
+                onboarding.token.text.user_account
+              </Link>,
+            }
+          }
+        />
+      </div>
+    </div>
+  </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/AnalysisCommand.tsx
new file mode 100644 (file)
index 0000000..dd66db2
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { getHostUrl } from 'sonar-ui-common/helpers/urls';
+import { LanguageConfig } from '../../types';
+import { getProjectKey } from '../ProjectAnalysisStep';
+import DotNet from './DotNet';
+import JavaGradle from './JavaGradle';
+import JavaMaven from './JavaMaven';
+import Other from './Other';
+
+interface Props {
+  component?: T.Component;
+  organization?: string;
+  languageConfig: LanguageConfig;
+  small?: boolean;
+  token?: string;
+}
+
+export default class AnalysisCommand extends React.PureComponent<Props> {
+  renderCommandForMaven = () => {
+    const { component, token } = this.props;
+    if (!token) {
+      return null;
+    }
+    return (
+      <JavaMaven
+        host={getHostUrl()}
+        organization={this.props.organization}
+        projectKey={component && component.key}
+        token={token}
+      />
+    );
+  };
+
+  renderCommandForGradle = () => {
+    const { component, token } = this.props;
+    if (!token) {
+      return null;
+    }
+    return (
+      <JavaGradle
+        host={getHostUrl()}
+        organization={this.props.organization}
+        projectKey={component && component.key}
+        token={token}
+      />
+    );
+  };
+
+  renderCommandForDotNet = () => {
+    const { component, languageConfig, small, token } = this.props;
+    const projectKey = getProjectKey(languageConfig, component);
+    if (!projectKey || !token) {
+      return null;
+    }
+    return (
+      <DotNet
+        host={getHostUrl()}
+        organization={this.props.organization}
+        projectKey={projectKey}
+        small={small}
+        token={token}
+      />
+    );
+  };
+
+  renderCommandForOther = () => {
+    const { component, languageConfig, token } = this.props;
+    const projectKey = getProjectKey(languageConfig, component);
+    if (!languageConfig || !projectKey || !languageConfig.os || !token) {
+      return null;
+    }
+    return (
+      <Other
+        host={getHostUrl()}
+        organization={this.props.organization}
+        os={languageConfig.os}
+        projectKey={projectKey}
+        token={token}
+      />
+    );
+  };
+
+  render() {
+    const { languageConfig } = this.props;
+
+    if (languageConfig.language === 'java') {
+      return languageConfig.javaBuild === 'maven'
+        ? this.renderCommandForMaven()
+        : this.renderCommandForGradle();
+    } else if (languageConfig.language === 'dotnet') {
+      return this.renderCommandForDotNet();
+    } else {
+      return this.renderCommandForOther();
+    }
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNet.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/DotNet.tsx
new file mode 100644 (file)
index 0000000..a1d61c8
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+import InstanceMessage from '../../../common/InstanceMessage';
+import MSBuildScanner from './MSBuildScanner';
+
+export interface Props {
+  host: string;
+  organization?: string;
+  projectKey: string;
+  small?: boolean;
+  token: string;
+}
+
+export default function DotNet(props: Props) {
+  const command1 = [
+    'SonarScanner.MSBuild.exe begin',
+    `/k:"${props.projectKey}"`,
+    props.organization && `/d:sonar.organization="${props.organization}"`,
+    `/d:sonar.host.url="${props.host}"`,
+    `/d:sonar.login="${props.token}"`
+  ];
+
+  const command2 = 'MsBuild.exe /t:Rebuild';
+
+  const command3 = ['SonarScanner.MSBuild.exe end', `/d:sonar.login="${props.token}"`];
+
+  return (
+    <div>
+      <MSBuildScanner />
+
+      <h4 className="huge-spacer-top spacer-bottom">
+        {translate('onboarding.analysis.msbuild.execute')}
+      </h4>
+      <InstanceMessage message={translate('onboarding.analysis.msbuild.execute.text')}>
+        {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
+      </InstanceMessage>
+      <CodeSnippet isOneLine={true} snippet={command1} />
+      <CodeSnippet isOneLine={false} snippet={command2} />
+      <CodeSnippet isOneLine={props.small} snippet={command3} />
+      <p className="big-spacer-top markdown">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.analysis.docs')}
+          id="onboarding.analysis.docs"
+          values={{
+            link: (
+              <a
+                href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+                rel="noopener noreferrer"
+                target="_blank">
+                {translate('onboarding.analysis.msbuild.docs_link')}
+              </a>
+            )
+          }}
+        />
+      </p>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaGradle.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaGradle.tsx
new file mode 100644 (file)
index 0000000..8a3be3f
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+import InstanceMessage from '../../../common/InstanceMessage';
+
+export interface Props {
+  host: string;
+  organization?: string;
+  projectKey?: string;
+  token: string;
+}
+
+export default function JavaGradle(props: Props) {
+  const config = 'plugins {\n  id "org.sonarqube" version "2.7"\n}';
+
+  const command = [
+    './gradlew sonarqube',
+    props.projectKey && `-Dsonar.projectKey=${props.projectKey}`,
+    props.organization && `-Dsonar.organization=${props.organization}`,
+    `-Dsonar.host.url=${props.host}`,
+    `-Dsonar.login=${props.token}`
+  ];
+
+  return (
+    <div>
+      <h4 className="spacer-bottom">{translate('onboarding.analysis.java.gradle.header')}</h4>
+      <InstanceMessage message={translate('onboarding.analysis.java.gradle.text.1')}>
+        {transformedMessage => (
+          <p className="spacer-bottom markdown">
+            <FormattedMessage
+              defaultMessage={transformedMessage}
+              id="onboarding.analysis.java.gradle.text.1"
+              values={{
+                plugin_code: <code>org.sonarqube</code>,
+                filename: <code>build.gradle</code>
+              }}
+            />
+          </p>
+        )}
+      </InstanceMessage>
+      <CodeSnippet snippet={config} />
+      <p className="spacer-top spacer-bottom markdown">
+        {translate('onboarding.analysis.java.gradle.text.2')}
+      </p>
+      <CodeSnippet snippet={command} />
+      <p className="big-spacer-top markdown">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.analysis.docs')}
+          id="onboarding.analysis.docs"
+          values={{
+            link: (
+              <a
+                href="http://redirect.sonarsource.com/doc/gradle.html"
+                rel="noopener noreferrer"
+                target="_blank">
+                {translate('onboarding.analysis.java.gradle.docs_link')}
+              </a>
+            )
+          }}
+        />
+      </p>
+      <p className="big-spacer-top markdown">
+        {props.projectKey
+          ? translate('onboarding.analysis.auto_refresh_after_analysis')
+          : translate('onboarding.analysis.browse_url_after_analysis')}
+      </p>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaMaven.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/JavaMaven.tsx
new file mode 100644 (file)
index 0000000..488fb76
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+import InstanceMessage from '../../../common/InstanceMessage';
+
+export interface Props {
+  host: string;
+  organization?: string;
+  projectKey?: string;
+  token: string;
+}
+
+export default function JavaMaven(props: Props) {
+  const command = [
+    'mvn sonar:sonar',
+    props.projectKey && `-Dsonar.projectKey=${props.projectKey}`,
+    props.organization && `-Dsonar.organization=${props.organization}`,
+    `-Dsonar.host.url=${props.host}`,
+    `-Dsonar.login=${props.token}`
+  ];
+
+  return (
+    <div>
+      <h4 className="spacer-bottom">{translate('onboarding.analysis.java.maven.header')}</h4>
+      <p className="spacer-bottom markdown">
+        <InstanceMessage message={translate('onboarding.analysis.java.maven.text')} />
+      </p>
+      <CodeSnippet snippet={command} />
+      <p className="big-spacer-top markdown">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.analysis.docs')}
+          id="onboarding.analysis.docs"
+          values={{
+            link: (
+              <a
+                href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+                rel="noopener noreferrer"
+                target="_blank">
+                {translate('onboarding.analysis.java.maven.docs_link')}
+              </a>
+            )
+          }}
+        />
+      </p>
+      <p className="big-spacer-top markdown">
+        {props.projectKey
+          ? translate('onboarding.analysis.auto_refresh_after_analysis')
+          : translate('onboarding.analysis.browse_url_after_analysis')}
+      </p>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/MSBuildScanner.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/MSBuildScanner.tsx
new file mode 100644 (file)
index 0000000..9ff8c9a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
+
+interface Props {
+  className?: string;
+}
+
+export default function MSBuildScanner(props: Props) {
+  return (
+    <div className={props.className}>
+      <h4 className="spacer-bottom">{translate('onboarding.analysis.msbuild.header')}</h4>
+      <p className="spacer-bottom markdown">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.analysis.msbuild.text')}
+          id="onboarding.analysis.msbuild.text"
+          values={{ code: <code>%PATH%</code> }}
+        />
+      </p>
+      <p>
+        <a
+          className="button"
+          href="https://sonarcloud.io/documentation/analysis/scan/sonarscanner-for-msbuild/"
+          rel="noopener noreferrer"
+          target="_blank">
+          {translate('download_verb')}
+        </a>
+      </p>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/Other.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/Other.tsx
new file mode 100644 (file)
index 0000000..61e3f9f
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../../common/CodeSnippet';
+import InstanceMessage from '../../../common/InstanceMessage';
+import { quote } from '../../utils';
+import SQScanner from './SQScanner';
+
+export interface Props {
+  host: string;
+  organization?: string;
+  os: string;
+  projectKey: string;
+  token: string;
+}
+
+export default function Other(props: Props) {
+  const q = quote(props.os);
+  const command = [
+    props.os === 'win' ? 'sonar-scanner.bat' : 'sonar-scanner',
+    '-D' + q(`sonar.projectKey=${props.projectKey}`),
+    props.organization && '-D' + q(`sonar.organization=${props.organization}`),
+    '-D' + q('sonar.sources=.'),
+    '-D' + q(`sonar.host.url=${props.host}`),
+    '-D' + q(`sonar.login=${props.token}`)
+  ];
+
+  return (
+    <div>
+      <SQScanner os={props.os} />
+
+      <h4 className="huge-spacer-top spacer-bottom">
+        {translate('onboarding.analysis.sq_scanner.execute')}
+      </h4>
+      <InstanceMessage message={translate('onboarding.analysis.sq_scanner.execute.text')}>
+        {transformedMessage => <p className="spacer-bottom markdown">{transformedMessage}</p>}
+      </InstanceMessage>
+      <CodeSnippet isOneLine={props.os === 'win'} snippet={command} />
+      <p className="big-spacer-top markdown">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.analysis.sq_scanner.docs')}
+          id="onboarding.analysis.sq_scanner.docs"
+          values={{
+            link: (
+              <a
+                href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+                rel="noopener noreferrer"
+                target="_blank">
+                {translate('onboarding.analysis.sq_scanner.docs_link')}
+              </a>
+            )
+          }}
+        />
+      </p>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/SQScanner.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/SQScanner.tsx
new file mode 100644 (file)
index 0000000..663fda6
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
+
+interface Props {
+  className?: string;
+  os: string;
+}
+
+export default function SQScanner(props: Props) {
+  return (
+    <div className={props.className}>
+      <h4 className="spacer-bottom">
+        {translate('onboarding.analysis.sq_scanner.header', props.os)}
+      </h4>
+      <p className="spacer-bottom markdown">
+        <FormattedMessage
+          defaultMessage={translate('onboarding.analysis.sq_scanner.text')}
+          id="onboarding.analysis.sq_scanner.text"
+          values={{
+            dir: <code>bin</code>,
+            env_var: <code>{props.os === 'win' ? '%PATH%' : 'PATH'}</code>
+          }}
+        />
+      </p>
+      <p>
+        <a
+          className="button"
+          href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+          rel="noopener noreferrer"
+          target="_blank">
+          {translate('download_verb')}
+        </a>
+      </p>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/AnalysisCommand-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/AnalysisCommand-test.tsx
new file mode 100644 (file)
index 0000000..6f8c57e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 AnalysisCommand from '../AnalysisCommand';
+
+jest.mock('sonar-ui-common/helpers/urls', () => ({
+  getHostUrl: () => 'HOST'
+}));
+
+it('display java command', () => {
+  expect(
+    getWrapper({ languageConfig: { language: 'java', javaBuild: 'gradle' } })
+  ).toMatchSnapshot();
+  expect(
+    getWrapper({ languageConfig: { language: 'java', javaBuild: 'maven' } })
+  ).toMatchSnapshot();
+});
+
+it('display c# command', () => {
+  expect(
+    getWrapper({ languageConfig: { language: 'dotnet', projectKey: 'project-foo' } })
+  ).toMatchSnapshot();
+});
+
+it('display others command', () => {
+  expect(
+    getWrapper({
+      languageConfig: { language: 'other', os: 'window', projectKey: 'project-foo' }
+    })
+  ).toMatchSnapshot();
+});
+
+function getWrapper(props = {}) {
+  return shallow(<AnalysisCommand languageConfig={{}} token="myToken" {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotNet-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/DotNet-test.tsx
new file mode 100644 (file)
index 0000000..8f00456
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import DotNet, { Props } from '../DotNet';
+
+it('DotNet renders correctly', () => {
+  expect(shallowRender).toMatchSnapshot();
+
+  expect(
+    shallowRender({
+      organization: 'organization',
+      small: true
+    })
+  ).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<Props> = {}) {
+  return shallow(<DotNet host="host" projectKey="projectKey" token="token" {...props} />);
+}
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaGradle-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaGradle-test.tsx
new file mode 100644 (file)
index 0000000..0fc13a7
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import JavaGradle from '../JavaGradle';
+
+it('renders correctly', () => {
+  expect(shallow(<JavaGradle host="host" token="token" />)).toMatchSnapshot();
+  expect(
+    shallow(<JavaGradle host="host" organization="organization" token="token" />)
+  ).toMatchSnapshot();
+});
+
+it('renders with projectKey', () => {
+  expect(
+    shallow(<JavaGradle host="host" organization="organization" projectKey="foo" token="token" />)
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaMaven-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/JavaMaven-test.tsx
new file mode 100644 (file)
index 0000000..7304894
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import JavaMaven from '../JavaMaven';
+
+it('renders correctly', () => {
+  expect(shallow(<JavaMaven host="host" token="token" />)).toMatchSnapshot();
+  expect(
+    shallow(<JavaMaven host="host" organization="organization" token="token" />)
+  ).toMatchSnapshot();
+});
+
+it('renders with projectKey', () => {
+  expect(
+    shallow(<JavaMaven host="host" organization="organization" projectKey="foo" token="token" />)
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/MSBuildScanner-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/MSBuildScanner-test.tsx
new file mode 100644 (file)
index 0000000..8da96cd
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 MSBuildScanner from '../MSBuildScanner';
+
+it('renders correctly', () => {
+  expect(shallow(<MSBuildScanner />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/Other-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/Other-test.tsx
new file mode 100644 (file)
index 0000000..f4ab19c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import * as React from 'react';
+import Other from '../Other';
+
+it('renders correctly', () => {
+  expect(
+    shallow(<Other host="host" os="win" projectKey="projectKey" token="token" />)
+  ).toMatchSnapshot();
+
+  expect(
+    shallow(<Other host="host" os="linux" projectKey="projectKey" token="token" />)
+  ).toMatchSnapshot();
+
+  expect(
+    shallow(<Other host="host" os="linux" projectKey="projectKey" token="token" />)
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/SQScanner-test.tsx b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/SQScanner-test.tsx
new file mode 100644 (file)
index 0000000..26a51ae
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 SQScanner from '../SQScanner';
+
+it('renders correctly', () => {
+  expect(shallow(<SQScanner os="win" />)).toMatchSnapshot();
+  expect(shallow(<SQScanner os="linux" />)).toMatchSnapshot();
+  expect(shallow(<SQScanner os="mac" />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap
new file mode 100644 (file)
index 0000000..6afb099
--- /dev/null
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`display c# command 1`] = `
+<DotNet
+  host="HOST"
+  projectKey="project-foo"
+  token="myToken"
+/>
+`;
+
+exports[`display java command 1`] = `
+<JavaGradle
+  host="HOST"
+  token="myToken"
+/>
+`;
+
+exports[`display java command 2`] = `
+<JavaMaven
+  host="HOST"
+  token="myToken"
+/>
+`;
+
+exports[`display others command 1`] = `
+<Other
+  host="HOST"
+  os="window"
+  projectKey="project-foo"
+  token="myToken"
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/DotNet-test.tsx.snap
new file mode 100644 (file)
index 0000000..19a2937
--- /dev/null
@@ -0,0 +1,63 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DotNet renders correctly 1`] = `[Function]`;
+
+exports[`DotNet renders correctly 2`] = `
+<div>
+  <MSBuildScanner />
+  <h4
+    className="huge-spacer-top spacer-bottom"
+  >
+    onboarding.analysis.msbuild.execute
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.msbuild.execute.text"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    isOneLine={true}
+    snippet={
+      Array [
+        "SonarScanner.MSBuild.exe begin",
+        "/k:\\"projectKey\\"",
+        "/d:sonar.organization=\\"organization\\"",
+        "/d:sonar.host.url=\\"host\\"",
+        "/d:sonar.login=\\"token\\"",
+      ]
+    }
+  />
+  <CodeSnippet
+    isOneLine={false}
+    snippet="MsBuild.exe /t:Rebuild"
+  />
+  <CodeSnippet
+    isOneLine={true}
+    snippet={
+      Array [
+        "SonarScanner.MSBuild.exe end",
+        "/d:sonar.login=\\"token\\"",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.msbuild.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaGradle-test.tsx.snap
new file mode 100644 (file)
index 0000000..7e1208e
--- /dev/null
@@ -0,0 +1,181 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.java.gradle.header
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.java.gradle.text.1"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    snippet="plugins {
+  id \\"org.sonarqube\\" version \\"2.7\\"
+}"
+  />
+  <p
+    className="spacer-top spacer-bottom markdown"
+  >
+    onboarding.analysis.java.gradle.text.2
+  </p>
+  <CodeSnippet
+    snippet={
+      Array [
+        "./gradlew sonarqube",
+        undefined,
+        undefined,
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/gradle.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.java.gradle.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top markdown"
+  >
+    onboarding.analysis.browse_url_after_analysis
+  </p>
+</div>
+`;
+
+exports[`renders correctly 2`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.java.gradle.header
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.java.gradle.text.1"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    snippet="plugins {
+  id \\"org.sonarqube\\" version \\"2.7\\"
+}"
+  />
+  <p
+    className="spacer-top spacer-bottom markdown"
+  >
+    onboarding.analysis.java.gradle.text.2
+  </p>
+  <CodeSnippet
+    snippet={
+      Array [
+        "./gradlew sonarqube",
+        undefined,
+        "-Dsonar.organization=organization",
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/gradle.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.java.gradle.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top markdown"
+  >
+    onboarding.analysis.browse_url_after_analysis
+  </p>
+</div>
+`;
+
+exports[`renders with projectKey 1`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.java.gradle.header
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.java.gradle.text.1"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    snippet="plugins {
+  id \\"org.sonarqube\\" version \\"2.7\\"
+}"
+  />
+  <p
+    className="spacer-top spacer-bottom markdown"
+  >
+    onboarding.analysis.java.gradle.text.2
+  </p>
+  <CodeSnippet
+    snippet={
+      Array [
+        "./gradlew sonarqube",
+        "-Dsonar.projectKey=foo",
+        "-Dsonar.organization=organization",
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/gradle.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.java.gradle.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top markdown"
+  >
+    onboarding.analysis.auto_refresh_after_analysis
+  </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap
new file mode 100644 (file)
index 0000000..7ee4d3b
--- /dev/null
@@ -0,0 +1,157 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.java.maven.header
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <InstanceMessage
+      message="onboarding.analysis.java.maven.text"
+    />
+  </p>
+  <CodeSnippet
+    snippet={
+      Array [
+        "mvn sonar:sonar",
+        undefined,
+        undefined,
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.java.maven.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top markdown"
+  >
+    onboarding.analysis.browse_url_after_analysis
+  </p>
+</div>
+`;
+
+exports[`renders correctly 2`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.java.maven.header
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <InstanceMessage
+      message="onboarding.analysis.java.maven.text"
+    />
+  </p>
+  <CodeSnippet
+    snippet={
+      Array [
+        "mvn sonar:sonar",
+        undefined,
+        "-Dsonar.organization=organization",
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.java.maven.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top markdown"
+  >
+    onboarding.analysis.browse_url_after_analysis
+  </p>
+</div>
+`;
+
+exports[`renders with projectKey 1`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.java.maven.header
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <InstanceMessage
+      message="onboarding.analysis.java.maven.text"
+    />
+  </p>
+  <CodeSnippet
+    snippet={
+      Array [
+        "mvn sonar:sonar",
+        "-Dsonar.projectKey=foo",
+        "-Dsonar.organization=organization",
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.docs"
+      id="onboarding.analysis.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner-maven.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.java.maven.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+  <p
+    className="big-spacer-top markdown"
+  >
+    onboarding.analysis.auto_refresh_after_analysis
+  </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/MSBuildScanner-test.tsx.snap
new file mode 100644 (file)
index 0000000..f1d82f5
--- /dev/null
@@ -0,0 +1,36 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.msbuild.header
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.msbuild.text"
+      id="onboarding.analysis.msbuild.text"
+      values={
+        Object {
+          "code": <code>
+            %PATH%
+          </code>,
+        }
+      }
+    />
+  </p>
+  <p>
+    <a
+      className="button"
+      href="https://sonarcloud.io/documentation/analysis/scan/sonarscanner-for-msbuild/"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      download_verb
+    </a>
+  </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/Other-test.tsx.snap
new file mode 100644 (file)
index 0000000..51b1479
--- /dev/null
@@ -0,0 +1,151 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div>
+  <SQScanner
+    os="win"
+  />
+  <h4
+    className="huge-spacer-top spacer-bottom"
+  >
+    onboarding.analysis.sq_scanner.execute
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.sq_scanner.execute.text"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    isOneLine={true}
+    snippet={
+      Array [
+        "sonar-scanner.bat",
+        "-D\\"sonar.projectKey=projectKey\\"",
+        undefined,
+        "-D\\"sonar.sources=.\\"",
+        "-D\\"sonar.host.url=host\\"",
+        "-D\\"sonar.login=token\\"",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.sq_scanner.docs"
+      id="onboarding.analysis.sq_scanner.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.sq_scanner.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+</div>
+`;
+
+exports[`renders correctly 2`] = `
+<div>
+  <SQScanner
+    os="linux"
+  />
+  <h4
+    className="huge-spacer-top spacer-bottom"
+  >
+    onboarding.analysis.sq_scanner.execute
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.sq_scanner.execute.text"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    isOneLine={false}
+    snippet={
+      Array [
+        "sonar-scanner",
+        "-Dsonar.projectKey=projectKey",
+        undefined,
+        "-Dsonar.sources=.",
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.sq_scanner.docs"
+      id="onboarding.analysis.sq_scanner.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.sq_scanner.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+</div>
+`;
+
+exports[`renders correctly 3`] = `
+<div>
+  <SQScanner
+    os="linux"
+  />
+  <h4
+    className="huge-spacer-top spacer-bottom"
+  >
+    onboarding.analysis.sq_scanner.execute
+  </h4>
+  <InstanceMessage
+    message="onboarding.analysis.sq_scanner.execute.text"
+  >
+    <Component />
+  </InstanceMessage>
+  <CodeSnippet
+    isOneLine={false}
+    snippet={
+      Array [
+        "sonar-scanner",
+        "-Dsonar.projectKey=projectKey",
+        undefined,
+        "-Dsonar.sources=.",
+        "-Dsonar.host.url=host",
+        "-Dsonar.login=token",
+      ]
+    }
+  />
+  <p
+    className="big-spacer-top markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.sq_scanner.docs"
+      id="onboarding.analysis.sq_scanner.docs"
+      values={
+        Object {
+          "link": <a
+            href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+            rel="noopener noreferrer"
+            target="_blank"
+          >
+            onboarding.analysis.sq_scanner.docs_link
+          </a>,
+        }
+      }
+    />
+  </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/manual/commands/__tests__/__snapshots__/SQScanner-test.tsx.snap
new file mode 100644 (file)
index 0000000..6ad4425
--- /dev/null
@@ -0,0 +1,115 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.sq_scanner.header.win
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.sq_scanner.text"
+      id="onboarding.analysis.sq_scanner.text"
+      values={
+        Object {
+          "dir": <code>
+            bin
+          </code>,
+          "env_var": <code>
+            %PATH%
+          </code>,
+        }
+      }
+    />
+  </p>
+  <p>
+    <a
+      className="button"
+      href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      download_verb
+    </a>
+  </p>
+</div>
+`;
+
+exports[`renders correctly 2`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.sq_scanner.header.linux
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.sq_scanner.text"
+      id="onboarding.analysis.sq_scanner.text"
+      values={
+        Object {
+          "dir": <code>
+            bin
+          </code>,
+          "env_var": <code>
+            PATH
+          </code>,
+        }
+      }
+    />
+  </p>
+  <p>
+    <a
+      className="button"
+      href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      download_verb
+    </a>
+  </p>
+</div>
+`;
+
+exports[`renders correctly 3`] = `
+<div>
+  <h4
+    className="spacer-bottom"
+  >
+    onboarding.analysis.sq_scanner.header.mac
+  </h4>
+  <p
+    className="spacer-bottom markdown"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.analysis.sq_scanner.text"
+      id="onboarding.analysis.sq_scanner.text"
+      values={
+        Object {
+          "dir": <code>
+            bin
+          </code>,
+          "env_var": <code>
+            PATH
+          </code>,
+        }
+      }
+    />
+  </p>
+  <p>
+    <a
+      className="button"
+      href="http://redirect.sonarsource.com/doc/install-configure-scanner.html"
+      rel="noopener noreferrer"
+      target="_blank"
+    >
+      download_verb
+    </a>
+  </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/tutorials/utils.ts b/server/sonar-web/src/main/js/components/tutorials/utils.ts
new file mode 100644 (file)
index 0000000..a1a28ed
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.
+ */
+export interface LanguageConfig {
+  language?: string;
+  javaBuild?: string;
+  cFamilyCompiler?: string;
+  os?: string;
+  projectKey?: string;
+}
+
+export function isLanguageConfigured(config?: LanguageConfig) {
+  if (!config) {
+    return false;
+  }
+  const { language, javaBuild, cFamilyCompiler, os, projectKey } = config;
+  const isJavaConfigured = language === 'java' && javaBuild != null;
+  const isDotNetConfigured = language === 'dotnet' && projectKey != null;
+  const isCFamilyConfigured =
+    language === 'c-family' && (cFamilyCompiler === 'msvc' || os != null) && projectKey != null;
+  const isOtherConfigured = language === 'other' && projectKey != null;
+
+  return isJavaConfigured || isDotNetConfigured || isCFamilyConfigured || isOtherConfigured;
+}
+
+export function quote(os: string): (s: string) => string {
+  return os === 'win' ? (s: string) => `"${s}"` : (s: string) => s;
+}
+
+export function getUniqueTokenName(tokens: T.UserToken[], initialTokenName = '') {
+  const hasToken = (name: string) => tokens.find(token => token.name === name) !== undefined;
+
+  if (!hasToken(initialTokenName)) {
+    return initialTokenName;
+  }
+
+  let i = 1;
+  while (hasToken(`${initialTokenName} ${i}`)) {
+    i++;
+  }
+  return `${initialTokenName} ${i}`;
+}
index 3940ca053da614cc43da0a55a7e486ec8fac2e70..2eab0248c9d729af0f35eda95c1e460504256323 100644 (file)
@@ -36,7 +36,7 @@ export function mockAlmSettingsInstance(
   };
 }
 
-export function mockAzureDefinition(
+export function mockAzureBindingDefinition(
   overrides: Partial<AzureBindingDefinition> = {}
 ): AzureBindingDefinition {
   return {
@@ -46,7 +46,7 @@ export function mockAzureDefinition(
   };
 }
 
-export function mockBitbucketDefinition(
+export function mockBitbucketBindingDefinition(
   overrides: Partial<BitbucketBindingDefinition> = {}
 ): BitbucketBindingDefinition {
   return {
@@ -57,7 +57,7 @@ export function mockBitbucketDefinition(
   };
 }
 
-export function mockGithubDefinition(
+export function mockGithubBindingDefinition(
   overrides: Partial<GithubBindingDefinition> = {}
 ): GithubBindingDefinition {
   return {
@@ -69,7 +69,7 @@ export function mockGithubDefinition(
   };
 }
 
-export function mockGitlabDefinition(
+export function mockGitlabBindingDefinition(
   overrides: Partial<GitlabBindingDefinition> = {}
 ): GitlabBindingDefinition {
   return {
index a25148c6fb2f5ffe3d2a31e4018376590cc03d91..1fbff623de29b3dd7b6e389ddcd232e5b96c02f4 100644 (file)
@@ -48,35 +48,32 @@ export interface GitlabBindingDefinition extends AlmBindingDefinition {
   url?: string;
 }
 
-export interface ProjectAlmBinding {
+export interface ProjectAlmBindingResponse {
+  alm: AlmKeys;
   key: string;
   repository?: string;
   slug?: string;
   summaryCommentEnabled?: boolean;
 }
 
-export interface AzureProjectAlmBinding {
+export interface ProjectAlmBindingParams {
   almSetting: string;
   project: string;
 }
 
-export interface BitbucketProjectAlmBinding {
-  almSetting: string;
-  project: string;
+export interface AzureProjectAlmBindingParams extends ProjectAlmBindingParams {}
+
+export interface BitbucketProjectAlmBindingParams extends ProjectAlmBindingParams {
   repository: string;
   slug: string;
 }
 
-export interface GithubProjectAlmBinding {
-  almSetting: string;
-  project: string;
+export interface GithubProjectAlmBindingParams extends ProjectAlmBindingParams {
   repository: string;
   summaryCommentEnabled: boolean;
 }
 
-export interface GitlabProjectAlmBinding {
-  almSetting: string;
-  project: string;
+export interface GitlabProjectAlmBindingParams extends ProjectAlmBindingParams {
   repository?: string;
 }
 
@@ -87,8 +84,8 @@ export interface AlmSettingsInstance {
 }
 
 export interface AlmSettingsBindingDefinitions {
-  azure: AzureBindingDefinition[];
-  bitbucket: BitbucketBindingDefinition[];
-  github: GithubBindingDefinition[];
-  gitlab: GitlabBindingDefinition[];
+  [AlmKeys.Azure]: AzureBindingDefinition[];
+  [AlmKeys.Bitbucket]: BitbucketBindingDefinition[];
+  [AlmKeys.GitHub]: GithubBindingDefinition[];
+  [AlmKeys.GitLab]: GitlabBindingDefinition[];
 }