diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2021-04-19 14:30:09 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-04-22 20:03:34 +0000 |
commit | 17453c73bc0eeab032ca66619347dee91bfec4bb (patch) | |
tree | f22c6cc82a5ddeb2c305330d485c3a175ff0342a | |
parent | c36b12d2160dd7c57bb7374345a02ebb91ae31e9 (diff) | |
download | sonarqube-17453c73bc0eeab032ca66619347dee91bfec4bb.tar.gz sonarqube-17453c73bc0eeab032ca66619347dee91bfec4bb.zip |
SONAR-13737 Show descriptive error messages for GitLab PAT validation
14 files changed, 220 insertions, 51 deletions
diff --git a/server/sonar-web/src/main/js/api/alm-integrations.ts b/server/sonar-web/src/main/js/api/alm-integrations.ts index cc287712341..7bdd9e14622 100644 --- a/server/sonar-web/src/main/js/api/alm-integrations.ts +++ b/server/sonar-web/src/main/js/api/alm-integrations.ts @@ -17,7 +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. */ -import { get, getJSON, post, postJSON } from 'sonar-ui-common/helpers/request'; +import { get, getJSON, parseError, post, postJSON } from 'sonar-ui-common/helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; import { AzureProject, @@ -34,15 +34,17 @@ export function setAlmPersonalAccessToken(almSetting: string, pat: string): Prom return post('/api/alm_integrations/set_pat', { almSetting, pat }).catch(throwGlobalError); } -export function checkPersonalAccessTokenIsValid(almSetting: string): Promise<boolean> { +export function checkPersonalAccessTokenIsValid( + almSetting: string +): Promise<{ status: boolean; error?: string }> { return get('/api/alm_integrations/check_pat', { almSetting }) - .then(() => true) - .catch((response: Response) => { + .then(() => ({ status: true })) + .catch(async (response: Response) => { if (response.status === 400) { - return false; - } else { - return throwGlobalError(response); + const error = await parseError(response); + return { status: false, error }; } + return throwGlobalError(response); }); } diff --git a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx index 04dba03bc88..0bb78cd2957 100644 --- a/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/AzureProjectCreate.tsx @@ -240,7 +240,7 @@ export default class AzureProjectCreate extends React.PureComponent<Props, State return Promise.resolve(false); } - return checkPersonalAccessTokenIsValid(settings.key); + return checkPersonalAccessTokenIsValid(settings.key).then(({ status }) => status); }; handlePersonalAccessTokenCreate = async (token: string) => { diff --git a/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx index 7c6f4fe80e0..c3d57d0cfbc 100644 --- a/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx @@ -122,7 +122,7 @@ export default class BitbucketProjectCreate extends React.PureComponent<Props, S return Promise.resolve(false); } - return checkPersonalAccessTokenIsValid(bitbucketSetting.key); + return checkPersonalAccessTokenIsValid(bitbucketSetting.key).then(({ status }) => status); }; fetchBitbucketProjects = (): Promise<BitbucketProject[] | undefined> => { diff --git a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx index 10560b235eb..39b8537581f 100644 --- a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreate.tsx @@ -19,6 +19,7 @@ */ import * as React from 'react'; import { WithRouterProps } from 'react-router'; +import { translate } from 'sonar-ui-common/helpers/l10n'; import { checkPersonalAccessTokenIsValid, getGitlabProjects, @@ -26,7 +27,7 @@ import { setAlmPersonalAccessToken } from '../../../api/alm-integrations'; import { GitlabProject } from '../../../types/alm-integration'; -import { AlmSettingsInstance } from '../../../types/alm-settings'; +import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings'; import GitlabProjectCreateRenderer from './GitlabProjectCreateRenderer'; interface Props extends Pick<WithRouterProps, 'location' | 'router'> { @@ -44,7 +45,7 @@ interface State { projectsPaging: T.Paging; submittingToken: boolean; tokenIsValid: boolean; - tokenValidationFailed: boolean; + tokenValidationErrorMessage?: string; searching: boolean; searchQuery: string; settings?: AlmSettingsInstance; @@ -66,8 +67,7 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat searching: false, searchQuery: '', settings: props.settings.length === 1 ? props.settings[0] : undefined, - submittingToken: false, - tokenValidationFailed: false + submittingToken: false }; } @@ -92,10 +92,10 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat fetchInitialData = async () => { this.setState({ loading: true }); - const tokenIsValid = await this.checkPersonalAccessToken(); + const { status, error } = await this.checkPersonalAccessToken(); let result; - if (tokenIsValid) { + if (status) { result = await this.fetchProjects(); } @@ -104,14 +104,15 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat const { projects, projectsPaging } = result; this.setState({ - tokenIsValid, + tokenIsValid: status, loading: false, projects, projectsPaging }); } else { this.setState({ - loading: false + loading: false, + tokenValidationErrorMessage: !status ? error : undefined }); } } @@ -121,10 +122,13 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat const { settings } = this.state; if (!settings) { - return Promise.resolve(false); + return Promise.resolve({ + status: false, + error: translate('onboarding.create_project.pat_incorrect', AlmKeys.GitLab) + }); } - return checkPersonalAccessTokenIsValid(settings.key).catch(() => false); + return checkPersonalAccessTokenIsValid(settings.key); }; handleError = () => { @@ -231,21 +235,21 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat return; } - this.setState({ submittingToken: true, tokenValidationFailed: false }); + this.setState({ submittingToken: true, tokenValidationErrorMessage: undefined }); try { await setAlmPersonalAccessToken(settings.key, token); - const patIsValid = await this.checkPersonalAccessToken(); + const { status, error } = await this.checkPersonalAccessToken(); if (this.mounted) { this.setState({ submittingToken: false, - tokenIsValid: patIsValid, - tokenValidationFailed: !patIsValid + tokenIsValid: status, + tokenValidationErrorMessage: error }); - if (patIsValid) { + if (status) { this.cleanUrl(); await this.fetchInitialData(); } @@ -270,7 +274,7 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat searchQuery, settings, submittingToken, - tokenValidationFailed + tokenValidationErrorMessage } = this.state; return ( @@ -290,7 +294,7 @@ export default class GitlabProjectCreate extends React.PureComponent<Props, Stat searchQuery={searchQuery} showPersonalAccessTokenForm={!tokenIsValid || Boolean(location.query.resetPat)} submittingToken={submittingToken} - tokenValidationFailed={tokenValidationFailed} + tokenValidationErrorMessage={tokenValidationErrorMessage} /> ); } diff --git a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx index d5d46201059..e75f5809845 100644 --- a/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/GitlabProjectCreateRenderer.tsx @@ -43,7 +43,7 @@ export interface GitlabProjectCreateRendererProps { settings?: AlmSettingsInstance; showPersonalAccessTokenForm?: boolean; submittingToken?: boolean; - tokenValidationFailed: boolean; + tokenValidationErrorMessage?: string; } export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRendererProps) { @@ -59,7 +59,7 @@ export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRe settings, showPersonalAccessTokenForm, submittingToken, - tokenValidationFailed + tokenValidationErrorMessage } = props; return ( @@ -91,7 +91,8 @@ export default function GitlabProjectCreateRenderer(props: GitlabProjectCreateRe almSetting={settings} onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate} submitting={submittingToken} - validationFailed={tokenValidationFailed} + validationFailed={Boolean(tokenValidationErrorMessage)} + validationErrorMessage={tokenValidationErrorMessage} /> ) : ( <GitlabProjectSelectionForm diff --git a/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx b/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx index 51811a40ac1..3fb0a2fe63f 100644 --- a/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/PersonalAccessTokenForm.tsx @@ -33,6 +33,7 @@ export interface PersonalAccessTokenFormProps { onPersonalAccessTokenCreate: (token: string) => void; submitting?: boolean; validationFailed: boolean; + validationErrorMessage?: string; } function getPatUrl(alm: AlmKeys, url: string) { @@ -50,7 +51,8 @@ export default function PersonalAccessTokenForm(props: PersonalAccessTokenFormPr const { almSetting: { alm, url }, submitting = false, - validationFailed + validationFailed, + validationErrorMessage } = props; const [touched, setTouched] = React.useState(false); @@ -59,6 +61,8 @@ export default function PersonalAccessTokenForm(props: PersonalAccessTokenFormPr }, [submitting]); const isInvalid = validationFailed && !touched; + const errorMessage = + validationErrorMessage ?? translate('onboarding.create_project.pat_incorrect', alm); return ( <div className="display-flex-start"> @@ -75,7 +79,7 @@ export default function PersonalAccessTokenForm(props: PersonalAccessTokenFormPr </p> <ValidationInput - error={isInvalid ? translate('onboarding.create_project.pat_incorrect', alm) : undefined} + error={isInvalid ? errorMessage : undefined} id="personal_access_token" isInvalid={isInvalid} isValid={false} diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx index f773c8f33f0..bb4ddc7d409 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreate-test.tsx @@ -37,7 +37,7 @@ import AzureProjectCreate from '../AzureProjectCreate'; jest.mock('../../../../api/alm-integrations', () => { return { - checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue(true), + checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue({ status: true }), setAlmPersonalAccessToken: jest.fn().mockResolvedValue(null), getAzureProjects: jest.fn().mockResolvedValue({ projects: [] }), getAzureRepositories: jest.fn().mockResolvedValue({ repositories: [] }), @@ -59,7 +59,7 @@ it('should correctly fetch binding info on mount', async () => { }); it('should correctly handle a valid PAT', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); @@ -67,7 +67,7 @@ it('should correctly handle a valid PAT', async () => { }); it('should correctly handle an invalid PAT', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: false }); const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); @@ -81,7 +81,7 @@ it('should correctly handle setting a new PAT', async () => { expect(setAlmPersonalAccessToken).toBeCalledWith('foo', 'token'); expect(wrapper.state().submittingToken).toBe(true); - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: false }); await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); expect(wrapper.state().submittingToken).toBe(false); diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx index e1317528df6..bb3fd5ae7df 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx @@ -39,7 +39,7 @@ jest.mock('../../../../api/alm-integrations', () => { '../../../../helpers/mocks/alm-integrations' ); return { - checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue(true), + checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue({ status: true }), getBitbucketServerProjects: jest.fn().mockResolvedValue({ projects: [ mockBitbucketProject({ key: 'project1', name: 'Project 1' }), @@ -76,7 +76,7 @@ it('should correctly fetch binding info on mount', async () => { }); it('should correctly handle a valid PAT', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); @@ -84,7 +84,7 @@ it('should correctly handle a valid PAT', async () => { }); it('should correctly handle an invalid PAT', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: false }); const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); @@ -97,7 +97,7 @@ it('should correctly handle setting a new PAT', async () => { expect(setAlmPersonalAccessToken).toBeCalledWith('foo', 'token'); expect(wrapper.state().submittingToken).toBe(true); - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: false }); await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); expect(wrapper.state().submittingToken).toBe(false); diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx index 890ccadc4f9..e5663ff7734 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreate-test.tsx @@ -33,7 +33,7 @@ import { AlmKeys } from '../../../../types/alm-settings'; import GitlabProjectCreate from '../GitlabProjectCreate'; jest.mock('../../../../api/alm-integrations', () => ({ - checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue(true), + checkPersonalAccessTokenIsValid: jest.fn().mockResolvedValue({ status: true }), setAlmPersonalAccessToken: jest.fn().mockResolvedValue(null), getGitlabProjects: jest.fn().mockRejectedValue('error'), importGitlabProject: jest.fn().mockRejectedValue('error') @@ -65,7 +65,7 @@ it('should correctly check PAT when settings are added after mount', async () => }); it('should correctly handle a valid PAT', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); (getGitlabProjects as jest.Mock).mockResolvedValueOnce({ projects: [mockGitlabProject()], projectsPaging: { @@ -80,7 +80,7 @@ it('should correctly handle a valid PAT', async () => { }); it('should correctly handle an invalid PAT', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: false }); const wrapper = shallowRender(); await waitAndUpdate(wrapper); expect(wrapper.state().tokenIsValid).toBe(false); @@ -95,7 +95,8 @@ describe('setting a new PAT', () => { }); it('should correctly handle it if invalid', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false); + const error = 'error message'; + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: false, error }); wrapper.instance().handlePersonalAccessTokenCreate('invalidtoken'); expect(setAlmPersonalAccessToken).toBeCalledWith(almSettingKey, 'invalidtoken'); @@ -103,11 +104,11 @@ describe('setting a new PAT', () => { await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); expect(wrapper.state().submittingToken).toBe(false); - expect(wrapper.state().tokenValidationFailed).toBe(true); + expect(wrapper.state().tokenValidationErrorMessage).toBe(error); }); it('should correctly handle it if valid', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); wrapper.instance().handlePersonalAccessTokenCreate('validtoken'); expect(setAlmPersonalAccessToken).toBeCalledWith(almSettingKey, 'validtoken'); @@ -115,14 +116,14 @@ describe('setting a new PAT', () => { await waitAndUpdate(wrapper); expect(checkPersonalAccessTokenIsValid).toBeCalled(); expect(wrapper.state().submittingToken).toBe(false); - expect(wrapper.state().tokenValidationFailed).toBe(false); + expect(wrapper.state().tokenValidationErrorMessage).toBeUndefined(); expect(routerReplace).toBeCalled(); }); }); it('should fetch more projects and preserve search', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); const projects = [ mockGitlabProject({ id: '1' }), @@ -166,7 +167,7 @@ it('should fetch more projects and preserve search', async () => { }); it('should search for projects', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); const projects = [ mockGitlabProject({ id: '1' }), @@ -210,7 +211,7 @@ it('should search for projects', async () => { }); it('should import', async () => { - (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(true); + (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce({ status: true }); const projects = [mockGitlabProject({ id: '1' }), mockGitlabProject({ id: '2' })]; (getGitlabProjects as jest.Mock).mockResolvedValueOnce({ diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx index 76ac851e494..3a41f9c6fde 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitlabProjectCreateRenderer-test.tsx @@ -35,6 +35,9 @@ it('should render correctly', () => { expect(shallowRender({ showPersonalAccessTokenForm: false })).toMatchSnapshot( 'project selection form' ); + expect(shallowRender({ tokenValidationErrorMessage: 'error' })).toMatchSnapshot( + 'pat validation error' + ); }); function shallowRender(props: Partial<GitlabProjectCreateRendererProps> = {}) { @@ -53,7 +56,6 @@ function shallowRender(props: Partial<GitlabProjectCreateRendererProps> = {}) { searchQuery="" showPersonalAccessTokenForm={true} submittingToken={false} - tokenValidationFailed={false} settings={mockAlmSettingsInstance({ alm: AlmKeys.GitLab })} {...props} /> diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx index 5e7571a211b..23fc3ad3138 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/PersonalAccessTokenForm-test.tsx @@ -30,6 +30,9 @@ it('should render correctly', () => { expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting'); expect(shallowRender({ validationFailed: true })).toMatchSnapshot('validation failed'); expect( + shallowRender({ validationFailed: true, validationErrorMessage: 'error' }) + ).toMatchSnapshot('validation failed, custom error message'); + expect( shallowRender({ almSetting: mockAlmSettingsInstance({ alm: AlmKeys.GitLab, url: 'https://gitlab.com/api/v4' }) }) diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap index 7d9b8e454d4..8b9520b04ec 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreate-test.tsx.snap @@ -26,6 +26,5 @@ exports[`should render correctly 1`] = ` } showPersonalAccessTokenForm={true} submittingToken={false} - tokenValidationFailed={false} /> `; diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap index 7a78bb9b202..72c564b381a 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/GitlabProjectCreateRenderer-test.tsx.snap @@ -102,6 +102,38 @@ exports[`should render correctly: pat form 1`] = ` </Fragment> `; +exports[`should render correctly: pat validation error 1`] = ` +<Fragment> + <CreateProjectPageHeader + title={ + <span + className="text-middle" + > + <img + alt="" + className="spacer-right" + height="24" + src="/images/alm/gitlab.svg" + /> + onboarding.create_project.gitlab.title + </span> + } + /> + <PersonalAccessTokenForm + almSetting={ + Object { + "alm": "gitlab", + "key": "key", + } + } + onPersonalAccessTokenCreate={[MockFunction]} + submitting={false} + validationErrorMessage="error" + validationFailed={true} + /> +</Fragment> +`; + exports[`should render correctly: project selection form 1`] = ` <Fragment> <CreateProjectPageHeader diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap index 4440b662d05..c2d3a437231 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/PersonalAccessTokenForm-test.tsx.snap @@ -562,3 +562,124 @@ exports[`should render correctly: validation failed 1`] = ` </Alert> </div> `; + +exports[`should render correctly: validation failed, custom error message 1`] = ` +<div + className="display-flex-start" +> + <form + className="width-50" + onSubmit={[Function]} + > + <h2 + className="big" + > + onboarding.create_project.pat_form.title.bitbucket + </h2> + <p + className="big-spacer-top big-spacer-bottom" + > + onboarding.create_project.pat_form.help.bitbucket + </p> + <ValidationInput + error="error" + id="personal_access_token" + isInvalid={true} + isValid={false} + label="onboarding.create_project.enter_pat" + required={true} + > + <input + autoFocus={true} + className="input-super-large is-invalid" + id="personal_access_token" + minLength={1} + name="personal_access_token" + onChange={[Function]} + type="text" + /> + </ValidationInput> + <SubmitButton + disabled={true} + > + save + </SubmitButton> + <DeferredSpinner + className="spacer-left" + loading={false} + /> + </form> + <Alert + className="big-spacer-left width-50" + display="block" + variant="info" + > + <h3> + onboarding.create_project.pat_help.title + </h3> + <p + className="big-spacer-top big-spacer-bottom" + > + <FormattedMessage + defaultMessage="onboarding.create_project.pat_help.instructions" + id="onboarding.create_project.pat_help.instructions" + values={ + Object { + "alm": "onboarding.alm.bitbucket", + } + } + /> + </p> + <div + className="text-middle" + > + <img + alt="" + className="spacer-right" + height="16" + src="/images/alm/bitbucket.svg" + /> + <a + href="http://www.example.com/plugins/servlet/access-tokens/add" + rel="noopener noreferrer" + target="_blank" + > + onboarding.create_project.pat_help.link + </a> + </div> + <p + className="big-spacer-top big-spacer-bottom" + > + onboarding.create_project.pat_help.instructions2.bitbucket + </p> + <ul> + <li> + <FormattedMessage + defaultMessage="onboarding.create_project.pat_help.bbs_permission_projects" + id="onboarding.create_project.pat_help.bbs_permission_projects" + values={ + Object { + "perm": <strong> + onboarding.create_project.pat_help.read_permission + </strong>, + } + } + /> + </li> + <li> + <FormattedMessage + defaultMessage="onboarding.create_project.pat_help.bbs_permission_repos" + id="onboarding.create_project.pat_help.bbs_permission_repos" + values={ + Object { + "perm": <strong> + onboarding.create_project.pat_help.read_permission + </strong>, + } + } + /> + </li> + </ul> + </Alert> +</div> +`; |