Browse Source

SONAR-13296 Add new Jenkins tutorial for Bitbucket Server

tags/8.4.0.35506
Wouter Admiraal 4 years ago
parent
commit
b7d68dca79
69 changed files with 3946 additions and 118 deletions
  1. 1
    0
      server/sonar-web/public/images/tutorials/commit.svg
  2. 1
    0
      server/sonar-web/public/images/tutorials/jenkins.svg
  3. 1
    0
      server/sonar-web/public/images/tutorials/refresh.svg
  4. 5
    1
      server/sonar-web/src/main/js/api/alm-settings.ts
  5. 2
    2
      server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
  6. 1
    1
      server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap
  7. 1
    0
      server/sonar-web/src/main/js/components/common/CodeSnippet.css
  8. 109
    0
      server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx
  9. 101
    0
      server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx
  10. 109
    0
      server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx
  11. 70
    0
      server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx
  12. 41
    0
      server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelection-test.tsx.snap
  13. 145
    0
      server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap
  14. 33
    0
      server/sonar-web/src/main/js/components/tutorials/components/LabelActionPair.tsx
  15. 36
    0
      server/sonar-web/src/main/js/components/tutorials/components/LabelValuePair.tsx
  16. 44
    0
      server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx
  17. 44
    0
      server/sonar-web/src/main/js/components/tutorials/components/SentenceWithHighlights.tsx
  18. 42
    0
      server/sonar-web/src/main/js/components/tutorials/components/Step.css
  19. 16
    12
      server/sonar-web/src/main/js/components/tutorials/components/Step.tsx
  20. 31
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelActionPair-test.tsx
  21. 33
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelValuePair-test.tsx
  22. 33
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithFilename-test.tsx
  23. 33
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithHighlights-test.tsx
  24. 12
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelActionPair-test.tsx.snap
  25. 20
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelValuePair-test.tsx.snap
  26. 19
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithFilename-test.tsx.snap
  27. 18
    0
      server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithHighlights-test.tsx.snap
  28. 143
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/BitbucketWebhookStep.tsx
  29. 89
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx
  30. 126
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx
  31. 113
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx
  32. 79
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx
  33. 51
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/BitbucketWebhookStep-test.tsx
  34. 91
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx
  35. 73
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx
  36. 44
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx
  37. 36
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/PreRequisitesStep-test.tsx
  38. 228
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/BitbucketWebhookStep-test.tsx.snap
  39. 82
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap
  40. 526
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap
  41. 119
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/MultiBranchPipelineStep-test.tsx.snap
  42. 70
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap
  43. 73
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CreateJenkinsfileBulletPoint.tsx
  44. 64
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx
  45. 90
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/MSBuild.tsx
  46. 62
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx
  47. 60
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx
  48. 36
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/CreateJenkinsfileBulletPoint-test.tsx
  49. 32
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Gradle-test.tsx
  50. 32
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/MSBuild-test.tsx
  51. 32
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Maven-test.tsx
  52. 32
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Other-test.tsx
  53. 77
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/CreateJenkinsfileBulletPoint-test.tsx.snap
  54. 37
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Gradle-test.tsx.snap
  55. 91
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/MSBuild-test.tsx.snap
  56. 33
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Maven-test.tsx.snap
  57. 31
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap
  58. 28
    0
      server/sonar-web/src/main/js/components/tutorials/jenkins/test-utils.ts
  59. 1
    16
      server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx
  60. 0
    1
      server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx
  61. 0
    77
      server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap
  62. 23
    0
      server/sonar-web/src/main/js/components/tutorials/styles.css
  63. 31
    0
      server/sonar-web/src/main/js/components/tutorials/types.ts
  64. 1
    7
      server/sonar-web/src/main/js/components/tutorials/utils.ts
  65. 41
    0
      server/sonar-web/src/main/js/helpers/__tests__/alm-settings.ts
  66. 38
    0
      server/sonar-web/src/main/js/helpers/alm-settings.ts
  67. 25
    1
      server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts
  68. 6
    0
      server/sonar-web/src/main/js/types/alm-settings.ts
  69. 99
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 1
- 0
server/sonar-web/public/images/tutorials/commit.svg View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="27" height="27"><g fill="none" fill-rule="evenodd" transform="rotate(-90 13.5 18.5)"><path fill="#236A97" fill-rule="nonzero" d="M23.755102 18.5408163L19.7526885 14.5v3.0306122H12v2.0204082h7.7526885v3.0306123z"/><path d="M0 0h36v36H0z"/><circle cx="18.5" cy="18.5" r="12" stroke="#236A97" stroke-width="3"/></g></svg>

+ 1
- 0
server/sonar-web/public/images/tutorials/jenkins.svg
File diff suppressed because it is too large
View File


+ 1
- 0
server/sonar-web/public/images/tutorials/refresh.svg View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28"><g fill="none" fill-rule="evenodd" transform="translate(-4 -4)"><path d="M0 0h36v36H0z"/><path fill="#236A97" fill-rule="nonzero" d="M31.3333333 15.2148148H21.28821l4.0595336-4.1777778c-4.0447178-3.99999996-10.5933086-4.14814811-14.6380264-.1481481-4.04471779 4.0148148-4.04471779 10.4888889 0 14.5037037 4.0447178 4.0148148 10.5933086 4.0148148 14.6380264 0 2.014951-1.9851852 3.0224265-4.3111111 3.0224265-7.2444445h2.9631632c0 2.9333334-1.3037918 6.7407408-3.9113754 9.3185186-5.2003515 5.1555555-13.6453667 5.1555555-18.84571821 0-5.18553566-5.1407408-5.2299831-13.4962963-.02963163-18.63703707 5.20035144-5.14074074 13.54165594-5.14074074 18.74200744 0l4.0447178-4.16296296V15.2148148zm-12.5934437-3.1407407v6.2962963l5.1855356 3.0814815-1.0667387 1.7925925-6.3411693-3.7629629v-7.4074074h2.2223724z"/></g></svg>

+ 5
- 1
server/sonar-web/src/main/js/api/alm-settings.ts View File

@@ -34,7 +34,11 @@ import {
} from '../types/alm-settings';

export function getAlmDefinitions(): Promise<AlmSettingsBindingDefinitions> {
return getJSON('/api/alm_settings/list_definitions').catch(throwGlobalError);
return getAlmDefinitionsNoCatch().catch(throwGlobalError);
}

export function getAlmDefinitionsNoCatch(): Promise<AlmSettingsBindingDefinitions> {
return getJSON('/api/alm_settings/list_definitions');
}

