@@ -0,0 +1,5 @@ | |||
Paste an existing token value into the input field. | |||
--- | |||
See also: [User Token](/user-guide/user-token/) |
@@ -22,7 +22,6 @@ import { Button } from 'sonar-ui-common/components/controls/buttons'; | |||
import OnboardingAddMembersIcon from 'sonar-ui-common/components/icons/OnboardingAddMembersIcon'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { Router, withRouter } from '../../../components/hoc/withRouter'; | |||
import '../../../components/tutorials/styles.css'; | |||
import './OrganizationEmpty.css'; | |||
interface Props { |
@@ -22,7 +22,6 @@ 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'; | |||
@@ -59,10 +58,10 @@ export class TutorialSelection extends React.PureComponent<Props, State> { | |||
]); | |||
if (this.mounted) { | |||
// We only support Bitbucket & GitHub for now. | |||
// We only support Bitbucket, GitHub & Gitlab for now. | |||
if ( | |||
projectBinding === undefined || | |||
(projectBinding.alm !== AlmKeys.Bitbucket && projectBinding.alm !== AlmKeys.GitHub) | |||
![AlmKeys.Bitbucket, AlmKeys.GitHub, AlmKeys.GitLab].includes(projectBinding.alm) | |||
) { | |||
this.setState({ loading: false, forceManual: true }); | |||
} else { |
@@ -21,7 +21,8 @@ | |||
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 { AlmBindingDefinition, AlmKeys, ProjectAlmBindingResponse } from '../../types/alm-settings'; | |||
import GitLabCITutorial from './gitlabci/GitLabCITutorial'; | |||
import JenkinsTutorial from './jenkins/JenkinsTutorial'; | |||
import ManualTutorial from './manual/ManualTutorial'; | |||
import { TutorialModes } from './types'; | |||
@@ -43,6 +44,9 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender | |||
return <i className="spinner" />; | |||
} | |||
const jenkinsAvailable = | |||
projectBinding && [AlmKeys.Bitbucket, AlmKeys.GitHub].includes(projectBinding.alm); | |||
return ( | |||
<> | |||
{selectedTutorial === undefined && ( | |||
@@ -53,23 +57,41 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender | |||
</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> | |||
<div className="display-flex-justify-center"> | |||
{projectBinding?.alm === AlmKeys.GitLab && ( | |||
<button | |||
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-gitlab" | |||
onClick={() => props.onSelectTutorial(TutorialModes.GitLabCI)} | |||
type="button"> | |||
<img | |||
alt="" // Should be ignored by screen readers | |||
height={80} | |||
src={`${getBaseUrl()}/images/alm/gitlab.svg`} | |||
/> | |||
<div className="medium big-spacer-top"> | |||
{translate('onboarding.tutorial.choose_method.gitlab_ci')} | |||
</div> | |||
</button> | |||
)} | |||
{jenkinsAvailable && ( | |||
<button | |||
className="button button-huge display-flex-column spacer-left spacer-right 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" | |||
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" | |||
onClick={() => props.onSelectTutorial(TutorialModes.Manual)} | |||
type="button"> | |||
<img | |||
@@ -96,6 +118,14 @@ export default function TutorialSelectionRenderer(props: TutorialSelectionRender | |||
projectBinding={projectBinding} | |||
/> | |||
)} | |||
{selectedTutorial === TutorialModes.GitLabCI && projectBinding !== undefined && ( | |||
<GitLabCITutorial | |||
component={component} | |||
currentUser={currentUser} | |||
projectBinding={projectBinding} | |||
/> | |||
)} | |||
</> | |||
); | |||
} |
@@ -57,8 +57,8 @@ it('should not select anything if project is bound to Bitbucket', async () => { | |||
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 }); | |||
it('should select manual if project is bound to unsupported ALM', async () => { | |||
(getProjectAlmBinding as jest.Mock).mockResolvedValueOnce({ alm: AlmKeys.Azure }); | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().forceManual).toBe(true); |
@@ -23,7 +23,8 @@ import * as React from 'react'; | |||
import { click } from 'sonar-ui-common/helpers/testUtils'; | |||
import { | |||
mockBitbucketBindingDefinition, | |||
mockProjectBitbucketBindingResponse | |||
mockProjectBitbucketBindingResponse, | |||
mockProjectGitLabBindingResponse | |||
} from '../../../helpers/mocks/alm-settings'; | |||
import { mockComponent, mockLoggedInUser } from '../../../helpers/testMocks'; | |||
import TutorialSelectionRenderer, { | |||
@@ -33,6 +34,9 @@ import { TutorialModes } from '../types'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('selection'); | |||
expect(shallowRender({ projectBinding: mockProjectBitbucketBindingResponse() })).toMatchSnapshot( | |||
'selection with jenkins available' | |||
); | |||
expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); | |||
expect(shallowRender({ selectedTutorial: TutorialModes.Manual })).toMatchSnapshot( | |||
'manual tutorial' | |||
@@ -43,11 +47,20 @@ it('should render correctly', () => { | |||
projectBinding: mockProjectBitbucketBindingResponse() | |||
}) | |||
).toMatchSnapshot('jenkins tutorial'); | |||
expect( | |||
shallowRender({ | |||
selectedTutorial: TutorialModes.GitLabCI, | |||
projectBinding: mockProjectGitLabBindingResponse() | |||
}) | |||
).toMatchSnapshot('gitlab tutorial'); | |||
}); | |||
it('should allow mode selection', () => { | |||
const onSelectTutorial = jest.fn(); | |||
const wrapper = shallowRender({ onSelectTutorial }); | |||
const wrapper = shallowRender({ | |||
onSelectTutorial, | |||
projectBinding: mockProjectBitbucketBindingResponse() | |||
}); | |||
click(wrapper.find('button.tutorial-mode-jenkins')); | |||
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Jenkins); | |||
@@ -56,6 +69,17 @@ it('should allow mode selection', () => { | |||
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.Manual); | |||
}); | |||
it('should allow gitlab selection', () => { | |||
const onSelectTutorial = jest.fn(); | |||
const wrapper = shallowRender({ | |||
onSelectTutorial, | |||
projectBinding: mockProjectGitLabBindingResponse() | |||
}); | |||
click(wrapper.find('button.tutorial-mode-gitlab')); | |||
expect(onSelectTutorial).toHaveBeenLastCalledWith(TutorialModes.GitLabCI); | |||
}); | |||
function shallowRender(props: Partial<TutorialSelectionRendererProps> = {}) { | |||
return shallow<TutorialSelectionRendererProps>( | |||
<TutorialSelectionRenderer |
@@ -1,5 +1,52 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: gitlab tutorial 1`] = ` | |||
<Fragment> | |||
<GitLabCITutorial | |||
component={ | |||
Object { | |||
"breadcrumbs": Array [], | |||
"key": "my-project", | |||
"name": "MyProject", | |||
"organization": "foo", | |||
"qualifier": "TRK", | |||
"qualityGate": Object { | |||
"isDefault": true, | |||
"key": "30", | |||
"name": "Sonar way", | |||
}, | |||
"qualityProfiles": Array [ | |||
Object { | |||
"deleted": false, | |||
"key": "my-qp", | |||
"language": "ts", | |||
"name": "Sonar way", | |||
}, | |||
], | |||
"tags": Array [], | |||
} | |||
} | |||
currentUser={ | |||
Object { | |||
"groups": Array [], | |||
"isLoggedIn": true, | |||
"login": "luke", | |||
"name": "Skywalker", | |||
"scmAccounts": Array [], | |||
} | |||
} | |||
projectBinding={ | |||
Object { | |||
"alm": "gitlab", | |||
"key": "foo", | |||
"repository": "PROJECT_KEY", | |||
"url": "https://gitlab.com/api/v4", | |||
} | |||
} | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`should render correctly: jenkins tutorial 1`] = ` | |||
<Fragment> | |||
<Connect(JenkinsTutorial) | |||
@@ -105,10 +152,48 @@ exports[`should render correctly: selection 1`] = ` | |||
</h1> | |||
</header> | |||
<div | |||
className="display-flex-space-around" | |||
className="display-flex-justify-center" | |||
> | |||
<button | |||
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" | |||
onClick={[Function]} | |||
type="button" | |||
> | |||
<img | |||
alt="" | |||
height={80} | |||
src="/images/sonarcloud/analysis/manual.svg" | |||
/> | |||
<div | |||
className="medium big-spacer-top" | |||
> | |||
onboarding.tutorial.choose_method.manual | |||
</div> | |||
</button> | |||
</div> | |||
</div> | |||
</Fragment> | |||
`; | |||
exports[`should render correctly: selection with jenkins available 1`] = ` | |||
<Fragment> | |||
<div | |||
className="tutorial-selection" | |||
> | |||
<header | |||
className="spacer-top spacer-bottom padded" | |||
> | |||
<h1 | |||
className="text-center big-spacer-bottom" | |||
> | |||
onboarding.tutorial.choose_method | |||
</h1> | |||
</header> | |||
<div | |||
className="display-flex-justify-center" | |||
> | |||
<button | |||
className="button button-huge display-flex-column tutorial-mode-jenkins" | |||
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-jenkins" | |||
onClick={[Function]} | |||
type="button" | |||
> | |||
@@ -124,7 +209,7 @@ exports[`should render correctly: selection 1`] = ` | |||
</div> | |||
</button> | |||
<button | |||
className="button button-huge display-flex-column tutorial-mode-manual" | |||
className="button button-huge display-flex-column spacer-left spacer-right tutorial-mode-manual" | |||
onClick={[Function]} | |||
type="button" | |||
> |
@@ -0,0 +1,186 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Link } from 'react-router'; | |||
import { Button, DeleteButton } from 'sonar-ui-common/components/controls/buttons'; | |||
import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; | |||
import SimpleModal from 'sonar-ui-common/components/controls/SimpleModal'; | |||
import { Alert } from 'sonar-ui-common/components/ui/Alert'; | |||
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | |||
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; | |||
import { generateToken, getTokens, revokeToken } from '../../../api/user-tokens'; | |||
import { getUniqueTokenName } from '../utils'; | |||
interface State { | |||
loading: boolean; | |||
token?: string; | |||
tokenName: string; | |||
} | |||
interface Props { | |||
component: T.Component; | |||
currentUser: T.LoggedInUser; | |||
onClose: (token?: string) => void; | |||
} | |||
export default class EditTokenModal extends React.PureComponent<Props, State> { | |||
mounted = false; | |||
state: State = { | |||
loading: true, | |||
tokenName: '' | |||
}; | |||
componentDidMount() { | |||
this.mounted = true; | |||
this.getTokensAndName(); | |||
} | |||
componentWillUnmount() { | |||
this.mounted = false; | |||
} | |||
getTokensAndName = async () => { | |||
const { component, currentUser } = this.props; | |||
const tokens = await getTokens(currentUser.login); | |||
if (this.mounted) { | |||
this.setState({ | |||
loading: false, | |||
tokenName: getUniqueTokenName(tokens, `Analyze "${component.name}"`) | |||
}); | |||
} | |||
}; | |||
getNewToken = async () => { | |||
const { tokenName } = this.state; | |||
const { token } = await generateToken({ name: tokenName }); | |||
if (this.mounted) { | |||
this.setState({ | |||
token, | |||
tokenName | |||
}); | |||
} | |||
}; | |||
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { | |||
this.setState({ | |||
tokenName: event.target.value | |||
}); | |||
}; | |||
handleTokenRevoke = async () => { | |||
const { tokenName } = this.state; | |||
if (tokenName) { | |||
await revokeToken({ name: tokenName }); | |||
if (this.mounted) { | |||
this.setState({ | |||
token: '', | |||
tokenName: '' | |||
}); | |||
} | |||
} | |||
}; | |||
render() { | |||
const { loading, token, tokenName } = this.state; | |||
const header = translate('onboarding.token.generate_token'); | |||
return ( | |||
<SimpleModal header={header} onClose={this.props.onClose} onSubmit={this.props.onClose}> | |||
{({ onCloseClick }) => ( | |||
<> | |||
<div className="modal-head"> | |||
<h2>{header}</h2> | |||
</div> | |||
<div className="modal-body"> | |||
<p className="spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.token.text')} | |||
id="onboarding.token.text" | |||
values={{ | |||
link: ( | |||
<Link target="_blank" to="/account/security"> | |||
{translate('onboarding.token.text.user_account')} | |||
</Link> | |||
) | |||
}} | |||
/> | |||
</p> | |||
{token ? ( | |||
<> | |||
<span className="text-middle"> | |||
{tokenName} | |||
{': '} | |||
</span> | |||
<div className="display-float-center"> | |||
<code className="rule spacer-right">{token}</code> | |||
<ClipboardIconButton copyValue={token} /> | |||
<DeleteButton onClick={this.handleTokenRevoke} /> | |||
</div> | |||
<Alert className="big-spacer-top" variant="warning"> | |||
{translateWithParameters('users.tokens.new_token_created', token)} | |||
</Alert> | |||
</> | |||
) : ( | |||
<div className="big-spacer-top"> | |||
{loading ? ( | |||
<DeferredSpinner /> | |||
) : ( | |||
<> | |||
<input | |||
className="input-super-large spacer-right text-middle" | |||
onChange={this.handleChange} | |||
placeholder={translate('onboarding.token.generate_token.placeholder')} | |||
required={true} | |||
type="text" | |||
value={tokenName} | |||
/> | |||
<Button | |||
className="text-middle" | |||
disabled={!tokenName} | |||
onClick={this.getNewToken}> | |||
{translate('onboarding.token.generate')} | |||
</Button> | |||
</> | |||
)} | |||
</div> | |||
)} | |||
</div> | |||
<div className="modal-foot"> | |||
<Button onClick={onCloseClick}>{translate('continue')}</Button> | |||
</div> | |||
</> | |||
)} | |||
</SimpleModal> | |||
); | |||
} | |||
} |
@@ -80,22 +80,21 @@ | |||
.onboarding-step ol.list-styled > li { | |||
position: relative; | |||
counter-increment: step-counter; | |||
counter-increment: li; | |||
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%; | |||
display: inline-block; | |||
width: 16px; | |||
position: absolute; | |||
left: 0; | |||
font-size: 10px; | |||
height: 16px; | |||
justify-content: center; | |||
align-items: center; | |||
margin-right: 8px; | |||
color: #fff; | |||
font-size: 12px; | |||
line-height: 16px; | |||
direction: rtl; | |||
text-align: center; | |||
background-color: #4b9fd5; | |||
border-radius: 50%; | |||
content: counter(li); | |||
} |
@@ -0,0 +1,112 @@ | |||
/* | |||
* 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 { generateToken, getTokens, revokeToken } from '../../../../api/user-tokens'; | |||
import { mockComponent, mockEvent, mockLoggedInUser } from '../../../../helpers/testMocks'; | |||
import { getUniqueTokenName } from '../../utils'; | |||
import EditTokenModal from '../EditTokenModal'; | |||
jest.mock('../../../../api/user-tokens', () => ({ | |||
generateToken: jest.fn().mockResolvedValue({ | |||
name: 'baz', | |||
createdAt: '2019-01-21T08:06:00+0100', | |||
login: 'luke', | |||
token: 'token_value' | |||
}), | |||
getTokens: jest.fn().mockResolvedValue([ | |||
{ | |||
name: 'foo', | |||
createdAt: '2019-01-15T15:06:33+0100', | |||
lastConnectionDate: '2019-01-18T15:06:33+0100' | |||
}, | |||
{ name: 'bar', createdAt: '2019-01-18T15:06:33+0100' } | |||
]), | |||
revokeToken: jest.fn().mockResolvedValue(Promise.resolve()) | |||
})); | |||
jest.mock('../../utils', () => ({ | |||
getUniqueTokenName: jest.fn().mockReturnValue('lightsaber-9000') | |||
})); | |||
beforeEach(() => { | |||
jest.clearAllMocks(); | |||
}); | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should get tokens and unique name', async () => { | |||
const wrapper = shallowRender(); | |||
const { getTokensAndName } = wrapper.instance(); | |||
getTokensAndName(); | |||
await waitAndUpdate(wrapper); | |||
expect(getTokens).toHaveBeenCalled(); | |||
expect(getUniqueTokenName).toHaveBeenCalled(); | |||
expect(wrapper.state('tokenName')).toBe('lightsaber-9000'); | |||
}); | |||
it('should get a new token', async () => { | |||
const wrapper = shallowRender(); | |||
const { getNewToken } = wrapper.instance(); | |||
getNewToken(); | |||
await waitAndUpdate(wrapper); | |||
expect(generateToken).toHaveBeenCalled(); | |||
expect(wrapper.state('token')).toBe('token_value'); | |||
}); | |||
it('should handle token revocation', async () => { | |||
const wrapper = shallowRender(); | |||
const { getTokensAndName, handleTokenRevoke } = wrapper.instance(); | |||
getTokensAndName(); | |||
await waitAndUpdate(wrapper); | |||
handleTokenRevoke(); | |||
await waitAndUpdate(wrapper); | |||
expect(revokeToken).toHaveBeenCalled(); | |||
expect(wrapper.state('token')).toBe(''); | |||
expect(wrapper.state('tokenName')).toBe(''); | |||
}); | |||
it('should handle change on user input', () => { | |||
const wrapper = shallowRender(); | |||
const instance = wrapper.instance(); | |||
instance.handleChange(mockEvent({ target: { value: 'my-token' } })); | |||
expect(wrapper.state('tokenName')).toBe('my-token'); | |||
}); | |||
function shallowRender(props: Partial<EditTokenModal['props']> = {}) { | |||
return shallow<EditTokenModal>( | |||
<EditTokenModal | |||
component={mockComponent()} | |||
currentUser={mockLoggedInUser()} | |||
onClose={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,11 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<SimpleModal | |||
header="onboarding.token.generate_token" | |||
onClose={[MockFunction]} | |||
onSubmit={[MockFunction]} | |||
> | |||
<Component /> | |||
</SimpleModal> | |||
`; |
@@ -0,0 +1,178 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Button } from 'sonar-ui-common/components/controls/buttons'; | |||
import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { getHostUrl } from 'sonar-ui-common/helpers/urls'; | |||
import EditTokenModal from '../components/EditTokenModal'; | |||
import Step from '../components/Step'; | |||
export interface EnvironmentVariablesStepProps { | |||
component: T.Component; | |||
currentUser: T.LoggedInUser; | |||
finished: boolean; | |||
onDone: () => void; | |||
onOpen: () => void; | |||
open: boolean; | |||
} | |||
const pipelineDescriptionLinkLabel = translate( | |||
'onboarding.tutorial.with.gitlab_ci.env_variables.description.link' | |||
); | |||
export default function EnvironmentVariablesStep(props: EnvironmentVariablesStepProps) { | |||
const { component, currentUser, finished, open } = props; | |||
const [isModalVisible, toggleModal] = React.useState(false); | |||
const toggleTokenModal = () => toggleModal(!isModalVisible); | |||
const closeTokenModal = () => toggleModal(false); | |||
const fieldValueTranslation = translate( | |||
'onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value' | |||
); | |||
const renderForm = () => ( | |||
<div className="boxed-group-inner"> | |||
<h2 className="spacer-bottom spacer-top"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.env_variables.section.title')} | |||
</h2> | |||
<FormattedMessage | |||
defaultMessage={translate( | |||
'onboarding.tutorial.with.gitlab_ci.env_variables.section.description' | |||
)} | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.section.description" | |||
values={{ | |||
/* This link will be added when the backend provides the project URL */ | |||
link: <strong>{pipelineDescriptionLinkLabel}</strong> | |||
}} | |||
/> | |||
<ol className="list-styled big-spacer-top"> | |||
<li className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={fieldValueTranslation} | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" | |||
values={{ | |||
extra: <ClipboardIconButton copyValue="SONAR_TOKEN" />, | |||
field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1'), | |||
value: <code className="rule">SONAR_TOKEN</code> | |||
}} | |||
/> | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={fieldValueTranslation} | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" | |||
values={{ | |||
extra: ( | |||
<Button className="spacer-left" onClick={toggleTokenModal}> | |||
{translate('onboarding.token.generate_token')} | |||
</Button> | |||
), | |||
field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'), | |||
value: translate( | |||
'onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value' | |||
) | |||
}} | |||
/> | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.env_variables.step3')} | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.env_variables.section.step4')} | |||
</li> | |||
</ol> | |||
<hr className="no-horizontal-margins" /> | |||
<h2 className="spacer-bottom big-spacer-top"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.env_variables.section2.title')} | |||
</h2> | |||
<FormattedMessage | |||
defaultMessage={translate( | |||
'onboarding.tutorial.with.gitlab_ci.env_variables.section2.description' | |||
)} | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.section2.description" | |||
values={{ | |||
/* This link will be added when the backend provides the project URL */ | |||
link: <strong>{pipelineDescriptionLinkLabel}</strong> | |||
}} | |||
/> | |||
<ol className="list-styled big-spacer-top big-spacer-bottom"> | |||
<li className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={fieldValueTranslation} | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" | |||
values={{ | |||
extra: <ClipboardIconButton copyValue="SONAR_HOST_URL" />, | |||
field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step1'), | |||
value: <code className="rule">SONAR_HOST_URL</code> | |||
}} | |||
/> | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={fieldValueTranslation} | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" | |||
values={{ | |||
extra: <ClipboardIconButton copyValue={getHostUrl()} />, | |||
field: translate('onboarding.tutorial.with.gitlab_ci.env_variables.step2'), | |||
value: <code className="rule">{getHostUrl()}</code> | |||
}} | |||
/> | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.env_variables.step3')} | |||
</li> | |||
<li className="big-spacer-bottom"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.env_variables.section2.step4')} | |||
</li> | |||
</ol> | |||
<Button className="big-spacer-bottom" onClick={props.onDone}> | |||
{translate('continue')} | |||
</Button> | |||
</div> | |||
); | |||
return ( | |||
<> | |||
{isModalVisible && ( | |||
<EditTokenModal component={component} currentUser={currentUser} onClose={closeTokenModal} /> | |||
)} | |||
<Step | |||
finished={finished} | |||
onOpen={props.onOpen} | |||
open={open} | |||
renderForm={renderForm} | |||
stepNumber={2} | |||
stepTitle={translate('onboarding.tutorial.with.gitlab_ci.env_variables.title')} | |||
/> | |||
</> | |||
); | |||
} |
@@ -0,0 +1,83 @@ | |||
/* | |||
* 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 { isProjectGitLabBindingResponse } from '../../../helpers/alm-settings'; | |||
import { ProjectAlmBindingResponse } from '../../../types/alm-settings'; | |||
import EnvironmentVariablesStep from './EnvironmentVariablesStep'; | |||
import ProjectKeyStep from './ProjectKeyStep'; | |||
import { BuildTools } from './types'; | |||
import YmlFileStep from './YmlFileStep'; | |||
export enum Steps { | |||
PROJECT_KEY, | |||
ENV_VARIABLES, | |||
YML | |||
} | |||
export interface GitLabCITutorialProps { | |||
component: T.Component; | |||
currentUser: T.LoggedInUser; | |||
projectBinding: ProjectAlmBindingResponse; | |||
} | |||
export default function GitLabCITutorial(props: GitLabCITutorialProps) { | |||
const { component, currentUser, projectBinding } = props; | |||
const [step, setStep] = React.useState(Steps.PROJECT_KEY); | |||
const [buildTool, setBuildTool] = React.useState<BuildTools | undefined>(); | |||
// Failsafe; should never happen. | |||
if (!isProjectGitLabBindingResponse(projectBinding)) { | |||
return ( | |||
<Alert variant="error">{translate('onboarding.tutorial.with.gitlab_ci.unsupported')}</Alert> | |||
); | |||
} | |||
return ( | |||
<> | |||
<div className="page-header big-spacer-bottom"> | |||
<h1 className="page-title">{translate('onboarding.tutorial.with.gitlab_ci.title')}</h1> | |||
</div> | |||
<ProjectKeyStep | |||
buildTool={buildTool} | |||
component={component} | |||
finished={step > Steps.PROJECT_KEY} | |||
onDone={() => setStep(Steps.ENV_VARIABLES)} | |||
onOpen={() => setStep(Steps.PROJECT_KEY)} | |||
open={step === Steps.PROJECT_KEY} | |||
setBuildTool={setBuildTool} | |||
/> | |||
<EnvironmentVariablesStep | |||
component={component} | |||
currentUser={currentUser} | |||
finished={step > Steps.ENV_VARIABLES} | |||
onDone={() => setStep(Steps.YML)} | |||
onOpen={() => setStep(Steps.ENV_VARIABLES)} | |||
open={step === Steps.ENV_VARIABLES} | |||
/> | |||
<YmlFileStep buildTool={buildTool} open={step === Steps.YML} /> | |||
</> | |||
); | |||
} |
@@ -0,0 +1,125 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Button } from 'sonar-ui-common/components/controls/buttons'; | |||
import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import CodeSnippet from '../../common/CodeSnippet'; | |||
import RenderOptions from '../components/RenderOptions'; | |||
import Step from '../components/Step'; | |||
import { BuildTools } from './types'; | |||
export interface ProjectKeyStepProps { | |||
buildTool?: BuildTools; | |||
component: T.Component; | |||
finished: boolean; | |||
onDone: () => void; | |||
onOpen: () => void; | |||
open: boolean; | |||
setBuildTool: (tool: BuildTools) => void; | |||
} | |||
const mavenSnippet = (key: string) => `<properties> | |||
<sonar.projectKey>${key}</sonar.projectKey> | |||
<sonar.qualitygate.wait>true</sonar.qualitygate.wait> | |||
</properties>`; | |||
const gradleSnippet = (key: string) => `plugins { | |||
id "org.sonarqube" version "3.0" | |||
} | |||
sonarqube { | |||
properties { | |||
property "sonar.projectKey", "${key}" | |||
property "sonar.qualitygate.wait", true | |||
} | |||
}`; | |||
const otherSnippet = (key: string) => `sonar.projectKey=${key} | |||
sonar.qualitygate.wait=true | |||
`; | |||
const snippetForBuildTool = { | |||
[BuildTools.Maven]: mavenSnippet, | |||
[BuildTools.Gradle]: gradleSnippet, | |||
[BuildTools.Other]: otherSnippet | |||
}; | |||
const filenameForBuildTool = { | |||
[BuildTools.Maven]: 'pom.xml', | |||
[BuildTools.Gradle]: 'build.gradle', | |||
[BuildTools.Other]: 'sonar-project.properties' | |||
}; | |||
export default function ProjectKeyStep(props: ProjectKeyStepProps) { | |||
const { buildTool, component, finished, open } = props; | |||
const renderForm = () => ( | |||
<div className="boxed-group-inner"> | |||
<ol className="list-styled"> | |||
<li> | |||
{translate('onboarding.build')} | |||
<RenderOptions | |||
checked={buildTool} | |||
name="buildtool" | |||
onCheck={value => props.setBuildTool(value as BuildTools)} | |||
optionLabelKey="onboarding.build" | |||
options={Object.values(BuildTools)} | |||
/> | |||
</li> | |||
{buildTool !== undefined && ( | |||
<li className="abs-width-600"> | |||
<FormattedMessage | |||
defaultMessage={translate( | |||
`onboarding.tutorial.with.gitlab_ci.project_key.${buildTool}.step2` | |||
)} | |||
id={`onboarding.tutorial.with.gitlab_ci.project_key.${buildTool}.step2`} | |||
values={{ | |||
file: ( | |||
<> | |||
<code className="rule">{filenameForBuildTool[buildTool]}</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue={filenameForBuildTool[buildTool]} | |||
/> | |||
</> | |||
) | |||
}} | |||
/> | |||
<CodeSnippet snippet={snippetForBuildTool[buildTool](component.key)} /> | |||
</li> | |||
)} | |||
</ol> | |||
{buildTool !== undefined && <Button onClick={props.onDone}>{translate('continue')}</Button>} | |||
</div> | |||
); | |||
return ( | |||
<Step | |||
finished={finished} | |||
onOpen={props.onOpen} | |||
open={open} | |||
renderForm={renderForm} | |||
stepNumber={1} | |||
stepTitle={translate('onboarding.tutorial.with.gitlab_ci.project_key.title')} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,136 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { FormattedMessage } from 'react-intl'; | |||
import { Link } from 'react-router'; | |||
import { ClipboardIconButton } from 'sonar-ui-common/components/controls/clipboard'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import Step from '../components/Step'; | |||
import PipeCommandGradle from './commands/PipeCommandGradle'; | |||
import PipeCommandMaven from './commands/PipeCommandMaven'; | |||
import PipeCommandOther from './commands/PipeCommandOther'; | |||
import { BuildTools } from './types'; | |||
export interface YmlFileStepProps { | |||
buildTool?: BuildTools; | |||
open: boolean; | |||
} | |||
export default function YmlFileStep({ buildTool, open }: YmlFileStepProps) { | |||
const renderForm = () => ( | |||
<div className="boxed-group-inner"> | |||
<div className="flex-columns"> | |||
<div className="flex-column-full"> | |||
{buildTool && ( | |||
<> | |||
<div className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={translate('onboarding.tutorial.with.gitlab_ci.yml.description')} | |||
id="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
values={{ | |||
filename: ( | |||
<> | |||
<code className="rule"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.yml.filename')} | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue={translate('onboarding.tutorial.with.gitlab_ci.yml.filename')} | |||
/> | |||
</> | |||
) | |||
}} | |||
/> | |||
</div> | |||
<div className="big-spacer-bottom"> | |||
{buildTool === BuildTools.Maven && <PipeCommandMaven />} | |||
{buildTool === BuildTools.Gradle && <PipeCommandGradle />} | |||
{buildTool === BuildTools.Other && <PipeCommandOther />} | |||
</div> | |||
<p className="little-spacer-bottom"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.yml.baseconfig')} | |||
</p> | |||
<p className="huge-spacer-bottom"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.yml.existing')} | |||
</p> | |||
<hr className="no-horizontal-margins" /> | |||
<div> | |||
<p className="big-spacer-bottom"> | |||
<strong>{translate('onboarding.tutorial.with.gitlab_ci.yml.done')} </strong>{' '} | |||
<FormattedMessage | |||
defaultMessage={translate( | |||
'onboarding.tutorial.with.gitlab_ci.yml.done.description' | |||
)} | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
values={{ | |||
/* This link will be added when the backend provides the project URL */ | |||
link: translate( | |||
'onboarding.tutorial.with.gitlab_ci.yml.done.description.link' | |||
) | |||
}} | |||
/> | |||
</p> | |||
<p className="big-spacer-bottom"> | |||
<strong> | |||
{translate('onboarding.tutorial.with.gitlab_ci.yml.done.then-what')} | |||
</strong>{' '} | |||
{translate('onboarding.tutorial.with.gitlab_ci.yml.done.then-what.description')} | |||
</p> | |||
<p className="big-spacer-bottom"> | |||
<FormattedMessage | |||
defaultMessage={translate( | |||
'onboarding.tutorial.with.gitlab_ci.yml.done.links.title' | |||
)} | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
values={{ | |||
links: ( | |||
<Link | |||
rel="noopener noreferrer" | |||
target="_blank" | |||
to="/documentation/user-guide/quality-gates/"> | |||
{translate('onboarding.tutorial.with.gitlab_ci.yml.done.links.QG')} | |||
</Link> | |||
) | |||
}} | |||
/> | |||
</p> | |||
</div> | |||
</> | |||
)} | |||
</div> | |||
</div> | |||
</div> | |||
); | |||
return ( | |||
<Step | |||
finished={false} | |||
open={open} | |||
renderForm={renderForm} | |||
stepNumber={3} | |||
stepTitle={translate('onboarding.tutorial.with.gitlab_ci.yml.title')} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks'; | |||
import { renderStepContent } from '../../jenkins/test-utils'; | |||
import EnvironmentVariablesStep, { | |||
EnvironmentVariablesStepProps | |||
} from '../EnvironmentVariablesStep'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot('Step wrapper'); | |||
expect(renderStepContent(wrapper)).toMatchSnapshot('initial content'); | |||
}); | |||
function shallowRender(props: Partial<EnvironmentVariablesStepProps> = {}) { | |||
return shallow<EnvironmentVariablesStepProps>( | |||
<EnvironmentVariablesStep | |||
currentUser={mockLoggedInUser()} | |||
component={mockComponent()} | |||
finished={false} | |||
onDone={jest.fn()} | |||
onOpen={jest.fn()} | |||
open={true} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* 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 { | |||
mockProjectGithubBindingResponse, | |||
mockProjectGitLabBindingResponse | |||
} from '../../../../helpers/mocks/alm-settings'; | |||
import { mockComponent, mockLoggedInUser } from '../../../../helpers/testMocks'; | |||
import GitLabCITutorial, { GitLabCITutorialProps } from '../GitLabCITutorial'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
expect(shallowRender({ projectBinding: mockProjectGithubBindingResponse() })).toMatchSnapshot( | |||
'wrong alm' | |||
); | |||
}); | |||
function shallowRender(props: Partial<GitLabCITutorialProps> = {}) { | |||
return shallow<GitLabCITutorialProps>( | |||
<GitLabCITutorial | |||
component={mockComponent()} | |||
currentUser={mockLoggedInUser()} | |||
projectBinding={mockProjectGitLabBindingResponse()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* 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 { renderStepContent } from '../../jenkins/test-utils'; | |||
import ProjectKeyStep, { ProjectKeyStepProps } from '../ProjectKeyStep'; | |||
import { BuildTools } from '../types'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot('Step wrapper'); | |||
expect(renderStepContent(wrapper)).toMatchSnapshot('initial content'); | |||
}); | |||
it.each([[BuildTools.Maven], [BuildTools.Gradle], [BuildTools.Other]])( | |||
'should render correctly for build tool %s', | |||
buildTool => { | |||
expect(renderStepContent(shallowRender({ buildTool }))).toMatchSnapshot(); | |||
} | |||
); | |||
it('should correctly callback with selected build tool', () => { | |||
const setBuildTool = jest.fn(); | |||
const wrapper = shallowRender({ setBuildTool }); | |||
selectBuildTool(wrapper, BuildTools.Maven); | |||
expect(setBuildTool).toBeCalledWith(BuildTools.Maven); | |||
}); | |||
function selectBuildTool(wrapper: ShallowWrapper<ProjectKeyStepProps>, tool: BuildTools) { | |||
const content = new ShallowWrapper(renderStepContent(wrapper) as JSX.Element); | |||
content | |||
.find(RenderOptions) | |||
.props() | |||
.onCheck(tool); | |||
} | |||
function shallowRender(props: Partial<ProjectKeyStepProps> = {}) { | |||
return shallow<ProjectKeyStepProps>( | |||
<ProjectKeyStep | |||
component={mockComponent()} | |||
finished={false} | |||
onDone={jest.fn()} | |||
onOpen={jest.fn()} | |||
open={true} | |||
setBuildTool={jest.fn()} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,41 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { renderStepContent } from '../../jenkins/test-utils'; | |||
import { BuildTools } from '../types'; | |||
import YmlFileStep, { YmlFileStepProps } from '../YmlFileStep'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot('Step wrapper'); | |||
expect(renderStepContent(wrapper)).toMatchSnapshot('initial content'); | |||
}); | |||
it.each([[BuildTools.Maven], [BuildTools.Gradle], [BuildTools.Other]])( | |||
'should render correctly for build tool %s', | |||
buildTool => { | |||
expect(renderStepContent(shallowRender({ buildTool }))).toMatchSnapshot(); | |||
} | |||
); | |||
function shallowRender(props: Partial<YmlFileStepProps> = {}) { | |||
return shallow<YmlFileStepProps>(<YmlFileStep open={true} {...props} />); | |||
} |
@@ -0,0 +1,173 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: Step wrapper 1`] = ` | |||
<Fragment> | |||
<Step | |||
finished={false} | |||
onOpen={[MockFunction]} | |||
open={true} | |||
renderForm={[Function]} | |||
stepNumber={2} | |||
stepTitle="onboarding.tutorial.with.gitlab_ci.env_variables.title" | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`should render correctly: initial content 1`] = ` | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<h2 | |||
className="spacer-bottom spacer-top" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section.title | |||
</h2> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.section.description" | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.section.description" | |||
values={ | |||
Object { | |||
"link": <strong> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.description.link | |||
</strong>, | |||
} | |||
} | |||
/> | |||
<ol | |||
className="list-styled big-spacer-top" | |||
> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" | |||
values={ | |||
Object { | |||
"extra": <ClipboardIconButton | |||
copyValue="SONAR_TOKEN" | |||
/>, | |||
"field": "onboarding.tutorial.with.gitlab_ci.env_variables.step1", | |||
"value": <code | |||
className="rule" | |||
> | |||
SONAR_TOKEN | |||
</code>, | |||
} | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" | |||
values={ | |||
Object { | |||
"extra": <Button | |||
className="spacer-left" | |||
onClick={[Function]} | |||
> | |||
onboarding.token.generate_token | |||
</Button>, | |||
"field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2", | |||
"value": "onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value", | |||
} | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.step3 | |||
</li> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section.step4 | |||
</li> | |||
</ol> | |||
<hr | |||
className="no-horizontal-margins" | |||
/> | |||
<h2 | |||
className="spacer-bottom big-spacer-top" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section2.title | |||
</h2> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.section2.description" | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.section2.description" | |||
values={ | |||
Object { | |||
"link": <strong> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.description.link | |||
</strong>, | |||
} | |||
} | |||
/> | |||
<ol | |||
className="list-styled big-spacer-top big-spacer-bottom" | |||
> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step1" | |||
values={ | |||
Object { | |||
"extra": <ClipboardIconButton | |||
copyValue="SONAR_HOST_URL" | |||
/>, | |||
"field": "onboarding.tutorial.with.gitlab_ci.env_variables.step1", | |||
"value": <code | |||
className="rule" | |||
> | |||
SONAR_HOST_URL | |||
</code>, | |||
} | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value" | |||
id="onboarding.tutorial.with.gitlab_ci.env_variables.step2" | |||
values={ | |||
Object { | |||
"extra": <ClipboardIconButton | |||
copyValue="http://localhost" | |||
/>, | |||
"field": "onboarding.tutorial.with.gitlab_ci.env_variables.step2", | |||
"value": <code | |||
className="rule" | |||
> | |||
http://localhost | |||
</code>, | |||
} | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.step3 | |||
</li> | |||
<li | |||
className="big-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section2.step4 | |||
</li> | |||
</ol> | |||
<Button | |||
className="big-spacer-bottom" | |||
onClick={[MockFunction]} | |||
> | |||
continue | |||
</Button> | |||
</div> | |||
`; |
@@ -0,0 +1,94 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<Fragment> | |||
<div | |||
className="page-header big-spacer-bottom" | |||
> | |||
<h1 | |||
className="page-title" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.title | |||
</h1> | |||
</div> | |||
<ProjectKeyStep | |||
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 [], | |||
} | |||
} | |||
finished={false} | |||
onDone={[Function]} | |||
onOpen={[Function]} | |||
open={true} | |||
setBuildTool={[Function]} | |||
/> | |||
<EnvironmentVariablesStep | |||
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 [], | |||
} | |||
} | |||
finished={false} | |||
onDone={[Function]} | |||
onOpen={[Function]} | |||
open={false} | |||
/> | |||
<YmlFileStep | |||
open={false} | |||
/> | |||
</Fragment> | |||
`; | |||
exports[`should render correctly: wrong alm 1`] = ` | |||
<Alert | |||
variant="error" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.unsupported | |||
</Alert> | |||
`; |
@@ -0,0 +1,226 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly for build tool 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", | |||
"other", | |||
] | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="abs-width-600" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.project_key.gradle.step2" | |||
id="onboarding.tutorial.with.gitlab_ci.project_key.gradle.step2" | |||
values={ | |||
Object { | |||
"file": <React.Fragment> | |||
<code | |||
className="rule" | |||
> | |||
build.gradle | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue="build.gradle" | |||
/> | |||
</React.Fragment>, | |||
} | |||
} | |||
/> | |||
<CodeSnippet | |||
snippet="plugins { | |||
id \\"org.sonarqube\\" version \\"3.0\\" | |||
} | |||
sonarqube { | |||
properties { | |||
property \\"sonar.projectKey\\", \\"my-project\\" | |||
property \\"sonar.qualitygate.wait\\", true | |||
} | |||
}" | |||
/> | |||
</li> | |||
</ol> | |||
<Button | |||
onClick={[MockFunction]} | |||
> | |||
continue | |||
</Button> | |||
</div> | |||
`; | |||
exports[`should render correctly for build tool 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", | |||
"other", | |||
] | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="abs-width-600" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.project_key.maven.step2" | |||
id="onboarding.tutorial.with.gitlab_ci.project_key.maven.step2" | |||
values={ | |||
Object { | |||
"file": <React.Fragment> | |||
<code | |||
className="rule" | |||
> | |||
pom.xml | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue="pom.xml" | |||
/> | |||
</React.Fragment>, | |||
} | |||
} | |||
/> | |||
<CodeSnippet | |||
snippet="<properties> | |||
<sonar.projectKey>my-project</sonar.projectKey> | |||
<sonar.qualitygate.wait>true</sonar.qualitygate.wait> | |||
</properties>" | |||
/> | |||
</li> | |||
</ol> | |||
<Button | |||
onClick={[MockFunction]} | |||
> | |||
continue | |||
</Button> | |||
</div> | |||
`; | |||
exports[`should render correctly for build tool 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", | |||
"other", | |||
] | |||
} | |||
/> | |||
</li> | |||
<li | |||
className="abs-width-600" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.project_key.other.step2" | |||
id="onboarding.tutorial.with.gitlab_ci.project_key.other.step2" | |||
values={ | |||
Object { | |||
"file": <React.Fragment> | |||
<code | |||
className="rule" | |||
> | |||
sonar-project.properties | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue="sonar-project.properties" | |||
/> | |||
</React.Fragment>, | |||
} | |||
} | |||
/> | |||
<CodeSnippet | |||
snippet="sonar.projectKey=my-project | |||
sonar.qualitygate.wait=true | |||
" | |||
/> | |||
</li> | |||
</ol> | |||
<Button | |||
onClick={[MockFunction]} | |||
> | |||
continue | |||
</Button> | |||
</div> | |||
`; | |||
exports[`should render correctly: Step wrapper 1`] = ` | |||
<Step | |||
finished={false} | |||
onOpen={[MockFunction]} | |||
open={true} | |||
renderForm={[Function]} | |||
stepNumber={1} | |||
stepTitle="onboarding.tutorial.with.gitlab_ci.project_key.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", | |||
"other", | |||
] | |||
} | |||
/> | |||
</li> | |||
</ol> | |||
</div> | |||
`; |
@@ -0,0 +1,349 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly for build tool gradle 1`] = ` | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<div | |||
className="flex-columns" | |||
> | |||
<div | |||
className="flex-column-full" | |||
> | |||
<React.Fragment> | |||
<div | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
values={ | |||
Object { | |||
"filename": <React.Fragment> | |||
<code | |||
className="rule" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.filename | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue="onboarding.tutorial.with.gitlab_ci.yml.filename" | |||
/> | |||
</React.Fragment>, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="big-spacer-bottom" | |||
> | |||
<PipeCommandGradle /> | |||
</div> | |||
<p | |||
className="little-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.baseconfig | |||
</p> | |||
<p | |||
className="huge-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.existing | |||
</p> | |||
<hr | |||
className="no-horizontal-margins" | |||
/> | |||
<div> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done | |||
</strong> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
values={ | |||
Object { | |||
"link": "onboarding.tutorial.with.gitlab_ci.yml.done.description.link", | |||
} | |||
} | |||
/> | |||
</p> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what | |||
</strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what.description | |||
</p> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
values={ | |||
Object { | |||
"links": <Link | |||
onlyActiveOnIndex={false} | |||
rel="noopener noreferrer" | |||
style={Object {}} | |||
target="_blank" | |||
to="/documentation/user-guide/quality-gates/" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.links.QG | |||
</Link>, | |||
} | |||
} | |||
/> | |||
</p> | |||
</div> | |||
</React.Fragment> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly for build tool maven 1`] = ` | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<div | |||
className="flex-columns" | |||
> | |||
<div | |||
className="flex-column-full" | |||
> | |||
<React.Fragment> | |||
<div | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
values={ | |||
Object { | |||
"filename": <React.Fragment> | |||
<code | |||
className="rule" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.filename | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue="onboarding.tutorial.with.gitlab_ci.yml.filename" | |||
/> | |||
</React.Fragment>, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="big-spacer-bottom" | |||
> | |||
<PipeCommandMaven /> | |||
</div> | |||
<p | |||
className="little-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.baseconfig | |||
</p> | |||
<p | |||
className="huge-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.existing | |||
</p> | |||
<hr | |||
className="no-horizontal-margins" | |||
/> | |||
<div> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done | |||
</strong> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
values={ | |||
Object { | |||
"link": "onboarding.tutorial.with.gitlab_ci.yml.done.description.link", | |||
} | |||
} | |||
/> | |||
</p> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what | |||
</strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what.description | |||
</p> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
values={ | |||
Object { | |||
"links": <Link | |||
onlyActiveOnIndex={false} | |||
rel="noopener noreferrer" | |||
style={Object {}} | |||
target="_blank" | |||
to="/documentation/user-guide/quality-gates/" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.links.QG | |||
</Link>, | |||
} | |||
} | |||
/> | |||
</p> | |||
</div> | |||
</React.Fragment> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly for build tool other 1`] = ` | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<div | |||
className="flex-columns" | |||
> | |||
<div | |||
className="flex-column-full" | |||
> | |||
<React.Fragment> | |||
<div | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.description" | |||
values={ | |||
Object { | |||
"filename": <React.Fragment> | |||
<code | |||
className="rule" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.filename | |||
</code> | |||
<ClipboardIconButton | |||
className="little-spacer-left" | |||
copyValue="onboarding.tutorial.with.gitlab_ci.yml.filename" | |||
/> | |||
</React.Fragment>, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="big-spacer-bottom" | |||
> | |||
<PipeCommandOther /> | |||
</div> | |||
<p | |||
className="little-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.baseconfig | |||
</p> | |||
<p | |||
className="huge-spacer-bottom" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.existing | |||
</p> | |||
<hr | |||
className="no-horizontal-margins" | |||
/> | |||
<div> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done | |||
</strong> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.description" | |||
values={ | |||
Object { | |||
"link": "onboarding.tutorial.with.gitlab_ci.yml.done.description.link", | |||
} | |||
} | |||
/> | |||
</p> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what | |||
</strong> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what.description | |||
</p> | |||
<p | |||
className="big-spacer-bottom" | |||
> | |||
<FormattedMessage | |||
defaultMessage="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
id="onboarding.tutorial.with.gitlab_ci.yml.done.links.title" | |||
values={ | |||
Object { | |||
"links": <Link | |||
onlyActiveOnIndex={false} | |||
rel="noopener noreferrer" | |||
style={Object {}} | |||
target="_blank" | |||
to="/documentation/user-guide/quality-gates/" | |||
> | |||
onboarding.tutorial.with.gitlab_ci.yml.done.links.QG | |||
</Link>, | |||
} | |||
} | |||
/> | |||
</p> | |||
</div> | |||
</React.Fragment> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: Step wrapper 1`] = ` | |||
<Step | |||
finished={false} | |||
open={true} | |||
renderForm={[Function]} | |||
stepNumber={3} | |||
stepTitle="onboarding.tutorial.with.gitlab_ci.yml.title" | |||
/> | |||
`; | |||
exports[`should render correctly: initial content 1`] = ` | |||
<div | |||
className="boxed-group-inner" | |||
> | |||
<div | |||
className="flex-columns" | |||
> | |||
<div | |||
className="flex-column-full" | |||
/> | |||
</div> | |||
</div> | |||
`; |
@@ -0,0 +1,42 @@ | |||
/* | |||
* 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'; | |||
export default function PipeCommandGradle() { | |||
const command = `sonarqube-check: | |||
image: gradle:jre11-slim | |||
variables: | |||
SONAR_USER_HOME: "\${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache | |||
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task | |||
cache: | |||
key: "\${CI_JOB_NAME}" | |||
paths: | |||
- .sonar/cache | |||
script: gradle sonarqube | |||
allow_failure: true | |||
only: | |||
- merge_requests | |||
- master | |||
- develop | |||
`; | |||
return <CodeSnippet snippet={command} />; | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import CodeSnippet from '../../../common/CodeSnippet'; | |||
export default function PipeCommandMaven() { | |||
const command = `sonarqube-check: | |||
image: maven:3.6.3-jdk-11 | |||
variables: | |||
SONAR_USER_HOME: "\${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache | |||
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task | |||
cache: | |||
key: "\${CI_JOB_NAME}" | |||
paths: | |||
- .sonar/cache | |||
script: | |||
- mvn verify sonar:sonar | |||
allow_failure: true | |||
only: | |||
- merge_requests | |||
- master | |||
- develop | |||
`; | |||
return <CodeSnippet snippet={command} />; | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* 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'; | |||
export default function PipeCommandOther() { | |||
const command = `sonarqube-check: | |||
image: | |||
name: sonarsource/sonar-scanner-cli:latest | |||
entrypoint: [""] | |||
variables: | |||
SONAR_USER_HOME: "\${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache | |||
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task | |||
cache: | |||
key: "\${CI_JOB_NAME}" | |||
paths: | |||
- .sonar/cache | |||
script: | |||
- sonar-scanner | |||
allow_failure: true | |||
only: | |||
- merge_requests | |||
- master | |||
- develop | |||
`; | |||
return <CodeSnippet snippet={command} />; | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import PipeCommandGradle from '../PipeCommandGradle'; | |||
it('should render correctly', () => { | |||
expect(shallow(<PipeCommandGradle />)).toMatchSnapshot(); | |||
}); |
@@ -0,0 +1,26 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import PipeCommandMaven from '../PipeCommandMaven'; | |||
it('should render correctly', () => { | |||
expect(shallow(<PipeCommandMaven />)).toMatchSnapshot(); | |||
}); |
@@ -0,0 +1,26 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import PipeCommandOther from '../PipeCommandOther'; | |||
it('should render correctly', () => { | |||
expect(shallow(<PipeCommandOther />)).toMatchSnapshot(); | |||
}); |
@@ -0,0 +1,22 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<CodeSnippet | |||
snippet="sonarqube-check: | |||
image: gradle:jre11-slim | |||
variables: | |||
SONAR_USER_HOME: \\"\${CI_PROJECT_DIR}/.sonar\\" # Defines the location of the analysis task cache | |||
GIT_DEPTH: \\"0\\" # Tells git to fetch all the branches of the project, required by the analysis task | |||
cache: | |||
key: \\"\${CI_JOB_NAME}\\" | |||
paths: | |||
- .sonar/cache | |||
script: gradle sonarqube | |||
allow_failure: true | |||
only: | |||
- merge_requests | |||
- master | |||
- develop | |||
" | |||
/> | |||
`; |
@@ -0,0 +1,23 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<CodeSnippet | |||
snippet="sonarqube-check: | |||
image: maven:3.6.3-jdk-11 | |||
variables: | |||
SONAR_USER_HOME: \\"\${CI_PROJECT_DIR}/.sonar\\" # Defines the location of the analysis task cache | |||
GIT_DEPTH: \\"0\\" # Tells git to fetch all the branches of the project, required by the analysis task | |||
cache: | |||
key: \\"\${CI_JOB_NAME}\\" | |||
paths: | |||
- .sonar/cache | |||
script: | |||
- mvn verify sonar:sonar | |||
allow_failure: true | |||
only: | |||
- merge_requests | |||
- master | |||
- develop | |||
" | |||
/> | |||
`; |
@@ -0,0 +1,25 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<CodeSnippet | |||
snippet="sonarqube-check: | |||
image: | |||
name: sonarsource/sonar-scanner-cli:latest | |||
entrypoint: [\\"\\"] | |||
variables: | |||
SONAR_USER_HOME: \\"\${CI_PROJECT_DIR}/.sonar\\" # Defines the location of the analysis task cache | |||
GIT_DEPTH: \\"0\\" # Tells git to fetch all the branches of the project, required by the analysis task | |||
cache: | |||
key: \\"\${CI_JOB_NAME}\\" | |||
paths: | |||
- .sonar/cache | |||
script: | |||
- sonar-scanner | |||
allow_failure: true | |||
only: | |||
- merge_requests | |||
- master | |||
- develop | |||
" | |||
/> | |||
`; |
@@ -17,7 +17,9 @@ | |||
* 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; | |||
export enum BuildTools { | |||
Maven = 'maven', | |||
Gradle = 'gradle', | |||
// MSBuild = 'msbuild', // Not yet supported | |||
Other = 'other' | |||
} |
@@ -19,7 +19,8 @@ | |||
*/ | |||
export enum TutorialModes { | |||
Manual = 'manual', | |||
Jenkins = 'jenkins' | |||
Jenkins = 'jenkins', | |||
GitLabCI = 'gitlab-ci' | |||
} | |||
export interface LanguageConfig { |
@@ -24,7 +24,8 @@ import { | |||
GithubBindingDefinition, | |||
ProjectAlmBindingResponse, | |||
ProjectBitbucketBindingResponse, | |||
ProjectGitHubBindingResponse | |||
ProjectGitHubBindingResponse, | |||
ProjectGitLabBindingResponse | |||
} from '../types/alm-settings'; | |||
export function isProjectBitbucketBindingResponse( | |||
@@ -39,6 +40,12 @@ export function isProjectGitHubBindingResponse( | |||
return binding.alm === AlmKeys.GitHub; | |||
} | |||
export function isProjectGitLabBindingResponse( | |||
binding: ProjectAlmBindingResponse | |||
): binding is ProjectGitLabBindingResponse { | |||
return binding.alm === AlmKeys.GitLab; | |||
} | |||
export function isBitbucketBindingDefinition( | |||
binding?: AlmBindingDefinition & { url?: string; personalAccessToken?: string } | |||
): binding is BitbucketBindingDefinition { |
@@ -26,7 +26,8 @@ import { | |||
GitlabBindingDefinition, | |||
ProjectAlmBindingResponse, | |||
ProjectBitbucketBindingResponse, | |||
ProjectGitHubBindingResponse | |||
ProjectGitHubBindingResponse, | |||
ProjectGitLabBindingResponse | |||
} from '../../types/alm-settings'; | |||
export function mockAlmSettingsInstance( | |||
@@ -116,3 +117,15 @@ export function mockProjectGithubBindingResponse( | |||
...overrides | |||
}; | |||
} | |||
export function mockProjectGitLabBindingResponse( | |||
overrides: Partial<ProjectGitLabBindingResponse> = {} | |||
): ProjectGitLabBindingResponse { | |||
return { | |||
alm: AlmKeys.GitLab, | |||
key: 'foo', | |||
repository: 'PROJECT_KEY', | |||
url: 'https://gitlab.com/api/v4', | |||
...overrides | |||
}; | |||
} |
@@ -69,6 +69,12 @@ export interface ProjectGitHubBindingResponse extends ProjectAlmBindingResponse | |||
repository: string; | |||
} | |||
export interface ProjectGitLabBindingResponse extends ProjectAlmBindingResponse { | |||
alm: AlmKeys.GitLab; | |||
repository: string; | |||
url: string; | |||
} | |||
export interface ProjectAlmBindingParams { | |||
almSetting: string; | |||
project: string; |
@@ -3239,6 +3239,7 @@ onboarding.token.generate_token=Generate a token | |||
onboarding.token.generate_token.placeholder=Enter a name for your token | |||
onboarding.token.use_existing_token=Use existing token | |||
onboarding.token.use_existing_token.placeholder=Enter your existing token | |||
onboarding.token.use_existing_token.label=Existing token value | |||
onboarding.token.invalid_format=The token you have entered has invalid format. | |||
onboarding.analysis.header=Run analysis on your project | |||
@@ -3322,6 +3323,41 @@ 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.choose_method.gitlab_ci=With GitLab CI | |||
onboarding.tutorial.with.gitlab_ci.title=Analyze your project with GitLab CI | |||
onboarding.tutorial.with.gitlab_ci.unsupported=This tutorial is only available for projects bound to GitLab. | |||
onboarding.tutorial.with.gitlab_ci.project_key.title=Set your project key | |||
onboarding.tutorial.with.gitlab_ci.project_key.maven.step2=Add the following to your {file} file: | |||
onboarding.tutorial.with.gitlab_ci.project_key.gradle.step2=Add the following to your {file} file: | |||
onboarding.tutorial.with.gitlab_ci.project_key.other.step2=Create a {file} file in your repository and paste the following code: | |||
onboarding.tutorial.with.gitlab_ci.env_variables.title=Add environment variables | |||
onboarding.tutorial.with.gitlab_ci.env_variables.enter_field_value=In the {field} field, enter {value} {extra} | |||
onboarding.tutorial.with.gitlab_ci.env_variables.description.link=Settings > CI/CD > Variables | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section.title=a. Define the SonarQube Token environment variable | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section.description=In GitLab, go to {link} to add the following variable and make sure it is available for your project: | |||
onboarding.tutorial.with.gitlab_ci.env_variables.edit.token.tooltip=Use an existing token or generate a new one. | |||
onboarding.tutorial.with.gitlab_ci.env_variables.step1=Key | |||
onboarding.tutorial.with.gitlab_ci.env_variables.step2=Value | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section.step2.value=an existing token, or a newly generated one: | |||
onboarding.tutorial.with.gitlab_ci.env_variables.step3=Uncheck the "Protect Variable" checkbox | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section.step4=Check the "Mask Variable" checkbox | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section2.title=b. Define the SonarQube URL environment variable | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section2.description=Still in {link} add a new variable and make sure it is available for your project: | |||
onboarding.tutorial.with.gitlab_ci.env_variables.section2.step4=Leave the "Mask variable" checkbox unchecked | |||
onboarding.tutorial.with.gitlab_ci.yml.title=Create or update the configuration file | |||
onboarding.tutorial.with.gitlab_ci.yml.description=Create or update your {filename} file with the following content. | |||
onboarding.tutorial.with.gitlab_ci.yml.filename=.gitlab-ci.yml | |||
onboarding.tutorial.with.gitlab_ci.yml.baseconfig=Note that this is a minimal base configuration to run a SonarQube analysis on your master branch and merge requests. | |||
onboarding.tutorial.with.gitlab_ci.yml.existing=If you already have a pipeline configured and running, you might want to add the example from this step to your existing yml file. | |||
onboarding.tutorial.with.gitlab_ci.yml.done=Is it done? | |||
onboarding.tutorial.with.gitlab_ci.yml.done.description=You should see the page refresh itself in a few moments with your analysis results if the {link}. | |||
onboarding.tutorial.with.gitlab_ci.yml.done.description.link=pipeline runs successfully | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what=And then what? | |||
onboarding.tutorial.with.gitlab_ci.yml.done.then-what.description=Each new push triggers an analysis by SonarQube. | |||
onboarding.tutorial.with.gitlab_ci.yml.done.links.title=Check this useful link while you wait: {links} | |||
onboarding.tutorial.with.gitlab_ci.yml.done.links.QG=What is a Quality Gate? | |||
onboarding.tutorial.with.jenkins.title=Analyze your project with Jenkins | |||
onboarding.tutorial.with.jenkins.unsupported=This tutorial is only available for projects bound to Bitbucket Server or GitHub. |