]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15135 Correct Azure DevOps onboarding error message
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Mon, 23 Aug 2021 14:44:13 +0000 (16:44 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 24 Aug 2021 20:07:41 +0000 (20:07 +0000)
server/sonar-web/src/main/js/apps/create/project/AzureProjectCreateRenderer.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/AzureProjectCreateRenderer-test.tsx
server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/AzureProjectCreateRenderer-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 3c954c02665b2f6e0ac55d8d1e4f638f38b23738..c026d8988a47b0d7a55129dff3d722deb9774a43 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router';
 import { Button } from '../../../components/controls/buttons';
 import SearchBox from '../../../components/controls/SearchBox';
+import { Alert } from '../../../components/ui/Alert';
 import DeferredSpinner from '../../../components/ui/DeferredSpinner';
 import { translate } from '../../../helpers/l10n';
 import { getBaseUrl } from '../../../helpers/system';
+import { getGlobalSettingsUrl } from '../../../helpers/urls';
 import { AzureProject, AzureRepository } from '../../../types/alm-integration';
 import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings';
+import { ALM_INTEGRATION } from '../../settings/components/AdditionalCategoryKeys';
 import AzurePersonalAccessTokenForm from './AzurePersonalAccessTokenForm';
 import AzureProjectsList from './AzureProjectsList';
 import CreateProjectPageHeader from './CreateProjectPageHeader';
@@ -70,11 +75,16 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
     tokenValidationFailed
   } = props;
 