export function getAlmSettings(project?: string): Promise<AlmSettingsInstance[]> {

+ 2
- 2
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx View File

@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import AnalyzeTutorial from '../../../components/tutorials/manual/AnalyzeTutorial';
import TutorialSelection from '../../../components/tutorials/TutorialSelection';
import { getBranchLikeDisplayName, isBranch, isMainBranch } from '../../../helpers/branch-like';
import { isLoggedIn } from '../../../helpers/users';
import { getCurrentUser, Store } from '../../../store/rootReducer';
@@ -87,7 +87,7 @@ export function EmptyOverview(props: Props) {
{isLoggedIn(currentUser) ? (
<>
{showWarning && <Alert variant="warning">{warning}</Alert>}
{showTutorial && <AnalyzeTutorial component={component} currentUser={currentUser} />}
{showTutorial && <TutorialSelection component={component} currentUser={currentUser} />}
</>
) : (
<Alert variant="warning">{warning}</Alert>

+ 1
- 1
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap View File

@@ -4,7 +4,7 @@ exports[`renders correctly 1`] = `
<div
className="page page-limited"
>
<AnalyzeTutorial
<withRouter(TutorialSelection)
component={
Object {
"breadcrumbs": Array [],

+ 1
- 0
server/sonar-web/src/main/js/components/common/CodeSnippet.css View File

@@ -40,6 +40,7 @@
font-size: 11px;
font-weight: normal;
user-select: none;
background: var(--gray40);
}

.code-snippet > button:hover,

+ 109
- 0
server/sonar-web/src/main/js/components/tutorials/TutorialSelection.tsx View File

@@ -0,0 +1,109 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { WithRouterProps } from 'react-router';
import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../api/alm-settings';
import { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings';
import { withRouter } from '../hoc/withRouter';
import './styles.css';
import TutorialSelectionRenderer from './TutorialSelectionRenderer';
import { TutorialModes } from './types';

interface Props extends Pick<WithRouterProps, 'router' | 'location'> {
component: T.Component;
currentUser: T.LoggedInUser;
}

interface State {
almBinding?: AlmBindingDefinition;
forceManual: boolean;
loading: boolean;
projectBinding?: ProjectAlmBindingResponse;
}

export class TutorialSelection extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
forceManual: true,
loading: true
};

componentDidMount() {
this.mounted = true;
this.fetchAlmBindings();
}

fetchAlmBindings = async () => {
const { component } = this.props;

const [almDefinitions, projectBinding] = await Promise.all([
getAlmDefinitionsNoCatch().catch(() => undefined),
getProjectAlmBinding(component.key).catch(() => undefined)
]);

if (this.mounted) {
// We only support Bitbucket for now.
if (projectBinding === undefined || projectBinding.alm !== AlmKeys.Bitbucket) {
this.setState({ loading: false, forceManual: true });
} else {
let almBinding;
if (almDefinitions !== undefined) {
almBinding = almDefinitions[projectBinding.alm].find(d => d.key === projectBinding.key);
}
this.setState({ almBinding, forceManual: false, projectBinding, loading: false });
}
}
};

handleSelectTutorial = (selectedTutorial: TutorialModes) => {
const {
router,
location: { pathname, query }
} = this.props;

router.push({
pathname,
query: { ...query, selectedTutorial }
});
};

render() {
const { component, currentUser, location } = this.props;
const { almBinding, forceManual, loading, projectBinding } = this.state;

const selectedTutorial: TutorialModes | undefined = forceManual
? TutorialModes.Manual
: location.query?.selectedTutorial;

return (
<TutorialSelectionRenderer
almBinding={almBinding}
component={component}
currentUser={currentUser}
loading={loading}
onSelectTutorial={this.handleSelectTutorial}
projectBinding={projectBinding}
selectedTutorial={selectedTutorial}
/>
);
}
}

export default withRouter(TutorialSelection);

+ 101
- 0
server/sonar-web/src/main/js/components/tutorials/TutorialSelectionRenderer.tsx View File

@@ -0,0 +1,101 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../types/alm-settings';
import JenkinsTutorial from './jenkins/JenkinsTutorial';
import ManualTutorial from './manual/ManualTutorial';
import { TutorialModes } from './types';

export interface TutorialSelectionRendererProps {
almBinding?: AlmBindingDefinition;
component: T.Component;
currentUser: T.LoggedInUser;
loading: boolean;
onSelectTutorial: (mode: TutorialModes) => void;
projectBinding?: ProjectAlmBindingResponse;
selectedTutorial?: TutorialModes;
}

export default function TutorialSelectionRenderer(props: TutorialSelectionRendererProps) {
const { almBinding, component, currentUser, loading, projectBinding, selectedTutorial } = props;

if (loading) {
return <i className="spinner" />;
}

return (
<>
{selectedTutorial === undefined && (
<div className="tutorial-selection">
<header className="spacer-top spacer-bottom padded">
<h1 className="text-center big-spacer-bottom">
{translate('onboarding.tutorial.choose_method')}
</h1>
</header>

<div className="display-flex-space-around">
<button
className="button button-huge display-flex-column tutorial-mode-jenkins"
onClick={() => props.onSelectTutorial(TutorialModes.Jenkins)}
type="button">
<img
alt="" // Should be ignored by screen readers
height={80}
src={`${getBaseUrl()}/images/tutorials/jenkins.svg`}
/>
<div className="medium big-spacer-top">
{translate('onboarding.tutorial.choose_method.jenkins')}
</div>
</button>

<button
className="button button-huge display-flex-column tutorial-mode-manual"
onClick={() => props.onSelectTutorial(TutorialModes.Manual)}
type="button">
<img
alt="" // Should be ignored by screen readers
height={80}
src={`${getBaseUrl()}/images/sonarcloud/analysis/manual.svg`}
/>
<div className="medium big-spacer-top">
{translate('onboarding.tutorial.choose_method.manual')}
</div>
</button>
</div>
</div>
)}

{selectedTutorial === TutorialModes.Manual && (
<ManualTutorial component={component} currentUser={currentUser} />
)}

{selectedTutorial === TutorialModes.Jenkins && projectBinding !== undefined && (
<JenkinsTutorial
almBinding={almBinding}
component={component}
projectBinding={projectBinding}
/>
)}
</>
);
}

+ 109
- 0
server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelection-test.tsx View File

@@ -0,0 +1,109 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { getAlmDefinitionsNoCatch, getProjectAlmBinding } from '../../../api/alm-settings';
import { mockBitbucketBindingDefinition } from '../../../helpers/mocks/alm-settings';
import {
mockComponent,
mockLocation,
mockLoggedInUser,
mockRouter
} from '../../../helpers/testMocks';
import { AlmKeys } from '../../../types/alm-settings';
import { TutorialSelection } from '../TutorialSelection';
import { TutorialModes } from '../types';

jest.mock('../../../api/alm-settings', () => ({
getProjectAlmBinding: jest.fn().mockRejectedValue(null),
getAlmDefinitionsNoCatch: jest.fn().mockRejectedValue(null)
}));

beforeEach(jest.clearAllMocks);

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

it('should select manual if project is not bound', async () => {
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().forceManual).toBe(true);
});

it('should not select anything if project is bound to Bitbucket', async () => {
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Bitbucket });
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().forceManual).toBe(false);
});

it('should select manual if project is bound to any other ALM', async () => {
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.GitLab });
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().forceManual).toBe(true);
});

it('should correctly find the global ALM binding definition', async () => {
const key = 'foo';
const almBinding = mockBitbucketBindingDefinition({ key });
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Bitbucket, key });
(getAlmDefinitionsNoCatch as jest.Mock).mockResolvedValueOnce({
[AlmKeys.Bitbucket]: [almBinding]
});
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(wrapper.state().almBinding).toBe(almBinding);
});

it('should handle selection', () => {
const push = jest.fn();
const wrapper = shallowRender({ router: mockRouter({ push }) });
const instance = wrapper.instance();

instance.handleSelectTutorial(TutorialModes.Manual);
expect(push).toHaveBeenLastCalledWith(
expect.objectContaining({
query: { selectedTutorial: TutorialModes.Manual }
})
);

instance.handleSelectTutorial(TutorialModes.Jenkins);
expect(push).toHaveBeenLastCalledWith(
expect.objectContaining({
query: { selectedTutorial: TutorialModes.Jenkins }
})
);
});

function shallowRender(props: Partial<TutorialSelection['props']> = {}) {
return shallow<TutorialSelection>(
<TutorialSelection
component={mockComponent()}
currentUser={mockLoggedInUser()}
location={mockLocation()}
router={mockRouter()}
{...props}
/>
);
}

+ 70
- 0
server/sonar-web/src/main/js/components/tutorials/__tests__/TutorialSelectionRenderer-test.tsx View File

@@ -0,0 +1,70 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { click } from 'sonar-ui-common/helpers/testUtils';
import {
mockBitbucketBindingDefinition,
mockProjectBitbucketBindingGet
} from '../../../helpers/mocks/alm-settings';
import { mockComponent, mockLoggedInUser } from '../../../helpers/testMocks';
import TutorialSelectionRenderer, {
TutorialSelectionRendererProps
} from '../TutorialSelectionRenderer';
import { TutorialModes } from '../types';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('selection');
expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
expect(shallowRender({ selectedTutorial: TutorialModes.Manual })).toMatchSnapshot(
'manual tutorial'
);
expect(
shallowRender({
selectedTutorial: TutorialModes.Jenkins,
projectBinding: mockProjectBitbucketBindingGet()
})
).toMatchSnapshot('jenkins tutorial');
});

it('should allow mode selection', () => {
const onSelectTutorial = jest.fn();
const wrapper = shallowRender({ onSelectTutorial });

click(wrapper.find('button.tutorial-mode-jenkins'));
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Jenkins);

click(wrapper.find('button.tutorial-mode-manual'));
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Manual);
});

function shallowRender(props: Partial<TutorialSelectionRendererProps> = {}) {
return shallow<TutorialSelectionRendererProps>(
<TutorialSelectionRenderer
almBinding={mockBitbucketBindingDefinition()}
component={mockComponent()}
currentUser={mockLoggedInUser()}
loading={false}
onSelectTutorial={jest.fn()}
{...props}
/>
);
}

+ 41
- 0
server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelection-test.tsx.snap View File

@@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<TutorialSelectionRenderer
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
currentUser={
Object {
"groups": Array [],
"isLoggedIn": true,
"login": "luke",
"name": "Skywalker",
"scmAccounts": Array [],
}
}
loading={true}
onSelectTutorial={[Function]}
selectedTutorial="manual"
/>
`;

+ 145
- 0
server/sonar-web/src/main/js/components/tutorials/__tests__/__snapshots__/TutorialSelectionRenderer-test.tsx.snap View File

@@ -0,0 +1,145 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: jenkins tutorial 1`] = `
<Fragment>
<JenkinsTutorial
almBinding={
Object {
"key": "key",
"personalAccessToken": "asdf1234",
"url": "http://bbs.enterprise.com",
}
}
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
projectBinding={
Object {
"alm": "bitbucket",
"key": "foo",
"repository": "PROJECT_KEY",
"slug": "repo-slug",
}
}
/>
</Fragment>
`;

exports[`should render correctly: loading 1`] = `
<i
className="spinner"
/>
`;

exports[`should render correctly: manual tutorial 1`] = `
<Fragment>
<ManualTutorial
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
currentUser={
Object {
"groups": Array [],
"isLoggedIn": true,
"login": "luke",
"name": "Skywalker",
"scmAccounts": Array [],
}
}
/>
</Fragment>
`;

exports[`should render correctly: selection 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-space-around"
>
<button
className="button button-huge display-flex-column 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 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>
`;

+ 33
- 0
server/sonar-web/src/main/js/components/tutorials/components/LabelActionPair.tsx View File

@@ -0,0 +1,33 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface LabelActionPairProps {
translationKey: string;
}

export default function LabelActionPair({ translationKey }: LabelActionPairProps) {
return (
<>
<strong>{translate(translationKey, 'label')}:</strong> {translate(translationKey, 'action')}
</>
);
}

+ 36
- 0
server/sonar-web/src/main/js/components/tutorials/components/LabelValuePair.tsx View File

@@ -0,0 +1,36 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface LabelValuePairProps {
translationKey: string;
value: string;
}

export default function LabelValuePair({ translationKey, value }: LabelValuePairProps) {
return (
<div className="display-flex-center">
<strong className="little-spacer-right">{translate(translationKey, 'label')}:</strong> {value}
<ClipboardIconButton className="little-spacer-left" copyValue={value} />
</div>
);
}

+ 44
- 0
server/sonar-web/src/main/js/components/tutorials/components/SentenceWithFilename.tsx View File

@@ -0,0 +1,44 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface SentenceWithFilenameProps {
filename: string;
translationKey: string;
}

export default function SentenceWithFilename({
filename,
translationKey
}: SentenceWithFilenameProps) {
return (
<span className="markdown">
<FormattedMessage
defaultMessage={translate(translationKey, 'sentence')}
id={`${translationKey}.sentence`}
values={{
file: <code>{filename}</code>
}}
/>
</span>
);
}

+ 44
- 0
server/sonar-web/src/main/js/components/tutorials/components/SentenceWithHighlights.tsx View File

@@ -0,0 +1,44 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface SentenceWithHighlightsProps {
highlightKeys: string[];
translationKey: string;
}

export default function SentenceWithHighlights({
highlightKeys,
translationKey
}: SentenceWithHighlightsProps) {
const values: T.Dict<JSX.Element> = {};
highlightKeys.forEach(key => {
values[key] = <strong>{translate(translationKey, 'sentence', key)}</strong>;
});
return (
<FormattedMessage
defaultMessage={translate(translationKey, 'sentence')}
id={`${translationKey}.sentence`}
values={values}
/>
);
}

+ 42
- 0
server/sonar-web/src/main/js/components/tutorials/components/Step.css View File

@@ -20,6 +20,11 @@
.onboarding-step {
position: relative;
padding-left: 34px;
margin-bottom: var(--gridSize);
}

.onboarding-step.no-step-number {
padding-left: 0;
}

.onboarding-step:not(.is-open):not(.is-finished) {
@@ -57,3 +62,40 @@
cursor: pointer;
outline: none;
}

.onboarding-step ol.list-styled {
list-style: none;
padding-left: 0;
counter-reset: step-counter;
}

.onboarding-step .markdown {
line-height: inherit;
}

.onboarding-step ul.list-styled li {
margin-top: var(--gridSize);
margin-bottom: var(--gridSize);
}

.onboarding-step ol.list-styled > li {
position: relative;
counter-increment: step-counter;
margin-bottom: calc(2 * var(--gridSize));
padding-left: calc(4 * var(--gridSize));
}

.onboarding-step ol.list-styled > li::before {
content: counter(step-counter);
color: white;
background-color: var(--blue);
display: inline-flex;
border-radius: 50%;
width: 16px;
position: absolute;
left: 0;
font-size: 10px;
height: 16px;
justify-content: center;
align-items: center;
}

+ 16
- 12
server/sonar-web/src/main/js/components/tutorials/components/Step.tsx View File

@@ -24,25 +24,29 @@ import './Step.css';

interface Props {
finished?: boolean;
onOpen: VoidFunction;
onOpen?: VoidFunction;
open: boolean;
renderForm: () => React.ReactNode;
renderResult: () => React.ReactNode;
stepNumber: number;
renderResult?: () => React.ReactNode;
stepNumber?: number;
stepTitle: React.ReactNode;
}

export default function Step(props: Props) {
const { finished, open, stepNumber, stepTitle } = props;
const className = classNames('boxed-group', 'onboarding-step', {
'is-open': props.open,
'is-finished': props.finished
'is-open': open,
'is-finished': finished,
'no-step-number': stepNumber === undefined
});

const clickable = !props.open && props.finished;
const clickable = !open && finished && props.onOpen !== undefined;

const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault();
props.onOpen();
if (props.onOpen !== undefined) {
props.onOpen();
}
};

return (
@@ -51,13 +55,13 @@ export default function Step(props: Props) {
onClick={clickable ? handleClick : undefined}
role={clickable ? 'button' : undefined}
tabIndex={clickable ? 0 : undefined}>
<div className="onboarding-step-number">{props.stepNumber}</div>
{!props.open && props.renderResult()}
{stepNumber !== undefined && <div className="onboarding-step-number">{stepNumber}</div>}
{!open && props.renderResult && props.renderResult()}
<div className="boxed-group-header">
<h2>{props.stepTitle}</h2>
<h2>{stepTitle}</h2>
</div>
{!props.open && <div className="boxed-group-inner" />}
<div className={classNames({ hidden: !props.open })}>{props.renderForm()}</div>
{!open && <div className="boxed-group-inner" />}
<div className={classNames({ hidden: !open })}>{props.renderForm()}</div>
</div>
);
}

+ 31
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelActionPair-test.tsx View File

@@ -0,0 +1,31 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import LabelActionPair, { LabelActionPairProps } from '../LabelActionPair';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<LabelActionPairProps> = {}) {
return shallow<LabelActionPairProps>(<LabelActionPair translationKey="foo" {...props} />);
}

+ 33
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/LabelValuePair-test.tsx View File

@@ -0,0 +1,33 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import LabelValuePair, { LabelValuePairProps } from '../LabelValuePair';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<LabelValuePairProps> = {}) {
return shallow<LabelValuePairProps>(
<LabelValuePair translationKey="foo" value="bar" {...props} />
);
}

+ 33
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithFilename-test.tsx View File

@@ -0,0 +1,33 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import SentenceWithFilename, { SentenceWithFilenameProps } from '../SentenceWithFilename';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<SentenceWithFilenameProps> = {}) {
return shallow<SentenceWithFilenameProps>(
<SentenceWithFilename filename="foo.txt" translationKey="bar" {...props} />
);
}

+ 33
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/SentenceWithHighlights-test.tsx View File

@@ -0,0 +1,33 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import SentenceWithHighlights, { SentenceWithHighlightsProps } from '../SentenceWithHighlights';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<SentenceWithHighlightsProps> = {}) {
return shallow<SentenceWithHighlightsProps>(
<SentenceWithHighlights highlightKeys={['hello', 'world']} translationKey="foo" {...props} />
);
}

+ 12
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelActionPair-test.tsx.snap View File

@@ -0,0 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<strong>
foo.label
:
</strong>
foo.action
</Fragment>
`;

+ 20
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/LabelValuePair-test.tsx.snap View File

@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="display-flex-center"
>
<strong
className="little-spacer-right"
>
foo.label
:
</strong>
bar
<ClipboardIconButton
className="little-spacer-left"
copyValue="bar"
/>
</div>
`;

+ 19
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithFilename-test.tsx.snap View File

@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<span
className="markdown"
>
<FormattedMessage
defaultMessage="bar.sentence"
id="bar.sentence"
values={
Object {
"file": <code>
foo.txt
</code>,
}
}
/>
</span>
`;

+ 18
- 0
server/sonar-web/src/main/js/components/tutorials/components/__tests__/__snapshots__/SentenceWithHighlights-test.tsx.snap View File

@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<FormattedMessage
defaultMessage="foo.sentence"
id="foo.sentence"
values={
Object {
"hello": <strong>
foo.sentence.hello
</strong>,
"world": <strong>
foo.sentence.world
</strong>,
}
}
/>
`;

+ 143
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/BitbucketWebhookStep.tsx View File

@@ -0,0 +1,143 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Button, ButtonLink } from 'sonar-ui-common/components/controls/buttons';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
BitbucketBindingDefinition,
ProjectBitbucketBindingResponse
} from '../../../types/alm-settings';
import CodeSnippet from '../../common/CodeSnippet';
import LabelActionPair from '../components/LabelActionPair';
import SentenceWithHighlights from '../components/SentenceWithHighlights';
import Step from '../components/Step';

export interface BitbucketWebhookStepProps {
almBinding?: BitbucketBindingDefinition;
finished: boolean;
onDone: () => void;
onOpen: () => void;
open: boolean;
projectBinding: ProjectBitbucketBindingResponse;
}

export default function BitbucketWebhookStep(props: BitbucketWebhookStepProps) {
const { almBinding, finished, open, projectBinding } = props;
return (
<Step
finished={finished}
onOpen={props.onOpen}
open={open}
renderForm={() => (
<div className="boxed-group-inner">
<p className="big-spacer-bottom">
<FormattedMessage
defaultMessage={translate(
'onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence'
)}
id="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence"
values={{
link: (
<ButtonLink onClick={props.onDone}>
{translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link')}
</ButtonLink>
)
}}
/>
</p>
<ol className="list-styled">
<li>
<FormattedMessage
defaultMessage={translate(
'onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence'
)}
id="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence"
values={{
link:
almBinding !== undefined ? (
<a
href={`${almBinding.url.replace(
/\/$/,
''
)}/plugins/servlet/webhooks/projects/${projectBinding.repository}/repos/${
projectBinding.slug
}/create`}
rel="noopener noreferrer"
target="_blank">
{translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link')}
</a>
) : (
translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link')
)
}}
/>
<ul className="list-styled">
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name" />
</li>
<li className="abs-width-600">
<p>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url" />
</p>
<CodeSnippet
isOneLine={true}
snippet={`***JENKINS_URL***/bitbucket-scmsource-hook/notify?server_url=${
almBinding !== undefined ? almBinding.url : '***BITBUCKET_URL***'
}`}
/>
<Alert variant="info">
{translate(
'onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning'
)}
</Alert>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={['events']}
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2"
/>
<ul className="list-styled">
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo" />
</li>
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr" />
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={['create']}
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step3"
/>
</li>
</ol>
<Button onClick={props.onDone}>{translate('continue')}</Button>
</div>
)}
stepNumber={2}
stepTitle={translate('onboarding.tutorial.with.jenkins.bitbucket_webhook.title')}
/>
);
}

+ 89
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsTutorial.tsx View File

@@ -0,0 +1,89 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import {
isBitbucketBindingDefinition,
isProjectBitbucketBindingResponse
} from '../../../helpers/alm-settings';
import { AlmBindingDefinition, ProjectAlmBindingResponse } from '../../../types/alm-settings';
import BitbucketWebhookStep from './BitbucketWebhookStep';
import JenkinsfileStep from './JenkinsfileStep';
import MultiBranchPipelineStep from './MultiBranchPipelineStep';
import PreRequisitesStep from './PreRequisitesStep';

export interface JenkinsTutorialProps {
almBinding?: AlmBindingDefinition;
component: T.Component;
projectBinding: ProjectAlmBindingResponse;
}

enum Steps {
PreRequisites = 0,
MultiBranchPipeline = 1,
BitbucketWebhook = 2,
Jenkinsfile = 3
}

export default function JenkinsTutorial(props: JenkinsTutorialProps) {
const { almBinding, component, projectBinding } = props;
const [step, setStep] = React.useState(Steps.PreRequisites);

// Failsafe; should never happen.
if (!isProjectBitbucketBindingResponse(projectBinding)) {
return (
<Alert variant="error">{translate('onboarding.tutorial.with.jenkins.only_bitbucket')}</Alert>
);
}

return (
<>
<div className="page-header big-spacer-bottom">
<h1 className="page-title">{translate('onboarding.tutorial.with.jenkins.title')}</h1>
</div>

<PreRequisitesStep
onDone={() => setStep(Steps.MultiBranchPipeline)}
onOpen={() => setStep(Steps.PreRequisites)}
open={step === Steps.PreRequisites}
/>

<MultiBranchPipelineStep
finished={step > Steps.MultiBranchPipeline}
onDone={() => setStep(Steps.BitbucketWebhook)}
onOpen={() => setStep(Steps.MultiBranchPipeline)}
open={step === Steps.MultiBranchPipeline}
projectBinding={projectBinding}
/>

<BitbucketWebhookStep
almBinding={almBinding && isBitbucketBindingDefinition(almBinding) ? almBinding : undefined}
finished={step > Steps.BitbucketWebhook}
onDone={() => setStep(Steps.Jenkinsfile)}
onOpen={() => setStep(Steps.BitbucketWebhook)}
open={step === Steps.BitbucketWebhook}
projectBinding={projectBinding}
/>

<JenkinsfileStep component={component} open={step === Steps.Jenkinsfile} />
</>
);
}

+ 126
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/JenkinsfileStep.tsx View File

@@ -0,0 +1,126 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
import RenderOptions from '../components/RenderOptions';
import SentenceWithHighlights from '../components/SentenceWithHighlights';
import Step from '../components/Step';
import Gradle from './buildtool-steps/Gradle';
import Maven from './buildtool-steps/Maven';
import MSBuild from './buildtool-steps/MSBuild';
import Other from './buildtool-steps/Other';

export interface JenkinsfileStepProps {
component: T.Component;
open: boolean;
}

export enum BuildTools {
Maven = 'maven',
Gradle = 'gradle',
MSBuild = 'msbuild',
Other = 'other'
}

const BUILDTOOL_COMPONENT_MAP: {
[x in BuildTools]: React.ComponentType<{ component: T.Component }>;
} = {
[BuildTools.Maven]: Maven,
[BuildTools.Gradle]: Gradle,
[BuildTools.MSBuild]: MSBuild,
[BuildTools.Other]: Other
};

export default function JenkinsfileStep(props: JenkinsfileStepProps) {
const { component, open } = props;
const [buildTool, setBuildTool] = React.useState<BuildTools | undefined>(undefined);
return (
<Step
finished={false}
open={open}
renderForm={() => (
<div className="boxed-group-inner">
<ol className="list-styled">
<li>
{translate('onboarding.build')}
<RenderOptions
checked={buildTool}
name="buildtool"
onCheck={value => setBuildTool(value as BuildTools)}
optionLabelKey="onboarding.build"
options={Object.values(BuildTools)}
/>
</li>
{buildTool !== undefined &&
React.createElement(BUILDTOOL_COMPONENT_MAP[buildTool], { component })}
</ol>
{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>{translate('onboarding.tutorial.with.jenkins.commit.why')}</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>
</>
)}
</div>
)}
stepNumber={3}
stepTitle={translate('onboarding.tutorial.with.jenkins.jenkinsfile.title')}
/>
);
}

+ 113
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/MultiBranchPipelineStep.tsx View File

@@ -0,0 +1,113 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { ProjectBitbucketBindingResponse } from '../../../types/alm-settings';
import LabelActionPair from '../components/LabelActionPair';
import LabelValuePair from '../components/LabelValuePair';
import SentenceWithHighlights from '../components/SentenceWithHighlights';
import Step from '../components/Step';

export interface MultiBranchPipelineStepProps {
finished: boolean;
onDone: () => void;
onOpen: () => void;
open: boolean;
projectBinding: ProjectBitbucketBindingResponse;
}

export default function MultiBranchPipelineStep(props: MultiBranchPipelineStepProps) {
const { finished, open, projectBinding } = props;
return (
<Step
finished={finished}
onOpen={props.onOpen}
open={open}
renderForm={() => (
<div className="boxed-group-inner">
<p className="big-spacer-bottom">
{translate('onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro')}
</p>
<ol className="list-styled">
<li>
<SentenceWithHighlights
highlightKeys={['new_item', 'type']}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1"
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={['tab']}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2"
/>
<ul className="list-styled">
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server" />
</li>
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds" />
</li>
<li>
<LabelValuePair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner"
value={projectBinding.repository}
/>
</li>
<li>
<LabelValuePair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo"
value={projectBinding.slug}
/>
</li>
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour" />
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={['tab']}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3"
/>
<ul className="list-styled">
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode" />
</li>
<li>
<LabelActionPair translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path" />
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={['save']}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4"
/>
</li>
</ol>
<Button onClick={props.onDone}>{translate('continue')}</Button>
</div>
)}
stepNumber={1}
stepTitle={translate('onboarding.tutorial.with.jenkins.multi_branch_pipeline.title')}
/>
);
}

+ 79
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/PreRequisitesStep.tsx View File

@@ -0,0 +1,79 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import { translate } from 'sonar-ui-common/helpers/l10n';
import SentenceWithHighlights from '../components/SentenceWithHighlights';
import Step from '../components/Step';

export interface PreRequisitesStepProps {
onDone: () => void;
onOpen: () => void;
open: boolean;
}

export default function PreRequisitesStep(props: PreRequisitesStepProps) {
const { open } = props;
return (
<Step
finished={!open}
onOpen={props.onOpen}
open={open}
renderForm={() => (
<div className="boxed-group-inner">
<p className="big-spacer-bottom">
<SentenceWithHighlights
highlightKeys={['must_have']}
translationKey="onboarding.tutorial.with.jenkins.prereqs.intro"
/>
</p>
<ul className="list-styled big-spacer-bottom">
<li>{translate('onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source')}</li>
<li>{translate('onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner')}</li>
</ul>
<p className="big-spacer-bottom">
<FormattedMessage
defaultMessage={translate(
'onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide'
)}
id="onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide"
values={{
link: (
<Link target="_blank" to="/documentation/analysis/jenkins/">
{translate('onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link')}
</Link>
)
}}
/>
</p>
<p className="big-spacer-bottom">
{translate('onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations')}
</p>
<Button onClick={props.onDone}>
{translate('onboarding.tutorial.with.jenkins.prereqs.done')}
</Button>
</div>
)}
stepTitle={translate('onboarding.tutorial.with.jenkins.prereqs.title')}
/>
);
}

+ 51
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/BitbucketWebhookStep-test.tsx View File

@@ -0,0 +1,51 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import {
mockBitbucketBindingDefinition,
mockProjectBitbucketBindingGet
} from '../../../../helpers/mocks/alm-settings';
import BitbucketWebhookStep, { BitbucketWebhookStepProps } from '../BitbucketWebhookStep';
import { renderStepContent } from '../test-utils';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot('Step wrapper');
expect(renderStepContent(wrapper)).toMatchSnapshot('content');
expect(renderStepContent(wrapper.setProps({ almBinding: undefined }))).toMatchSnapshot(
'no alm binding'
);
});

function shallowRender(props: Partial<BitbucketWebhookStepProps> = {}) {
return shallow<BitbucketWebhookStepProps>(
<BitbucketWebhookStep
almBinding={mockBitbucketBindingDefinition()}
finished={false}
onDone={jest.fn()}
onOpen={jest.fn()}
open={true}
projectBinding={mockProjectBitbucketBindingGet()}
{...props}
/>
);
}

+ 91
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsTutorial-test.tsx View File

@@ -0,0 +1,91 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import {
mockProjectAlmBindingResponse,
mockProjectBitbucketBindingGet
} from '../../../../helpers/mocks/alm-settings';
import { mockComponent } from '../../../../helpers/testMocks';
import BitbucketWebhookStep from '../BitbucketWebhookStep';
import JenkinsfileStep from '../JenkinsfileStep';
import JenkinsTutorial, { JenkinsTutorialProps } from '../JenkinsTutorial';
import MultiBranchPipelineStep from '../MultiBranchPipelineStep';
import PreRequisitesStep from '../PreRequisitesStep';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ projectBinding: mockProjectAlmBindingResponse() })).toMatchSnapshot(
'not Bitbucket binding'
);
});

it('should correctly navigate between steps', () => {
const wrapper = shallowRender();

expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(true);
expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(false);
expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(false);
expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(false);

// Pre-reqs done.
wrapper.find(PreRequisitesStep).prop('onDone')();
expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(false);
expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(true);
expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(false);
expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(false);

// Multibranch done.
wrapper.find(MultiBranchPipelineStep).prop('onDone')();
expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(false);
expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(false);
expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(true);
expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(false);

// Webhook done.
wrapper.find(BitbucketWebhookStep).prop('onDone')();
expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(false);
expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(false);
expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(false);
expect(wrapper.find(JenkinsfileStep).prop('open')).toBe(true);

// Open Pre-reqs.
wrapper.find(PreRequisitesStep).prop('onOpen')();
expect(wrapper.find(PreRequisitesStep).prop('open')).toBe(true);

// Open Multibranch.
wrapper.find(MultiBranchPipelineStep).prop('onOpen')();
expect(wrapper.find(MultiBranchPipelineStep).prop('open')).toBe(true);

// Open Webhook.
wrapper.find(BitbucketWebhookStep).prop('onOpen')();
expect(wrapper.find(BitbucketWebhookStep).prop('open')).toBe(true);
});

function shallowRender(props: Partial<JenkinsTutorialProps> = {}) {
return shallow<JenkinsTutorialProps>(
<JenkinsTutorial
component={mockComponent()}
projectBinding={mockProjectBitbucketBindingGet()}
{...props}
/>
);
}

+ 73
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/JenkinsfileStep-test.tsx View File

@@ -0,0 +1,73 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow, ShallowWrapper } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../../helpers/testMocks';
import RenderOptions from '../../components/RenderOptions';
import Step from '../../components/Step';
import JenkinsfileStep, { BuildTools, JenkinsfileStepProps } from '../JenkinsfileStep';
import { renderStepContent } from '../test-utils';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot('Step wrapper');
expect(renderStepContent(wrapper)).toMatchSnapshot('initial content');
});

it('should render correctly for Maven', () => {
const wrapper = shallowRender();
selectBuildTool(wrapper, BuildTools.Maven);
expect(
wrapper
.find(Step)
.props()
.renderForm()
).toMatchSnapshot();
});

it('should render correctly for Gradle', () => {
const wrapper = shallowRender();
selectBuildTool(wrapper, BuildTools.Gradle);
expect(renderStepContent(wrapper)).toMatchSnapshot();
});

it('should render correctly for MSBuild', () => {
const wrapper = shallowRender();
selectBuildTool(wrapper, BuildTools.MSBuild);
expect(renderStepContent(wrapper)).toMatchSnapshot();
});

it('should render correctly for Other', () => {
const wrapper = shallowRender();
selectBuildTool(wrapper, BuildTools.Other);
expect(renderStepContent(wrapper)).toMatchSnapshot();
});

function selectBuildTool(wrapper: ShallowWrapper<JenkinsfileStepProps>, tool: BuildTools) {
const content = new ShallowWrapper(renderStepContent(wrapper) as JSX.Element);
content.find(RenderOptions).prop('onCheck')(tool);
}

function shallowRender(props: Partial<JenkinsfileStepProps> = {}) {
return shallow<JenkinsfileStepProps>(
<JenkinsfileStep component={mockComponent()} open={true} {...props} />
);
}

+ 44
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/MultiBranchPipelineStep-test.tsx View File

@@ -0,0 +1,44 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { mockProjectBitbucketBindingGet } from '../../../../helpers/mocks/alm-settings';
import MultiBranchPipelineStep, { MultiBranchPipelineStepProps } from '../MultiBranchPipelineStep';
import { renderStepContent } from '../test-utils';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot('Step wrapper');
expect(renderStepContent(wrapper)).toMatchSnapshot('content');
});

function shallowRender(props: Partial<MultiBranchPipelineStepProps> = {}) {
return shallow<MultiBranchPipelineStepProps>(
<MultiBranchPipelineStep
finished={false}
onDone={jest.fn()}
onOpen={jest.fn()}
open={true}
projectBinding={mockProjectBitbucketBindingGet()}
{...props}
/>
);
}

+ 36
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/PreRequisitesStep-test.tsx View File

@@ -0,0 +1,36 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import PreRequisitesStep, { PreRequisitesStepProps } from '../PreRequisitesStep';
import { renderStepContent } from '../test-utils';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot('Step wrapper');
expect(renderStepContent(wrapper)).toMatchSnapshot('content');
});

function shallowRender(props: Partial<PreRequisitesStepProps> = {}) {
return shallow<PreRequisitesStepProps>(
<PreRequisitesStep onDone={jest.fn()} onOpen={jest.fn()} open={false} {...props} />
);
}

+ 228
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/BitbucketWebhookStep-test.tsx.snap View File

@@ -0,0 +1,228 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: Step wrapper 1`] = `
<Step
finished={false}
onOpen={[MockFunction]}
open={true}
renderForm={[Function]}
stepNumber={2}
stepTitle="onboarding.tutorial.with.jenkins.bitbucket_webhook.title"
/>
`;

exports[`should render correctly: content 1`] = `
<div
className="boxed-group-inner"
>
<p
className="big-spacer-bottom"
>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence"
id="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence"
values={
Object {
"link": <ButtonLink
onClick={[MockFunction]}
>
onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link
</ButtonLink>,
}
}
/>
</p>
<ol
className="list-styled"
>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence"
id="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence"
values={
Object {
"link": <a
href="http://bbs.enterprise.com/plugins/servlet/webhooks/projects/PROJECT_KEY/repos/repo-slug/create"
rel="noopener noreferrer"
target="_blank"
>
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link
</a>,
}
}
/>
<ul
className="list-styled"
>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name"
/>
</li>
<li
className="abs-width-600"
>
<p>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url"
/>
</p>
<CodeSnippet
isOneLine={true}
snippet="***JENKINS_URL***/bitbucket-scmsource-hook/notify?server_url=http://bbs.enterprise.com"
/>
<Alert
variant="info"
>
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning
</Alert>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"events",
]
}
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2"
/>
<ul
className="list-styled"
>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo"
/>
</li>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr"
/>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"create",
]
}
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step3"
/>
</li>
</ol>
<Button
onClick={[MockFunction]}
>
continue
</Button>
</div>
`;

exports[`should render correctly: no alm binding 1`] = `
<div
className="boxed-group-inner"
>
<p
className="big-spacer-bottom"
>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence"
id="onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence"
values={
Object {
"link": <ButtonLink
onClick={[MockFunction]}
>
onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link
</ButtonLink>,
}
}
/>
</p>
<ol
className="list-styled"
>
<li>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence"
id="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence"
values={
Object {
"link": "onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link",
}
}
/>
<ul
className="list-styled"
>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name"
/>
</li>
<li
className="abs-width-600"
>
<p>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url"
/>
</p>
<CodeSnippet
isOneLine={true}
snippet="***JENKINS_URL***/bitbucket-scmsource-hook/notify?server_url=***BITBUCKET_URL***"
/>
<Alert
variant="info"
>
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning
</Alert>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"events",
]
}
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2"
/>
<ul
className="list-styled"
>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo"
/>
</li>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr"
/>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"create",
]
}
translationKey="onboarding.tutorial.with.jenkins.bitbucket_webhook.step3"
/>
</li>
</ol>
<Button
onClick={[MockFunction]}
>
continue
</Button>
</div>
`;

+ 82
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsTutorial-test.tsx.snap View File

@@ -0,0 +1,82 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: default 1`] = `
<Fragment>
<div
className="page-header big-spacer-bottom"
>
<h1
className="page-title"
>
onboarding.tutorial.with.jenkins.title
</h1>
</div>
<PreRequisitesStep
onDone={[Function]}
onOpen={[Function]}
open={true}
/>
<MultiBranchPipelineStep
finished={false}
onDone={[Function]}
onOpen={[Function]}
open={false}
projectBinding={
Object {
"alm": "bitbucket",
"key": "foo",
"repository": "PROJECT_KEY",
"slug": "repo-slug",
}
}
/>
<BitbucketWebhookStep
finished={false}
onDone={[Function]}
onOpen={[Function]}
open={false}
projectBinding={
Object {
"alm": "bitbucket",
"key": "foo",
"repository": "PROJECT_KEY",
"slug": "repo-slug",
}
}
/>
<JenkinsfileStep
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
open={false}
/>
</Fragment>
`;

exports[`should render correctly: not Bitbucket binding 1`] = `
<Alert
variant="error"
>
onboarding.tutorial.with.jenkins.only_bitbucket
</Alert>
`;

+ 526
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/JenkinsfileStep-test.tsx.snap View File

@@ -0,0 +1,526 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly for Gradle 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",
"msbuild",
"other",
]
}
/>
</li>
<Gradle
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
/>
</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
</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 for MSBuild 1`] = `
<div
className="boxed-group-inner"
>
<ol
className="list-styled"
>
<li>
onboarding.build
<RenderOptions
checked="msbuild"
name="buildtool"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"maven",
"gradle",
"msbuild",
"other",
]
}
/>
</li>
<MSBuild
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
/>
</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
</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 for Maven 1`] = `
<div
className="boxed-group-inner"
>
<ol
className="list-styled"
>
<li>
onboarding.build
<RenderOptions
checked="maven"
name="buildtool"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"maven",
"gradle",
"msbuild",
"other",
]
}
/>
</li>
<Maven
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
/>
</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
</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 for Other 1`] = `
<div
className="boxed-group-inner"
>
<ol
className="list-styled"
>
<li>
onboarding.build
<RenderOptions
checked="other"
name="buildtool"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"maven",
"gradle",
"msbuild",
"other",
]
}
/>
</li>
<Other
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
/>
</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
</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: Step wrapper 1`] = `
<Step
finished={false}
open={true}
renderForm={[Function]}
stepNumber={3}
stepTitle="onboarding.tutorial.with.jenkins.jenkinsfile.title"
/>
`;

