diff options
54 files changed, 2917 insertions, 633 deletions
diff --git a/server/sonar-web/public/images/tutorials/github-actions.svg b/server/sonar-web/public/images/tutorials/github-actions.svg new file mode 100644 index 00000000000..6f0c864641a --- /dev/null +++ b/server/sonar-web/public/images/tutorials/github-actions.svg @@ -0,0 +1,16 @@ +<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M10 1C14.9707 1 19 5.02932 19 10C19 14.9707 14.9707 19 10 19C5.02932 19 1 14.9707 1 10C1 5.02932 5.02932 1 10 1Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M23 19C26.3137 19 29 21.6863 29 25C29 28.3137 26.3137 31 23 31C19.6863 31 17 28.3137 17 25C17 21.6863 19.6863 19 23 19Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M23 35C26.3137 35 29 37.6863 29 41C29 44.3137 26.3137 47 23 47C19.6863 47 17 44.3137 17 41C17 37.6863 19.6863 35 23 35Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M41 35C44.3137 35 47 37.6863 47 41C47 44.3137 44.3137 47 41 47C37.6863 47 35 44.3137 35 41C35 37.6863 37.6863 35 41 35Z" stroke="#cae3f2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M25.0365 23.6069L21.9673 26.672L20.4766 25.1866" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M41 19C44.3137 19 47 21.6863 47 25C47 28.3137 44.3137 31 41 31C37.6863 31 35 28.3137 35 25C35 21.6863 37.6863 19 41 19Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M43.0365 23.6069L39.9673 26.672L38.4766 25.1866" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M7 6.81201C7 6.02498 7.86709 5.54641 8.533 5.9659L13.6462 9.18692C14.2715 9.58082 14.2682 10.4935 13.6402 10.8829L8.52697 14.0534C7.8608 14.4664 7 13.9873 7 13.2035V6.81201Z" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M9 19V34C9 37.866 12.1772 41 16 41H17" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M16.949 26C17.5013 26 17.949 25.5523 17.949 25C17.949 24.4477 17.5013 24 16.949 24V26ZM8 19.0352C8 22.8817 11.1183 26 14.9648 26V24C12.2228 24 10 21.7772 10 19.0352H8ZM14.9648 26H16.949V24H14.9648V26Z" fill="#236a97"/> +<path d="M29.0547 25H34.9993" stroke="#236a97" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M21 40C21.5518 40 22 40.4482 22 41C22 41.5526 21.5518 42 21 42C20.4482 42 20 41.5526 20 41C20 40.4482 20.4482 40 21 40Z" fill="#cae3f2"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M25 40C25.5518 40 26 40.4482 26 41C26 41.5526 25.5518 42 25 42C24.4482 42 24 41.5526 24 41C24 40.4482 24.4482 40 25 40Z" fill="#cae3f2"/> +<path d="M34.0048 41.0069L32.9922 41.0399" stroke="#cae3f2" stroke-width="2" stroke-linecap="round"/> +</svg> diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css index 6e6ba4d39c8..ae61dfde85c 100644 --- a/server/sonar-web/src/main/js/app/styles/init/misc.css +++ b/server/sonar-web/src/main/js/app/styles/init/misc.css @@ -361,6 +361,10 @@ th.huge-spacer-right { width: 600px !important; } +.abs-width-800 { + width: 800px !important; +} + .abs-height-50 { height: 50px !important; } 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 323c6a682cc..bd1f9862b8a 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 { 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 GitHubActionTutorial from './github-action/GitHubActionTutorial'; import GitLabCITutorial from './gitlabci/GitLabCITutorial'; import JenkinsTutorial from './jenkins/JenkinsTutorial'; import ManualTutorial from './manual/ManualTutorial'; @@ -68,6 +69,23 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender </header> <div className="display-flex-justify-center"> + {projectBinding?.alm === AlmKeys.GitHub && ( + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github" + onClick={() => props.onSelectTutorial(TutorialModes.GitHubActions)} + type="button"> + <img + alt="" // Should be ignored by screen readers + height={64} + className="spacer-bottom spacer-top" + src={`${getBaseUrl()}/images/tutorials/github-actions.svg`} + /> + <div className="medium big-spacer-top"> + {translate('onboarding.tutorial.choose_method.github_action')} + </div> + </button> + )} + {projectBinding?.alm === AlmKeys.GitLab && ( <button className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab" @@ -137,6 +155,15 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender <ManualTutorial component={component} currentUser={currentUser} /> )} + {selectedTutorial === TutorialModes.GitHubActions && projectBinding !== undefined && ( + <GitHubActionTutorial + baseUrl={baseUrl} + component={component} + currentUser={currentUser} + projectBinding={projectBinding} + /> + )} + {selectedTutorial === TutorialModes.Jenkins && projectBinding !== undefined && ( <JenkinsTutorial almBinding={almBinding} 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 5ff0893230d..4709f308631 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 @@ -33,11 +33,17 @@ import TutorialSelectionRenderer, { } from '../TutorialSelectionRenderer'; import { TutorialModes } from '../types'; +it.each([ + ['bitbucket server', mockProjectBitbucketBindingResponse()], + ['github', mockProjectGithubBindingResponse()], + ['gitlab', mockProjectGitLabBindingResponse()], + ['azure', mockProjectAzureBindingResponse()] +])('should render correctly for %s', (_, projectBinding) => { + expect(shallowRender({ projectBinding })).toMatchSnapshot(); +}); + it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot('selection'); - expect(shallowRender({ projectBinding: mockProjectBitbucketBindingResponse() })).toMatchSnapshot( - 'selection with jenkins available' - ); expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); expect(shallowRender({ selectedTutorial: TutorialModes.Manual })).toMatchSnapshot( 'manual tutorial' @@ -50,6 +56,12 @@ it('should render correctly', () => { ).toMatchSnapshot('jenkins tutorial'); expect( shallowRender({ + selectedTutorial: TutorialModes.GitHubActions, + projectBinding: mockProjectGitLabBindingResponse() + }) + ).toMatchSnapshot('github actions tutorial'); + expect( + shallowRender({ selectedTutorial: TutorialModes.GitLabCI, projectBinding: mockProjectGitLabBindingResponse() }) @@ -88,6 +100,9 @@ it('should allow mode selection for Github', () => { click(wrapper.find('button.tutorial-mode-manual')); expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Manual); + + click(wrapper.find('button.tutorial-mode-github')); + expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitHubActions); }); it('should allow mode selection for GitLab', () => { 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 bcdfec2e016..69f3cb08ae5 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,254 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`should render correctly for azure 1`] = ` +<Fragment> + <div + className="tutorial-selection" + > + <header + className="spacer-top spacer-bottom padded" + > + <h1 + className="text-center big-spacer-bottom" + > + onboarding.tutorial.choose_method + </h1> + </header> + <div + className="display-flex-justify-center" + > + <button + className="button button-huge display-flex-column spacer-left spacer-right azure-pipelines" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/alm/azure.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.azure_pipelines + </div> + </button> + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/sonarcloud/analysis/manual.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.manual + </div> + </button> + </div> + </div> +</Fragment> +`; + +exports[`should render correctly for bitbucket server 1`] = ` +<Fragment> + <div + className="tutorial-selection" + > + <header + className="spacer-top spacer-bottom padded" + > + <h1 + className="text-center big-spacer-bottom" + > + onboarding.tutorial.choose_method + </h1> + </header> + <div + className="display-flex-justify-center" + > + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/tutorials/jenkins.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.jenkins + </div> + </button> + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/sonarcloud/analysis/manual.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.manual + </div> + </button> + </div> + </div> +</Fragment> +`; + +exports[`should render correctly for github 1`] = ` +<Fragment> + <div + className="tutorial-selection" + > + <header + className="spacer-top spacer-bottom padded" + > + <h1 + className="text-center big-spacer-bottom" + > + onboarding.tutorial.choose_method + </h1> + </header> + <div + className="display-flex-justify-center" + > + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-github" + onClick={[Function]} + type="button" + > + <img + alt="" + className="spacer-bottom spacer-top" + height={64} + src="/images/tutorials/github-actions.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.github_action + </div> + </button> + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/tutorials/jenkins.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.jenkins + </div> + </button> + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/sonarcloud/analysis/manual.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.manual + </div> + </button> + </div> + </div> +</Fragment> +`; + +exports[`should render correctly for gitlab 1`] = ` +<Fragment> + <div + className="tutorial-selection" + > + <header + className="spacer-top spacer-bottom padded" + > + <h1 + className="text-center big-spacer-bottom" + > + onboarding.tutorial.choose_method + </h1> + </header> + <div + className="display-flex-justify-center" + > + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/alm/gitlab.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.gitlab_ci + </div> + </button> + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/tutorials/jenkins.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.jenkins + </div> + </button> + <button + className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" + onClick={[Function]} + type="button" + > + <img + alt="" + height={80} + src="/images/sonarcloud/analysis/manual.svg" + /> + <div + className="medium big-spacer-top" + > + onboarding.tutorial.choose_method.manual + </div> + </button> + </div> + </div> +</Fragment> +`; + exports[`should render correctly: azure pipelines tutorial 1`] = ` <Fragment> <AzurePipelinesTutorial @@ -49,6 +298,54 @@ exports[`should render correctly: azure pipelines tutorial 1`] = ` </Fragment> `; +exports[`should render correctly: github actions tutorial 1`] = ` +<Fragment> + <GitHubActionTutorial + baseUrl="http://localhost:9000" + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + currentUser={ + Object { + "groups": Array [], + "isLoggedIn": true, + "login": "luke", + "name": "Skywalker", + "scmAccounts": Array [], + } + } + projectBinding={ + Object { + "alm": "gitlab", + "key": "foo", + "monorepo": true, + "repository": "PROJECT_KEY", + "url": "https://gitlab.com/api/v4", + } + } + /> +</Fragment> +`; + exports[`should render correctly: gitlab tutorial 1`] = ` <Fragment> <GitLabCITutorial @@ -223,57 +520,3 @@ exports[`should render correctly: selection 1`] = ` </div> </Fragment> `; - -exports[`should render correctly: selection with jenkins available 1`] = ` -<Fragment> - <div - className="tutorial-selection" - > - <header - className="spacer-top spacer-bottom padded" - > - <h1 - className="text-center big-spacer-bottom" - > - onboarding.tutorial.choose_method - </h1> - </header> - <div - className="display-flex-justify-center" - > - <button - className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins" - onClick={[Function]} - type="button" - > - <img - alt="" - height={80} - src="/images/tutorials/jenkins.svg" - /> - <div - className="medium big-spacer-top" - > - onboarding.tutorial.choose_method.jenkins - </div> - </button> - <button - className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" - onClick={[Function]} - type="button" - > - <img - alt="" - height={80} - src="/images/sonarcloud/analysis/manual.svg" - /> - <div - className="medium big-spacer-top" - > - onboarding.tutorial.choose_method.manual - </div> - </button> - </div> - </div> -</Fragment> -`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx new file mode 100644 index 00000000000..74dd138b77b --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/AllSet.tsx @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; +import { withAppState } from '../../hoc/withAppState'; +import SentenceWithHighlights from './SentenceWithHighlights'; + +export interface AllSetProps { + appState: T.AppState; +} + +export function AllSet(props: AllSetProps) { + const { + appState: { branchesEnabled } + } = props; + + return ( + <div className="abs-width-600"> + <p className="big-spacer-bottom"> + <SentenceWithHighlights + highlightKeys={['all_set']} + translationKey="onboarding.tutorial.ci_outro.all_set" + /> + </p> + <div className="display-flex-row big-spacer-bottom"> + <div> + <img + alt="" // Should be ignored by screen readers + className="big-spacer-right" + width={30} + src={`${getBaseUrl()}/images/tutorials/commit.svg`} + /> + </div> + <div> + <p className="little-spacer-bottom"> + <strong>{translate('onboarding.tutorial.ci_outro.commit')}</strong> + </p> + <p> + {branchesEnabled + ? translate('onboarding.tutorial.ci_outro.commit.why') + : translate('onboarding.tutorial.ci_outro.commit.why.no_branches')} + </p> + </div> + </div> + <div className="display-flex-row huge-spacer-bottom"> + <div> + <img + alt="" // Should be ignored by screen readers + className="big-spacer-right" + width={30} + src={`${getBaseUrl()}/images/tutorials/refresh.svg`} + /> + </div> + <div> + <p className="little-spacer-bottom"> + <strong>{translate('onboarding.tutorial.ci_outro.refresh')}</strong> + </p> + <p>{translate('onboarding.tutorial.ci_outro.refresh.why')}</p> + </div> + </div> + </div> + ); +} + +export default withAppState(AllSet); diff --git a/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx b/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx new file mode 100644 index 00000000000..70f457f5798 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/DefaultProjectKey.tsx @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import CodeSnippet from '../../common/CodeSnippet'; +import SentenceWithFilename from './SentenceWithFilename'; + +export interface DefaultProjectKeyProps { + component: T.Component; +} + +const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`; + +export default function DefaultProjectKey(props: DefaultProjectKeyProps) { + const { component } = props; + return ( + <li className="abs-width-600"> + <SentenceWithFilename + filename="sonar-project.properties" + translationKey="onboarding.tutorial.other.project_key" + /> + <CodeSnippet snippet={sonarProjectSnippet(component.key)} /> + </li> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx b/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx new file mode 100644 index 00000000000..dea0fa4a8c6 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/TokenStepGenerator.tsx @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Button } from 'sonar-ui-common/components/controls/buttons'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import EditTokenModal from './EditTokenModal'; + +export interface TokenStepGeneratorProps { + component: T.Component; + currentUser: T.LoggedInUser; +} + +export default function TokenStepGenerator(props: TokenStepGeneratorProps) { + const { component, currentUser } = props; + const [isModalVisible, toggleModal] = React.useState(false); + + const toggleTokenModal = () => toggleModal(visible => !visible); + const closeTokenModal = () => toggleModal(false); + + return ( + <> + <li className="big-spacer-bottom"> + <FormattedMessage + defaultMessage={translate('onboarding.tutorial.env_variables')} + id="onboarding.tutorial.env_variables" + values={{ + extra: ( + <Button className="spacer-left" onClick={toggleTokenModal}> + {translate('onboarding.token.generate_token')} + </Button> + ), + field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>, + value: translate('onboarding.tutorial.env_variables.token_generator.value') + }} + /> + </li> + {isModalVisible && ( + <EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} /> + )} + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSet-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSet-test.tsx new file mode 100644 index 00000000000..1616135a49a --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/AllSet-test.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockAppState } from '../../../../helpers/testMocks'; +import { AllSet, AllSetProps } from '../AllSet'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ appState: mockAppState({ branchesEnabled: false }) })).toMatchSnapshot( + 'without branch' + ); +}); + +function shallowRender(props: Partial<AllSetProps> = {}) { + return shallow<AllSetProps>(<AllSet appState={mockAppState()} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/DefaultProjectKey-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/DefaultProjectKey-test.tsx new file mode 100644 index 00000000000..e4e023f0bd5 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/DefaultProjectKey-test.tsx @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../helpers/testMocks'; +import DefaultProjectKey, { DefaultProjectKeyProps } from '../DefaultProjectKey'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<DefaultProjectKeyProps> = {}) { + return shallow<DefaultProjectKeyProps>( + <DefaultProjectKey component={mockComponent()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/TokenStepGenerator-test.tsx b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/TokenStepGenerator-test.tsx new file mode 100644 index 00000000000..b93f00bcdb2 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/TokenStepGenerator-test.tsx @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks'; +import TokenStepGenerator, { TokenStepGeneratorProps } from '../TokenStepGenerator'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<TokenStepGeneratorProps> = {}) { + return shallow<TokenStepGeneratorProps>( + <TokenStepGenerator component={mockComponent()} currentUser={mockLoggedInUser()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap new file mode 100644 index 00000000000..6afe9cd744c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/AllSet-test.tsx.snap @@ -0,0 +1,135 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="abs-width-600" +> + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "all_set", + ] + } + translationKey="onboarding.tutorial.ci_outro.all_set" + /> + </p> + <div + className="display-flex-row big-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/commit.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.ci_outro.commit + </strong> + </p> + <p> + onboarding.tutorial.ci_outro.commit.why.no_branches + </p> + </div> + </div> + <div + className="display-flex-row huge-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/refresh.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.ci_outro.refresh + </strong> + </p> + <p> + onboarding.tutorial.ci_outro.refresh.why + </p> + </div> + </div> +</div> +`; + +exports[`should render correctly: without branch 1`] = ` +<div + className="abs-width-600" +> + <p + className="big-spacer-bottom" + > + <SentenceWithHighlights + highlightKeys={ + Array [ + "all_set", + ] + } + translationKey="onboarding.tutorial.ci_outro.all_set" + /> + </p> + <div + className="display-flex-row big-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/commit.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.ci_outro.commit + </strong> + </p> + <p> + onboarding.tutorial.ci_outro.commit.why.no_branches + </p> + </div> + </div> + <div + className="display-flex-row huge-spacer-bottom" + > + <div> + <img + alt="" + className="big-spacer-right" + src="/images/tutorials/refresh.svg" + width={30} + /> + </div> + <div> + <p + className="little-spacer-bottom" + > + <strong> + onboarding.tutorial.ci_outro.refresh + </strong> + </p> + <p> + onboarding.tutorial.ci_outro.refresh.why + </p> + </div> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/DefaultProjectKey-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/DefaultProjectKey-test.tsx.snap new file mode 100644 index 00000000000..cb21627f326 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/DefaultProjectKey-test.tsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<li + className="abs-width-600" +> + <SentenceWithFilename + filename="sonar-project.properties" + translationKey="onboarding.tutorial.other.project_key" + /> + <CodeSnippet + snippet="sonar.projectKey=my-project" + /> +</li> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap new file mode 100644 index 00000000000..f7a17687227 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/TokenStepGenerator-test.tsx.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <li + className="big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.env_variables" + id="onboarding.tutorial.env_variables" + values={ + Object { + "extra": <Button + className="spacer-left" + onClick={[Function]} + > + onboarding.token.generate_token + </Button>, + "field": <strong> + onboarding.tutorial.env_variables.field + </strong>, + "value": "onboarding.tutorial.env_variables.token_generator.value", + } + } + /> + </li> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx new file mode 100644 index 00000000000..c0147f4be56 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/AnalysisCommand.tsx @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { withAppState } from '../../hoc/withAppState'; +import { BuildTools } from '../types'; +import DotNet from './commands/DotNet'; +import Gradle from './commands/Gradle'; +import JavaMaven from './commands/JavaMaven'; +import Others from './commands/Others'; + +export interface AnalysisCommandProps { + appState: T.AppState; + buildTool?: BuildTools; + component: T.Component; +} + +export function AnalysisCommand(props: AnalysisCommandProps) { + const { + buildTool, + component, + appState: { branchesEnabled } + } = props; + + if (!buildTool) { + return null; + } + + switch (buildTool) { + case BuildTools.Maven: + return <JavaMaven branchesEnabled={branchesEnabled} component={component} />; + case BuildTools.Gradle: + return <Gradle branchesEnabled={branchesEnabled} component={component} />; + case BuildTools.DotNet: + return <DotNet branchesEnabled={branchesEnabled} component={component} />; + case BuildTools.Other: + return <Others branchesEnabled={branchesEnabled} component={component} />; + } + return null; +} + +export default withAppState(AnalysisCommand); diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx new file mode 100644 index 00000000000..dc176fd64e4 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/GitHubActionTutorial.tsx @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import Step from '../components/Step'; +import SecretStep from './SecretStep'; +import YamlFileStep from './YamlFileStep'; + +export enum Steps { + CREATE_SECRET = 1, + YAML = 2 +} + +export interface GitHubActionTutorialProps { + baseUrl: string; + component: T.Component; + currentUser: T.LoggedInUser; + projectBinding: ProjectAlmBindingResponse; +} + +export default function GitHubActionTutorial(props: GitHubActionTutorialProps) { + const { baseUrl, currentUser, component, projectBinding } = props; + + const [step, setStep] = React.useState<Steps>(Steps.CREATE_SECRET); + return ( + <> + <Step + finished={step > Steps.CREATE_SECRET} + onOpen={() => setStep(Steps.CREATE_SECRET)} + open={step === Steps.CREATE_SECRET} + renderForm={() => ( + <SecretStep + baseUrl={baseUrl} + component={component} + currentUser={currentUser} + projectBinding={projectBinding} + onDone={() => setStep(Steps.YAML)} + /> + )} + stepNumber={Steps.CREATE_SECRET} + stepTitle={translate('onboarding.tutorial.with.github_action.create_secret.title')} + /> + <Step + onOpen={() => setStep(Steps.YAML)} + open={step === Steps.YAML} + renderForm={() => <YamlFileStep component={component} />} + stepNumber={Steps.YAML} + stepTitle={translate('onboarding.tutorial.with.github_action.yaml.title')} + /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx new file mode 100644 index 00000000000..67796e981bb --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx @@ -0,0 +1,128 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { 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 { ProjectAlmBindingResponse } from '../../../types/alm-settings'; +import SentenceWithHighlights from '../components/SentenceWithHighlights'; +import TokenStepGenerator from '../components/TokenStepGenerator'; + +export interface SecretStepProps { + baseUrl: string; + component: T.Component; + currentUser: T.LoggedInUser; + projectBinding: ProjectAlmBindingResponse; + onDone: () => void; +} + +export default function SecretStep(props: SecretStepProps) { + const { + baseUrl, + component, + currentUser, + projectBinding: { repository } + } = props; + + return ( + <div className="boxed-group-inner"> + <p className="big-spacer-bottom"> + <FormattedMessage + defaultMessage={translate('onboarding.tutorial.with.github_action.secret.intro')} + id="onboarding.tutorial.with.github_action.secret.intro" + values={{ + settings_secret: repository ? ( + <a + href={`https://github.com/${repository}/settings/secrets`} + target="_blank" + rel="noopener noreferrer"> + {translate('onboarding.tutorial.with.github_action.secret.intro.link')} + </a> + ) : ( + translate('onboarding.tutorial.with.github_action.secret.intro.link') + ) + }} + /> + </p> + <ol className="list-styled"> + <li> + <SentenceWithHighlights + translationKey="onboarding.tutorial.with.github_action.secret.new" + highlightKeys={['new_secret']} + /> + </li> + <li> + <SentenceWithHighlights + translationKey="onboarding.tutorial.with.github_action.secret.name" + highlightKeys={['name']} + /> + <code className="rule little-spacer-left">SONAR_TOKEN</code> + <ClipboardIconButton copyValue="SONAR_TOKEN" /> + </li> + <TokenStepGenerator component={component} currentUser={currentUser} /> + <li> + <SentenceWithHighlights + translationKey="onboarding.tutorial.with.github_action.secret.add" + highlightKeys={['add_secret']} + /> + </li> + </ol> + + <hr className="no-horizontal-margins" /> + + <ol className="list-styled big-spacer-top big-spacer-bottom"> + <li> + <SentenceWithHighlights + translationKey="onboarding.tutorial.with.github_action.secret.new" + highlightKeys={['new_secret']} + /> + </li> + <li> + <SentenceWithHighlights + translationKey="onboarding.tutorial.with.github_action.secret.name" + highlightKeys={['name']} + /> + + <code className="rule little-spacer-left">SONAR_HOST_URL</code> + <ClipboardIconButton copyValue="SONAR_HOST_URL" /> + </li> + <li className="big-spacer-bottom"> + <FormattedMessage + defaultMessage={translate('onboarding.tutorial.env_variables')} + id="onboarding.tutorial.env_variables" + values={{ + extra: <ClipboardIconButton copyValue={baseUrl} />, + field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>, + value: <code className="rule">{baseUrl}</code> + }} + /> + </li> + <li> + <SentenceWithHighlights + translationKey="onboarding.tutorial.with.github_action.secret.add" + highlightKeys={['add_secret']} + /> + </li> + </ol> + <Button onClick={props.onDone}>{translate('continue')}</Button> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/YamlFileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/YamlFileStep.tsx new file mode 100644 index 00000000000..e76055a96e3 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/YamlFileStep.tsx @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import AllSet from '../components/AllSet'; +import RenderOptions from '../components/RenderOptions'; +import { BuildTools } from '../types'; +import AnalysisCommand from './AnalysisCommand'; + +export interface YamlFileStepProps { + component: T.Component; +} + +export default function YamlFileStep(props: YamlFileStepProps) { + const { component } = props; + const buildTools = [BuildTools.Maven, BuildTools.Gradle, BuildTools.DotNet, BuildTools.Other]; + const [buildToolSelected, setBuildToolSelected] = React.useState<BuildTools>(); + + return ( + <> + <ol className="list-styled big-spacer-top big-spacer-bottom"> + <li> + {translate('onboarding.build')} + + <RenderOptions + checked={buildToolSelected} + name="language" + onCheck={value => setBuildToolSelected(value as BuildTools)} + options={buildTools} + optionLabelKey="onboarding.build" + /> + </li> + <AnalysisCommand buildTool={buildToolSelected} component={component} /> + </ol> + {buildToolSelected !== undefined && ( + <> + <hr className="huge-spacer-top huge-spacer-bottom" /> + <AllSet /> + </> + )} + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/AnalysisCommand-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/AnalysisCommand-test.tsx new file mode 100644 index 00000000000..461b5ac519c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/AnalysisCommand-test.tsx @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockAppState, mockComponent } from '../../../../helpers/testMocks'; +import { BuildTools } from '../../types'; +import { AnalysisCommand, AnalysisCommandProps } from '../AnalysisCommand'; + +it.each([ + undefined, + BuildTools.CFamily, + BuildTools.DotNet, + BuildTools.Gradle, + BuildTools.Maven, + BuildTools.Other +])('should render correctly for %p', buildTool => { + expect(shallowRender({ buildTool })).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<AnalysisCommandProps> = {}) { + return shallow<AnalysisCommandProps>( + <AnalysisCommand appState={mockAppState()} component={mockComponent()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx new file mode 100644 index 00000000000..cc7f4a33e99 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/GitHubActionTutorial-test.tsx @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockProjectGithubBindingResponse } from '../../../../helpers/mocks/alm-settings'; +import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks'; +import Step from '../../components/Step'; +import GitHubActionTutorial, { GitHubActionTutorialProps } from '../GitHubActionTutorial'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot('For secret steps'); + const stepYaml = wrapper.find(Step).at(1); + stepYaml.simulate('open'); + expect(wrapper).toMatchSnapshot('For yaml steps'); +}); + +function shallowRender(props: Partial<GitHubActionTutorialProps> = {}) { + return shallow<GitHubActionTutorialProps>( + <GitHubActionTutorial + baseUrl="test" + currentUser={mockLoggedInUser()} + component={mockComponent()} + projectBinding={mockProjectGithubBindingResponse()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/SecretStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/SecretStep-test.tsx new file mode 100644 index 00000000000..57b80abbc28 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/SecretStep-test.tsx @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockProjectGithubBindingResponse } from '../../../../helpers/mocks/alm-settings'; +import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks'; +import SecretStep, { SecretStepProps } from '../SecretStep'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<SecretStepProps> = {}) { + return shallow<SecretStepProps>( + <SecretStep + baseUrl="test" + component={mockComponent()} + currentUser={mockLoggedInUser()} + projectBinding={mockProjectGithubBindingResponse()} + onDone={jest.fn()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/YamlFileStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/YamlFileStep-test.tsx new file mode 100644 index 00000000000..1d1f3c07d1b --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/YamlFileStep-test.tsx @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../helpers/testMocks'; +import YamlFileStep, { YamlFileStepProps } from '../YamlFileStep'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<YamlFileStepProps> = {}) { + return shallow<YamlFileStepProps>(<YamlFileStep component={mockComponent()} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap new file mode 100644 index 00000000000..4f86247edbf --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/AnalysisCommand-test.tsx.snap @@ -0,0 +1,113 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for "cfamily" 1`] = `""`; + +exports[`should render correctly for "dotnet" 1`] = ` +<DotNet + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } +/> +`; + +exports[`should render correctly for "gradle" 1`] = ` +<Gradle + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } +/> +`; + +exports[`should render correctly for "maven" 1`] = ` +<JavaMaven + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } +/> +`; + +exports[`should render correctly for "other" 1`] = ` +<Others + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } +/> +`; + +exports[`should render correctly for undefined 1`] = `""`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GitHubActionTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GitHubActionTutorial-test.tsx.snap new file mode 100644 index 00000000000..caec4a2f43f --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/GitHubActionTutorial-test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: For secret steps 1`] = ` +<Fragment> + <Step + finished={false} + onOpen={[Function]} + open={true} + renderForm={[Function]} + stepNumber={1} + stepTitle="onboarding.tutorial.with.github_action.create_secret.title" + /> + <Step + onOpen={[Function]} + open={false} + renderForm={[Function]} + stepNumber={2} + stepTitle="onboarding.tutorial.with.github_action.yaml.title" + /> +</Fragment> +`; + +exports[`should render correctly: For yaml steps 1`] = ` +<Fragment> + <Step + finished={true} + onOpen={[Function]} + open={false} + renderForm={[Function]} + stepNumber={1} + stepTitle="onboarding.tutorial.with.github_action.create_secret.title" + /> + <Step + onOpen={[Function]} + open={true} + renderForm={[Function]} + stepNumber={2} + stepTitle="onboarding.tutorial.with.github_action.yaml.title" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/SecretStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/SecretStep-test.tsx.snap new file mode 100644 index 00000000000..357fd880db0 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/SecretStep-test.tsx.snap @@ -0,0 +1,175 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="boxed-group-inner" +> + <p + className="big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.github_action.secret.intro" + id="onboarding.tutorial.with.github_action.secret.intro" + values={ + Object { + "settings_secret": <a + href="https://github.com/PROJECT_KEY/settings/secrets" + rel="noopener noreferrer" + target="_blank" + > + onboarding.tutorial.with.github_action.secret.intro.link + </a>, + } + } + /> + </p> + <ol + className="list-styled" + > + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "new_secret", + ] + } + translationKey="onboarding.tutorial.with.github_action.secret.new" + /> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "name", + ] + } + translationKey="onboarding.tutorial.with.github_action.secret.name" + /> + <code + className="rule little-spacer-left" + > + SONAR_TOKEN + </code> + <ClipboardIconButton + copyValue="SONAR_TOKEN" + /> + </li> + <TokenStepGenerator + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + currentUser={ + Object { + "groups": Array [], + "isLoggedIn": true, + "login": "luke", + "name": "Skywalker", + "scmAccounts": Array [], + } + } + /> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "add_secret", + ] + } + translationKey="onboarding.tutorial.with.github_action.secret.add" + /> + </li> + </ol> + <hr + className="no-horizontal-margins" + /> + <ol + className="list-styled big-spacer-top big-spacer-bottom" + > + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "new_secret", + ] + } + translationKey="onboarding.tutorial.with.github_action.secret.new" + /> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "name", + ] + } + translationKey="onboarding.tutorial.with.github_action.secret.name" + /> + <code + className="rule little-spacer-left" + > + SONAR_HOST_URL + </code> + <ClipboardIconButton + copyValue="SONAR_HOST_URL" + /> + </li> + <li + className="big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.env_variables" + id="onboarding.tutorial.env_variables" + values={ + Object { + "extra": <ClipboardIconButton + copyValue="test" + />, + "field": <strong> + onboarding.tutorial.env_variables.field + </strong>, + "value": <code + className="rule" + > + test + </code>, + } + } + /> + </li> + <li> + <SentenceWithHighlights + highlightKeys={ + Array [ + "add_secret", + ] + } + translationKey="onboarding.tutorial.with.github_action.secret.add" + /> + </li> + </ol> + <Button + onClick={[MockFunction]} + > + continue + </Button> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/YamlFileStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/YamlFileStep-test.tsx.snap new file mode 100644 index 00000000000..8f3bfa8a26a --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/__tests__/__snapshots__/YamlFileStep-test.tsx.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <ol + className="list-styled big-spacer-top big-spacer-bottom" + > + <li> + onboarding.build + <RenderOptions + name="language" + onCheck={[Function]} + optionLabelKey="onboarding.build" + options={ + Array [ + "maven", + "gradle", + "dotnet", + "other", + ] + } + /> + </li> + <Connect(withAppState(AnalysisCommand)) + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + </ol> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CreateYmlFile.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CreateYmlFile.tsx new file mode 100644 index 00000000000..e7c47ed263d --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/CreateYmlFile.tsx @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import CodeSnippet from '../../../common/CodeSnippet'; + +export interface CreateYmlFileProps { + yamlTemplate: string; +} + +export default function CreateYmlFile(props: CreateYmlFileProps) { + const { yamlTemplate } = props; + return ( + <li className="abs-width-800"> + <FormattedMessage + defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.create_yml')} + id="onboarding.tutorial.with.github_action.yaml.create_yml" + values={{ + file: ( + <> + <code className="rule">.github/workflows/build.yml</code> + <ClipboardIconButton copyValue=".github/workflows/build.yml" /> + </> + ) + }} + /> + <CodeSnippet snippet={yamlTemplate} /> + </li> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx new file mode 100644 index 00000000000..f8a5d5edb14 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/DotNet.tsx @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import CreateYmlFile from './CreateYmlFile'; + +export interface DotNetProps { + branchesEnabled?: boolean; + component: T.Component; +} + +const dotnetYamlTemplate = (projectKey: string, branchesEnabled: boolean) => `name: Build +on: + push: + branches: + - master # or the name of your main branch +${branchesEnabled ? ' pull_request:\n types: [opened, synchronize, reopened]' : ''} +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 1.11 + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~\\sonar\\cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache SonarQube scanner + id: cache-sonar-scanner + uses: actions/cache@v1 + with: + path: .\\.sonar\\scanner + key: \${{ runner.os }}-sonar-scanner + restore-keys: \${{ runner.os }}-sonar-scanner + - name: Install SonarQube scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\\.sonar\\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\\.sonar\\scanner + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + shell: powershell + run: | + .\\.sonar\\scanner\\dotnet-sonarscanner begin /k:"${projectKey}" /d:sonar.login="\${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="\${{ secrets.SONAR_HOST_URL }}" + dotnet build + .\\.sonar\\scanner\\dotnet-sonarscanner end /d:sonar.login="\${{ secrets.SONAR_TOKEN }}"`; + +export default function DotNet(props: DotNetProps) { + const { component, branchesEnabled } = props; + return <CreateYmlFile yamlTemplate={dotnetYamlTemplate(component.key, !!branchesEnabled)} />; +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx new file mode 100644 index 00000000000..6f3d323f13f --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Gradle.tsx @@ -0,0 +1,93 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import CodeSnippet from '../../../common/CodeSnippet'; +import { buildGradleSnippet } from '../../utils'; +import CreateYmlFile from './CreateYmlFile'; + +export interface GradleProps { + branchesEnabled?: boolean; + component: T.Component; +} +const gradleYamlTemplate = (branchesEnabled: boolean) => `name: Build +on: + push: + branches: + - master # or the name of your main branch +${branchesEnabled ? ' pull_request:\n types: [opened, synchronize, reopened]' : ''} +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: \${{ runner.os }}-gradle-\${{ hashFiles('**/*.gradle') }} + restore-keys: \${{ runner.os }}-gradle + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + run: ./gradlew build sonarqube --info`; + +export default function Gradle(props: GradleProps) { + const { component, branchesEnabled } = props; + + return ( + <> + <li className="abs-width-600"> + <FormattedMessage + defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.gradle')} + id="onboarding.tutorial.with.github_action.yaml.gradle" + values={{ + gradle: ( + <> + <code className="rule">build.gradle</code> + <ClipboardIconButton copyValue="build.gradle" /> + </> + ), + sq: <code className="rule">org.sonarqube</code> + }} + /> + <CodeSnippet snippet={buildGradleSnippet(component.key)} /> + </li> + <CreateYmlFile yamlTemplate={gradleYamlTemplate(!!branchesEnabled)} /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx new file mode 100644 index 00000000000..cbafc007bf5 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/JavaMaven.tsx @@ -0,0 +1,92 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import CodeSnippet from '../../../common/CodeSnippet'; +import { mavenPomSnippet } from '../../utils'; +import CreateYmlFile from './CreateYmlFile'; + +export interface JavaMavenProps { + branchesEnabled?: boolean; + component: T.Component; +} + +const mavenYamlTemplte = (branchesEnabled: boolean) => `name: Build +on: + push: + branches: + - master # or the name of your main branch +${branchesEnabled ? ' pull_request:\n types: [opened, synchronize, reopened]' : ''} +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: \${{ runner.os }}-m2-\${{ hashFiles('**/pom.xml') }} + restore-keys: \${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar`; + +export default function JavaMaven(props: JavaMavenProps) { + const { component, branchesEnabled } = props; + return ( + <> + <li className="abs-width-600"> + <FormattedMessage + defaultMessage={translate('onboarding.tutorial.with.github_action.yaml.maven.pom')} + id="onboarding.tutorial.with.github_action.yaml.maven.pom" + values={{ + pom: ( + <> + <code className="rule">pom.xml</code> + <ClipboardIconButton copyValue="pom.xml" /> + </> + ) + }} + /> + <CodeSnippet snippet={mavenPomSnippet(component.key)} /> + </li> + <CreateYmlFile yamlTemplate={mavenYamlTemplte(!!branchesEnabled)} /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx new file mode 100644 index 00000000000..b2de4454b8b --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/Others.tsx @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import DefaultProjectKey from '../../components/DefaultProjectKey'; +import CreateYmlFile from './CreateYmlFile'; + +export interface OthersProps { + branchesEnabled?: boolean; + component: T.Component; +} + +const dotnetYamlTemplate = (branchesEnabled: boolean) => `name: Build +on: + push: + branches: + - master # or the name of your main branch +${branchesEnabled ? ' pull_request:\n types: [opened, synchronize, reopened]' : ''} +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: docker://sonarsource/sonar-scanner-cli:latest + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}`; + +export default function Others(props: OthersProps) { + const { component, branchesEnabled } = props; + return ( + <> + <DefaultProjectKey component={component} /> + <CreateYmlFile yamlTemplate={dotnetYamlTemplate(!!branchesEnabled)} /> + </> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/CreateYmlFile-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/CreateYmlFile-test.tsx new file mode 100644 index 00000000000..9dfe3cbcb55 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/CreateYmlFile-test.tsx @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import CreateYmlFile, { CreateYmlFileProps } from '../CreateYmlFile'; +import { GradleProps } from '../Gradle'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<CreateYmlFileProps> = {}) { + return shallow<GradleProps>(<CreateYmlFile yamlTemplate="temaplate" {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/DotNet-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/DotNet-test.tsx new file mode 100644 index 00000000000..64f0b1e4517 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/DotNet-test.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import DotNet, { DotNetProps } from '../DotNet'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled'); +}); + +function shallowRender(props: Partial<DotNetProps> = {}) { + return shallow<DotNetProps>( + <DotNet branchesEnabled={true} component={mockComponent()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Gradle-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Gradle-test.tsx new file mode 100644 index 00000000000..cc1adcff93c --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Gradle-test.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import Gradle, { GradleProps } from '../Gradle'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled'); +}); + +function shallowRender(props: Partial<GradleProps> = {}) { + return shallow<GradleProps>( + <Gradle branchesEnabled={true} component={mockComponent()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/JavaMaven-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/JavaMaven-test.tsx new file mode 100644 index 00000000000..809996402a8 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/JavaMaven-test.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import JavaMaven, { JavaMavenProps } from '../../commands/JavaMaven'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled'); +}); + +function shallowRender(props: Partial<JavaMavenProps> = {}) { + return shallow<JavaMavenProps>( + <JavaMaven branchesEnabled={true} component={mockComponent()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Others-test.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Others-test.tsx new file mode 100644 index 00000000000..8130c93369e --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/Others-test.tsx @@ -0,0 +1,34 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockComponent } from '../../../../../helpers/testMocks'; +import Others, { OthersProps } from '../../commands/Others'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch enabled'); +}); + +function shallowRender(props: Partial<OthersProps> = {}) { + return shallow<OthersProps>( + <Others branchesEnabled={true} component={mockComponent()} {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/CreateYmlFile-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/CreateYmlFile-test.tsx.snap new file mode 100644 index 00000000000..61e32f72b14 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/CreateYmlFile-test.tsx.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<li + className="abs-width-800" +> + <FormattedMessage + defaultMessage="onboarding.tutorial.with.github_action.yaml.create_yml" + id="onboarding.tutorial.with.github_action.yaml.create_yml" + values={ + Object { + "file": <React.Fragment> + <code + className="rule" + > + .github/workflows/build.yml + </code> + <ClipboardIconButton + copyValue=".github/workflows/build.yml" + /> + </React.Fragment>, + } + } + /> + <CodeSnippet + snippet="temaplate" + /> +</li> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/DotNet-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/DotNet-test.tsx.snap new file mode 100644 index 00000000000..ae1bd7ae9cd --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/DotNet-test.tsx.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 1.11 + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~\\\\sonar\\\\cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache SonarQube scanner + id: cache-sonar-scanner + uses: actions/cache@v1 + with: + path: .\\\\.sonar\\\\scanner + key: \${{ runner.os }}-sonar-scanner + restore-keys: \${{ runner.os }}-sonar-scanner + - name: Install SonarQube scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\\\\.sonar\\\\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\\\\.sonar\\\\scanner + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + shell: powershell + run: | + .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner begin /k:\\"my-project\\" /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\" /d:sonar.host.url=\\"\${{ secrets.SONAR_HOST_URL }}\\" + dotnet build + .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner end /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\"" +/> +`; + +exports[`should render correctly: without branch enabled 1`] = ` +<CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + +jobs: + build: + name: Build + runs-on: windows-latest + steps: + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 1.11 + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~\\\\sonar\\\\cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache SonarQube scanner + id: cache-sonar-scanner + uses: actions/cache@v1 + with: + path: .\\\\.sonar\\\\scanner + key: \${{ runner.os }}-sonar-scanner + restore-keys: \${{ runner.os }}-sonar-scanner + - name: Install SonarQube scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\\\\.sonar\\\\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\\\\.sonar\\\\scanner + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + shell: powershell + run: | + .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner begin /k:\\"my-project\\" /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\" /d:sonar.host.url=\\"\${{ secrets.SONAR_HOST_URL }}\\" + dotnet build + .\\\\.sonar\\\\scanner\\\\dotnet-sonarscanner end /d:sonar.login=\\"\${{ secrets.SONAR_TOKEN }}\\"" +/> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Gradle-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Gradle-test.tsx.snap new file mode 100644 index 00000000000..1c61bf06e9f --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Gradle-test.tsx.snap @@ -0,0 +1,164 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.github_action.yaml.gradle" + id="onboarding.tutorial.with.github_action.yaml.gradle" + values={ + Object { + "gradle": <React.Fragment> + <code + className="rule" + > + build.gradle + </code> + <ClipboardIconButton + copyValue="build.gradle" + /> + </React.Fragment>, + "sq": <code + className="rule" + > + org.sonarqube + </code>, + } + } + /> + <CodeSnippet + snippet="plugins { + id \\"org.sonarqube\\" version \\"3.1.1\\" +} + +sonarqube { + properties { + property \\"sonar.projectKey\\", \\"my-project\\" + } +}" + /> + </li> + <CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: \${{ runner.os }}-gradle-\${{ hashFiles('**/*.gradle') }} + restore-keys: \${{ runner.os }}-gradle + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + run: ./gradlew build sonarqube --info" + /> +</Fragment> +`; + +exports[`should render correctly: without branch enabled 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.github_action.yaml.gradle" + id="onboarding.tutorial.with.github_action.yaml.gradle" + values={ + Object { + "gradle": <React.Fragment> + <code + className="rule" + > + build.gradle + </code> + <ClipboardIconButton + copyValue="build.gradle" + /> + </React.Fragment>, + "sq": <code + className="rule" + > + org.sonarqube + </code>, + } + } + /> + <CodeSnippet + snippet="plugins { + id \\"org.sonarqube\\" version \\"3.1.1\\" +} + +sonarqube { + properties { + property \\"sonar.projectKey\\", \\"my-project\\" + } +}" + /> + </li> + <CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: \${{ runner.os }}-gradle-\${{ hashFiles('**/*.gradle') }} + restore-keys: \${{ runner.os }}-gradle + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + run: ./gradlew build sonarqube --info" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap new file mode 100644 index 00000000000..66617347189 --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/JavaMaven-test.tsx.snap @@ -0,0 +1,142 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.github_action.yaml.maven.pom" + id="onboarding.tutorial.with.github_action.yaml.maven.pom" + values={ + Object { + "pom": <React.Fragment> + <code + className="rule" + > + pom.xml + </code> + <ClipboardIconButton + copyValue="pom.xml" + /> + </React.Fragment>, + } + } + /> + <CodeSnippet + snippet="<properties> + <sonar.projectKey>my-project</sonar.projectKey> +</properties>" + /> + </li> + <CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: \${{ runner.os }}-m2-\${{ hashFiles('**/pom.xml') }} + restore-keys: \${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar" + /> +</Fragment> +`; + +exports[`should render correctly: without branch enabled 1`] = ` +<Fragment> + <li + className="abs-width-600" + > + <FormattedMessage + defaultMessage="onboarding.tutorial.with.github_action.yaml.maven.pom" + id="onboarding.tutorial.with.github_action.yaml.maven.pom" + values={ + Object { + "pom": <React.Fragment> + <code + className="rule" + > + pom.xml + </code> + <ClipboardIconButton + copyValue="pom.xml" + /> + </React.Fragment>, + } + } + /> + <CodeSnippet + snippet="<properties> + <sonar.projectKey>my-project</sonar.projectKey> +</properties>" + /> + </li> + <CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: \${{ runner.os }}-sonar + restore-keys: \${{ runner.os }}-sonar + - name: Cache Maven packages + uses: actions/cache@v1 + with: + path: ~/.m2 + key: \${{ runner.os }}-m2-\${{ hashFiles('**/pom.xml') }} + restore-keys: \${{ runner.os }}-m2 + - name: Build and analyze + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }} + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Others-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Others-test.tsx.snap new file mode 100644 index 00000000000..27415bab1ff --- /dev/null +++ b/server/sonar-web/src/main/js/components/tutorials/github-action/commands/__tests__/__snapshots__/Others-test.tsx.snap @@ -0,0 +1,102 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<Fragment> + <DefaultProjectKey + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + <CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: docker://sonarsource/sonar-scanner-cli:latest + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}" + /> +</Fragment> +`; + +exports[`should render correctly: without branch enabled 1`] = ` +<Fragment> + <DefaultProjectKey + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> + <CreateYmlFile + yamlTemplate="name: Build +on: + push: + branches: + - master # or the name of your main branch + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: docker://sonarsource/sonar-scanner-cli:latest + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: \${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: \${{ secrets.SONAR_HOST_URL }}" + /> +</Fragment> +`; diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx b/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx index 47e02fc1dc1..beb4f2f9ae6 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/EnvironmentVariablesStep.tsx @@ -22,8 +22,8 @@ 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 EditTokenModal from '../components/EditTokenModal'; import Step from '../components/Step'; +import TokenStepGenerator from '../components/TokenStepGenerator'; export interface EnvironmentVariablesStepProps { baseUrl: string; @@ -42,14 +42,7 @@ const pipelineDescriptionLinkLabel = translate( export default function EnvironmentVariablesStep(props: EnvironmentVariablesStepProps) { const { baseUrl, component, currentUser, finished, open } = props; - const [isModalVisible, toggleModal] = React.useState(false); - - const toggleTokenModal = () => toggleModal(!isModalVisible); - const closeTokenModal = () => toggleModal(false); - - const fieldValueTranslation = translate( - 'onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value' - ); + const fieldValueTranslation = translate('onboarding.tutorial.env_variables'); const renderForm = () => ( <div className="boxed-group-inner"> @@ -75,28 +68,16 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" values={{ extra: <ClipboardIconButton copyValue="SONAR_TOKEN" />, - field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1'), - value: <code className="rule">SONAR_TOKEN</code> - }} - /> - </li> - <li className="big-spacer-bottom"> - <FormattedMessage - defaultMessage={fieldValueTranslation} - id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" - values={{ - extra: ( - <Button className="spacer-left" onClick={toggleTokenModal}> - {translate('onboarding.token.generate_token')} - </Button> + field: ( + <strong> + {translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1')} + </strong> ), - field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'), - value: translate( - 'onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value' - ) + value: <code className="rule">SONAR_TOKEN</code> }} /> </li> + <TokenStepGenerator component={component} currentUser={currentUser} /> <li className="big-spacer-bottom"> {translate('onboarding.tutorial.with.gitlab_ci.env_variables.step3')} </li> @@ -129,7 +110,11 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" values={{ extra: <ClipboardIconButton copyValue="SONAR_HOST_URL" />, - field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1'), + field: ( + <strong> + {translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1')} + </strong> + ), value: <code className="rule">SONAR_HOST_URL</code> }} /> @@ -140,7 +125,7 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" values={{ extra: <ClipboardIconButton copyValue={baseUrl} />, - field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'), + field: <strong>{translate('onboarding.tutorial.env_variables.field')}</strong>, value: <code className="rule">{baseUrl}</code> }} /> @@ -160,19 +145,13 @@ export default function EnvironmentVariablesStep(props: EnvironmentVariablesStep ); return ( - <> - {isModalVisible && ( - <EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} /> - )} - - <Step - finished={finished} - onOpen={props.onOpen} - open={open} - renderForm={renderForm} - stepNumber={2} - stepTitle={translate('onboarding.tutorial.with.gitlab_ci.env_variables.title')} - /> - </> + <Step + finished={finished} + onOpen={props.onOpen} + open={open} + renderForm={renderForm} + stepNumber={2} + stepTitle={translate('onboarding.tutorial.with.gitlab_ci.env_variables.title')} + /> ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/EnvironmentVariablesStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/EnvironmentVariablesStep-test.tsx.snap index c1e8ff710f1..3def93d2518 100644 --- a/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/EnvironmentVariablesStep-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/gitlabci/__tests__/__snapshots__/EnvironmentVariablesStep-test.tsx.snap @@ -1,16 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly: Step wrapper 1`] = ` -<Fragment> - <Step - finished={false} - onOpen={[MockFunction]} - open={true} - renderForm={[Function]} - stepNumber={2} - stepTitle="onboarding.tutorial.with.gitlab_ci.env_variables.title" - /> -</Fragment> +<Step + finished={false} + onOpen={[MockFunction]} + open={true} + renderForm={[Function]} + stepNumber={2} + stepTitle="onboarding.tutorial.with.gitlab_ci.env_variables.title" +/> `; exports[`should render correctly: initial content 1`] = ` @@ -40,14 +38,16 @@ exports[`should render correctly: initial content 1`] = ` className="big-spacer-bottom" > <FormattedMessage - defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" + defaultMessage="onboarding.tutorial.env_variables" id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" values={ Object { "extra": <ClipboardIconButton copyValue="SONAR_TOKEN" />, - "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step1", + "field": <strong> + onboarding.tutorial.with.gitlab_ci.env_variables.step1 + </strong>, "value": <code className="rule" > @@ -57,26 +57,39 @@ exports[`should render correctly: initial content 1`] = ` } /> </li> - <li - className="big-spacer-bottom" - > - <FormattedMessage - defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" - id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" - values={ - Object { - "extra": <Button - className="spacer-left" - onClick={[Function]} - > - onboarding.token.generate_token - </Button>, - "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2", - "value": "onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value", - } + <TokenStepGenerator + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], } - /> - </li> + } + currentUser={ + Object { + "groups": Array [], + "isLoggedIn": true, + "login": "luke", + "name": "Skywalker", + "scmAccounts": Array [], + } + } + /> <li className="big-spacer-bottom" > @@ -114,14 +127,16 @@ exports[`should render correctly: initial content 1`] = ` className="big-spacer-bottom" > <FormattedMessage - defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" + defaultMessage="onboarding.tutorial.env_variables" id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" values={ Object { "extra": <ClipboardIconButton copyValue="SONAR_HOST_URL" />, - "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step1", + "field": <strong> + onboarding.tutorial.with.gitlab_ci.env_variables.step1 + </strong>, "value": <code className="rule" > @@ -135,14 +150,16 @@ exports[`should render correctly: initial content 1`] = ` className="big-spacer-bottom" > <FormattedMessage - defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" + defaultMessage="onboarding.tutorial.env_variables" id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" values={ Object { "extra": <ClipboardIconButton copyValue="http://localhost:9000" />, - "field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2", + "field": <strong> + onboarding.tutorial.env_variables.field + </strong>, "value": <code className="rule" > diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx index 576463cf716..4a2c4e2cf95 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx @@ -19,10 +19,8 @@ */ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { withAppState } from '../../hoc/withAppState'; +import AllSet from '../components/AllSet'; import RenderOptions from '../components/RenderOptions'; -import SentenceWithHighlights from '../components/SentenceWithHighlights'; import Step from '../components/Step'; import { BuildTools } from '../types'; import DotNet from './buildtool-steps/DotNet'; @@ -31,7 +29,6 @@ import Maven from './buildtool-steps/Maven'; import Other from './buildtool-steps/Other'; export interface JenkinsfileStepProps { - appState: T.AppState; component: T.Component; open: boolean; } @@ -48,12 +45,8 @@ const BUILDTOOL_COMPONENT_MAP: { [BuildTools.Other]: Other }; -export function JenkinsfileStep(props: JenkinsfileStepProps) { - const { - appState: { branchesEnabled }, - component, - open - } = props; +export default function JenkinsfileStep(props: JenkinsfileStepProps) { + const { component, open } = props; const [buildTool, setBuildTool] = React.useState<BuildToolsWithoutCFamily | undefined>(undefined); return ( <Step @@ -78,50 +71,7 @@ export function JenkinsfileStep(props: JenkinsfileStepProps) { {buildTool !== undefined && ( <> <hr className="huge-spacer-top huge-spacer-bottom" /> - <div className="abs-width-600"> - <p className="big-spacer-bottom"> - <SentenceWithHighlights - highlightKeys={['all_set']} - translationKey="onboarding.tutorial.with.jenkins.all_set" - /> - </p> - <div className="display-flex-row big-spacer-bottom"> - <div> - <img - alt="" // Should be ignored by screen readers - className="big-spacer-right" - width={30} - src={`${getBaseUrl()}/images/tutorials/commit.svg`} - /> - </div> - <div> - <p className="little-spacer-bottom"> - <strong>{translate('onboarding.tutorial.with.jenkins.commit')}</strong> - </p> - <p> - {branchesEnabled - ? translate('onboarding.tutorial.with.jenkins.commit.why') - : translate('onboarding.tutorial.with.jenkins.commit.why.no_branches')} - </p> - </div> - </div> - <div className="display-flex-row huge-spacer-bottom"> - <div> - <img - alt="" // Should be ignored by screen readers - className="big-spacer-right" - width={30} - src={`${getBaseUrl()}/images/tutorials/refresh.svg`} - /> - </div> - <div> - <p className="little-spacer-bottom"> - <strong>{translate('onboarding.tutorial.with.jenkins.refresh')}</strong> - </p> - <p>{translate('onboarding.tutorial.with.jenkins.refresh.why')}</p> - </div> - </div> - </div> + <AllSet /> </> )} </div> @@ -131,5 +81,3 @@ export function JenkinsfileStep(props: JenkinsfileStepProps) { /> ); } - -export default withAppState(JenkinsfileStep); diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx index bdab1582f60..d738038d7c5 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx @@ -19,11 +19,11 @@ */ import { shallow, ShallowWrapper } from 'enzyme'; import * as React from 'react'; -import { mockAppState, mockComponent } from '../../../../helpers/testMocks'; +import { mockComponent } from '../../../../helpers/testMocks'; import RenderOptions from '../../components/RenderOptions'; import Step from '../../components/Step'; import { BuildTools } from '../../types'; -import { JenkinsfileStep, JenkinsfileStepProps } from '../JenkinsfileStep'; +import JenkinsfileStep, { JenkinsfileStepProps } from '../JenkinsfileStep'; import { renderStepContent } from '../test-utils'; it('should render correctly', () => { @@ -32,12 +32,6 @@ it('should render correctly', () => { expect(renderStepContent(wrapper)).toMatchSnapshot('initial content'); }); -it('should render correctly with no branches', () => { - const wrapper = shallowRender({ appState: mockAppState({ branchesEnabled: false }) }); - selectBuildTool(wrapper, BuildTools.Gradle); - expect(renderStepContent(wrapper)).toMatchSnapshot(); -}); - it('should render correctly for Maven', () => { const wrapper = shallowRender(); selectBuildTool(wrapper, BuildTools.Maven); @@ -74,11 +68,6 @@ function selectBuildTool(wrapper: ShallowWrapper<JenkinsfileStepProps>, tool: Bu function shallowRender(props: Partial<JenkinsfileStepProps> = {}) { return shallow<JenkinsfileStepProps>( - <JenkinsfileStep - appState={mockAppState({ branchesEnabled: true })} - component={mockComponent()} - open={true} - {...props} - /> + <JenkinsfileStep component={mockComponent()} open={true} {...props} /> ); } diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap index b6759ddfa38..a7ecadf597a 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap @@ -43,7 +43,7 @@ exports[`should render correctly: branches not enabled 1`] = ` } } /> - <Connect(withAppState(JenkinsfileStep)) + <JenkinsfileStep component={ Object { "breadcrumbs": Array [], @@ -122,7 +122,7 @@ exports[`should render correctly: default 1`] = ` } } /> - <Connect(withAppState(JenkinsfileStep)) + <JenkinsfileStep component={ Object { "breadcrumbs": Array [], diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap index a4610804993..909464f7296 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap @@ -53,70 +53,7 @@ exports[`should render correctly for .NET 1`] = ` <hr className="huge-spacer-top huge-spacer-bottom" /> - <div - className="abs-width-600" - > - <p - className="big-spacer-bottom" - > - <SentenceWithHighlights - highlightKeys={ - Array [ - "all_set", - ] - } - translationKey="onboarding.tutorial.with.jenkins.all_set" - /> - </p> - <div - className="display-flex-row big-spacer-bottom" - > - <div> - <img - alt="" - className="big-spacer-right" - src="/images/tutorials/commit.svg" - width={30} - /> - </div> - <div> - <p - className="little-spacer-bottom" - > - <strong> - onboarding.tutorial.with.jenkins.commit - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.commit.why - </p> - </div> - </div> - <div - className="display-flex-row huge-spacer-bottom" - > - <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.jenkins.refresh - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.refresh.why - </p> - </div> - </div> - </div> + <Connect(withAppState(AllSet)) /> </React.Fragment> </div> `; @@ -174,70 +111,7 @@ exports[`should render correctly for Gradle 1`] = ` <hr className="huge-spacer-top huge-spacer-bottom" /> - <div - className="abs-width-600" - > - <p - className="big-spacer-bottom" - > - <SentenceWithHighlights - highlightKeys={ - Array [ - "all_set", - ] - } - translationKey="onboarding.tutorial.with.jenkins.all_set" - /> - </p> - <div - className="display-flex-row big-spacer-bottom" - > - <div> - <img - alt="" - className="big-spacer-right" - src="/images/tutorials/commit.svg" - width={30} - /> - </div> - <div> - <p - className="little-spacer-bottom" - > - <strong> - onboarding.tutorial.with.jenkins.commit - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.commit.why - </p> - </div> - </div> - <div - className="display-flex-row huge-spacer-bottom" - > - <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.jenkins.refresh - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.refresh.why - </p> - </div> - </div> - </div> + <Connect(withAppState(AllSet)) /> </React.Fragment> </div> `; @@ -295,70 +169,7 @@ exports[`should render correctly for Maven 1`] = ` <hr className="huge-spacer-top huge-spacer-bottom" /> - <div - className="abs-width-600" - > - <p - className="big-spacer-bottom" - > - <SentenceWithHighlights - highlightKeys={ - Array [ - "all_set", - ] - } - translationKey="onboarding.tutorial.with.jenkins.all_set" - /> - </p> - <div - className="display-flex-row big-spacer-bottom" - > - <div> - <img - alt="" - className="big-spacer-right" - src="/images/tutorials/commit.svg" - width={30} - /> - </div> - <div> - <p - className="little-spacer-bottom" - > - <strong> - onboarding.tutorial.with.jenkins.commit - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.commit.why - </p> - </div> - </div> - <div - className="display-flex-row huge-spacer-bottom" - > - <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.jenkins.refresh - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.refresh.why - </p> - </div> - </div> - </div> + <Connect(withAppState(AllSet)) /> </React.Fragment> </div> `; @@ -416,191 +227,7 @@ exports[`should render correctly for Other 1`] = ` <hr className="huge-spacer-top huge-spacer-bottom" /> - <div - className="abs-width-600" - > - <p - className="big-spacer-bottom" - > - <SentenceWithHighlights - highlightKeys={ - Array [ - "all_set", - ] - } - translationKey="onboarding.tutorial.with.jenkins.all_set" - /> - </p> - <div - className="display-flex-row big-spacer-bottom" - > - <div> - <img - alt="" - className="big-spacer-right" - src="/images/tutorials/commit.svg" - width={30} - /> - </div> - <div> - <p - className="little-spacer-bottom" - > - <strong> - onboarding.tutorial.with.jenkins.commit - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.commit.why - </p> - </div> - </div> - <div - className="display-flex-row huge-spacer-bottom" - > - <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.jenkins.refresh - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.refresh.why - </p> - </div> - </div> - </div> - </React.Fragment> -</div> -`; - -exports[`should render correctly with no branches 1`] = ` -<div - className="boxed-group-inner" -> - <ol - className="list-styled" - > - <li> - onboarding.build - <RenderOptions - checked="gradle" - name="buildtool" - onCheck={[Function]} - optionLabelKey="onboarding.build" - options={ - Array [ - "maven", - "gradle", - "dotnet", - "other", - ] - } - /> - </li> - <Gradle - component={ - Object { - "breadcrumbs": Array [], - "key": "my-project", - "name": "MyProject", - "qualifier": "TRK", - "qualityGate": Object { - "isDefault": true, - "key": "30", - "name": "Sonar way", - }, - "qualityProfiles": Array [ - Object { - "deleted": false, - "key": "my-qp", - "language": "ts", - "name": "Sonar way", - }, - ], - "tags": Array [], - } - } - /> - </ol> - <React.Fragment> - <hr - className="huge-spacer-top huge-spacer-bottom" - /> - <div - className="abs-width-600" - > - <p - className="big-spacer-bottom" - > - <SentenceWithHighlights - highlightKeys={ - Array [ - "all_set", - ] - } - translationKey="onboarding.tutorial.with.jenkins.all_set" - /> - </p> - <div - className="display-flex-row big-spacer-bottom" - > - <div> - <img - alt="" - className="big-spacer-right" - src="/images/tutorials/commit.svg" - width={30} - /> - </div> - <div> - <p - className="little-spacer-bottom" - > - <strong> - onboarding.tutorial.with.jenkins.commit - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.commit.why.no_branches - </p> - </div> - </div> - <div - className="display-flex-row huge-spacer-bottom" - > - <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.jenkins.refresh - </strong> - </p> - <p> - onboarding.tutorial.with.jenkins.refresh.why - </p> - </div> - </div> - </div> + <Connect(withAppState(AllSet)) /> </React.Fragment> </div> `; diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx index 9ecebd02c11..9436298c2f8 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx @@ -20,22 +20,13 @@ import * as React from 'react'; import CodeSnippet from '../../../common/CodeSnippet'; import SentenceWithFilename from '../../components/SentenceWithFilename'; +import { buildGradleSnippet } from '../../utils'; import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; export interface GradleProps { component: T.Component; } -const buildGradleSnippet = (key: string) => `plugins { - id "org.sonarqube" version "3.1.1" -} - -sonarqube { - properties { - property "sonar.projectKey", "${key}" - } -}`; - const JENKINSFILE_SNIPPET = `node { stage('SCM') { checkout scm diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx index 1f074b12121..669ed20f4c9 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx @@ -20,16 +20,13 @@ import * as React from 'react'; import CodeSnippet from '../../../common/CodeSnippet'; import SentenceWithFilename from '../../components/SentenceWithFilename'; +import { mavenPomSnippet } from '../../utils'; import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; export interface MavenProps { component: T.Component; } -const pomSnippet = (key: string) => `<properties> - <sonar.projectKey>${key}</sonar.projectKey> -</properties>`; - const JENKINSFILE_SNIPPET = `node { stage('SCM') { checkout scm @@ -50,7 +47,7 @@ export default function Maven({ component }: MavenProps) { filename="pom.xml" translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2" /> - <CodeSnippet snippet={pomSnippet(component.key)} /> + <CodeSnippet snippet={mavenPomSnippet(component.key)} /> </li> <CreateJenkinsfileBulletPoint alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3" diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx index 9fa8d96c280..c66cd92cca6 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx @@ -18,16 +18,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import CodeSnippet from '../../../common/CodeSnippet'; -import SentenceWithFilename from '../../components/SentenceWithFilename'; +import DefaultProjectKey from '../../components/DefaultProjectKey'; import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint'; export interface OtherProps { component: T.Component; } -const sonarProjectSnippet = (key: string) => `sonar.projectKey=${key}`; - const JENKINSFILE_SNIPPET = `node { stage('SCM') { checkout scm @@ -43,13 +40,7 @@ const JENKINSFILE_SNIPPET = `node { export default function Other({ component }: OtherProps) { return ( <> - <li className="abs-width-600"> - <SentenceWithFilename - filename="sonar-project.properties" - translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.other.step2" - /> - <CodeSnippet snippet={sonarProjectSnippet(component.key)} /> - </li> + <DefaultProjectKey component={component} /> <CreateJenkinsfileBulletPoint alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3" snippet={JENKINSFILE_SNIPPET} diff --git a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap index c5a549388dd..d77f4bc3a58 100644 --- a/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap @@ -2,17 +2,30 @@ exports[`should render correctly 1`] = ` <Fragment> - <li - className="abs-width-600" - > - <SentenceWithFilename - filename="sonar-project.properties" - translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.other.step2" - /> - <CodeSnippet - snippet="sonar.projectKey=my-project" - /> - </li> + <DefaultProjectKey + component={ + Object { + "breadcrumbs": Array [], + "key": "my-project", + "name": "MyProject", + "qualifier": "TRK", + "qualityGate": Object { + "isDefault": true, + "key": "30", + "name": "Sonar way", + }, + "qualityProfiles": Array [ + Object { + "deleted": false, + "key": "my-qp", + "language": "ts", + "name": "Sonar way", + }, + ], + "tags": Array [], + } + } + /> <CreateJenkinsfileBulletPoint alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3" snippet="node { 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 5f9ba566b61..ddfe8881ade 100644 --- a/server/sonar-web/src/main/js/components/tutorials/types.ts +++ b/server/sonar-web/src/main/js/components/tutorials/types.ts @@ -21,6 +21,7 @@ export enum TutorialModes { Manual = 'manual', Jenkins = 'jenkins', GitLabCI = 'gitlab-ci', + GitHubActions = 'github-actions', AzurePipelines = 'azure-pipelines' } diff --git a/server/sonar-web/src/main/js/components/tutorials/utils.ts b/server/sonar-web/src/main/js/components/tutorials/utils.ts index 825a08e8d2a..463538e36a7 100644 --- a/server/sonar-web/src/main/js/components/tutorials/utils.ts +++ b/server/sonar-web/src/main/js/components/tutorials/utils.ts @@ -23,6 +23,24 @@ export function quote(os: string): (s: string) => string { return os === 'win' ? (s: string) => `"${s}"` : (s: string) => s; } +export function mavenPomSnippet(key: string) { + return `<properties> + <sonar.projectKey>${key}</sonar.projectKey> +</properties>`; +} + +export function buildGradleSnippet(key: string) { + return `plugins { + id "org.sonarqube" version "3.1.1" +} + +sonarqube { + properties { + property "sonar.projectKey", "${key}" + } +}`; +} + export function getUniqueTokenName(tokens: T.UserToken[], initialTokenName = '') { const hasToken = (name: string) => tokens.find(token => token.name === name) !== undefined; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 35e787a8e89..8fc86bed269 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3296,6 +3296,10 @@ onboarding.token.use_existing_token.placeholder=Enter your existing token onboarding.token.use_existing_token.label=Existing token value onboarding.token.invalid_format=The token you have entered has invalid format. +onboarding.tutorial.env_variables=In the {field} field, enter {value} {extra} +onboarding.tutorial.env_variables.field=Value +onboarding.tutorial.env_variables.token_generator.value=an existing token, or a newly generated one: + onboarding.analysis.header=Run analysis on your project onboarding.analysis.auto_refresh_after_analysis=Once the analysis is completed, this page will automatically refresh and you will be able to browse the analysis results. @@ -3383,13 +3387,37 @@ onboarding.analysis.dotnetcore.global.text=As a prerequisite you need to have th onboarding.analysis.dotnetcore.global.text.path=Make sure dotnet tools folder is in your path. See dotnet global tools documentation for more information. onboarding.tutorial.return_to_list=Choose another option +onboarding.tutorial.ci_outro.all_set.sentence={all_set} and ready to improve the quality and security of your code! +onboarding.tutorial.ci_outro.all_set.sentence.all_set=You're all set +onboarding.tutorial.ci_outro.commit=Commit and push your code to start the analysis. +onboarding.tutorial.ci_outro.commit.why=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. +onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube. +onboarding.tutorial.ci_outro.refresh=This page will then refresh with your analysis results. +onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration. +onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code: onboarding.tutorial.choose_method=How do you want to analyze your repository? onboarding.tutorial.choose_method.manual=Manually onboarding.tutorial.choose_method.jenkins=With Jenkins +onboarding.tutorial.choose_method.github_action=With GitHub Actions onboarding.tutorial.choose_method.gitlab_ci=With GitLab CI onboarding.tutorial.choose_method.azure_pipelines=With Azure Pipelines +onboarding.tutorial.with.github_action.create_secret.title=Create GitHub Secrets +onboarding.tutorial.with.github_action.secret.intro=In your GitHub repository, go to {settings_secret} and create two new secrets: +onboarding.tutorial.with.github_action.secret.intro.link=Settings > Secrets +onboarding.tutorial.with.github_action.secret.name.sentence=In the {name} field, enter +onboarding.tutorial.with.github_action.secret.name.sentence.name=Name +onboarding.tutorial.with.github_action.secret.new.sentence=Click on {new_secret} +onboarding.tutorial.with.github_action.secret.new.sentence.new_secret=New repository secret +onboarding.tutorial.with.github_action.secret.add.sentence=Click on {add_secret} +onboarding.tutorial.with.github_action.secret.add.sentence.add_secret=Add secret +onboarding.tutorial.with.github_action.yaml.title=Create Workflow YAML File +onboarding.tutorial.with.github_action.yaml.create_yml=Create or update your {file} YAML file with the following content: +onboarding.tutorial.with.github_action.yaml.maven.pom=Update your {pom} file with the following properties: +onboarding.tutorial.with.github_action.yaml.gradle=Update your {gradle} file with the {sq} plugin and it's configuration: + + 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.gitlab_ci.project_key.title=Set your project key @@ -3400,14 +3428,12 @@ onboarding.tutorial.with.gitlab_ci.project_key.dotnet.step2=Create a {file} file onboarding.tutorial.with.gitlab_ci.env_variables.title=Add environment variables -onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value=In the {field} field, enter {value} {extra} onboarding.tutorial.with.gitlab_ci.env_variables.description.link=Settings > CI/CD > Variables onboarding.tutorial.with.gitlab_ci.env_variables.section.title=a. Define the SonarQube Token environment variable onboarding.tutorial.with.gitlab_ci.env_variables.section.description=In GitLab, go to {link} to add the following variable and make sure it is available for your project: onboarding.tutorial.with.gitlab_ci.env_variables.edit.token.tooltip=Use an existing token or generate a new one. onboarding.tutorial.with.gitlab_ci.env_variables.step1=Key -onboarding.tutorial.with.gitlab_ci.env_variables.step2=Value -onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value=an existing token, or a newly generated one: + onboarding.tutorial.with.gitlab_ci.env_variables.step3=Uncheck the "Protect Variable" checkbox onboarding.tutorial.with.gitlab_ci.env_variables.section.step4=Check the "Mask Variable" checkbox onboarding.tutorial.with.gitlab_ci.env_variables.section2.title=b. Define the SonarQube URL environment variable @@ -3620,7 +3646,6 @@ onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence=Under {in onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence.install_from=Install from GitHub onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence.install_auto=Install automatically -onboarding.tutorial.with.jenkins.jenkinsfile.other.step2.sentence=Create a {file} file in your repository and paste the following code: onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence=Make sure to replace {default} with the name you gave to your SonarQube Scanner tool {in_jenkins}. onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.default=SonarScanner onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.in_jenkins=in Jenkins @@ -3629,13 +3654,7 @@ onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence.path=Man onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence=The name is located under the {path} section, in the {name} field. onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.path=SonarQube Scanner > SonarQube Scanner installations onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.name=Name -onboarding.tutorial.with.jenkins.all_set.sentence={all_set} and ready to improve the quality and security of your code! -onboarding.tutorial.with.jenkins.all_set.sentence.all_set=You're all set -onboarding.tutorial.with.jenkins.commit=Commit and push your code to start the analysis. -onboarding.tutorial.with.jenkins.commit.why=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. -onboarding.tutorial.with.jenkins.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube. -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.os=What is your agent host? onboarding.tutorial.with.azure_pipelines.title=Analyze your project with Azure DevOps Pipelines @@ -3654,7 +3673,7 @@ onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence=Enter yo 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 analysis -onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info=The following steps assume you are using the Azure Pipelines classic editor. Check out our {doc_link} for the yaml counterpart. +onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info=The following steps assume you are using the Azure Pipelines classic editor. Check out our {doc_link} for the YAML counterpart. onboarding.tutorial.with.azure_pipelines.BranchAnalysis.info.doc_link=Azure DevOps integration page onboarding.tutorial.with.azure_pipelines.BranchAnalysis.build_wrapper.ccpp.sentence=In Azure DevOps, create or edit a build {pipeline} to make Build Wrapper available on the build agent. |