+  const settingIsValid = settings && settings.url;
+  const showCountError = !loading && !settings;
+  const showUrlError = !loading && settings && !settings.url;
+
   return (
     <>
       <CreateProjectPageHeader
         additionalActions={
-          !showPersonalAccessTokenForm && (
+          !showPersonalAccessTokenForm &&
+          settingIsValid && (
             <div className="display-flex-center pull-right">
               <DeferredSpinner className="spacer-right" loading={importing} />
               <Button
@@ -101,12 +111,32 @@ export default function AzureProjectCreateRenderer(props: AzureProjectCreateRend
 
       {loading && <i className="spinner" />}
 
-      {!loading && !(settings && settings.url) && (
-        <WrongBindingCountAlert alm={AlmKeys.Azure} canAdmin={!!canAdmin} />
+      {showUrlError && (
+        <Alert variant="error">
+          {canAdmin ? (
+            <FormattedMessage
+              defaultMessage={translate('onboarding.create_project.azure.no_url.admin')}
+              id="onboarding.create_project.azure.no_url.admin"
+              values={{
+                alm: translate('onboarding.alm', AlmKeys.Azure),
+                url: (
+                  <Link to={getGlobalSettingsUrl(ALM_INTEGRATION)}>
+                    {translate('settings.page')}
+                  </Link>
+                )
+              }}
+            />
+          ) : (
+            translate('onboarding.create_project.azure.no_url')
+          )}
+        </Alert>
       )}
 
+      {showCountError && <WrongBindingCountAlert alm={AlmKeys.Azure} canAdmin={!!canAdmin} />}
+
       {!loading &&
         settings &&
+        settings.url &&
         (showPersonalAccessTokenForm ? (
           <div className="display-flex-justify-center">
             <AzurePersonalAccessTokenForm
index a11317e3bbf101dc7fd67beb805335d4115c4aac..4b1e3197d8d7867978f35996ff03769beaedb829 100644 (file)
@@ -31,10 +31,22 @@ it('should render correctly', () => {
   expect(shallowRender({ loading: true })).toMatchSnapshot('loading');
   expect(shallowRender({ settings: undefined })).toMatchSnapshot('no settings');
   expect(shallowRender({ showPersonalAccessTokenForm: true })).toMatchSnapshot('token form');
-  expect(shallowRender({})).toMatchSnapshot('project list');
+  expect(shallowRender()).toMatchSnapshot('project list');
+  expect(
+    shallowRender({
+      settings: mockAlmSettingsInstance({ alm: AlmKeys.Azure }),
+      showPersonalAccessTokenForm: true
+    })
+  ).toMatchSnapshot('setting missing url, admin');
+  expect(
+    shallowRender({
+      canAdmin: false,
+      settings: mockAlmSettingsInstance({ alm: AlmKeys.Azure })
+    })
+  ).toMatchSnapshot('setting missing url, not admin');
 });
 
-function shallowRender(overrides: Partial<AzureProjectCreateRendererProps>) {
+function shallowRender(overrides: Partial<AzureProjectCreateRendererProps> = {}) {
   const project = mockAzureProject();
 
   return shallow(
@@ -51,7 +63,7 @@ function shallowRender(overrides: Partial<AzureProjectCreateRendererProps>) {
       projects={[project]}
       repositories={{ [project.name]: [mockAzureRepository()] }}
       tokenValidationFailed={false}
-      settings={mockAlmSettingsInstance({ alm: AlmKeys.Azure })}
+      settings={mockAlmSettingsInstance({ alm: AlmKeys.Azure, url: 'https://azure.company.com' })}
       showPersonalAccessTokenForm={false}
       submittingToken={false}
       {...overrides}
index ea0a2ef9a3f3abf38b27edde744876114b822f65..dfcbf392e44965705d1fb039f9f013380d2236e1 100644 (file)
@@ -43,23 +43,6 @@ exports[`should render correctly: loading 1`] = `
 exports[`should render correctly: no settings 1`] = `
 <Fragment>
   <CreateProjectPageHeader
-    additionalActions={
-      <div
-        className="display-flex-center pull-right"
-      >
-        <DeferredSpinner
-          className="spacer-right"
-          loading={false}
-        />
-        <Button
-          className="button-large button-primary"
-          disabled={true}
-          onClick={[MockFunction]}
-        >
-          onboarding.create_project.import_selected_repo
-        </Button>
-      </div>
-    }
     title={
       <span
         className="text-middle"
@@ -115,10 +98,6 @@ exports[`should render correctly: project list 1`] = `
       </span>
     }
   />
-  <WrongBindingCountAlert
-    alm="azure"
-    canAdmin={true}
-  />
   <div
     className="huge-spacer-bottom"
   >
@@ -158,7 +137,7 @@ exports[`should render correctly: project list 1`] = `
 </Fragment>
 `;
 
-exports[`should render correctly: token form 1`] = `
+exports[`should render correctly: setting missing url, admin 1`] = `
 <Fragment>
   <CreateProjectPageHeader
     additionalActions={false}
@@ -176,9 +155,78 @@ exports[`should render correctly: token form 1`] = `
       </span>
     }
   />
-  <WrongBindingCountAlert
-    alm="azure"
-    canAdmin={true}
+  <Alert
+    variant="error"
+  >
+    <FormattedMessage
+      defaultMessage="onboarding.create_project.azure.no_url.admin"
+      id="onboarding.create_project.azure.no_url.admin"
+      values={
+        Object {
+          "alm": "onboarding.alm.azure",
+          "url": <Link
+            onlyActiveOnIndex={false}
+            style={Object {}}
+            to={
+              Object {
+                "pathname": "/admin/settings",
+                "query": Object {
+                  "category": "almintegration",
+                },
+              }
+            }
+          >
+            settings.page
+          </Link>,
+        }
+      }
+    />
+  </Alert>
+</Fragment>
+`;
+
+exports[`should render correctly: setting missing url, not admin 1`] = `
+<Fragment>
+  <CreateProjectPageHeader
+    title={
+      <span
+        className="text-middle"
+      >
+        <img
+          alt=""
+          className="spacer-right"
+          height="24"
+          src="/images/alm/azure.svg"
+        />
+        onboarding.create_project.azure.title
+      </span>
+    }
+  />
+  <Alert
+    variant="error"
+  >
+    onboarding.create_project.azure.no_url
+  </Alert>
+</Fragment>
+`;
+
+exports[`should render correctly: token form 1`] = `
+<Fragment>
+  <CreateProjectPageHeader
+    additionalActions={false}
+    title={
+      <span
+        className="text-middle"
+      >
+        <img
+          alt=""
+          className="spacer-right"
+          height="24"
+          src="/images/alm/azure.svg"
+        />
+        onboarding.create_project.azure.title
+      </span>
+    }
   />
   <div
     className="display-flex-justify-center"
@@ -188,6 +236,7 @@ exports[`should render correctly: token form 1`] = `
         Object {
           "alm": "azure",
           "key": "key",
+          "url": "https://azure.company.com",
         }
       }
       onPersonalAccessTokenCreate={[MockFunction]}
index 7680c16a82deac2c8fc32db1e8131a4232cd3558..91bed6abaf359dc946a1ef2ce7a7e7fc8e7b7586 100644 (file)
@@ -3339,6 +3339,8 @@ onboarding.create_project.select_method=How do you want to create your project?
 onboarding.create_project.too_many_alm_instances_X=This method requires exactly one {0} configuration.
 onboarding.create_project.wrong_binding_count=You must have exactly 1 {alm} 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.wrong_binding_count.admin=You must have exactly 1 {alm} instance configured in order to use this method. You can configure instances under {url}.
+onboarding.create_project.azure.no_url.admin=Your Azure DevOps instance configuration is missing a URL. We cannot import projects in the current state. You can configure instances under {url}.
+onboarding.create_project.azure.no_url=Your Azure DevOps instance configuration is missing a URL. We cannot import projects in the current state. Please contact your system administrator.
 onboarding.create_project.enter_pat=Enter personal access token
 onboarding.create_project.enter_pat.bitbucketcloud=Enter your app password
 onboarding.create_project.enter_username=Enter your Bitbucket username