exports[`should render correctly: initial content 1`] = `
<div
className="boxed-group-inner"
>
<ol
className="list-styled"
>
<li>
onboarding.build
<RenderOptions
name="buildtool"
onCheck={[Function]}
optionLabelKey="onboarding.build"
options={
Array [
"maven",
"gradle",
"msbuild",
"other",
]
}
/>
</li>
</ol>
</div>
`;

+ 119
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/MultiBranchPipelineStep-test.tsx.snap View File

@@ -0,0 +1,119 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: Step wrapper 1`] = `
<Step
finished={false}
onOpen={[MockFunction]}
open={true}
renderForm={[Function]}
stepNumber={1}
stepTitle="onboarding.tutorial.with.jenkins.multi_branch_pipeline.title"
/>
`;

exports[`should render correctly: content 1`] = `
<div
className="boxed-group-inner"
>
<p
className="big-spacer-bottom"
>
onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro
</p>
<ol
className="list-styled"
>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"new_item",
"type",
]
}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1"
/>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"tab",
]
}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2"
/>
<ul
className="list-styled"
>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server"
/>
</li>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds"
/>
</li>
<li>
<LabelValuePair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner"
value="PROJECT_KEY"
/>
</li>
<li>
<LabelValuePair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo"
value="repo-slug"
/>
</li>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour"
/>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"tab",
]
}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3"
/>
<ul
className="list-styled"
>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode"
/>
</li>
<li>
<LabelActionPair
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path"
/>
</li>
</ul>
</li>
<li>
<SentenceWithHighlights
highlightKeys={
Array [
"save",
]
}
translationKey="onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4"
/>
</li>
</ol>
<Button
onClick={[MockFunction]}
>
continue
</Button>
</div>
`;

+ 70
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/__tests__/__snapshots__/PreRequisitesStep-test.tsx.snap View File

@@ -0,0 +1,70 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: Step wrapper 1`] = `
<Step
finished={true}
onOpen={[MockFunction]}
open={false}
renderForm={[Function]}
stepTitle="onboarding.tutorial.with.jenkins.prereqs.title"
/>
`;

