import * as React from 'react';
import { WithRouterProps } from 'react-router';
import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../api/alm-settings';
-import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings';
+import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../types/alm-settings';
import { withRouter } from '../hoc/withRouter';
import TutorialSelectionRenderer from './TutorialSelectionRenderer';
import { TutorialModes } from './types';
]);
if (this.mounted) {
- // We only support Bitbucket, GitHub & Gitlab for now.
- if (
- projectBinding === undefined ||
- ![AlmKeys.Bitbucket, AlmKeys.GitHub, AlmKeys.GitLab].includes(projectBinding.alm)
- ) {
+ if (projectBinding === undefined) {
this.setState({ loading: false, forceManual: true });
} else {
let almBinding;
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings';
+import AzurePipelinesTutorial from './azure-pipelines/AzurePipelinesTutorial';
import GitLabCITutorial from './gitlabci/GitLabCITutorial';
import JenkinsTutorial from './jenkins/JenkinsTutorial';
import ManualTutorial from './manual/ManualTutorial';
</button>
)}
+ {projectBinding?.alm === AlmKeys.Azure && (
+ <button
+ className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines"
+ onClick={() => props.onSelectTutorial(TutorialModes.AzurePipelines)}
+ type="button">
+ <img
+ alt="" // Should be ignored by screen readers
+ height={80}
+ src={`${getBaseUrl()}/images/alm/azure.svg`}
+ />
+ <div className="medium big-spacer-top">
+ {translate('onboarding.tutorial.choose_method.azure_pipelines')}
+ </div>
+ </button>
+ )}
+
{jenkinsAvailable && (
<button
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins"
projectBinding={projectBinding}
/>
)}
+
+ {selectedTutorial === TutorialModes.AzurePipelines && projectBinding !== undefined && (
+ <AzurePipelinesTutorial
+ component={component}
+ currentUser={currentUser}
+ projectBinding={projectBinding}
+ />
+ )}
</>
);
}
expect(wrapper.state().forceManual).toBe(true);
});
-it('should not select anything if project is bound to Bitbucket', async () => {
+it('should not select anything if project is bound', async () => {
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Bitbucket });
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().forceManual).toBe(false);
});
-it('should select manual if project is bound to unsupported ALM', async () => {
- (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Azure });
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper.state().forceManual).toBe(true);
-});
-
it('should correctly find the global ALM binding definition', async () => {
const key = 'foo';
const almBinding = mockBitbucketBindingDefinition({ key });
import { click } from 'sonar-ui-common/helpers/testUtils';
import {
mockBitbucketBindingDefinition,
+ mockProjectAzureBindingResponse,
mockProjectBitbucketBindingResponse,
mockProjectGitLabBindingResponse
} from '../../../helpers/mocks/alm-settings';
projectBinding: mockProjectGitLabBindingResponse()
})
).toMatchSnapshot('gitlab tutorial');
+ expect(
+ shallowRender({
+ selectedTutorial: TutorialModes.AzurePipelines,
+ projectBinding: mockProjectAzureBindingResponse()
+ })
+ ).toMatchSnapshot('azure pipelines tutorial');
});
it('should allow mode selection', () => {
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitLabCI);
});
+it('should allow azure pipelines selection', () => {
+ const onSelectTutorial = jest.fn();
+ const wrapper = shallowRender({
+ onSelectTutorial,
+ projectBinding: mockProjectAzureBindingResponse()
+ });
+
+ click(wrapper.find('button.azure-pipelines'));
+ expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.AzurePipelines);
+});
+
function shallowRender(props: Partial<TutorialSelectionRendererProps> = {}) {
return shallow<TutorialSelectionRendererProps>(
<TutorialSelectionRenderer
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should render correctly: azure pipelines tutorial 1`] = `
+<Fragment>
+ <AzurePipelinesTutorial
+ 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 [],
+ }
+ }
+ currentUser={
+ Object {
+ "groups": Array [],
+ "isLoggedIn": true,
+ "login": "luke",
+ "name": "Skywalker",
+ "scmAccounts": Array [],
+ }
+ }
+ projectBinding={
+ Object {
+ "alm": "azure",
+ "key": "foo",
+ "repository": "REPOSITORY_NAME",
+ "slug": "PROJECT_NAME",
+ "url": "https://ado.my_company.com/mycollection",
+ }
+ }
+ />
+</Fragment>
+`;
+
exports[`should render correctly: gitlab tutorial 1`] = `
<Fragment>
<GitLabCITutorial
--- /dev/null
+/*
+ * 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 { Button } from 'sonar-ui-common/components/controls/buttons';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import {
+ isProjectAzureBindingResponse,
+ ProjectAlmBindingResponse
+} from '../../../types/alm-settings';
+import Step from '../components/Step';
+import BranchAnalysisStepContent from './BranchAnalysisStepContent';
+import ExtensionInstallationStepContent from './ExtensionInstallationStepContent';
+import SaveAndRunStepContent from './SaveAndRunStepContent';
+import ServiceEndpointStepContent from './ServiceEndpointStepContent';
+
+export interface AzurePipelinesTutorialProps {
+ component: T.Component;
+ currentUser: T.LoggedInUser;
+ projectBinding: ProjectAlmBindingResponse;
+}
+
+export enum Steps {
+ ExtensionInstallation,
+ ServiceEndpoint,
+ BranchAnalysis,
+ SaveAndRun
+}
+
+interface Step {
+ step: Steps;
+ content: JSX.Element;
+ checkValidity?: boolean;
+}
+
+export default function AzurePipelinesTutorial(props: AzurePipelinesTutorialProps) {
+ const { component, currentUser, projectBinding } = props;
+
+ const [currentStep, setCurrentStep] = React.useState(Steps.ExtensionInstallation);
+ const [isCurrentStepValid, setIsCurrentStepValid] = React.useState(false);
+
+ // Failsafe; should never happen.
+ if (!isProjectAzureBindingResponse(projectBinding)) {
+ return (
+ <Alert variant="error">
+ {translate('onboarding.tutorial.with.azure_pipelines.unsupported')}
+ </Alert>
+ );
+ }
+
+ const steps: Array<Step> = [
+ { step: Steps.ExtensionInstallation, content: <ExtensionInstallationStepContent /> },
+ {
+ step: Steps.ServiceEndpoint,
+ content: <ServiceEndpointStepContent component={component} currentUser={currentUser} />
+ },
+ {
+ step: Steps.BranchAnalysis,
+ content: (
+ <BranchAnalysisStepContent
+ component={component}
+ onStepValidationChange={isValid => setIsCurrentStepValid(isValid)}
+ />
+ ),
+ checkValidity: true
+ },
+ { step: Steps.SaveAndRun, content: <SaveAndRunStepContent /> }
+ ];
+
+ const switchCurrentStep = (step: Steps) => {
+ setCurrentStep(step);
+ setIsCurrentStepValid(false);
+ };
+
+ const canContinue = (step: Step, i: number) =>
+ i < steps.length - 1 && (!step.checkValidity || isCurrentStepValid);
+
+ return (
+ <>
+ <div className="page-header big-spacer-bottom">
+ <h1 className="page-title">
+ {translate('onboarding.tutorial.with.azure_pipelines.title')}
+ </h1>
+ </div>
+
+ {steps.map((step, i) => (
+ <Step
+ key={step.step}
+ stepNumber={i + 1}
+ stepTitle={translate(
+ `onboarding.tutorial.with.azure_pipelines.${Steps[step.step]}.title`
+ )}
+ open={step.step === currentStep}
+ finished={step.step < currentStep}
+ onOpen={() => switchCurrentStep(step.step)}
+ renderForm={() => (
+ <div className="boxed-group-inner">
+ <div>{step.content}</div>
+ {canContinue(step, i) && (
+ <Button
+ className="big-spacer-top spacer-bottom"
+ onClick={() => switchCurrentStep(step.step + 1)}>
+ {translate('continue')}
+ </Button>
+ )}
+ </div>
+ )}
+ />
+ ))}
+ </>
+ );
+}
--- /dev/null
+/*
+ * 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 { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
+import { Alert } from 'sonar-ui-common/components/ui/Alert';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import CodeSnippet from '../../common/CodeSnippet';
+import RenderOptions from '../components/RenderOptions';
+import SentenceWithHighlights from '../components/SentenceWithHighlights';
+
+export interface BranchesAnalysisStepProps {
+ component: T.Component;
+ onStepValidationChange: (isValid: boolean) => void;
+}
+
+export enum BuildTechnology {
+ DotNet = 'dotnet',
+ Maven = 'maven',
+ Gradle = 'gradle',
+ Other = 'other'
+}
+
+export default function BranchAnalysisStepContent(props: BranchesAnalysisStepProps) {
+ const { component, onStepValidationChange } = props;
+
+ const [buildTechnology, setBuildTechnology] = React.useState<BuildTechnology | undefined>();
+
+ React.useEffect(() => {
+ if (buildTechnology) {
+ onStepValidationChange(true);
+ }
+ }, [buildTechnology, onStepValidationChange]);
+
+ const MAVEN_GRADLE_PROPS_SNIPPET = `# Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=${component.key}`;
+
+ return (
+ <>
+ <span>{translate('onboarding.build')}</span>
+ <RenderOptions
+ checked={buildTechnology}
+ name="buildTechnology"
+ onCheck={value => setBuildTechnology(value as BuildTechnology)}
+ optionLabelKey="onboarding.build"
+ options={Object.values(BuildTechnology)}
+ />
+ <ol className="list-styled big-spacer-top">
+ {buildTechnology && (
+ <>
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+ highlightKeys={['pipeline', 'task', 'before']}
+ />
+ </li>
+ <ul className="list-styled">
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
+ highlightKeys={['endpoint']}
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ defaultMessage={translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis'
+ )}
+ values={{
+ section: (
+ <strong>
+ {translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section'
+ )}
+ </strong>
+ ),
+ run_analysis_value: (
+ <strong>
+ {translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values',
+ buildTechnology
+ )}
+ </strong>
+ )
+ }}
+ />
+ </li>
+ {buildTechnology === BuildTechnology.Other && (
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual"
+ highlightKeys={['mode']}
+ />
+ </li>
+ )}
+ {[BuildTechnology.DotNet, BuildTechnology.Other].includes(buildTechnology) && (
+ <li>
+ <FormattedMessage
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
+ defaultMessage={translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence'
+ )}
+ values={{
+ key: <code className="rule">{component.key}</code>,
+ button: <ClipboardIconButton copyValue={component.key} />
+ }}
+ />
+ </li>
+ )}
+ {[BuildTechnology.Maven, BuildTechnology.Gradle].includes(buildTechnology) && (
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
+ highlightKeys={['section', 'properties']}
+ />
+ :
+ <CodeSnippet snippet={MAVEN_GRADLE_PROPS_SNIPPET} />
+ </li>
+ )}
+ </ul>
+ {[BuildTechnology.DotNet, BuildTechnology.Other].includes(buildTechnology) && (
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
+ highlightKeys={['task', 'after']}
+ />
+ </li>
+ )}
+ {[BuildTechnology.Maven, BuildTechnology.Gradle].includes(buildTechnology) && (
+ <>
+ <li>
+ {translateWithParameters(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java',
+ translate('onboarding.build', buildTechnology)
+ )}
+ </li>
+ <ul className="list-styled big-spacer-bottom">
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
+ highlightKeys={['section', 'option']}
+ />
+ </li>
+ </ul>
+ </>
+ )}
+
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
+ highlightKeys={['task']}
+ />
+ <Alert variant="info" className="spacer-top">
+ {translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1'
+ )}
+ </Alert>
+ </li>
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
+ highlightKeys={['tab', 'continuous_integration']}
+ />
+ </li>
+ <hr />
+ <FormattedMessage
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ defaultMessage={translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection'
+ )}
+ values={{
+ link: (
+ <Link to="/documentation/analysis/azuredevops-integration/" target="_blank">
+ {translate(
+ 'onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link'
+ )}
+ </Link>
+ )
+ }}
+ />
+ </>
+ )}
+ </ol>
+ </>
+ );
+}
--- /dev/null
+/*
+ * 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';
+
+export default function ExtensionInstallationStepContent() {
+ return (
+ <span>
+ <FormattedMessage
+ defaultMessage={translate(
+ 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence'
+ )}
+ id="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
+ values={{
+ link: (
+ <a
+ href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube"
+ rel="noopener noreferrer"
+ target="_blank">
+ {translate(
+ 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link'
+ )}
+ </a>
+ ),
+ button: (
+ <strong>
+ {translate(
+ 'onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button'
+ )}
+ </strong>
+ )
+ }}
+ />
+ </span>
+ );
+}
--- /dev/null
+/*
+ * 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 { getBaseUrl } from '../../../helpers/system';
+
+export default function SaveAndRunStepContent() {
+ return (
+ <>
+ <div className="display-flex-row big-spacer-bottom">
+ <div>
+ <img
+ alt="" // Should be ignored by screen readers
+ className="big-spacer-right"
+ width={30}
+ src={`${getBaseUrl()}/images/tutorials/commit.svg`}
+ />
+ </div>
+ <div>
+ <p className="little-spacer-bottom">
+ <strong>
+ {translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit')}
+ </strong>
+ </p>
+ <p>{translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit.why')}</p>
+ </div>
+ </div>
+ <div className="display-flex-row">
+ <div>
+ <img
+ alt="" // Should be ignored by screen readers
+ className="big-spacer-right"
+ width={30}
+ src={`${getBaseUrl()}/images/tutorials/refresh.svg`}
+ />
+ </div>
+ <div>
+ <p className="little-spacer-bottom">
+ <strong>
+ {translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh')}
+ </strong>
+ </p>
+ <p>{translate('onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh.why')}</p>
+ </div>
+ </div>
+ </>
+ );
+}
--- /dev/null
+/*
+ * 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 { Button } from 'sonar-ui-common/components/controls/buttons';
+import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { getHostUrl } from 'sonar-ui-common/helpers/urls';
+import EditTokenModal from '../components/EditTokenModal';
+import SentenceWithHighlights from '../components/SentenceWithHighlights';
+
+export interface ServiceEndpointStepProps {
+ component: T.Component;
+ currentUser: T.LoggedInUser;
+}
+
+export default function ServiceEndpointStepContent(props: ServiceEndpointStepProps) {
+ const { component, currentUser } = props;
+
+ const [isModalVisible, toggleModal] = React.useState(false);
+
+ return (
+ <>
+ <ol className="list-styled">
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1"
+ highlightKeys={['menu']}
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2"
+ highlightKeys={['type']}
+ />
+ </li>
+ <li>
+ {translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence')}
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage={translate(
+ 'onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence'
+ )}
+ id="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence"
+ values={{
+ url: <code className="rule">{getHostUrl()}</code>,
+ button: <ClipboardIconButton copyValue={getHostUrl()} />
+ }}
+ />
+ </li>
+ <li>
+ <span>
+ {translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence')}:
+ </span>
+ <Button className="spacer-left" onClick={() => toggleModal(true)}>
+ {translate('onboarding.token.generate_token')}
+ </Button>
+ </li>
+ <li>
+ {translate('onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence')}
+ </li>
+ </ol>
+
+ {isModalVisible && (
+ <EditTokenModal
+ component={component}
+ currentUser={currentUser}
+ onClose={() => toggleModal(false)}
+ />
+ )}
+ </>
+ );
+}
--- /dev/null
+/*
+ * 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 { Button } from 'sonar-ui-common/components/controls/buttons';
+import { click } from 'sonar-ui-common/helpers/testUtils';
+import {
+ mockProjectAzureBindingResponse,
+ mockProjectGithubBindingResponse
+} from '../../../../helpers/mocks/alm-settings';
+import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
+import Step from '../../components/Step';
+import AzurePipelinesTutorial, { AzurePipelinesTutorialProps } from '../AzurePipelinesTutorial';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+ expect(
+ wrapper
+ .find(Step)
+ .first()
+ .dive()
+ ).toMatchSnapshot('first-step-wrapper');
+ expect(
+ wrapper
+ .find(Step)
+ .last()
+ .dive()
+ ).toMatchSnapshot('last-step-wrapper');
+ expect(shallowRender({ projectBinding: mockProjectGithubBindingResponse() })).toMatchSnapshot(
+ 'wrong alm'
+ );
+});
+
+it('should display the next step when one is finished', () => {
+ const wrapper = shallowRender();
+ expect(
+ wrapper
+ .find(Step)
+ .filterWhere(elt => elt.props().open === true)
+ .props().stepNumber
+ ).toBe(1);
+
+ click(
+ wrapper
+ .find(Step)
+ .first()
+ .dive()
+ .find(Button)
+ );
+
+ expect(
+ wrapper
+ .find(Step)
+ .filterWhere(elt => elt.props().open === true)
+ .props().stepNumber
+ ).toBe(2);
+});
+
+it('should open a step when user click on it', () => {
+ const wrapper = shallowRender();
+ expect(
+ wrapper
+ .find(Step)
+ .filterWhere(elt => elt.props().open === true)
+ .props().stepNumber
+ ).toBe(1);
+
+ (
+ wrapper
+ .find(Step)
+ .filterWhere(elt => elt.props().stepNumber === 3)
+ .props().onOpen ?? (() => {})
+ )();
+
+ expect(
+ wrapper
+ .find(Step)
+ .filterWhere(elt => elt.props().open === true)
+ .props().stepNumber
+ ).toBe(3);
+});
+
+function shallowRender(props: Partial<AzurePipelinesTutorialProps> = {}) {
+ return shallow<AzurePipelinesTutorialProps>(
+ <AzurePipelinesTutorial
+ component={mockComponent()}
+ currentUser={mockLoggedInUser()}
+ projectBinding={mockProjectAzureBindingResponse()}
+ {...props}
+ />
+ );
+}
--- /dev/null
+/*
+ * 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 RenderOptions from '../../components/RenderOptions';
+import BranchAnalysisStepContent, {
+ BranchesAnalysisStepProps,
+ BuildTechnology
+} from '../BranchAnalysisStepContent';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+});
+
+it.each([
+ BuildTechnology.DotNet,
+ BuildTechnology.Gradle,
+ BuildTechnology.Maven,
+ BuildTechnology.Other
+])('should render correctly', (buildTech: BuildTechnology) => {
+ const wrapper = shallowRender();
+ wrapper
+ .find(RenderOptions)
+ .props()
+ .onCheck(buildTech);
+
+ expect(wrapper).toMatchSnapshot(buildTech);
+});
+
+function shallowRender(props: Partial<BranchesAnalysisStepProps> = {}) {
+ return shallow(
+ <BranchAnalysisStepContent
+ component={mockComponent()}
+ onStepValidationChange={jest.fn()}
+ {...props}
+ />
+ );
+}
--- /dev/null
+/*
+ * 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 ExtensionInstallationStepContent from '../ExtensionInstallationStepContent';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+});
+
+function shallowRender() {
+ return shallow(<ExtensionInstallationStepContent />);
+}
--- /dev/null
+/*
+ * 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 SaveAndRunStepContent from '../SaveAndRunStepContent';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+});
+
+function shallowRender() {
+ return shallow(<SaveAndRunStepContent />);
+}
--- /dev/null
+/*
+ * 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 { Button } from 'sonar-ui-common/components/controls/buttons';
+import { click } from 'sonar-ui-common/helpers/testUtils';
+import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks';
+import EditTokenModal from '../../components/EditTokenModal';
+import ServiceEndpointStepContent from '../ServiceEndpointStepContent';
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should render open/close the token modal when asked to', () => {
+ const wrapper = shallowRender();
+ expect(wrapper.exists(EditTokenModal)).toBe(false);
+
+ click(wrapper.find(Button));
+ expect(wrapper.exists(EditTokenModal)).toBe(true);
+
+ wrapper
+ .find(EditTokenModal)
+ .props()
+ .onClose();
+ expect(wrapper.exists(EditTokenModal)).toBe(false);
+});
+
+function shallowRender() {
+ return shallow(
+ <ServiceEndpointStepContent component={mockComponent()} currentUser={mockLoggedInUser()} />
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <div
+ className="page-header big-spacer-bottom"
+ >
+ <h1
+ className="page-title"
+ >
+ onboarding.tutorial.with.azure_pipelines.title
+ </h1>
+ </div>
+ <Step
+ finished={false}
+ key="0"
+ onOpen={[Function]}
+ open={true}
+ renderForm={[Function]}
+ stepNumber={1}
+ stepTitle="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title"
+ />
+ <Step
+ finished={false}
+ key="1"
+ onOpen={[Function]}
+ open={false}
+ renderForm={[Function]}
+ stepNumber={2}
+ stepTitle="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title"
+ />
+ <Step
+ finished={false}
+ key="2"
+ onOpen={[Function]}
+ open={false}
+ renderForm={[Function]}
+ stepNumber={3}
+ stepTitle="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title"
+ />
+ <Step
+ finished={false}
+ key="3"
+ onOpen={[Function]}
+ open={false}
+ renderForm={[Function]}
+ stepNumber={4}
+ stepTitle="onboarding.tutorial.with.azure_pipelines.SaveAndRun.title"
+ />
+</Fragment>
+`;
+
+exports[`should render correctly: first-step-wrapper 1`] = `
+<div
+ className="boxed-group onboarding-step is-open"
+>
+ <div
+ className="onboarding-step-number"
+ >
+ 1
+ </div>
+ <div
+ className="boxed-group-header"
+ >
+ <h2>
+ onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title
+ </h2>
+ </div>
+ <div
+ className=""
+ >
+ <div
+ className="boxed-group-inner"
+ >
+ <div>
+ <ExtensionInstallationStepContent />
+ </div>
+ <Button
+ className="big-spacer-top spacer-bottom"
+ onClick={[Function]}
+ >
+ continue
+ </Button>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: last-step-wrapper 1`] = `
+<div
+ className="boxed-group onboarding-step"
+>
+ <div
+ className="onboarding-step-number"
+ >
+ 4
+ </div>
+ <div
+ className="boxed-group-header"
+ >
+ <h2>
+ onboarding.tutorial.with.azure_pipelines.SaveAndRun.title
+ </h2>
+ </div>
+ <div
+ className="boxed-group-inner"
+ />
+ <div
+ className="hidden"
+ >
+ <div
+ className="boxed-group-inner"
+ >
+ <div>
+ <SaveAndRunStepContent />
+ </div>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`should render correctly: wrong alm 1`] = `
+<Alert
+ variant="error"
+>
+ onboarding.tutorial.with.azure_pipelines.unsupported
+</Alert>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <span>
+ onboarding.build
+ </span>
+ <RenderOptions
+ name="buildTechnology"
+ onCheck={[Function]}
+ optionLabelKey="onboarding.build"
+ options={
+ Array [
+ "dotnet",
+ "maven",
+ "gradle",
+ "other",
+ ]
+ }
+ />
+ <ol
+ className="list-styled big-spacer-top"
+ />
+</Fragment>
+`;
+
+exports[`should render correctly: dotnet 1`] = `
+<Fragment>
+ <span>
+ onboarding.build
+ </span>
+ <RenderOptions
+ checked="dotnet"
+ name="buildTechnology"
+ onCheck={[Function]}
+ optionLabelKey="onboarding.build"
+ options={
+ Array [
+ "dotnet",
+ "maven",
+ "gradle",
+ "other",
+ ]
+ }
+ />
+ <ol
+ className="list-styled big-spacer-top"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "pipeline",
+ "task",
+ "before",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+ />
+ </li>
+ <ul
+ className="list-styled"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "endpoint",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ values={
+ Object {
+ "run_analysis_value": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet
+ </strong>,
+ "section": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+ </strong>,
+ }
+ }
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
+ values={
+ Object {
+ "button": <ClipboardIconButton
+ copyValue="my-project"
+ />,
+ "key": <code
+ className="rule"
+ >
+ my-project
+ </code>,
+ }
+ }
+ />
+ </li>
+ </ul>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "task",
+ "after",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "task",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
+ />
+ <Alert
+ className="spacer-top"
+ variant="info"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+ </Alert>
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "tab",
+ "continuous_integration",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
+ />
+ </li>
+ <hr />
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/analysis/azuredevops-integration/"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+ </Link>,
+ }
+ }
+ />
+ </ol>
+</Fragment>
+`;
+
+exports[`should render correctly: gradle 1`] = `
+<Fragment>
+ <span>
+ onboarding.build
+ </span>
+ <RenderOptions
+ checked="gradle"
+ name="buildTechnology"
+ onCheck={[Function]}
+ optionLabelKey="onboarding.build"
+ options={
+ Array [
+ "dotnet",
+ "maven",
+ "gradle",
+ "other",
+ ]
+ }
+ />
+ <ol
+ className="list-styled big-spacer-top"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "pipeline",
+ "task",
+ "before",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+ />
+ </li>
+ <ul
+ className="list-styled"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "endpoint",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ values={
+ Object {
+ "run_analysis_value": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle
+ </strong>,
+ "section": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+ </strong>,
+ }
+ }
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "section",
+ "properties",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
+ />
+ :
+ <CodeSnippet
+ snippet="# Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project"
+ />
+ </li>
+ </ul>
+ <li>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.gradle
+ </li>
+ <ul
+ className="list-styled big-spacer-bottom"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "section",
+ "option",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
+ />
+ </li>
+ </ul>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "task",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
+ />
+ <Alert
+ className="spacer-top"
+ variant="info"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+ </Alert>
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "tab",
+ "continuous_integration",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
+ />
+ </li>
+ <hr />
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/analysis/azuredevops-integration/"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+ </Link>,
+ }
+ }
+ />
+ </ol>
+</Fragment>
+`;
+
+exports[`should render correctly: maven 1`] = `
+<Fragment>
+ <span>
+ onboarding.build
+ </span>
+ <RenderOptions
+ checked="maven"
+ name="buildTechnology"
+ onCheck={[Function]}
+ optionLabelKey="onboarding.build"
+ options={
+ Array [
+ "dotnet",
+ "maven",
+ "gradle",
+ "other",
+ ]
+ }
+ />
+ <ol
+ className="list-styled big-spacer-top"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "pipeline",
+ "task",
+ "before",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+ />
+ </li>
+ <ul
+ className="list-styled"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "endpoint",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ values={
+ Object {
+ "run_analysis_value": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.maven
+ </strong>,
+ "section": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+ </strong>,
+ }
+ }
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "section",
+ "properties",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties"
+ />
+ :
+ <CodeSnippet
+ snippet="# Additional properties that will be passed to the scanner,
+# Put one key=value per line, example:
+# sonar.exclusions=**/*.bin
+sonar.projectKey=my-project"
+ />
+ </li>
+ </ul>
+ <li>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.onboarding.build.maven
+ </li>
+ <ul
+ className="list-styled big-spacer-bottom"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "section",
+ "option",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings"
+ />
+ </li>
+ </ul>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "task",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
+ />
+ <Alert
+ className="spacer-top"
+ variant="info"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+ </Alert>
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "tab",
+ "continuous_integration",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
+ />
+ </li>
+ <hr />
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/analysis/azuredevops-integration/"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+ </Link>,
+ }
+ }
+ />
+ </ol>
+</Fragment>
+`;
+
+exports[`should render correctly: other 1`] = `
+<Fragment>
+ <span>
+ onboarding.build
+ </span>
+ <RenderOptions
+ checked="other"
+ name="buildTechnology"
+ onCheck={[Function]}
+ optionLabelKey="onboarding.build"
+ options={
+ Array [
+ "dotnet",
+ "maven",
+ "gradle",
+ "other",
+ ]
+ }
+ />
+ <ol
+ className="list-styled big-spacer-top"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "pipeline",
+ "task",
+ "before",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare"
+ />
+ </li>
+ <ul
+ className="list-styled"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "endpoint",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint"
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis"
+ values={
+ Object {
+ "run_analysis_value": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.other
+ </strong>,
+ "section": <strong>
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section
+ </strong>,
+ }
+ }
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "mode",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual"
+ />
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence"
+ values={
+ Object {
+ "button": <ClipboardIconButton
+ copyValue="my-project"
+ />,
+ "key": <code
+ className="rule"
+ >
+ my-project
+ </code>,
+ }
+ }
+ />
+ </li>
+ </ul>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "task",
+ "after",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run"
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "task",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg"
+ />
+ <Alert
+ className="spacer-top"
+ variant="info"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1
+ </Alert>
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "tab",
+ "continuous_integration",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration"
+ />
+ </li>
+ <hr />
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ id="onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/analysis/azuredevops-integration/"
+ >
+ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link
+ </Link>,
+ }
+ }
+ />
+ </ol>
+</Fragment>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<span>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
+ id="onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence"
+ values={
+ Object {
+ "button": <strong>
+ onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button
+ </strong>,
+ "link": <a
+ href="https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube"
+ rel="noopener noreferrer"
+ target="_blank"
+ >
+ onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link
+ </a>,
+ }
+ }
+ />
+</span>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <div
+ className="display-flex-row big-spacer-bottom"
+ >
+ <div>
+ <img
+ alt=""
+ className="big-spacer-right"
+ src="/images/tutorials/commit.svg"
+ width={30}
+ />
+ </div>
+ <div>
+ <p
+ className="little-spacer-bottom"
+ >
+ <strong>
+ onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit
+ </strong>
+ </p>
+ <p>
+ onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit.why
+ </p>
+ </div>
+ </div>
+ <div
+ className="display-flex-row"
+ >
+ <div>
+ <img
+ alt=""
+ className="big-spacer-right"
+ src="/images/tutorials/refresh.svg"
+ width={30}
+ />
+ </div>
+ <div>
+ <p
+ className="little-spacer-bottom"
+ >
+ <strong>
+ onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh
+ </strong>
+ </p>
+ <p>
+ onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh.why
+ </p>
+ </div>
+ </div>
+</Fragment>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <ol
+ className="list-styled"
+ >
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "menu",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1"
+ />
+ </li>
+ <li>
+ <SentenceWithHighlights
+ highlightKeys={
+ Array [
+ "type",
+ ]
+ }
+ translationKey="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2"
+ />
+ </li>
+ <li>
+ onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence
+ </li>
+ <li>
+ <FormattedMessage
+ defaultMessage="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence"
+ id="onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence"
+ values={
+ Object {
+ "button": <ClipboardIconButton
+ copyValue="http://localhost"
+ />,
+ "url": <code
+ className="rule"
+ >
+ http://localhost
+ </code>,
+ }
+ }
+ />
+ </li>
+ <li>
+ <span>
+ onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence
+ :
+ </span>
+ <Button
+ className="spacer-left"
+ onClick={[Function]}
+ >
+ onboarding.token.generate_token
+ </Button>
+ </li>
+ <li>
+ onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence
+ </li>
+ </ol>
+</Fragment>
+`;
import * as React from 'react';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { isProjectGitLabBindingResponse } from '../../../helpers/alm-settings';
-import { ProjectAlmBindingResponse } from '../../../types/alm-settings';
+import {
+ isProjectGitLabBindingResponse,
+ ProjectAlmBindingResponse
+} from '../../../types/alm-settings';
import EnvironmentVariablesStep from './EnvironmentVariablesStep';
import ProjectKeyStep from './ProjectKeyStep';
import { BuildTools } from './types';
import { connect } from 'react-redux';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import {
- isProjectBitbucketBindingResponse,
- isProjectGitHubBindingResponse
-} from '../../../helpers/alm-settings';
import { getCurrentUserSetting, Store } from '../../../store/rootReducer';
import { setCurrentUserSetting } from '../../../store/users';
-import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings';
+import {
+ AlmBindingDefinition,
+ isProjectBitbucketBindingResponse,
+ isProjectGitHubBindingResponse,
+ ProjectAlmBindingResponse
+} from '../../../types/alm-settings';
import JenkinsfileStep from './JenkinsfileStep';
import MultiBranchPipelineStep from './MultiBranchPipelineStep';
import PreRequisitesStep from './PreRequisitesStep';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
+ AlmBindingDefinition,
isGithubBindingDefinition,
isProjectBitbucketBindingResponse,
- isProjectGitHubBindingResponse
-} from '../../../helpers/alm-settings';
-import {
- AlmBindingDefinition,
+ isProjectGitHubBindingResponse,
ProjectBitbucketBindingResponse,
ProjectGitHubBindingResponse
} from '../../../types/alm-settings';
import { FormattedMessage } from 'react-intl';
import { Button, ButtonLink } from 'sonar-ui-common/components/controls/buttons';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import {
- isBitbucketBindingDefinition,
- isGithubBindingDefinition
-} from '../../../helpers/alm-settings';
import {
AlmBindingDefinition,
AlmKeys,
+ isBitbucketBindingDefinition,
+ isGithubBindingDefinition,
ProjectAlmBindingResponse
} from '../../../types/alm-settings';
import Step from '../components/Step';
export enum TutorialModes {
Manual = 'manual',
Jenkins = 'jenkins',
- GitLabCI = 'gitlab-ci'
+ GitLabCI = 'gitlab-ci',
+ AzurePipelines = 'azure-pipelines'
}
export enum BuildTools {
+++ /dev/null
-/*
- * 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 { AlmKeys } from '../../types/alm-settings';
-import {
- isBitbucketBindingDefinition,
- isGithubBindingDefinition,
- isProjectBitbucketBindingResponse,
- isProjectGitHubBindingResponse
-} from '../alm-settings';
-import {
- mockBitbucketBindingDefinition,
- mockGithubBindingDefinition,
- mockProjectAlmBindingResponse,
- mockProjectBitbucketBindingResponse,
- mockProjectGithubBindingResponse
-} from '../mocks/alm-settings';
-
-/* eslint-disable sonarjs/no-duplicate-string */
-
-describe('isProjectBitbucketBindingResponse', () => {
- it('works as expected', () => {
- expect(isProjectBitbucketBindingResponse(mockProjectAlmBindingResponse())).toBe(false);
- expect(isProjectBitbucketBindingResponse(mockProjectBitbucketBindingResponse())).toBe(true);
- });
-});
-
-describe('isBitbucketBindingDefinition', () => {
- it('works as expected', () => {
- expect(isBitbucketBindingDefinition(undefined)).toBe(false);
- expect(isBitbucketBindingDefinition(mockGithubBindingDefinition())).toBe(false);
- expect(isBitbucketBindingDefinition(mockBitbucketBindingDefinition())).toBe(true);
- });
-});
-
-describe('isProjectGithubBindingResponse', () => {
- it('works as expected', () => {
- expect(
- isProjectGitHubBindingResponse(mockProjectAlmBindingResponse({ alm: AlmKeys.Azure }))
- ).toBe(false);
- expect(isProjectGitHubBindingResponse(mockProjectGithubBindingResponse())).toBe(true);
- });
-});
-
-describe('isGithubBindingDefinition', () => {
- it('works as expected', () => {
- expect(isGithubBindingDefinition(undefined)).toBe(false);
- expect(isGithubBindingDefinition(mockBitbucketBindingDefinition())).toBe(false);
- expect(isGithubBindingDefinition(mockGithubBindingDefinition())).toBe(true);
- });
-});
+++ /dev/null
-/*
- * 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 {
- AlmBindingDefinition,
- AlmKeys,
- BitbucketBindingDefinition,
- GithubBindingDefinition,
- ProjectAlmBindingResponse,
- ProjectBitbucketBindingResponse,
- ProjectGitHubBindingResponse,
- ProjectGitLabBindingResponse
-} from '../types/alm-settings';
-
-export function isProjectBitbucketBindingResponse(
- binding: ProjectAlmBindingResponse
-): binding is ProjectBitbucketBindingResponse {
- return binding.alm === AlmKeys.Bitbucket;
-}
-
-export function isProjectGitHubBindingResponse(
- binding: ProjectAlmBindingResponse
-): binding is ProjectGitHubBindingResponse {
- return binding.alm === AlmKeys.GitHub;
-}
-
-export function isProjectGitLabBindingResponse(
- binding: ProjectAlmBindingResponse
-): binding is ProjectGitLabBindingResponse {
- return binding.alm === AlmKeys.GitLab;
-}
-
-export function isBitbucketBindingDefinition(
- binding?: AlmBindingDefinition & { url?: string; personalAccessToken?: string }
-): binding is BitbucketBindingDefinition {
- return (
- binding !== undefined && binding.url !== undefined && binding.personalAccessToken !== undefined
- );
-}
-
-export function isGithubBindingDefinition(
- binding?: AlmBindingDefinition & { appId?: string; privateKey?: string; url?: string }
-): binding is GithubBindingDefinition {
- return (
- binding !== undefined &&
- binding.appId !== undefined &&
- binding.privateKey !== undefined &&
- binding.url !== undefined
- );
-}
GithubBindingDefinition,
GitlabBindingDefinition,
ProjectAlmBindingResponse,
+ ProjectAzureBindingResponse,
ProjectBitbucketBindingResponse,
ProjectGitHubBindingResponse,
ProjectGitLabBindingResponse
};
}
+export function mockProjectAzureBindingResponse(
+ overrides: Partial<ProjectAzureBindingResponse> = {}
+): ProjectAzureBindingResponse {
+ return {
+ alm: AlmKeys.Azure,
+ key: 'foo',
+ slug: 'PROJECT_NAME',
+ repository: 'REPOSITORY_NAME',
+ url: 'https://ado.my_company.com/mycollection',
+ ...overrides
+ };
+}
+
export function mockAlmSettingsBindingStatus(
overrides: Partial<AlmSettingsBindingStatus>
): AlmSettingsBindingStatus {
summaryCommentEnabled?: boolean;
}
+export interface ProjectAzureBindingResponse extends ProjectAlmBindingResponse {
+ alm: AlmKeys.Azure;
+ repository: string;
+ slug: string;
+ url: string;
+}
+
export interface ProjectBitbucketBindingResponse extends ProjectAlmBindingResponse {
alm: AlmKeys.Bitbucket;
repository: string;
Failure,
Warning
}
+
+export function isProjectBitbucketBindingResponse(
+ binding: ProjectAlmBindingResponse
+): binding is ProjectBitbucketBindingResponse {
+ return binding.alm === AlmKeys.Bitbucket;
+}
+
+export function isProjectGitHubBindingResponse(
+ binding: ProjectAlmBindingResponse
+): binding is ProjectGitHubBindingResponse {
+ return binding.alm === AlmKeys.GitHub;
+}
+
+export function isProjectGitLabBindingResponse(
+ binding: ProjectAlmBindingResponse
+): binding is ProjectGitLabBindingResponse {
+ return binding.alm === AlmKeys.GitLab;
+}
+
+export function isProjectAzureBindingResponse(
+ binding: ProjectAlmBindingResponse
+): binding is ProjectAzureBindingResponse {
+ return binding.alm === AlmKeys.Azure;
+}
+
+export function isBitbucketBindingDefinition(
+ binding?: AlmBindingDefinition & { url?: string; personalAccessToken?: string }
+): binding is BitbucketBindingDefinition {
+ return (
+ binding !== undefined && binding.url !== undefined && binding.personalAccessToken !== undefined
+ );
+}
+
+export function isGithubBindingDefinition(
+ binding?: AlmBindingDefinition & { appId?: string; privateKey?: string; url?: string }
+): binding is GithubBindingDefinition {
+ return (
+ binding !== undefined &&
+ binding.appId !== undefined &&
+ binding.privateKey !== undefined &&
+ binding.url !== undefined
+ );
+}
onboarding.tutorial.choose_method.manual=Manually
onboarding.tutorial.choose_method.jenkins=With Jenkins
onboarding.tutorial.choose_method.gitlab_ci=With GitLab CI
+onboarding.tutorial.choose_method.azure_pipelines=With Azure Pipelines
onboarding.tutorial.with.gitlab_ci.title=Analyze your project with GitLab CI
onboarding.tutorial.with.gitlab_ci.unsupported=This tutorial is only available for projects bound to GitLab.
onboarding.tutorial.with.jenkins.refresh=This page will then refresh with your analysis results.
onboarding.tutorial.with.jenkins.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration.
+onboarding.tutorial.with.azure_pipelines.title=Analyze your project with Azure DevOps Pipelines
+onboarding.tutorial.with.azure_pipelines.unsupported=This tutorial is only available for projects bound to Azure DevOps Server.
+onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title=Install SonarQube extension for Azure DevOps Server
+onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence=From your Azure DevOps Server instance, navigate to the Visual Studio Marketplace and install the {link} by clicking the {button} button.
+onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link=SonarQube extension
+onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button=Get it free
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title=Add a new SonarQube Service Endpoint
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence=In Azure DevOps Server, go to {menu}
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence.menu=Project settings > Service connections
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence=Add a new service connection of type {type}
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence.type=SonarQube
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence=Enter a memorable connection name
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence=Enter your SonarQube server url: {url} {button}
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence=Enter an existing token, or a newly generated one
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence=Create the service connection
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.title=Configure Branch analysis
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence=In Azure DevOps Server, create or edit a {pipeline} and add a new {task} task {before} your build task
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipeline=Build Pipeline
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task=Prepare Analysis Configuration
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before=before
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence=Select the {endpoint} you created in Step 2
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint=SonarQube server endpoint
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis=Under {section}, select {run_analysis_value}
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section=Choose the way to run the analysis
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet=Integrate with MSBuild
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.maven=Integrate with Maven or Gradle
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.gradle=Integrate with Maven or Gradle
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.other=Use standalone scanner
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence=Select the {mode} mode
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.manual.sentence.mode=Manually provide configuration
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence=Add a new {task} task {after} your build task
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.task=Run Code Analysis
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.sentence.after=after
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.run.key.sentence=In the project key field, enter {key} {button}
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence=Expand the {section} and replace the {properties} with the following snippet
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.section=Advanced section
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.advanced_properties.sentence.properties=Additional Properties
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java=Edit or add a new {0} task
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence=Under {section}, check {option}
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.section=Code Analysis
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option=Run SonarQube or SonarCloud Analysis
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence=Add a new {task} task to publish SonarQube's Quality Gate results on your build pipeline summary
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1=This task may increase your build time as your pipeline will have to wait for SonarQube to process the analysis report. It is highly recommended but optional.
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task=Publish Quality Gate Result
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence=Under the {tab} tab of your pipeline, check {continuous_integration} and select all the branches for which you want the SonarQube analysis to run automatically
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab=Triggers
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration=Enable continuous integration
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection=To make sure your Pull Requests are analyzed automatically and aren't merged when they're failing their quality gate, check out the {link}.
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.branch_protection.link=documentation
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.title=Save and run your pipeline
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit=Save your pipeline and push your code to start the analysis
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.commit.why=Each new push you make on your branches will trigger a new analysis in SonarQube
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh=This page will then refresh with your analysis results
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.refresh.why=If this page doesn't refresh after a while, please double-check your pipeline configuration
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.then={what} Checkout our {link} to make sure SonarQube analyze and decorate all of your Pull Requests
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.then.what=Then what?
+onboarding.tutorial.with.azure_pipelines.SaveAndRun.then.link=Azure integration documentation
#------------------------------------------------------------------------------
#
# BRANCHES