]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13004 Validate Bitbucket personal access token when saving
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Thu, 13 Feb 2020 07:56:40 +0000 (08:56 +0100)
committerSonarTech <sonartech@sonarsource.com>
Thu, 20 Feb 2020 19:46:18 +0000 (20:46 +0100)
14 files changed:
server/sonar-web/src/main/js/apps/create/project/BitbucketImportRepositoryForm.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketPersonalAccessTokenForm.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketProjectAccordion.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreate.tsx
server/sonar-web/src/main/js/apps/create/project/BitbucketProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreateRenderer-test.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketImportRepositoryForm-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketPersonalAccessTokenForm-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectAccordion-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreate-test.tsx.snap
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 017c2acbb460dc5486b783065fffd5126479d2d7..2f999613558b79f31f5ff9cab92bbde489717bda 100644 (file)
@@ -18,6 +18,8 @@
  * 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 SearchBox from 'sonar-ui-common/components/controls/SearchBox';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate } from 'sonar-ui-common/helpers/l10n';
@@ -28,6 +30,7 @@ import {
 } from '../../../types/alm-integration';
 import BitbucketRepositories from './BitbucketRepositories';
 import BitbucketSearchResults from './BitbucketSearchResults';
+import { CreateProjectModes } from './types';
 
 export interface BitbucketImportRepositoryFormProps {
   disableRepositories: boolean;
@@ -53,7 +56,21 @@ export default function BitbucketImportRepositoryForm(props: BitbucketImportRepo
   if (projects.length === 0) {
     return (
       <Alert className="spacer-top" variant="warning">
-        {translate('onboarding.create_project.no_bbs_projects')}
+        <FormattedMessage
+          defaultMessage={translate('onboarding.create_project.no_bbs_projects')}
+          id="onboarding.create_project.no_bbs_projects"
+          values={{
+            link: (
+              <Link
+                to={{
+                  pathname: '/projects/create',
+                  query: { mode: CreateProjectModes.BitbucketServer, resetPat: 1 }
+                }}>
+                {translate('onboarding.create_project.update_your_token')}
+              </Link>
+            )
+          }}
+        />
       </Alert>
     );
   }
index 8d5a07e31618d19a7b37f2dfd5046b13dc06c15e..4f6826be68a26ff4ad9af1120e32f4f82eff7b4d 100644 (file)
@@ -32,6 +32,7 @@ export interface BitbucketPersonalAccessTokenFormProps {
   bitbucketSetting: AlmSettingsInstance;
   onPersonalAccessTokenCreate: (token: string) => void;
   submitting?: boolean;
+  validationFailed: boolean;
 }
 
 export default function BitbucketPersonalAccessTokenForm(
@@ -39,17 +40,24 @@ export default function BitbucketPersonalAccessTokenForm(
 ) {
   const {
     bitbucketSetting: { url },
-    submitting = false
+    submitting = false,
+    validationFailed
   } = props;
-  const [personalAccessToken, setPersonalAccessToken] = React.useState('');
-  const isValid = personalAccessToken.length > 0;
+  const [touched, setTouched] = React.useState(false);
+
+  React.useEffect(() => {
+    setTouched(false);
+  }, [submitting]);
+
+  const isInvalid = validationFailed && !touched;
 
   return (
     <div className="display-flex-start">
       <form
         onSubmit={(e: React.SyntheticEvent<HTMLFormElement>) => {
           e.preventDefault();
-          props.onPersonalAccessTokenCreate(personalAccessToken);
+          const value = new FormData(e.currentTarget).get('personal_access_token') as string;
+          props.onPersonalAccessTokenCreate(value);
         }}>
         <h2 className="big">{translate('onboarding.create_project.grant_access_to_bbs.title')}</h2>
         <p className="big-spacer-top big-spacer-bottom">
@@ -57,28 +65,30 @@ export default function BitbucketPersonalAccessTokenForm(
         </p>
 
         <ValidationInput
-          error={undefined}
+          error={isInvalid ? translate('onboarding.create_project.pat_incorrect') : undefined}
           id="personal_access_token"
-          isInvalid={false}
-          isValid={isValid}
+          isInvalid={isInvalid}
+          isValid={false}
           label={translate('onboarding.create_project.enter_pat')}
           required={true}>
           <input
             autoFocus={true}
             className={classNames('input-super-large', {
-              'is-valid': isValid
+              'is-invalid': isInvalid
             })}
             id="personal_access_token"
             minLength={1}
-            onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
-              setPersonalAccessToken(e.currentTarget.value)
-            }
+            name="personal_access_token"
+            onChange={() => {
+              setTouched(true);
+            }}
             type="text"
-            value={personalAccessToken}
           />
         </ValidationInput>
 
-        <SubmitButton disabled={!isValid || submitting}>{translate('save')}</SubmitButton>
+        <SubmitButton disabled={isInvalid || submitting || !touched}>
+          {translate('save')}
+        </SubmitButton>
         <DeferredSpinner className="spacer-left" loading={submitting} />
       </form>
 
index 66a13c5c3505c65a4fe61f0779c02ab194b34339..9d9ce31adf4d23377bf0326cc6ff96d861b10ce0 100644 (file)
  */
 import * as classNames from 'classnames';
 import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router';
 import BoxedGroupAccordion from 'sonar-ui-common/components/controls/BoxedGroupAccordion';
 import Radio from 'sonar-ui-common/components/controls/Radio';
-import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
 import CheckIcon from 'sonar-ui-common/components/icons/CheckIcon';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
 import { colors } from '../../../app/theme';
 import { getProjectUrl } from '../../../helpers/urls';
 import { BitbucketProject, BitbucketRepository } from '../../../types/alm-integration';
+import { CreateProjectModes } from './types';
 
 export interface BitbucketProjectAccordionProps {
   disableRepositories: boolean;
@@ -73,7 +74,23 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
       {open && (
         <div className="display-flex-wrap">
           {repositoryCount === 0 && (
-            <Alert variant="warning">{translate('onboarding.create_project.no_bbs_repos')}</Alert>
+            <Alert variant="warning">
+              <FormattedMessage
+                defaultMessage={translate('onboarding.create_project.no_bbs_repos')}
+                id="onboarding.create_project.no_bbs_repos"
+                values={{
+                  link: (
+                    <Link
+                      to={{
+                        pathname: '/projects/create',
+                        query: { mode: CreateProjectModes.BitbucketServer, resetPat: 1 }
+                      }}>
+                      {translate('onboarding.create_project.update_your_token')}
+                    </Link>
+                  )
+                }}
+              />
+            </Alert>
           )}
 
           {repositories.map(repo =>
@@ -84,11 +101,9 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
                 <CheckIcon className="spacer-right" fill={colors.green} size={14} />
                 <div className="overflow-hidden">
                   <div className="little-spacer-bottom text-ellipsis">
-                    <Tooltip overlay={repo.name}>
-                      <strong>
-                        <Link to={getProjectUrl(repo.sqProjectKey)}>{repo.name}</Link>
-                      </strong>
-                    </Tooltip>
+                    <strong title={repo.name}>
+                      <Link to={getProjectUrl(repo.sqProjectKey)}>{repo.name}</Link>
+                    </strong>
                   </div>
                   <em>{translate('onboarding.create_project.repository_imported')}</em>
                 </div>
@@ -107,9 +122,9 @@ export default function BitbucketProjectAccordion(props: BitbucketProjectAccordi
                 key={repo.id}
                 onCheck={() => props.onSelectRepository(repo)}
                 value={String(repo.id)}>
-                <Tooltip overlay={repo.name}>
-                  <strong className="text-ellipsis">{repo.name}</strong>
-                </Tooltip>
+                <strong className="text-ellipsis" title={repo.name}>
+                  {repo.name}
+                </strong>
               </Radio>
             )
           )}
index e4f489c0eae8182bdd81191d89d93ca7ea7c1f88..ef6d825005a2964c9549a7e48d80d449c203896b 100644 (file)
@@ -55,6 +55,7 @@ interface State {
   searchResults?: BitbucketRepository[];
   selectedRepository?: BitbucketRepository;
   submittingToken?: boolean;
+  tokenValidationFailed: boolean;
 }
 
 export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
@@ -68,7 +69,8 @@ export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
       bitbucketSetting: props.bitbucketSettings[0],
       importing: false,
       loading: false,
-      searching: false
+      searching: false,
+      tokenValidationFailed: false
     };
   }
 
@@ -79,8 +81,9 @@ export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
 
   componentDidUpdate(prevProps: Props) {
     if (prevProps.bitbucketSettings.length === 0 && this.props.bitbucketSettings.length > 0) {
-      this.setState({ bitbucketSetting: this.props.bitbucketSettings[0] });
-      this.fetchInitialData();
+      this.setState({ bitbucketSetting: this.props.bitbucketSettings[0] }, () =>
+        this.fetchInitialData()
+      );
     }
   }
 
@@ -169,12 +172,15 @@ export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
       return;
     }
 
-    this.setState({ submittingToken: true });
+    this.setState({ submittingToken: true, tokenValidationFailed: false });
     setAlmPersonalAccessToken(bitbucketSetting.key, token)
-      .then(() => {
+      .then(this.checkPersonalAccessToken)
+      .then(patIsValid => {
         if (this.mounted) {
-          this.setState({ submittingToken: false });
-          this.fetchInitialData();
+          this.setState({ submittingToken: false, patIsValid, tokenValidationFailed: !patIsValid });
+          if (patIsValid) {
+            this.fetchInitialData();
+          }
         }
       })
       .catch(() => {
@@ -241,7 +247,7 @@ export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
   };
 
   render() {
-    const { canAdmin, loadingBindings } = this.props;
+    const { canAdmin, loadingBindings, location } = this.props;
     const {
       bitbucketSetting,
       importing,
@@ -252,7 +258,8 @@ export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
       searching,
       searchResults,
       selectedRepository,
-      submittingToken
+      submittingToken,
+      tokenValidationFailed
     } = this.state;
 
     return (
@@ -271,8 +278,9 @@ export class BitbucketProjectCreate extends React.PureComponent<Props, State> {
         searchResults={searchResults}
         searching={searching}
         selectedRepository={selectedRepository}
-        showPersonalAccessTokenForm={!patIsValid}
+        showPersonalAccessTokenForm={!patIsValid || Boolean(location.query.resetPat)}
         submittingToken={submittingToken}
+        tokenValidationFailed={tokenValidationFailed}
       />
     );
   }
index 8b9092d484efef2b6bba1ba81578b1efd8a39841..94afa8a8d68dd036a91f989fc7d55d4df21a3c58 100644 (file)
@@ -53,6 +53,7 @@ export interface BitbucketProjectCreateRendererProps {
   selectedRepository?: BitbucketRepository;
   showPersonalAccessTokenForm?: boolean;
   submittingToken?: boolean;
+  tokenValidationFailed: boolean;
 }
 
 export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCreateRendererProps) {
@@ -67,7 +68,8 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
     searching,
     searchResults,
     showPersonalAccessTokenForm,
-    submittingToken
+    submittingToken,
+    tokenValidationFailed
   } = props;
 
   return (
@@ -133,6 +135,7 @@ export default function BitbucketProjectCreateRenderer(props: BitbucketProjectCr
             bitbucketSetting={bitbucketSetting}
             onPersonalAccessTokenCreate={props.onPersonalAccessTokenCreate}
             submitting={submittingToken}
+            validationFailed={tokenValidationFailed}
           />
         ) : (
           <BitbucketImportRepositoryForm
index 5dc8bcb0b671459f404850a40212d8954211ad2f..570833989cd5e8e3770b427d3908925ede217e4a 100644 (file)
@@ -21,7 +21,7 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
-import { change, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
+import { change, submit } from 'sonar-ui-common/helpers/testUtils';
 import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings';
 import { AlmKeys } from '../../../../types/alm-settings';
 import BitbucketPersonalAccessTokenForm, {
@@ -31,9 +31,10 @@ import BitbucketPersonalAccessTokenForm, {
 it('should render correctly', () => {
   expect(shallowRender()).toMatchSnapshot('default');
   expect(shallowRender({ submitting: true })).toMatchSnapshot('submitting');
+  expect(shallowRender({ validationFailed: true })).toMatchSnapshot('validation failed');
 });
 
-it('should correctly handle form interactions', async () => {
+it('should correctly handle form interactions', () => {
   const onPersonalAccessTokenCreate = jest.fn();
   const wrapper = shallowRender({ onPersonalAccessTokenCreate });
 
@@ -46,8 +47,14 @@ it('should correctly handle form interactions', async () => {
 
   // Expect correct calls to be made when submitting.
   submit(wrapper.find('form'));
-  await waitAndUpdate(wrapper);
   expect(onPersonalAccessTokenCreate).toBeCalled();
+
+  // If validation fails, we toggle the submitting flag and call useEffect()
+  // to set the `touched` flag to false again. Trigger a re-render, and mock
+  // useEffect(). This should de-activate the submit button again.
+  jest.spyOn(React, 'useEffect').mockImplementationOnce(f => f());
+  wrapper.setProps({ submitting: false });
+  expect(wrapper.find(SubmitButton).prop('disabled')).toBe(true);
 });
 
 function shallowRender(props: Partial<BitbucketPersonalAccessTokenFormProps> = {}) {
@@ -58,6 +65,7 @@ function shallowRender(props: Partial<BitbucketPersonalAccessTokenFormProps> = {
         url: 'http://www.example.com'
       })}
       onPersonalAccessTokenCreate={jest.fn()}
+      validationFailed={false}
       {...props}
     />
   );
index 2f999c1d91bc7a75e041d1cd7115204d4c84b3c2..6d1405bb9390a8a4df38a1c85159ceb77d6c1119 100644 (file)
@@ -92,10 +92,17 @@ it('should correctly handle an invalid PAT', async () => {
   expect(wrapper.state().patIsValid).toBe(false);
 });
 
-it('should correctly handle setting a new PAT', () => {
+it('should correctly handle setting a new PAT', async () => {
   const wrapper = shallowRender();
   wrapper.instance().handlePersonalAccessTokenCreate('token');
   expect(setAlmPersonalAccessToken).toBeCalledWith('foo', 'token');
+  expect(wrapper.state().submittingToken).toBe(true);
+
+  (checkPersonalAccessTokenIsValid as jest.Mock).mockResolvedValueOnce(false);
+  await waitAndUpdate(wrapper);
+  expect(checkPersonalAccessTokenIsValid).toBeCalled();
+  expect(wrapper.state().submittingToken).toBe(false);
+  expect(wrapper.state().tokenValidationFailed).toBe(true);
 });
 
 it('should correctly fetch projects and repos', async () => {
index 9ef88e6a9ef61964db18ebfd8e063c8661647b17..964e30b4d9a0ba50a5ea55c2ac3be79f7a31bad3 100644 (file)
@@ -60,6 +60,7 @@ function shallowRender(props: Partial<BitbucketProjectCreateRendererProps> = {})
       projectRepositories={{ foo: { allShown: true, repositories: [mockBitbucketRepository()] } }}
       projects={[mockBitbucketProject({ key: 'foo' })]}
       searching={false}
+      tokenValidationFailed={false}
       {...props}
     />
   );
index cedc3c4e22d129a9f9eca5bc49af2854c7cd9b15..6ba025c784ece6a2f4cf1eb4f80bde731b3982c3 100644 (file)
@@ -56,7 +56,29 @@ exports[`should render correctly: no projects 1`] = `
   className="spacer-top"
   variant="warning"
 >
-  onboarding.create_project.no_bbs_projects
+  <FormattedMessage
+    defaultMessage="onboarding.create_project.no_bbs_projects"
+    id="onboarding.create_project.no_bbs_projects"
+    values={
+      Object {
+        "link": <Link
+          onlyActiveOnIndex={false}
+          style={Object {}}
+          to={
+            Object {
+              "pathname": "/projects/create",
+              "query": Object {
+                "mode": "bbs",
+                "resetPat": 1,
+              },
+            }
+          }
+        >
+          onboarding.create_project.update_your_token
+        </Link>,
+      }
+    }
+  />
 </Alert>
 `;
 
index f7dd160502f0f1792d31725ff88ee040658aa9a7..18a3bf5e37b59bc3a662ce8f703b05f72accd011 100644 (file)
@@ -29,9 +29,9 @@ exports[`should render correctly: default 1`] = `
         className="input-super-large"
         id="personal_access_token"
         minLength={1}
+        name="personal_access_token"
         onChange={[Function]}
         type="text"
-        value=""
       />
     </ValidationInput>
     <SubmitButton
@@ -141,9 +141,9 @@ exports[`should render correctly: submitting 1`] = `
         className="input-super-large"
         id="personal_access_token"
         minLength={1}
+        name="personal_access_token"
         onChange={[Function]}
         type="text"
-        value=""
       />
     </ValidationInput>
     <SubmitButton
@@ -223,3 +223,116 @@ exports[`should render correctly: submitting 1`] = `
   </Alert>
 </div>
 `;
+
+exports[`should render correctly: validation failed 1`] = `
+<div
+  className="display-flex-start"
+>
+  <form
+    onSubmit={[Function]}
+  >
+    <h2
+      className="big"
+    >
+      onboarding.create_project.grant_access_to_bbs.title
+    </h2>
+    <p
+      className="big-spacer-top big-spacer-bottom"
+    >
+      onboarding.create_project.grant_access_to_bbs.help
+    </p>
+    <ValidationInput
+      error="onboarding.create_project.pat_incorrect"
+      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}
+      timeout={100}
+    />
+  </form>
+  <Alert
+    className="big-spacer-left big-spacer-top"
+    display="block"
+    variant="info"
+  >
+    <h3>
+      onboarding.create_project.pat_help.title
+    </h3>
+    <p
+      className="big-spacer-top big-spacer-bottom"
+    >
+      onboarding.create_project.pat_help.bbs_help_1
+    </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.bbs_help_2
+    </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>
+`;
index f1fc7af0d460f6dee4db7df45a0021f1098ff43e..ced072128a27d30bd896a6056992baa2fc08fa22 100644 (file)
@@ -36,15 +36,12 @@ exports[`should render correctly: default 1`] = `
       onCheck={[Function]}
       value="1"
     >
-      <Tooltip
-        overlay="Repo"
+      <strong
+        className="text-ellipsis"
+        title="Repo"
       >
-        <strong
-          className="text-ellipsis"
-        >
-          Repo
-        </strong>
-      </Tooltip>
+        Repo
+      </strong>
     </Radio>
     <div
       className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
@@ -61,27 +58,25 @@ exports[`should render correctly: default 1`] = `
         <div
           className="little-spacer-bottom text-ellipsis"
         >
-          <Tooltip
-            overlay="Bar"
+          <strong
+            title="Bar"
           >
-            <strong>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "query": Object {
-                      "branch": undefined,
-                      "id": "bar",
-                    },
-                  }
+            <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              to={
+                Object {
+                  "pathname": "/dashboard",
+                  "query": Object {
+                    "branch": undefined,
+                    "id": "bar",
+                  },
                 }
-              >
-                Bar
-              </Link>
-            </strong>
-          </Tooltip>
+              }
+            >
+              Bar
+            </Link>
+          </strong>
         </div>
         <em>
           onboarding.create_project.repository_imported
@@ -114,15 +109,12 @@ exports[`should render correctly: disable options 1`] = `
       onCheck={[Function]}
       value="1"
     >
-      <Tooltip
-        overlay="Repo"
+      <strong
+        className="text-ellipsis"
+        title="Repo"
       >
-        <strong
-          className="text-ellipsis"
-        >
-          Repo
-        </strong>
-      </Tooltip>
+        Repo
+      </strong>
     </Radio>
     <div
       className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
@@ -139,27 +131,25 @@ exports[`should render correctly: disable options 1`] = `
         <div
           className="little-spacer-bottom text-ellipsis"
         >
-          <Tooltip
-            overlay="Bar"
+          <strong
+            title="Bar"
           >
-            <strong>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "query": Object {
-                      "branch": undefined,
-                      "id": "bar",
-                    },
-                  }
+            <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              to={
+                Object {
+                  "pathname": "/dashboard",
+                  "query": Object {
+                    "branch": undefined,
+                    "id": "bar",
+                  },
                 }
-              >
-                Bar
-              </Link>
-            </strong>
-          </Tooltip>
+              }
+            >
+              Bar
+            </Link>
+          </strong>
         </div>
         <em>
           onboarding.create_project.repository_imported
@@ -192,15 +182,12 @@ exports[`should render correctly: no click handler 1`] = `
       onCheck={[Function]}
       value="1"
     >
-      <Tooltip
-        overlay="Repo"
+      <strong
+        className="text-ellipsis"
+        title="Repo"
       >
-        <strong
-          className="text-ellipsis"
-        >
-          Repo
-        </strong>
-      </Tooltip>
+        Repo
+      </strong>
     </Radio>
     <div
       className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
@@ -217,27 +204,25 @@ exports[`should render correctly: no click handler 1`] = `
         <div
           className="little-spacer-bottom text-ellipsis"
         >
-          <Tooltip
-            overlay="Bar"
+          <strong
+            title="Bar"
           >
-            <strong>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "query": Object {
-                      "branch": undefined,
-                      "id": "bar",
-                    },
-                  }
+            <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              to={
+                Object {
+                  "pathname": "/dashboard",
+                  "query": Object {
+                    "branch": undefined,
+                    "id": "bar",
+                  },
                 }
-              >
-                Bar
-              </Link>
-            </strong>
-          </Tooltip>
+              }
+            >
+              Bar
+            </Link>
+          </strong>
         </div>
         <em>
           onboarding.create_project.repository_imported
@@ -266,7 +251,29 @@ exports[`should render correctly: no repos 1`] = `
     <Alert
       variant="warning"
     >
-      onboarding.create_project.no_bbs_repos
+      <FormattedMessage
+        defaultMessage="onboarding.create_project.no_bbs_repos"
+        id="onboarding.create_project.no_bbs_repos"
+        values={
+          Object {
+            "link": <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              to={
+                Object {
+                  "pathname": "/projects/create",
+                  "query": Object {
+                    "mode": "bbs",
+                    "resetPat": 1,
+                  },
+                }
+              }
+            >
+              onboarding.create_project.update_your_token
+            </Link>,
+          }
+        }
+      />
     </Alert>
   </div>
 </BoxedGroupAccordion>
@@ -294,15 +301,12 @@ exports[`should render correctly: not showing all repos 1`] = `
       onCheck={[Function]}
       value="1"
     >
-      <Tooltip
-        overlay="Repo"
+      <strong
+        className="text-ellipsis"
+        title="Repo"
       >
-        <strong
-          className="text-ellipsis"
-        >
-          Repo
-        </strong>
-      </Tooltip>
+        Repo
+      </strong>
     </Radio>
     <div
       className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
@@ -319,27 +323,25 @@ exports[`should render correctly: not showing all repos 1`] = `
         <div
           className="little-spacer-bottom text-ellipsis"
         >
-          <Tooltip
-            overlay="Bar"
+          <strong
+            title="Bar"
           >
-            <strong>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "query": Object {
-                      "branch": undefined,
-                      "id": "bar",
-                    },
-                  }
+            <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              to={
+                Object {
+                  "pathname": "/dashboard",
+                  "query": Object {
+                    "branch": undefined,
+                    "id": "bar",
+                  },
                 }
-              >
-                Bar
-              </Link>
-            </strong>
-          </Tooltip>
+              }
+            >
+              Bar
+            </Link>
+          </strong>
         </div>
         <em>
           onboarding.create_project.repository_imported
@@ -377,15 +379,12 @@ exports[`should render correctly: selected repo 1`] = `
       onCheck={[Function]}
       value="1"
     >
-      <Tooltip
-        overlay="Repo"
+      <strong
+        className="text-ellipsis"
+        title="Repo"
       >
-        <strong
-          className="text-ellipsis"
-        >
-          Repo
-        </strong>
-      </Tooltip>
+        Repo
+      </strong>
     </Radio>
     <div
       className="display-flex-start spacer-right spacer-bottom create-project-import-bbs-repo"
@@ -402,27 +401,25 @@ exports[`should render correctly: selected repo 1`] = `
         <div
           className="little-spacer-bottom text-ellipsis"
         >
-          <Tooltip
-            overlay="Bar"
+          <strong
+            title="Bar"
           >
-            <strong>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to={
-                  Object {
-                    "pathname": "/dashboard",
-                    "query": Object {
-                      "branch": undefined,
-                      "id": "bar",
-                    },
-                  }
+            <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              to={
+                Object {
+                  "pathname": "/dashboard",
+                  "query": Object {
+                    "branch": undefined,
+                    "id": "bar",
+                  },
                 }
-              >
-                Bar
-              </Link>
-            </strong>
-          </Tooltip>
+              }
+            >
+              Bar
+            </Link>
+          </strong>
         </div>
         <em>
           onboarding.create_project.repository_imported
index 1b5c6ad18e4e94ee920f125de9f822ee97ae84b8..a8e54b76f9a8df0314deea48dfa4b9e07d81a3f9 100644 (file)
@@ -17,5 +17,6 @@ exports[`should render correctly 1`] = `
   onSelectRepository={[Function]}
   searching={false}
   showPersonalAccessTokenForm={true}
+  tokenValidationFailed={false}
 />
 `;
index 895cf6c3de4eda972e2641d6238dd70bb60ab105..57c131750140589674dcd0a9e484f2e9ba0a443c 100644 (file)
@@ -316,6 +316,7 @@ exports[`should render correctly: pat form 1`] = `
       }
     }
     onPersonalAccessTokenCreate={[MockFunction]}
+    validationFailed={false}
   />
 </Fragment>
 `;
index 1dd1d65c54ab95324f2d4df04675fae641c7a9e5..2fc4cb6a01f7c2d46a9e62f9042d1b941c4b57b0 100644 (file)
@@ -3114,6 +3114,7 @@ onboarding.create_project.bbs_not_configured=This feature isn't available
 onboarding.create_project.no_bbs_binding=You must have exactly at least 1 Bitbucket Server instance configured in order to use this method, but none were found. Either create the project manually, or contact your system administrator.
 onboarding.create_project.no_bbs_binding.admin=You must have exactly at least 1 Bitbucket Server instance configured in order to use this method. You can configure instances under {url}.
 onboarding.create_project.enter_pat=Enter personal access token
+onboarding.create_project.pat_incorrect=Your personal access token failed to validate.
 onboarding.create_project.pat_help.title=How to create a personal access token?
 onboarding.create_project.pat_help.bbs_help_1=Click the following link to generate a token in Bitbucket Server, and copy-paste it into the personal access token field.
 onboarding.create_project.pat_help.bbs_help_2=Set a name, for example "SonarQube", and select the following permissions:
@@ -3121,10 +3122,9 @@ onboarding.create_project.pat_help.link=Create personal access token
 onboarding.create_project.pat_help.bbs_permission_projects=Projects: {perm}
 onboarding.create_project.pat_help.bbs_permission_repos=Repositories: {perm}
 onboarding.create_project.pat_help.read_permission=Read
-onboarding.create_project.error_fetching_bbs_projects=There was an error fetching the projects from Bitbucket Server. Contact your system administrator, or check your personal access token.
-onboarding.create_project.error_fetching_bbs_repos=There was an error fetching the repositories from Bitbucket Server. Contact your system administrator, or check your personal access token.
-onboarding.create_project.no_bbs_projects=No projects could be fetched from Bitbucket Server. Contact your system administrator, or check your personal access token.
-onboarding.create_project.no_bbs_repos=No repositories were found for this project. Contact your system administrator, or check your personal access token.
+onboarding.create_project.no_bbs_projects=No projects could be fetched from Bitbucket Server. Contact your system administrator, or {link}.
+onboarding.create_project.no_bbs_repos=No repositories were found for this project. Contact your system administrator, or {link}.
+onboarding.create_project.update_your_token=update your personal access token
 onboarding.create_project.no_bbs_repos.filter=No repositories match your filter.
 onboarding.create_project.only_showing_X_first_repos=We're only displaying the first {0} repositories. If you're looking for a repository that's not in this list, use the search above.
 onboarding.create_project.import_selected_repo=Set up selected repository