exports[`should render correctly: content 1`] = `
<div
className="boxed-group-inner"
>
<p
className="big-spacer-bottom"
>
<SentenceWithHighlights
highlightKeys={
Array [
"must_have",
]
}
translationKey="onboarding.tutorial.with.jenkins.prereqs.intro"
/>
</p>
<ul
className="list-styled big-spacer-bottom"
>
<li>
onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source
</li>
<li>
onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner
</li>
</ul>
<p
className="big-spacer-bottom"
>
<FormattedMessage
defaultMessage="onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide"
id="onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
target="_blank"
to="/documentation/analysis/jenkins/"
>
onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link
</Link>,
}
}
/>
</p>
<p
className="big-spacer-bottom"
>
onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations
</p>
<Button
onClick={[MockFunction]}
>
onboarding.tutorial.with.jenkins.prereqs.done
</Button>
</div>
`;

+ 73
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/CreateJenkinsfileBulletPoint.tsx View File

@@ -0,0 +1,73 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import CodeSnippet from '../../../common/CodeSnippet';
import SentenceWithFilename from '../../components/SentenceWithFilename';
import SentenceWithHighlights from '../../components/SentenceWithHighlights';

export interface CreateJenkinsfileBulletPointProps {
snippet: string;
alertTranslationKeyPart?: string;
}

