diff options
author | Philippe Perrin <philippe.perrin@sonarsource.com> | 2020-11-18 13:23:52 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-12-02 20:06:58 +0000 |
commit | 9dea56b68988cf5c77a51a9fb6a3b076c546cb93 (patch) | |
tree | 0596673d8f33f4c1065de030679eb9685518b52a /server/sonar-web | |
parent | 346d2c41b28c15d99cc092ac792f36b6d3ffd9da (diff) | |
download | sonarqube-9dea56b68988cf5c77a51a9fb6a3b076c546cb93.tar.gz sonarqube-9dea56b68988cf5c77a51a9fb6a3b076c546cb93.zip |
SONAR-14079 Add Azure Pipelines tutorial for Azure DevOps Server
Diffstat (limited to 'server/sonar-web')
29 files changed, 1895 insertions, 163 deletions
diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx index a077d399d1a..24095a0d008 100644 --- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx @@ -20,7 +20,7 @@ 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'; @@ -58,11 +58,7 @@ export class TutorialSelection extends React.PureComponent<Props, State> { ]); 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; diff --git a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx index 1fc339d8dcf..1cf814c2646 100644 --- a/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx @@ -22,6 +22,7 @@ import * as React from 'react'; 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'; @@ -74,6 +75,22 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender </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" @@ -126,6 +143,14 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender projectBinding={projectBinding} /> )} + + {selectedTutorial === TutorialModes.AzurePipelines && projectBinding !== undefined && ( + <AzurePipelinesTutorial + component={component} + currentUser={currentUser} + projectBinding={projectBinding} + /> + )} </> ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx index 06ba0070978..9585564dcc5 100644 --- a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx @@ -50,20 +50,13 @@ it('should select manual if project is not bound', async () => { 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 }); diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx index bd45cbac9c1..9231d66da02 100644 --- a/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx @@ -23,6 +23,7 @@ import * as React from 'react'; import { click } from 'sonar-ui-common/helpers/testUtils'; import { mockBitbucketBindingDefinition, + mockProjectAzureBindingResponse, mockProjectBitbucketBindingResponse, mockProjectGitLabBindingResponse } from '../../../helpers/mocks/alm-settings'; @@ -53,6 +54,12 @@ it('should render correctly', () => { projectBinding: mockProjectGitLabBindingResponse() }) ).toMatchSnapshot('gitlab tutorial'); + expect( + shallowRender({ + selectedTutorial: TutorialModes.AzurePipelines, + projectBinding: mockProjectAzureBindingResponse() + }) + ).toMatchSnapshot('azure pipelines tutorial'); }); it('should allow mode selection', () => { @@ -80,6 +87,17 @@ it('should allow gitlab 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 diff --git a/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap index ef26e810291..10796cbddc1 100644 --- a/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap @@ -1,5 +1,53 @@ // 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 diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx new file mode 100644 index 00000000000..8cde6833184 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/AzurePipelinesTutorial.tsx @@ -0,0 +1,130 @@ +/* + * 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> + )} + /> + ))} + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx new file mode 100644 index 00000000000..f091e226786 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/BranchAnalysisStepContent.tsx @@ -0,0 +1,208 @@ +/* + * 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> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ExtensionInstallationStepContent.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ExtensionInstallationStepContent.tsx new file mode 100644 index 00000000000..4e00f962e9c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ExtensionInstallationStepContent.tsx @@ -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 { 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> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/SaveAndRunStepContent.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/SaveAndRunStepContent.tsx new file mode 100644 index 00000000000..ed70ae4ccc8 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/SaveAndRunStepContent.tsx @@ -0,0 +1,66 @@ +/* + * 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> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx new file mode 100644 index 00000000000..69a907af0c4 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/ServiceEndpointStepContent.tsx @@ -0,0 +1,92 @@ +/* + * 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)} + /> + )} + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx new file mode 100644 index 00000000000..d7ad84a21c2 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/AzurePipelinesTutorial-test.tsx @@ -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 { 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} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx new file mode 100644 index 00000000000..f6f45aed90b --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/BranchAnalysisStepContent-test.tsx @@ -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. + */ + +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} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx new file mode 100644 index 00000000000..da361004576 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ExtensionInstallationStepContent-test.tsx @@ -0,0 +1,32 @@ +/* + * 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 />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/SaveAndRunStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/SaveAndRunStepContent-test.tsx new file mode 100644 index 00000000000..0e0af022fe5 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/SaveAndRunStepContent-test.tsx @@ -0,0 +1,32 @@ +/* + * 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 />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx new file mode 100644 index 00000000000..683e9321561 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/ServiceEndpointStepContent-test.tsx @@ -0,0 +1,52 @@ +/* + * 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()} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap new file mode 100644 index 00000000000..84e32877db7 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/AzurePipelinesTutorial-test.tsx.snap @@ -0,0 +1,128 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap new file mode 100644 index 00000000000..07602310f0f --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/BranchAnalysisStepContent-test.tsx.snap @@ -0,0 +1,613 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap new file mode 100644 index 00000000000..9a8c93a3e5f --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ExtensionInstallationStepContent-test.tsx.snap @@ -0,0 +1,24 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/SaveAndRunStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/SaveAndRunStepContent-test.tsx.snap new file mode 100644 index 00000000000..9f4dd3417bd --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/SaveAndRunStepContent-test.tsx.snap @@ -0,0 +1,54 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap new file mode 100644 index 00000000000..1596e44fa1c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/azure-pipelines/__tests__/__snapshots__/ServiceEndpointStepContent-test.tsx.snap @@ -0,0 +1,66 @@ +// 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> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx index 8d4dee3d6bc..d66ac7b3771 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/GitLabCITutorial.tsx @@ -20,8 +20,10 @@ 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx index a516260891f..cdc00fbf235 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx @@ -21,13 +21,14 @@ import * as React from 'react'; 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx index d0feb3133bc..26279e2c0c7 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx @@ -21,12 +21,10 @@ import * as React from 'react'; 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx index 60dfc618314..4243c27cc2e 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/WebhookStep.tsx @@ -22,12 +22,10 @@ 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'; diff --git a/server/sonar-web/src/main/js/components/tutorials/types.ts b/server/sonar-web/src/main/js/components/tutorials/types.ts index 80e22751eda..46edfc4f552 100644 --- a/server/sonar-web/src/main/js/components/tutorials/types.ts +++ b/server/sonar-web/src/main/js/components/tutorials/types.ts @@ -20,7 +20,8 @@ export enum TutorialModes { Manual = 'manual', Jenkins = 'jenkins', - GitLabCI = 'gitlab-ci' + GitLabCI = 'gitlab-ci', + AzurePipelines = 'azure-pipelines' } export enum BuildTools { diff --git a/server/sonar-web/src/main/js/helpers/__tests__/alm-settings-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/alm-settings-test.ts deleted file mode 100644 index 2f8c1c4a189..00000000000 --- a/server/sonar-web/src/main/js/helpers/__tests__/alm-settings-test.ts +++ /dev/null @@ -1,67 +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 { 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); - }); -}); diff --git a/server/sonar-web/src/main/js/helpers/alm-settings.ts b/server/sonar-web/src/main/js/helpers/alm-settings.ts deleted file mode 100644 index bd0013775fc..00000000000 --- a/server/sonar-web/src/main/js/helpers/alm-settings.ts +++ /dev/null @@ -1,66 +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 { - 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 - ); -} diff --git a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts index b4a67f72610..b8de0c0aa21 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts @@ -27,6 +27,7 @@ import { GithubBindingDefinition, GitlabBindingDefinition, ProjectAlmBindingResponse, + ProjectAzureBindingResponse, ProjectBitbucketBindingResponse, ProjectGitHubBindingResponse, ProjectGitLabBindingResponse @@ -132,6 +133,19 @@ export function mockProjectGitLabBindingResponse( }; } +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 { diff --git a/server/sonar-web/src/main/js/types/alm-settings.ts b/server/sonar-web/src/main/js/types/alm-settings.ts index c27018afb25..c70a371b878 100644 --- a/server/sonar-web/src/main/js/types/alm-settings.ts +++ b/server/sonar-web/src/main/js/types/alm-settings.ts @@ -60,6 +60,13 @@ export interface ProjectAlmBindingResponse { 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; @@ -126,3 +133,46 @@ export enum AlmSettingsBindingStatusType { 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 + ); +} |