export default function CreateJenkinsfileBulletPoint(props: CreateJenkinsfileBulletPointProps) {
const { snippet, alertTranslationKeyPart } = props;

return (
<li className="abs-width-600">
<SentenceWithFilename
filename="Jenkinsfile"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step"
/>
{alertTranslationKeyPart !== undefined && (
<Alert className="spacer-top" variant="info">
<p className="text-middle">
<SentenceWithHighlights
highlightKeys={['default', 'in_jenkins']}
translationKey={`${alertTranslationKeyPart}.replace`}
/>
<HelpTooltip
className="little-spacer-left"
overlay={
<>
<p className="spacer-bottom">
<SentenceWithHighlights
highlightKeys={['path']}
translationKey={`${alertTranslationKeyPart}.help1`}
/>
</p>
<p>
<SentenceWithHighlights
highlightKeys={['path', 'name']}
translationKey={`${alertTranslationKeyPart}.help2`}
/>
</p>
</>
}
/>
</p>
</Alert>
)}
<CodeSnippet snippet={snippet} />
</li>
);
}

+ 64
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Gradle.tsx View File

@@ -0,0 +1,64 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as React from 'react';
import CodeSnippet from '../../../common/CodeSnippet';
import SentenceWithFilename from '../../components/SentenceWithFilename';
import CreateJenkinsfileBulletPoint from './CreateJenkinsfileBulletPoint';

export interface GradleProps {
component: T.Component;
}

const buildGradleSnippet = (key: string) => `plugins {
id "org.sonarqube" version "2.7"
}

sonarqube {
properties {
property "sonar.projectKey", "${key}"
}
}`;

const JENKINSFILE_SNIPPET = `node {
stage('SCM') {
checkout scm
}
stage('SonarQube Analysis') {
withSonarQubeEnv() {
sh "./gradlew sonarqube"
}
}
}`;

export default function Gradle({ component }: GradleProps) {
return (
<>
<li className="abs-width-600">
<SentenceWithFilename
filename="build.gradle"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.gradle.step2"
/>
<CodeSnippet snippet={buildGradleSnippet(component.key)} />
</li>
<CreateJenkinsfileBulletPoint snippet={JENKINSFILE_SNIPPET} />
</>
);
}

+ 90
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/MSBuild.tsx View File

@@ -0,0 +1,90 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import CodeSnippet from '../../../common/CodeSnippet';
import SentenceWithFilename from '../../components/SentenceWithFilename';
import SentenceWithHighlights from '../../components/SentenceWithHighlights';

export interface MSBuildProps {
component: T.Component;
}

const jenkinsfileSnippet = (key: string) => `node {
stage('SCM') {
checkout scm
}
stage('SonarQube Analysis') {
def msbuildHome = tool 'Default MSBuild'
def scannerHome = tool 'SonarScanner for MSBuild'
withSonarQubeEnv() {
bat "\\"\${scannerHome}\\\\SonarScanner.MSBuild.exe\\" begin /k:\\"${key}\\""
bat "\\"\${msbuildHome}\\\\MSBuild.exe\\" /t:Rebuild"
bat "\\"\${scannerHome}\\\\SonarScanner.MSBuild.exe\\" end"
}
}
}
`;

export default function MSBuild({ component }: MSBuildProps) {
return (
<li className="abs-width-600">
<SentenceWithFilename
filename="Jenkinsfile"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step"
/>
<Alert className="spacer-top" variant="info">
<p className="text-middle">
<SentenceWithHighlights
highlightKeys={['default_msbuild', 'default_scanner', 'in_jenkins']}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace"
/>
<HelpTooltip
className="little-spacer-left"
overlay={
<>
<p className="spacer-bottom">
<SentenceWithHighlights
highlightKeys={['path']}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1"
/>
</p>
<p className="spacer-bottom">
<SentenceWithHighlights
highlightKeys={['path', 'name']}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2"
/>
</p>
<p>
<SentenceWithHighlights
highlightKeys={['path', 'name']}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3"
/>
</p>
</>
}
/>
</p>
</Alert>
<CodeSnippet snippet={jenkinsfileSnippet(component.key)} />
</li>
);
}

+ 62
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Maven.tsx View File

@@ -0,0 +1,62 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as React from 'react';
import CodeSnippet from '../../../common/CodeSnippet';
import SentenceWithFilename from '../../components/SentenceWithFilename';
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
}
stage('SonarQube Analysis') {
def mvn = tool 'Default Maven';
withSonarQubeEnv() {
sh "\${mvn}/bin/mvn sonar:sonar"
}
}
}`;

export default function Maven({ component }: MavenProps) {
return (
<>
<li className="abs-width-600">
<SentenceWithFilename
filename="pom.xml"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2"
/>
<CodeSnippet snippet={pomSnippet(component.key)} />
</li>
<CreateJenkinsfileBulletPoint
alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3"
snippet={JENKINSFILE_SNIPPET}
/>
</>
);
}

+ 60
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/Other.tsx View File

@@ -0,0 +1,60 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import * as React from 'react';
import CodeSnippet from '../../../common/CodeSnippet';
import SentenceWithFilename from '../../components/SentenceWithFilename';
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
}
stage('SonarQube Analysis') {
def scannerHome = tool 'SonarScanner';
withSonarQubeEnv() {
sh "\${scannerHome}/bin/sonar-scanner"
}
}
}`;

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>
<CreateJenkinsfileBulletPoint
alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3"
snippet={JENKINSFILE_SNIPPET}
/>
</>
);
}

+ 36
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/CreateJenkinsfileBulletPoint-test.tsx View File

@@ -0,0 +1,36 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import CreateJenkinsfileBulletPoint, {
CreateJenkinsfileBulletPointProps
} from '../CreateJenkinsfileBulletPoint';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ alertTranslationKeyPart: 'baz' })).toMatchSnapshot('with alert');
});

function shallowRender(props: Partial<CreateJenkinsfileBulletPointProps> = {}) {
return shallow<CreateJenkinsfileBulletPointProps>(
<CreateJenkinsfileBulletPoint snippet="foo { bar() }" {...props} />
);
}

+ 32
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Gradle-test.tsx View File

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../../../helpers/testMocks';
import Gradle, { GradleProps } from '../Gradle';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<GradleProps> = {}) {
return shallow<GradleProps>(<Gradle component={mockComponent()} {...props} />);
}

+ 32
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/MSBuild-test.tsx View File

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../../../helpers/testMocks';
import MSBuild, { MSBuildProps } from '../MSBuild';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<MSBuildProps> = {}) {
return shallow<MSBuildProps>(<MSBuild component={mockComponent()} {...props} />);
}

+ 32
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Maven-test.tsx View File

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../../../helpers/testMocks';
import Maven, { MavenProps } from '../Maven';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<MavenProps> = {}) {
return shallow<MavenProps>(<Maven component={mockComponent()} {...props} />);
}

+ 32
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/Other-test.tsx View File

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { shallow } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../../../helpers/testMocks';
import Other, { OtherProps } from '../Other';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender(props: Partial<OtherProps> = {}) {
return shallow<OtherProps>(<Other component={mockComponent()} {...props} />);
}

+ 77
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/CreateJenkinsfileBulletPoint-test.tsx.snap View File

@@ -0,0 +1,77 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: default 1`] = `
<li
className="abs-width-600"
>
<SentenceWithFilename
filename="Jenkinsfile"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step"
/>
<CodeSnippet
snippet="foo { bar() }"
/>
</li>
`;

exports[`should render correctly: with alert 1`] = `
<li
className="abs-width-600"
>
<SentenceWithFilename
filename="Jenkinsfile"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step"
/>
<Alert
className="spacer-top"
variant="info"
>
<p
className="text-middle"
>
<SentenceWithHighlights
highlightKeys={
Array [
"default",
"in_jenkins",
]
}
translationKey="baz.replace"
/>
<HelpTooltip
className="little-spacer-left"
overlay={
<React.Fragment>
<p
className="spacer-bottom"
>
<SentenceWithHighlights
highlightKeys={
Array [
"path",
]
}
translationKey="baz.help1"
/>
</p>
<p>
<SentenceWithHighlights
highlightKeys={
Array [
"path",
"name",
]
}
translationKey="baz.help2"
/>
</p>
</React.Fragment>
}
/>
</p>
</Alert>
<CodeSnippet
snippet="foo { bar() }"
/>
</li>
`;

+ 37
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Gradle-test.tsx.snap View File

@@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<li
className="abs-width-600"
>
<SentenceWithFilename
filename="build.gradle"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.gradle.step2"
/>
<CodeSnippet
snippet="plugins {
id \\"org.sonarqube\\" version \\"2.7\\"
}

sonarqube {
properties {
property \\"sonar.projectKey\\", \\"my-project\\"
}
}"
/>
</li>
<CreateJenkinsfileBulletPoint
snippet="node {
stage('SCM') {
checkout scm
}
stage('SonarQube Analysis') {
withSonarQubeEnv() {
sh \\"./gradlew sonarqube\\"
}
}
}"
/>
</Fragment>
`;

+ 91
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/MSBuild-test.tsx.snap View File

@@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<li
className="abs-width-600"
>
<SentenceWithFilename
filename="Jenkinsfile"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step"
/>
<Alert
className="spacer-top"
variant="info"
>
<p
className="text-middle"
>
<SentenceWithHighlights
highlightKeys={
Array [
"default_msbuild",
"default_scanner",
"in_jenkins",
]
}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace"
/>
<HelpTooltip
className="little-spacer-left"
overlay={
<React.Fragment>
<p
className="spacer-bottom"
>
<SentenceWithHighlights
highlightKeys={
Array [
"path",
]
}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1"
/>
</p>
<p
className="spacer-bottom"
>
<SentenceWithHighlights
highlightKeys={
Array [
"path",
"name",
]
}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2"
/>
</p>
<p>
<SentenceWithHighlights
highlightKeys={
Array [
"path",
"name",
]
}
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3"
/>
</p>
</React.Fragment>
}
/>
</p>
</Alert>
<CodeSnippet
snippet="node {
stage('SCM') {
checkout scm
}
stage('SonarQube Analysis') {
def msbuildHome = tool 'Default MSBuild'
def scannerHome = tool 'SonarScanner for MSBuild'
withSonarQubeEnv() {
bat \\"\\\\\\"\${scannerHome}\\\\\\\\SonarScanner.MSBuild.exe\\\\\\" begin /k:\\\\\\"my-project\\\\\\"\\"
bat \\"\\\\\\"\${msbuildHome}\\\\\\\\MSBuild.exe\\\\\\" /t:Rebuild\\"
bat \\"\\\\\\"\${scannerHome}\\\\\\\\SonarScanner.MSBuild.exe\\\\\\" end\\"
}
}
}
"
/>
</li>
`;

+ 33
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Maven-test.tsx.snap View File

@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<li
className="abs-width-600"
>
<SentenceWithFilename
filename="pom.xml"
translationKey="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2"
/>
<CodeSnippet
snippet="<properties>
<sonar.projectKey>my-project</sonar.projectKey>
</properties>"
/>
</li>
<CreateJenkinsfileBulletPoint
alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3"
snippet="node {
stage('SCM') {
checkout scm
}
stage('SonarQube Analysis') {
def mvn = tool 'Default Maven';
withSonarQubeEnv() {
sh \\"\${mvn}/bin/mvn sonar:sonar\\"
}
}
}"
/>
</Fragment>
`;

+ 31
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/buildtool-steps/__tests__/__snapshots__/Other-test.tsx.snap View File

@@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

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>
<CreateJenkinsfileBulletPoint
alertTranslationKeyPart="onboarding.tutorial.with.jenkins.jenkinsfile.other.step3"
snippet="node {
stage('SCM') {
checkout scm
}
stage('SonarQube Analysis') {
def scannerHome = tool 'SonarScanner';
withSonarQubeEnv() {
sh \\"\${scannerHome}/bin/sonar-scanner\\"
}
}
}"
/>
</Fragment>
`;

+ 28
- 0
server/sonar-web/src/main/js/components/tutorials/jenkins/test-utils.ts View File

@@ -0,0 +1,28 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { ShallowWrapper } from 'enzyme';
import Step from '../components/Step';

export function renderStepContent(wrapper: ShallowWrapper<React.ReactNode>) {
return wrapper
.find(Step)
.props()
.renderForm();
}

+ 1
- 16
server/sonar-web/src/main/js/components/tutorials/manual/ManualTutorial.tsx View File

@@ -18,8 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import BackIcon from 'sonar-ui-common/components/icons/BackIcon';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { isVSTS } from '../../../helpers/almIntegrations';
import InstanceMessage from '../../common/InstanceMessage';
@@ -34,7 +32,6 @@ export enum Steps {
interface Props {
component: T.Component;
currentUser: T.LoggedInUser;
onBack?: () => void;
}

interface State {
@@ -61,19 +58,7 @@ export default class ManualTutorial extends React.PureComponent<Props, State> {
return (
<>
<div className="page-header big-spacer-bottom">
<h1 className="page-title">
{this.props.onBack !== undefined && (
<Tooltip overlay={translate('onboarding.tutorial.return_to_list')}>
<a
aria-label={translate('onboarding.tutorial.return_to_list')}
className="link-no-underline big-spacer-right"
onClick={this.props.onBack}>
<BackIcon />
</a>
</Tooltip>
)}
{translate('onboarding.project_analysis.header')}
</h1>
<h1 className="page-title">{translate('onboarding.project_analysis.header')}</h1>
<p className="page-description">
<InstanceMessage message={translate('onboarding.project_analysis.description')} />
</p>

+ 0
- 1
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/ManualTutorial-test.tsx View File

@@ -26,7 +26,6 @@ import TokenStep from '../TokenStep';

it('renders correctly', () => {
expect(shallowRender()).toMatchSnapshot('default');
expect(shallowRender({ onBack: jest.fn() })).toMatchSnapshot('with back button');
});

it('allows to navigate between steps', () => {

+ 0
- 77
server/sonar-web/src/main/js/components/tutorials/manual/__tests__/__snapshots__/ManualTutorial-test.tsx.snap View File

@@ -65,80 +65,3 @@ exports[`renders correctly: default 1`] = `
/>
</Fragment>
`;

exports[`renders correctly: with back button 1`] = `
<Fragment>
<div
className="page-header big-spacer-bottom"
>
<h1
className="page-title"
>
<Tooltip
overlay="onboarding.tutorial.return_to_list"
>
<a
aria-label="onboarding.tutorial.return_to_list"
className="link-no-underline big-spacer-right"
onClick={[MockFunction]}
>
<BackIcon />
</a>
</Tooltip>
onboarding.project_analysis.header
</h1>
<p
className="page-description"
>
<InstanceMessage
message="onboarding.project_analysis.description"
/>
</p>
</div>
<TokenStep
currentUser={
Object {
"groups": Array [],
"isLoggedIn": true,
"login": "luke",
"name": "Skywalker",
"scmAccounts": Array [],
}
}
finished={false}
initialTokenName="Analyze \\"MyProject\\""
onContinue={[Function]}
onOpen={[Function]}
open={true}
stepNumber={1}
/>
<ProjectAnalysisStep
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
displayRowLayout={true}
open={false}
stepNumber={2}
/>
</Fragment>
`;

+ 23
- 0
server/sonar-web/src/main/js/components/tutorials/styles.css View File

@@ -0,0 +1,23 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
.tutorial-selection {
margin: 0 auto;
max-width: 500px;
}

+ 31
- 0
server/sonar-web/src/main/js/components/tutorials/types.ts View File

@@ -0,0 +1,31 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
export enum TutorialModes {
Manual = 'manual',
Jenkins = 'jenkins'
}

export interface LanguageConfig {
language?: string;
javaBuild?: string;
cFamilyCompiler?: string;
os?: string;
projectKey?: string;
}

+ 1
- 7
server/sonar-web/src/main/js/components/tutorials/utils.ts View File

@@ -17,13 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
export interface LanguageConfig {
language?: string;
javaBuild?: string;
cFamilyCompiler?: string;
os?: string;
projectKey?: string;
}
import { LanguageConfig } from './types';

export function isLanguageConfigured(config?: LanguageConfig) {
if (!config) {

+ 41
- 0
server/sonar-web/src/main/js/helpers/__tests__/alm-settings.ts View File

@@ -0,0 +1,41 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { isBitbucketBindingDefinition, isProjectBitbucketBindingResponse } from '../alm-settings';
import {
mockBitbucketBindingDefinition,
mockGithubBindingDefinition,
mockProjectAlmBindingResponse,
mockProjectBitbucketBindingGet
} from '../mocks/alm-settings';

describe('isProjectBitbucketBindingResponse', () => {
it('works as expected', () => {
expect(isProjectBitbucketBindingResponse(mockProjectAlmBindingResponse())).toBe(false);
expect(isProjectBitbucketBindingResponse(mockProjectBitbucketBindingGet())).toBe(true);
});
});

describe('isBitbucketBindingDefinition', () => {
it('works as expected', () => {
expect(isBitbucketBindingDefinition(mockGithubBindingDefinition())).toBe(false);
expect(isBitbucketBindingDefinition(mockBitbucketBindingDefinition())).toBe(true);
});
});

+ 38
- 0
server/sonar-web/src/main/js/helpers/alm-settings.ts View File

@@ -0,0 +1,38 @@
/*
* SonarQube
* Copyright (C) 2009-2020 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import {
AlmBindingDefinition,
AlmKeys,
BitbucketBindingDefinition,
ProjectAlmBindingResponse,
ProjectBitbucketBindingResponse
} from '../types/alm-settings';

export function isProjectBitbucketBindingResponse(
binding: ProjectAlmBindingResponse
): binding is ProjectBitbucketBindingResponse {
return binding.alm === AlmKeys.Bitbucket;
}

export function isBitbucketBindingDefinition(
binding: AlmBindingDefinition & { url?: string; personalAccessToken?: string }
): binding is BitbucketBindingDefinition {
return binding.url !== undefined && binding.personalAccessToken !== undefined;
}

+ 25
- 1
server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts View File

@@ -23,7 +23,9 @@ import {
AzureBindingDefinition,
BitbucketBindingDefinition,
GithubBindingDefinition,
GitlabBindingDefinition
GitlabBindingDefinition,
ProjectAlmBindingResponse,
ProjectBitbucketBindingResponse
} from '../../types/alm-settings';

export function mockAlmSettingsInstance(
@@ -78,3 +80,25 @@ export function mockGitlabBindingDefinition(
...overrides
};
}

export function mockProjectAlmBindingResponse(
overrides: Partial<ProjectAlmBindingResponse> = {}
): ProjectAlmBindingResponse {
return {
alm: AlmKeys.GitHub,
key: 'foo',
...overrides
};
}

export function mockProjectBitbucketBindingGet(
overrides: Partial<ProjectBitbucketBindingResponse> = {}
): ProjectBitbucketBindingResponse {
return {
alm: AlmKeys.Bitbucket,
key: 'foo',
repository: 'PROJECT_KEY',
slug: 'repo-slug',
...overrides
};
}

+ 6
- 0
server/sonar-web/src/main/js/types/alm-settings.ts View File

@@ -56,6 +56,12 @@ export interface ProjectAlmBindingResponse {
summaryCommentEnabled?: boolean;
}

export interface ProjectBitbucketBindingResponse extends ProjectAlmBindingResponse {
alm: AlmKeys.Bitbucket;
repository: string;
slug: string;
}

export interface ProjectAlmBindingParams {
almSetting: string;
project: string;

+ 99
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -3180,6 +3180,7 @@ onboarding.build=What is your build technology?
onboarding.build.maven=Maven
onboarding.build.gradle=Gradle
onboarding.build.make=Make
onboarding.build.msbuild=MSBuild
onboarding.build.other=Other (for JS, TS, Go, Python, PHP, ...)

onboarding.language=What is your project's main language?
@@ -3252,6 +3253,104 @@ onboarding.analysis.sqscanner.docs.gradle.example_project.title=live Gradle-base

onboarding.tutorial.return_to_list=Choose another option

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.with.jenkins.title=Analyze your project with Jenkins
onboarding.tutorial.with.jenkins.only_bitbucket=This tutorial is only available for projects bound to Bitbucket Server.
onboarding.tutorial.with.jenkins.prereqs.title=Prerequisites
onboarding.tutorial.with.jenkins.prereqs.intro.sentence=To run your project analyses with Jenkins, the following plugins {must_have}
onboarding.tutorial.with.jenkins.prereqs.intro.sentence.must_have=must be installed and configured:
onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source=Bitbucket Branch Source plugin for Jenkins - version 2.7 or later
onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner=SonarQube Scanner plugin for Jenkins - version 2.11 or later
onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide=For a step by step guide on installing and configuring those plugins in Jenkins, visit the {link} documentation page.
onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link=Analysis Prerequisites
onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations=We recommend using the configuration in the following steps for the best results, but you can customize it as needed.
onboarding.tutorial.with.jenkins.prereqs.done=Configure Analysis
onboarding.tutorial.with.jenkins.multi_branch_pipeline.title=Create a Multibranch Pipeline Job
onboarding.tutorial.with.jenkins.multi_branch_pipeline.intro=Create a Multibranch Pipeline in order to automatically analyze all your branches and pull requests.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1.sentence=From Jenkins' dashboard, click {new_item} and create a {type}.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1.sentence.new_item=New Item
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step1.sentence.type=Multibranch Pipeline Job
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.sentence=Under {tab}, add a Bitbucket source and enter the following information:
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.sentence.tab=Branch Sources
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server.label=Server
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.server.action=select the instance hosting the repository you want to analyze.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds.label=Credentials
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.creds.action=select the Bitbucket Server credentials.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner.label=Owner
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.owner.action=enter your project key.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo.label=Repository
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.repo.action=select the repository you want to analyze.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour.label=Behavior
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step2.behaviour.action=select Exclude branches that are also filed as PRs.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.sentence=Jump to the {tab} section and set the following parameters:
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.sentence.tab=Build Configuration
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode.label=Mode
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.mode.action=by Jenkinsfile
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path.label=Script Path
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step3.script_path.action=Jenkinsfile
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4.sentence=Click {save}.
onboarding.tutorial.with.jenkins.multi_branch_pipeline.step4.sentence.save=Save
onboarding.tutorial.with.jenkins.bitbucket_webhook.title=Create a Bitbucket Server Webhook
onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.sentence=Create a Webhook in your repository to trigger the Jenkins job on push. Already have a Webhook configured? {link}
onboarding.tutorial.with.jenkins.bitbucket_webhook.intro.link=Skip this step.
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.sentence=Go to the {link} and enter the following information:
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.link=Bitbucket Server Webhook creation page for your repository
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name.label=Name
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.name.action=give a unique name.
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.label=URL
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.action=Enter the following URL, replacing the tokens as needed:
onboarding.tutorial.with.jenkins.bitbucket_webhook.step1.url.warning=The Bitbucket Server URL must be identical to the one in your Jenkins configuration. Watch out for any missing or extra "/" at the end.
onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.sentence=Under {events}, make sure the following options are checked:
onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.sentence.events=Events
onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo.label=Repository
onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.repo.action=Push
onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr.label=Pull Request
onboarding.tutorial.with.jenkins.bitbucket_webhook.step2.pr.action=Opened
onboarding.tutorial.with.jenkins.bitbucket_webhook.step3.sentence=Click {create}.
onboarding.tutorial.with.jenkins.bitbucket_webhook.step3.sentence.create=Create
onboarding.tutorial.with.jenkins.jenkinsfile.title=Create a Jenkinsfile
onboarding.tutorial.with.jenkins.jenkinsfile.jenkinsfile_step.sentence=Create a {file} file in your repository and paste the following code:
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step2.sentence=Add the following to your {file} file:
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.replace.sentence=Make sure to replace {default} with the name you gave to your Maven tool {in_jenkins}.
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.replace.sentence.default=Default Maven
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.replace.sentence.in_jenkins=in Jenkins
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help1.sentence=To get the name of your Maven tool in Jenkins, navigate to {path}.
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help1.sentence.path=Manage Jenkins > Global Tool Configuration
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help2.sentence=The name is located under the {path} section, in the {name} field.
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help2.sentence.path=Maven > Maven installations
onboarding.tutorial.with.jenkins.jenkinsfile.maven.step3.help2.sentence.name=Name
onboarding.tutorial.with.jenkins.jenkinsfile.gradle.step2.sentence=Add the following to your {file} file:
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence=Make sure to replace {default_msbuild} and {default_scanner} with the names you gave to your MSBuild and SonarScanner for MSBuild tools {in_jenkins}.
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence.default_msbuild=Default MSBuild
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence.default_scanner=SonarScanner for MSBuild
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.replace.sentence.in_jenkins=in Jenkins
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1.sentence=To get the name of these tools in Jenkins, navigate to {path}.
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help1.sentence.path=Manage Jenkins > Global Tool Configuration
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2.sentence=For your MSBuild tool, the name is located under the {path} section, in the {name} field.
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2.sentence.path=MSBuild > MSBuild installations
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help2.sentence.name=Name
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3.sentence=For your SonarScanner for MSBuild tool, the name is located under the {path} section, in the {name} field.
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3.sentence.path=SonarScanner for MSBuild > SonarScanner for MSBuild installations
onboarding.tutorial.with.jenkins.jenkinsfile.msbuild.step2.help3.sentence.name=Name
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
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence=To get the name of your SonarQube Scanner tool in Jenkins, navigate to {path}.
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence.path=Manage Jenkins > Global Tool Configuration
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.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.

#------------------------------------------------------------------------------
#
# BRANCHES

Loading…
Cancel
Save