]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12773 Add administration settings for GitLab MR decoration
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Fri, 6 Dec 2019 13:06:33 +0000 (14:06 +0100)
committerSonarTech <sonartech@sonarsource.com>
Mon, 16 Dec 2019 19:46:13 +0000 (20:46 +0100)
44 files changed:
server/sonar-web/public/images/alm/gitlab.svg [new file with mode: 0644]
server/sonar-web/src/main/js/api/almSettings.ts
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModal.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModalRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureFormModal.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTabRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/BitbucketTabRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTabRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabFormModal.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTab.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTabRenderer.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTabs.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PullRequestDecoration.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/TabHeader.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModal-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModalRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureFormModal-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTabRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketFormModal-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketTabRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubFormModal-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTab-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTabRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabFormModal-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTab-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTabRenderer-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PRDecorationTabs-test.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormModal-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureFormModal-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTabRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/BitbucketTabRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTabRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabFormModal-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTab-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTabRenderer-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PRDecorationTabs-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PullRequestDecoration-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts [new file with mode: 0644]
server/sonar-web/src/main/js/helpers/testMocks.ts
server/sonar-web/src/main/js/types/alm-settings.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/public/images/alm/gitlab.svg b/server/sonar-web/public/images/alm/gitlab.svg
new file mode 100644 (file)
index 0000000..c7db12e
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" data-name="logo art" width="342.955" height="318.789"><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/><defs><style>.cls-1{fill:#fc6d26}.cls-2{fill:#e24329}.cls-3{fill:#fca326}</style></defs><g class="currentLayer"><g id="g44"><path id="path46" class="cls-1" d="M339.562 180.724l-18.91-58.12-37.42-115.28a6.47 6.47 0 00-12.27 0l-37.42 115.21h-124.33L71.792 7.324a6.46 6.46 0 00-12.26 0l-37.36 115.21-18.91 58.19a12.88 12.88 0 004.66 14.39l163.47 118.78 163.44-118.78a12.9 12.9 0 004.73-14.39"/></g><g id="g48"><path id="path50" class="cls-2" d="M171.392 313.804l62.16-191.28h-124.29l62.13 191.28z"/></g><g id="g56"><path id="path58" class="cls-1" d="M171.392 313.804l-62.18-191.28h-87l149.18 191.28z"/></g><g id="g64"><path id="path66" class="cls-3" d="M22.142 122.584l-18.91 58.12a12.88 12.88 0 004.66 14.39l163.5 118.8-149.25-191.31z"/></g><g id="g72"><path id="path74" class="cls-2" d="M22.172 122.584h87.11l-37.49-115.2a6.47 6.47 0 00-12.27 0l-37.35 115.2z"/></g><g id="g76"><path id="path78" class="cls-1" d="M171.392 313.804l62.16-191.28h87.14l-149.3 191.28z"/></g><g id="g80"><path id="path82" class="cls-3" d="M320.632 122.584l18.91 58.12a12.85 12.85 0 01-4.66 14.39l-163.49 118.71 149.2-191.22z"/></g><g id="g84"><path id="path86" class="cls-2" d="M320.672 122.584h-87.1l37.42-115.2a6.46 6.46 0 0112.26 0l37.42 115.2z"/></g></g></svg>
\ No newline at end of file
index 79a35575adb7c93166d8209d486ed24712a75c78..aa2cdeb6d31d0317ecae8764710381ee0e88ffeb 100644 (file)
@@ -28,6 +28,8 @@ import {
   BitbucketProjectAlmBinding,
   GithubBindingDefinition,
   GithubProjectAlmBinding,
+  GitlabBindingDefinition,
+  GitlabProjectAlmBinding,
   ProjectAlmBinding
 } from '../types/alm-settings';
 
@@ -67,6 +69,14 @@ export function updateBitbucketConfiguration(
   return post('/api/alm_settings/update_bitbucket', data).catch(throwGlobalError);
 }
 
+export function createGitlabConfiguration(data: GitlabBindingDefinition) {
+  return post('/api/alm_settings/create_gitlab', data).catch(throwGlobalError);
+}
+
+export function updateGitlabConfiguration(data: GitlabBindingDefinition & { newKey: string }) {
+  return post('/api/alm_settings/update_gitlab', data).catch(throwGlobalError);
+}
+
 export function deleteConfiguration(key: string) {
   return post('/api/alm_settings/delete', { key }).catch(throwGlobalError);
 }
@@ -96,3 +106,7 @@ export function setProjectBitbucketBinding(data: BitbucketProjectAlmBinding) {
 export function setProjectGithubBinding(data: GithubProjectAlmBinding) {
   return post('/api/alm_settings/set_github_binding', data).catch(throwGlobalError);
 }
+
+export function setProjectGitlabBinding(data: GitlabProjectAlmBinding) {
+  return post('/api/alm_settings/set_gitlab_binding', data).catch(throwGlobalError);
+}
index d420f7d73b4dad2e8c294c9b1362f13143975a70..aa5c5f3a33fd3301bd4f66758eeea48eb90e61d8 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { AlmSettingsBinding } from '../../../../types/alm-settings';
+import { AlmSettingsBinding, ALM_KEYS } from '../../../../types/alm-settings';
 import AlmPRDecorationFormModalRenderer from './AlmPRDecorationFormModalRenderer';
 
 interface ChildrenProps<AlmBindingDefinitionType> {
@@ -27,6 +27,7 @@ interface ChildrenProps<AlmBindingDefinitionType> {
 }
 
 interface Props<B> {
+  alm: ALM_KEYS;
   children: (props: ChildrenProps<B>) => React.ReactNode;
   bindingDefinition: B;
   onCancel: () => void;
@@ -67,11 +68,12 @@ export default class AlmPRDecorationFormModal<
   };
 
   render() {
-    const { children, bindingDefinition } = this.props;
+    const { alm, children, bindingDefinition } = this.props;
     const { formData } = this.state;
 
     return (
       <AlmPRDecorationFormModalRenderer
+        alm={alm}
         canSubmit={this.canSubmit}
         onCancel={this.props.onCancel}
         onSubmit={this.handleFormSubmit}
index cd575d4e50e52a07982105c1b8bc0cd08ec29f35..cbb4576ecdd4ae779bf3c59e9e3c0e23a84ae5fa 100644 (file)
@@ -22,8 +22,10 @@ import { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/contro
 import SimpleModal from 'sonar-ui-common/components/controls/SimpleModal';
 import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { ALM_KEYS } from '../../../../types/alm-settings';
 
 export interface AlmPRDecorationFormModalProps {
+  alm: ALM_KEYS;
   canSubmit: () => boolean;
   children: React.ReactNode;
   onCancel: () => void;
@@ -32,8 +34,13 @@ export interface AlmPRDecorationFormModalProps {
 }
 
 export default function AlmPRDecorationFormModalRenderer(props: AlmPRDecorationFormModalProps) {
-  const { children, originalKey } = props;
-  const header = translate('settings.pr_decoration.form.header', originalKey ? 'edit' : 'create');
+  const { alm, children, originalKey } = props;
+  const header = translate(
+    'settings',
+    alm === ALM_KEYS.GITLAB ? 'mr_decoration' : 'pr_decoration',
+    'form.header',
+    originalKey ? 'edit' : 'create'
+  );
 
   return (
     <SimpleModal header={header} onClose={props.onCancel} onSubmit={props.onSubmit} size="medium">
index 896200bf05dd2d766c95a0c7812caf1df2d65f58..97eb5f447932f1707193593e1068a11bbb323776 100644 (file)
@@ -41,7 +41,7 @@ export default function AzureFormModal(props: AzureFormModalProps) {
         value={formData.key}
       />
       <AlmDefinitionFormField
-        help={translate('settings.pr_decoration.form.personal_access_token.help')}
+        help={translate('settings.pr_decoration.form.personal_access_token.azure.help')}
         id="personal_access_token"
         isTextArea={true}
         onFieldChange={onFieldChange}
index db6a806d09da3c1a54b2866d27707ca40e3d3028..5efc4f2ff4c968aea84cafb67f68524afcab2e1f 100644 (file)
@@ -61,6 +61,7 @@ export default function AzureTabRenderer(props: AzureTabRendererProps) {
 
       {editedDefinition && (
         <AlmPRDecorationFormModal
+          alm={ALM_KEYS.AZURE}
           bindingDefinition={editedDefinition}
           onCancel={props.onCancel}
           onSubmit={props.onSubmit}>
index 5b791d83461184a3c0ef4260d10f3fcb7cb0c6b0..b156f43371394faa2d60d516314c1c8953c47346 100644 (file)
@@ -64,6 +64,7 @@ export default function BitbucketTabRenderer(props: BitbucketTabRendererProps) {
 
       {editedDefinition && (
         <AlmPRDecorationFormModal
+          alm={ALM_KEYS.BITBUCKET}
           bindingDefinition={editedDefinition}
           onCancel={props.onCancel}
           onSubmit={props.onSubmit}>
index c03ec15d8b14ae6678e2a53e2f94000974003ec4..ac4b0927d76deecb4a7f334e680628dc809de6fd 100644 (file)
@@ -65,6 +65,7 @@ export default function GithubTabRenderer(props: GithubTabRendererProps) {
 
       {editedDefinition && (
         <AlmPRDecorationFormModal
+          alm={ALM_KEYS.GITHUB}
           bindingDefinition={editedDefinition}
           onCancel={props.onCancel}
           onSubmit={props.onSubmit}>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabFormModal.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabFormModal.tsx
new file mode 100644 (file)
index 0000000..bf259c2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from 'sonar-ui-common/helpers/l10n';
+import { GitlabBindingDefinition } from '../../../../types/alm-settings';
+import { AlmDefinitionFormField } from './AlmDefinitionFormField';
+
+export interface GitlabFormModalProps {
+  formData: GitlabBindingDefinition;
+  onFieldChange: (fieldId: keyof GitlabBindingDefinition, value: string) => void;
+}
+
+export function GitlabFormModal(props: GitlabFormModalProps) {
+  const { formData, onFieldChange } = props;
+
+  return (
+    <>
+      <AlmDefinitionFormField
+        autoFocus={true}
+        help={translate('settings.pr_decoration.form.name.gitlab.help')}
+        id="name.gitlab"
+        onFieldChange={onFieldChange}
+        propKey="key"
+        value={formData.key}
+      />
+      <AlmDefinitionFormField
+        help={translate('settings.pr_decoration.form.personal_access_token.gitlab.help')}
+        id="personal_access_token"
+        isTextArea={true}
+        onFieldChange={onFieldChange}
+        propKey="personalAccessToken"
+        value={formData.personalAccessToken}
+      />
+    </>
+  );
+}
+
+export default React.memo(GitlabFormModal);
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTab.tsx
new file mode 100644 (file)
index 0000000..8607993
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { createGitlabConfiguration, updateGitlabConfiguration } from '../../../../api/almSettings';
+import { GitlabBindingDefinition } from '../../../../types/alm-settings';
+import GitlabTabRenderer from './GitlabTabRenderer';
+
+interface Props {
+  definitions: GitlabBindingDefinition[];
+  loading: boolean;
+  onDelete: (definitionKey: string) => void;
+  onUpdateDefinitions: () => void;
+}
+
+interface State {
+  editedDefinition?: GitlabBindingDefinition;
+  projectCount?: number;
+}
+
+export default class GitlabTab extends React.PureComponent<Props, State> {
+  mounted = false;
+  state: State = {};
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  handleEdit = (definitionKey: string) => {
+    const editedDefinition = this.props.definitions.find(d => d.key === definitionKey);
+    this.setState({ editedDefinition });
+  };
+
+  handleSubmit = (config: GitlabBindingDefinition, originalKey: string) => {
+    const call = originalKey
+      ? updateGitlabConfiguration({ newKey: config.key, ...config, key: originalKey })
+      : createGitlabConfiguration(config);
+    return call.then(this.props.onUpdateDefinitions).then(() => {
+      if (this.mounted) {
+        this.setState({ editedDefinition: undefined });
+      }
+    });
+  };
+
+  handleCancel = () => {
+    this.setState({
+      editedDefinition: undefined
+    });
+  };
+
+  handleCreate = () => {
+    this.setState({ editedDefinition: { key: '', personalAccessToken: '' } });
+  };
+
+  render() {
+    const { definitions, loading } = this.props;
+    const { editedDefinition } = this.state;
+    return (
+      <GitlabTabRenderer
+        definitions={definitions}
+        editedDefinition={editedDefinition}
+        loading={loading}
+        onCancel={this.handleCancel}
+        onCreate={this.handleCreate}
+        onDelete={this.props.onDelete}
+        onEdit={this.handleEdit}
+        onSubmit={this.handleSubmit}
+      />
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTabRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTabRenderer.tsx
new file mode 100644 (file)
index 0000000..17a268b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
+import { ALM_KEYS, GitlabBindingDefinition } from '../../../../types/alm-settings';
+import AlmPRDecorationFormModal from './AlmPRDecorationFormModal';
+import AlmPRDecorationTable from './AlmPRDecorationTable';
+import GitlabFormModal from './GitlabFormModal';
+import TabHeader from './TabHeader';
+
+export interface GitlabTabRendererProps {
+  editedDefinition?: GitlabBindingDefinition;
+  definitions: GitlabBindingDefinition[];
+  loading: boolean;
+  onCancel: () => void;
+  onCreate: () => void;
+  onDelete: (definitionKey: string) => void;
+  onEdit: (definitionKey: string) => void;
+  onSubmit: (config: GitlabBindingDefinition, originalKey: string) => void;
+}
+
+export default function GitlabTabRenderer(props: GitlabTabRendererProps) {
+  const { definitions, editedDefinition, loading } = props;
+  return (
+    <>
+      <TabHeader
+        alm={ALM_KEYS.GITLAB}
+        definitionCount={definitions.length}
+        onCreate={props.onCreate}
+      />
+
+      <DeferredSpinner loading={loading}>
+        <AlmPRDecorationTable
+          additionalColumnsHeaders={[]}
+          alm={ALM_KEYS.GITLAB}
+          definitions={definitions.map(({ key }) => ({
+            key,
+            additionalColumns: []
+          }))}
+          onDelete={props.onDelete}
+          onEdit={props.onEdit}
+        />
+      </DeferredSpinner>
+
+      {editedDefinition && (
+        <AlmPRDecorationFormModal
+          alm={ALM_KEYS.GITLAB}
+          bindingDefinition={editedDefinition}
+          onCancel={props.onCancel}
+          onSubmit={props.onSubmit}>
+          {childProps => <GitlabFormModal {...childProps} />}
+        </AlmPRDecorationFormModal>
+      )}
+    </>
+  );
+}
index 7f1adb7fa3757705527311211c049c40f525f48b..096d4fb1e980561fcf0a1ed9c083b6674ff20dd1 100644 (file)
@@ -26,6 +26,7 @@ import AzureTab from './AzureTab';
 import BitbucketTab from './BitbucketTab';
 import DeleteModal from './DeleteModal';
 import GithubTab from './GithubTab';
+import GitlabTab from './GitlabTab';
 
 export interface PRDecorationTabsProps {
   currentAlm: ALM_KEYS;
@@ -43,7 +44,8 @@ export interface PRDecorationTabsProps {
 export const almName = {
   [ALM_KEYS.AZURE]: 'Azure DevOps Server',
   [ALM_KEYS.BITBUCKET]: 'Bitbucket Server',
-  [ALM_KEYS.GITHUB]: 'GitHub'
+  [ALM_KEYS.GITHUB]: 'GitHub',
+  [ALM_KEYS.GITLAB]: 'GitLab'
 };
 
 export default function PRDecorationTabs(props: PRDecorationTabsProps) {
@@ -69,8 +71,8 @@ export default function PRDecorationTabs(props: PRDecorationTabsProps) {
                 <img
                   alt="github"
                   className="spacer-right"
+                  height={16}
                   src={`${getBaseUrl()}/images/alm/github.svg`}
-                  width={16}
                 />
                 {almName[ALM_KEYS.GITHUB]}
               </>
@@ -83,8 +85,8 @@ export default function PRDecorationTabs(props: PRDecorationTabsProps) {
                 <img
                   alt="bitbucket"
                   className="spacer-right"
+                  height={16}
                   src={`${getBaseUrl()}/images/alm/bitbucket.svg`}
-                  width={16}
                 />
                 {almName[ALM_KEYS.BITBUCKET]}
               </>
@@ -97,12 +99,26 @@ export default function PRDecorationTabs(props: PRDecorationTabsProps) {
                 <img
                   alt="azure"
                   className="spacer-right"
+                  height={16}
                   src={`${getBaseUrl()}/images/alm/azure.svg`}
-                  width={16}
                 />
                 {almName[ALM_KEYS.AZURE]}
               </>
             )
+          },
+          {
+            key: ALM_KEYS.GITLAB,
+            label: (
+              <>
+                <img
+                  alt="gitlab"
+                  className="spacer-right"
+                  height={16}
+                  src={`${getBaseUrl()}/images/alm/gitlab.svg`}
+                />
+                {almName[ALM_KEYS.GITLAB]}
+              </>
+            )
           }
         ]}
       />
@@ -132,6 +148,14 @@ export default function PRDecorationTabs(props: PRDecorationTabsProps) {
             onUpdateDefinitions={props.onUpdateDefinitions}
           />
         )}
+        {currentAlm === ALM_KEYS.GITLAB && (
+          <GitlabTab
+            definitions={definitions.gitlab}
+            loading={loading}
+            onDelete={props.onDelete}
+            onUpdateDefinitions={props.onUpdateDefinitions}
+          />
+        )}
       </div>
 
       {definitionKeyForDeletion && (
index 6cfa366286a4d6d59851aa8f8c44f65f541139a4..fcc47030045a4ee7a7f1004046938b94e8f91f8b 100644 (file)
@@ -41,7 +41,8 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State
     definitions: {
       [ALM_KEYS.AZURE]: [],
       [ALM_KEYS.BITBUCKET]: [],
-      [ALM_KEYS.GITHUB]: []
+      [ALM_KEYS.GITHUB]: [],
+      [ALM_KEYS.GITLAB]: []
     },
     loading: true
   };
index c0afb6b7176fe251bf85b804f79ca0f3683be72d..27e1d749a64320a8978f2942897eb97114b9a6b2 100644 (file)
@@ -57,7 +57,13 @@ export function TabHeader(props: TabHeaderProps) {
       </Alert>
 
       <div className="big-spacer-bottom display-flex-space-between">
-        <h4 className="display-inline">{translate('settings.pr_decoration.table.title')}</h4>
+        <h4 className="display-inline">
+          {translate(
+            'settings',
+            alm === ALM_KEYS.GITLAB ? 'mr_decoration' : 'pr_decoration',
+            'table.title'
+          )}
+        </h4>
         {showButton && (
           <Button data-test="settings__alm-create" onClick={props.onCreate}>
             {translate('settings.pr_decoration.table.create')}
index 05a0fac085e35c6bdccf6990b0b0efa1410d49c2..c50624e99a65152786a0a1de6428850b01dcf53f 100644 (file)
@@ -20,8 +20,8 @@
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { mockGithubDefinition } from '../../../../../helpers/testMocks';
-import { GithubBindingDefinition } from '../../../../../types/alm-settings';
+import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { ALM_KEYS, GithubBindingDefinition } from '../../../../../types/alm-settings';
 import AlmPRDecorationFormModal from '../AlmPRDecorationFormModal';
 
 it('should render correctly', () => {
@@ -80,6 +80,7 @@ function shallowRender(
 ) {
   return shallow<AlmPRDecorationFormModal<GithubBindingDefinition>>(
     <AlmPRDecorationFormModal
+      alm={ALM_KEYS.GITHUB}
       bindingDefinition={{ appId: '', key: '', privateKey: '', url: '' }}
       onCancel={jest.fn()}
       onSubmit={jest.fn()}
index 2372fac9458dff43b8c7b17abc26d535f8c7f9a8..249f469a464a8d204b7be2ffd8eae4ad4794e12d 100644 (file)
@@ -19,6 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { ALM_KEYS } from '../../../../../types/alm-settings';
 import AlmPRDecorationFormModalRenderer, {
   AlmPRDecorationFormModalProps
 } from '../AlmPRDecorationFormModalRenderer';
@@ -30,6 +31,7 @@ it('should render correctly', () => {
 function shallowRender(props: Partial<AlmPRDecorationFormModalProps> = {}) {
   return shallow(
     <AlmPRDecorationFormModalRenderer
+      alm={ALM_KEYS.GITHUB}
       canSubmit={jest.fn()}
       onCancel={jest.fn()}
       onSubmit={jest.fn()}
index 37078fe9d465c595140cbaa21a955cbafc7a9c6e..487caa2beeada00dcb7af9e335d17df5fae640b4 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAzureDefinition } from '../../../../../helpers/testMocks';
+import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings';
 import AzureFormModal, { AzureFormModalProps } from '../AzureFormModal';
 
 it('should render correctly', () => {
index 4f394a34214966fef3b26162e1d207df73e44c28..17ef4dd7ea57d23042e39d21e029309007c3f55a 100644 (file)
@@ -21,7 +21,7 @@ import { shallow } from 'enzyme';
 import * as React from 'react';
 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
 import { createAzureConfiguration, updateAzureConfiguration } from '../../../../../api/almSettings';
-import { mockAzureDefinition } from '../../../../../helpers/testMocks';
+import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings';
 import AzureTab from '../AzureTab';
 
 jest.mock('../../../../../api/almSettings', () => ({
index ea3bfed089cdfe0befe22b53dfff5d059a7029ce..f8b6c713b00643314220a4ca4c31c4d73b6e3643 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockAzureDefinition } from '../../../../../helpers/testMocks';
+import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings';
 import AzureTabRenderer, { AzureTabRendererProps } from '../AzureTabRenderer';
 
 it('should render correctly', () => {
index 8f475d160895ba2dc8929f541c6d92dd6a2a0806..a05f9c9fd4b0f4f0db22067674b9ca0ca78255e3 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockBitbucketDefinition } from '../../../../../helpers/testMocks';
+import { mockBitbucketDefinition } from '../../../../../helpers/mocks/alm-settings';
 import BitbucketFormModal, { BitbucketFormModalProps } from '../BitbucketFormModal';
 
 it('should render correctly', () => {
index 7131b243ec8d5d552de94956253c7c0b955e0aa5..836bd1d3f2a973771f6a660e9a92af9aba09070a 100644 (file)
@@ -24,7 +24,7 @@ import {
   createBitbucketConfiguration,
   updateBitbucketConfiguration
 } from '../../../../../api/almSettings';
-import { mockBitbucketDefinition } from '../../../../../helpers/testMocks';
+import { mockBitbucketDefinition } from '../../../../../helpers/mocks/alm-settings';
 import BitbucketTab from '../BitbucketTab';
 
 jest.mock('../../../../../api/almSettings', () => ({
index 1dc73768335d1c4d69ff2ef01d0dfa1dd0170454..f2daaa98fa5345448f2a67153b10439ac6934195 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockBitbucketDefinition } from '../../../../../helpers/testMocks';
+import { mockBitbucketDefinition } from '../../../../../helpers/mocks/alm-settings';
 import BitbucketTabRenderer, { BitbucketTabRendererProps } from '../BitbucketTabRenderer';
 
 it('should render correctly', () => {
index cd9a383fee7931965ae0af001afc61b911fecd2b..44f595894a59493c10067e07c371d493e3337e48 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGithubDefinition } from '../../../../../helpers/testMocks';
+import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GithubFormModal, { GithubFormModalProps } from '../GithubFormModal';
 
 it('should render correctly', () => {
index 2e422a0fbc551763f66f4d775e11f932f9d1dc0e..b1209124f02d1f24ac8ef8aef133f9602106e3c1 100644 (file)
@@ -24,7 +24,7 @@ import {
   createGithubConfiguration,
   updateGithubConfiguration
 } from '../../../../../api/almSettings';
-import { mockGithubDefinition } from '../../../../../helpers/testMocks';
+import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GithubTab from '../GithubTab';
 
 jest.mock('../../../../../api/almSettings', () => ({
index 75e531a741ef16509f40117b1aa5b9312162347e..b81f8b6628f1f1b50005ef2dabe924ac20beb8f9 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { mockGithubDefinition } from '../../../../../helpers/testMocks';
+import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings';
 import GithubTabRenderer, { GithubTabRendererProps } from '../GithubTabRenderer';
 
 it('should render correctly', () => {
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabFormModal-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabFormModal-test.tsx
new file mode 100644 (file)
index 0000000..54886e5
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { mockGitlabDefinition } from '../../../../../helpers/mocks/alm-settings';
+import { GitlabFormModal, GitlabFormModalProps } from '../GitlabFormModal';
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ formData: mockGitlabDefinition() })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<GitlabFormModalProps> = {}) {
+  return shallow(
+    <GitlabFormModal
+      formData={{ key: '', personalAccessToken: '' }}
+      onFieldChange={jest.fn()}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTab-test.tsx
new file mode 100644 (file)
index 0000000..abe042b
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 {
+  createGitlabConfiguration,
+  updateGitlabConfiguration
+} from '../../../../../api/almSettings';
+import { mockGitlabDefinition } from '../../../../../helpers/mocks/alm-settings';
+import GitlabTab from '../GitlabTab';
+
+jest.mock('../../../../../api/almSettings', () => ({
+  countBindedProjects: jest.fn().mockResolvedValue(2),
+  createGitlabConfiguration: jest.fn().mockResolvedValue({}),
+  deleteConfiguration: jest.fn().mockResolvedValue({}),
+  updateGitlabConfiguration: jest.fn().mockResolvedValue({})
+}));
+
+beforeEach(() => {
+  jest.clearAllMocks();
+});
+
+it('should render correctly', () => {
+  expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should handle cancel', async () => {
+  const wrapper = shallowRender();
+
+  wrapper.setState({
+    editedDefinition: mockGitlabDefinition()
+  });
+
+  wrapper.instance().handleCancel();
+
+  await waitAndUpdate(wrapper);
+
+  expect(wrapper.state().editedDefinition).toBeUndefined();
+});
+
+it('should handle edit', async () => {
+  const config = mockGitlabDefinition();
+  const wrapper = shallowRender({ definitions: [config] });
+  wrapper.instance().handleEdit(config.key);
+  await waitAndUpdate(wrapper);
+  expect(wrapper.state().editedDefinition).toEqual(config);
+});
+
+it('should create config', async () => {
+  const onUpdateDefinitions = jest.fn();
+  const config = mockGitlabDefinition();
+  const wrapper = shallowRender({ onUpdateDefinitions });
+  wrapper.setState({ editedDefinition: config });
+
+  await wrapper.instance().handleSubmit(config, '');
+
+  expect(createGitlabConfiguration).toBeCalledWith(config);
+  expect(onUpdateDefinitions).toBeCalled();
+  expect(wrapper.state().editedDefinition).toBeUndefined();
+});
+
+it('should update config', async () => {
+  const onUpdateDefinitions = jest.fn();
+  const config = mockGitlabDefinition();
+  const wrapper = shallowRender({ onUpdateDefinitions });
+  wrapper.setState({ editedDefinition: config });
+
+  await wrapper.instance().handleSubmit(config, 'originalKey');
+
+  expect(updateGitlabConfiguration).toBeCalledWith({
+    newKey: 'foo',
+    ...config,
+    key: 'originalKey'
+  });
+  expect(onUpdateDefinitions).toBeCalled();
+  expect(wrapper.state().editedDefinition).toBeUndefined();
+});
+
+function shallowRender(props: Partial<GitlabTab['props']> = {}) {
+  return shallow<GitlabTab>(
+    <GitlabTab
+      definitions={[]}
+      loading={false}
+      onDelete={jest.fn()}
+      onUpdateDefinitions={jest.fn()}
+      {...props}
+    />
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTabRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTabRenderer-test.tsx
new file mode 100644 (file)
index 0000000..5fe001e
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { mockGitlabDefinition } from '../../../../../helpers/mocks/alm-settings';
+import GitlabTabRenderer, { GitlabTabRendererProps } from '../GitlabTabRenderer';
+
+it('should render correctly', () => {
+  expect(shallowRender({ loading: true })).toMatchSnapshot();
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ editedDefinition: mockGitlabDefinition() })).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<GitlabTabRendererProps> = {}) {
+  return shallow(
+    <GitlabTabRenderer
+      definitions={[]}
+      loading={false}
+      onCancel={jest.fn()}
+      onCreate={jest.fn()}
+      onDelete={jest.fn()}
+      onEdit={jest.fn()}
+      onSubmit={jest.fn()}
+      {...props}
+    />
+  );
+}
index 1adeb9be6e71c1f0d1715a665425227cfea8b9f5..9a84060a2ade66b804f1068c646928d33e895471 100644 (file)
@@ -26,14 +26,15 @@ it('should render correctly', () => {
   expect(shallowRender({ loading: true })).toMatchSnapshot();
   expect(shallowRender({ definitionKeyForDeletion: 'keyToDelete' })).toMatchSnapshot();
   expect(shallowRender({ currentAlm: ALM_KEYS.AZURE })).toMatchSnapshot();
-  expect(shallowRender({ currentAlm: ALM_KEYS.GITHUB })).toMatchSnapshot();
+  expect(shallowRender({ currentAlm: ALM_KEYS.BITBUCKET })).toMatchSnapshot();
+  expect(shallowRender({ currentAlm: ALM_KEYS.GITLAB })).toMatchSnapshot();
 });
 
 function shallowRender(props: Partial<PRDecorationTabsProps> = {}) {
   return shallow(
     <PRDecorationTabs
       currentAlm={ALM_KEYS.GITHUB}
-      definitions={{ azure: [], bitbucket: [], github: [] }}
+      definitions={{ azure: [], bitbucket: [], github: [], gitlab: [] }}
       loading={false}
       onCancel={jest.fn()}
       onConfirmDelete={jest.fn()}
index d27a4c4a6f33850d79cb15179e2dd980bc26795c..b980d1d72cd179b03ee846388745ffe41745bc65 100644 (file)
@@ -11,7 +11,7 @@ exports[`should render correctly 1`] = `
     value=""
   />
   <AlmDefinitionFormField
-    help="settings.pr_decoration.form.personal_access_token.help"
+    help="settings.pr_decoration.form.personal_access_token.azure.help"
     id="personal_access_token"
     isTextArea={true}
     onFieldChange={[MockFunction]}
@@ -32,7 +32,7 @@ exports[`should render correctly 2`] = `
     value="key"
   />
   <AlmDefinitionFormField
-    help="settings.pr_decoration.form.personal_access_token.help"
+    help="settings.pr_decoration.form.personal_access_token.azure.help"
     id="personal_access_token"
     isTextArea={true}
     onFieldChange={[MockFunction]}
index 0e6a491e86a0021c3173bb0783d8c509a071a30b..8e82621775adea82014df2df5481c8f41623ee8f 100644 (file)
@@ -64,6 +64,7 @@ exports[`should render correctly 3`] = `
     />
   </DeferredSpinner>
   <AlmPRDecorationFormModal
+    alm="azure"
     bindingDefinition={
       Object {
         "key": "key",
index 65b1f534c8a7b575ee68b88993c4622abebd64cc..5c58a79578458ae2f5619e4ccecc3074b1e01c2c 100644 (file)
@@ -76,6 +76,7 @@ exports[`should render correctly 3`] = `
     />
   </DeferredSpinner>
   <AlmPRDecorationFormModal
+    alm="bitbucket"
     bindingDefinition={
       Object {
         "key": "key",
index 50af2ec9f0032fda68230f7edeb237e9f8026e94..aa48a283a5653c8d457edccc2dae2b7ba346c210 100644 (file)
@@ -79,6 +79,7 @@ exports[`should render correctly 3`] = `
     />
   </DeferredSpinner>
   <AlmPRDecorationFormModal
+    alm="github"
     bindingDefinition={
       Object {
         "appId": "123456",
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabFormModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabFormModal-test.tsx.snap
new file mode 100644 (file)
index 0000000..8c2b54c
--- /dev/null
@@ -0,0 +1,43 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <AlmDefinitionFormField
+    autoFocus={true}
+    help="settings.pr_decoration.form.name.gitlab.help"
+    id="name.gitlab"
+    onFieldChange={[MockFunction]}
+    propKey="key"
+    value=""
+  />
+  <AlmDefinitionFormField
+    help="settings.pr_decoration.form.personal_access_token.gitlab.help"
+    id="personal_access_token"
+    isTextArea={true}
+    onFieldChange={[MockFunction]}
+    propKey="personalAccessToken"
+    value=""
+  />
+</Fragment>
+`;
+
+exports[`should render correctly 2`] = `
+<Fragment>
+  <AlmDefinitionFormField
+    autoFocus={true}
+    help="settings.pr_decoration.form.name.gitlab.help"
+    id="name.gitlab"
+    onFieldChange={[MockFunction]}
+    propKey="key"
+    value="foo"
+  />
+  <AlmDefinitionFormField
+    help="settings.pr_decoration.form.personal_access_token.gitlab.help"
+    id="personal_access_token"
+    isTextArea={true}
+    onFieldChange={[MockFunction]}
+    propKey="personalAccessToken"
+    value="foobar"
+  />
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTab-test.tsx.snap
new file mode 100644 (file)
index 0000000..95bb1ac
--- /dev/null
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<GitlabTabRenderer
+  definitions={Array []}
+  loading={false}
+  onCancel={[Function]}
+  onCreate={[Function]}
+  onDelete={[MockFunction]}
+  onEdit={[Function]}
+  onSubmit={[Function]}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTabRenderer-test.tsx.snap
new file mode 100644 (file)
index 0000000..3b57267
--- /dev/null
@@ -0,0 +1,80 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<Fragment>
+  <Connect(withAppState(TabHeader))
+    alm="gitlab"
+    definitionCount={0}
+    onCreate={[MockFunction]}
+  />
+  <DeferredSpinner
+    loading={true}
+    timeout={100}
+  >
+    <AlmPRDecorationTable
+      additionalColumnsHeaders={Array []}
+      alm="gitlab"
+      definitions={Array []}
+      onDelete={[MockFunction]}
+      onEdit={[MockFunction]}
+    />
+  </DeferredSpinner>
+</Fragment>
+`;
+
+exports[`should render correctly 2`] = `
+<Fragment>
+  <Connect(withAppState(TabHeader))
+    alm="gitlab"
+    definitionCount={0}
+    onCreate={[MockFunction]}
+  />
+  <DeferredSpinner
+    loading={false}
+    timeout={100}
+  >
+    <AlmPRDecorationTable
+      additionalColumnsHeaders={Array []}
+      alm="gitlab"
+      definitions={Array []}
+      onDelete={[MockFunction]}
+      onEdit={[MockFunction]}
+    />
+  </DeferredSpinner>
+</Fragment>
+`;
+
+exports[`should render correctly 3`] = `
+<Fragment>
+  <Connect(withAppState(TabHeader))
+    alm="gitlab"
+    definitionCount={0}
+    onCreate={[MockFunction]}
+  />
+  <DeferredSpinner
+    loading={false}
+    timeout={100}
+  >
+    <AlmPRDecorationTable
+      additionalColumnsHeaders={Array []}
+      alm="gitlab"
+      definitions={Array []}
+      onDelete={[MockFunction]}
+      onEdit={[MockFunction]}
+    />
+  </DeferredSpinner>
+  <AlmPRDecorationFormModal
+    alm="gitlab"
+    bindingDefinition={
+      Object {
+        "key": "foo",
+        "personalAccessToken": "foobar",
+      }
+    }
+    onCancel={[MockFunction]}
+    onSubmit={[MockFunction]}
+  >
+    <Component />
+  </AlmPRDecorationFormModal>
+</Fragment>
+`;
index 52d7fc4e680c2c3a48b31bde41f99a4afc2de800..fa3df3d8b61e947bd46f4fbbad51d72cfd92c1ed 100644 (file)
@@ -27,8 +27,8 @@ exports[`should render correctly 1`] = `
             <img
               alt="github"
               className="spacer-right"
+              height={16}
               src="/images/alm/github.svg"
-              width={16}
             />
             GitHub
           </React.Fragment>,
@@ -39,8 +39,8 @@ exports[`should render correctly 1`] = `
             <img
               alt="bitbucket"
               className="spacer-right"
+              height={16}
               src="/images/alm/bitbucket.svg"
-              width={16}
             />
             Bitbucket Server
           </React.Fragment>,
@@ -51,12 +51,24 @@ exports[`should render correctly 1`] = `
             <img
               alt="azure"
               className="spacer-right"
+              height={16}
               src="/images/alm/azure.svg"
-              width={16}
             />
             Azure DevOps Server
           </React.Fragment>,
         },
+        Object {
+          "key": "gitlab",
+          "label": <React.Fragment>
+            <img
+              alt="gitlab"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/gitlab.svg"
+            />
+            GitLab
+          </React.Fragment>,
+        },
       ]
     }
   />
@@ -100,8 +112,8 @@ exports[`should render correctly 2`] = `
             <img
               alt="github"
               className="spacer-right"
+              height={16}
               src="/images/alm/github.svg"
-              width={16}
             />
             GitHub
           </React.Fragment>,
@@ -112,8 +124,8 @@ exports[`should render correctly 2`] = `
             <img
               alt="bitbucket"
               className="spacer-right"
+              height={16}
               src="/images/alm/bitbucket.svg"
-              width={16}
             />
             Bitbucket Server
           </React.Fragment>,
@@ -124,12 +136,24 @@ exports[`should render correctly 2`] = `
             <img
               alt="azure"
               className="spacer-right"
+              height={16}
               src="/images/alm/azure.svg"
-              width={16}
             />
             Azure DevOps Server
           </React.Fragment>,
         },
+        Object {
+          "key": "gitlab",
+          "label": <React.Fragment>
+            <img
+              alt="gitlab"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/gitlab.svg"
+            />
+            GitLab
+          </React.Fragment>,
+        },
       ]
     }
   />
@@ -178,8 +202,8 @@ exports[`should render correctly 3`] = `
             <img
               alt="github"
               className="spacer-right"
+              height={16}
               src="/images/alm/github.svg"
-              width={16}
             />
             GitHub
           </React.Fragment>,
@@ -190,8 +214,8 @@ exports[`should render correctly 3`] = `
             <img
               alt="bitbucket"
               className="spacer-right"
+              height={16}
               src="/images/alm/bitbucket.svg"
-              width={16}
             />
             Bitbucket Server
           </React.Fragment>,
@@ -202,12 +226,24 @@ exports[`should render correctly 3`] = `
             <img
               alt="azure"
               className="spacer-right"
+              height={16}
               src="/images/alm/azure.svg"
-              width={16}
             />
             Azure DevOps Server
           </React.Fragment>,
         },
+        Object {
+          "key": "gitlab",
+          "label": <React.Fragment>
+            <img
+              alt="gitlab"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/gitlab.svg"
+            />
+            GitLab
+          </React.Fragment>,
+        },
       ]
     }
   />
@@ -242,7 +278,7 @@ exports[`should render correctly 4`] = `
   </div>
   <BoxedTabs
     onSelect={[MockFunction]}
-    selected="github"
+    selected="bitbucket"
     tabs={
       Array [
         Object {
@@ -251,8 +287,8 @@ exports[`should render correctly 4`] = `
             <img
               alt="github"
               className="spacer-right"
+              height={16}
               src="/images/alm/github.svg"
-              width={16}
             />
             GitHub
           </React.Fragment>,
@@ -263,8 +299,8 @@ exports[`should render correctly 4`] = `
             <img
               alt="bitbucket"
               className="spacer-right"
+              height={16}
               src="/images/alm/bitbucket.svg"
-              width={16}
             />
             Bitbucket Server
           </React.Fragment>,
@@ -275,19 +311,116 @@ exports[`should render correctly 4`] = `
             <img
               alt="azure"
               className="spacer-right"
+              height={16}
               src="/images/alm/azure.svg"
-              width={16}
             />
             Azure DevOps Server
           </React.Fragment>,
         },
+        Object {
+          "key": "gitlab",
+          "label": <React.Fragment>
+            <img
+              alt="gitlab"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/gitlab.svg"
+            />
+            GitLab
+          </React.Fragment>,
+        },
       ]
     }
   />
   <div
     className="boxed-group boxed-group-inner"
   >
-    <GithubTab
+    <BitbucketTab
+      definitions={Array []}
+      loading={false}
+      onDelete={[MockFunction]}
+      onUpdateDefinitions={[MockFunction]}
+    />
+  </div>
+</Fragment>
+`;
+
+exports[`should render correctly 5`] = `
+<Fragment>
+  <header
+    className="page-header"
+  >
+    <h1
+      className="page-title"
+    >
+      settings.pr_decoration.title
+    </h1>
+  </header>
+  <div
+    className="markdown small spacer-top big-spacer-bottom"
+  >
+    settings.pr_decoration.description
+  </div>
+  <BoxedTabs
+    onSelect={[MockFunction]}
+    selected="gitlab"
+    tabs={
+      Array [
+        Object {
+          "key": "github",
+          "label": <React.Fragment>
+            <img
+              alt="github"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/github.svg"
+            />
+            GitHub
+          </React.Fragment>,
+        },
+        Object {
+          "key": "bitbucket",
+          "label": <React.Fragment>
+            <img
+              alt="bitbucket"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/bitbucket.svg"
+            />
+            Bitbucket Server
+          </React.Fragment>,
+        },
+        Object {
+          "key": "azure",
+          "label": <React.Fragment>
+            <img
+              alt="azure"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/azure.svg"
+            />
+            Azure DevOps Server
+          </React.Fragment>,
+        },
+        Object {
+          "key": "gitlab",
+          "label": <React.Fragment>
+            <img
+              alt="gitlab"
+              className="spacer-right"
+              height={16}
+              src="/images/alm/gitlab.svg"
+            />
+            GitLab
+          </React.Fragment>,
+        },
+      ]
+    }
+  />
+  <div
+    className="boxed-group boxed-group-inner"
+  >
+    <GitlabTab
       definitions={Array []}
       loading={false}
       onDelete={[MockFunction]}
index 4f286f4d173c402963d4ce9856970edf78d25a26..fc902c9c1e3bf1f706a71f07c4b89ec308f0e3d3 100644 (file)
@@ -8,6 +8,7 @@ exports[`should render correctly 1`] = `
       "azure": Array [],
       "bitbucket": Array [],
       "github": Array [],
+      "gitlab": Array [],
     }
   }
   loading={true}
index e5f40286a539e331c30dc8230d701f0d9aaa8ac5..e9a6f9690697a8a41a3674346b03d11f749a0c3c 100644 (file)
@@ -24,7 +24,8 @@ import {
   getProjectAlmBinding,
   setProjectAzureBinding,
   setProjectBitbucketBinding,
-  setProjectGithubBinding
+  setProjectGithubBinding,
+  setProjectGitlabBinding
 } from '../../../../api/almSettings';
 import throwGlobalError from '../../../../app/utils/throwGlobalError';
 import { AlmSettingsInstance, ALM_KEYS, ProjectAlmBinding } from '../../../../types/alm-settings';
@@ -47,7 +48,8 @@ interface State {
 const FIELDS_BY_ALM: { [almKey in ALM_KEYS]: Array<keyof T.Omit<ProjectAlmBinding, 'key'>> } = {
   [ALM_KEYS.AZURE]: [],
   [ALM_KEYS.BITBUCKET]: ['repository', 'slug'],
-  [ALM_KEYS.GITHUB]: ['repository']
+  [ALM_KEYS.GITHUB]: ['repository'],
+  [ALM_KEYS.GITLAB]: []
 };
 
 export default class PRDecorationBinding extends React.PureComponent<Props, State> {
@@ -167,6 +169,13 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat
           repository
         });
       }
+
+      case ALM_KEYS.GITLAB:
+        return setProjectGitlabBinding({
+          almSetting,
+          project
+        });
+
       default:
         return Promise.reject();
     }
diff --git a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts
new file mode 100644 (file)
index 0000000..a48b73e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 {
+  AzureBindingDefinition,
+  BitbucketBindingDefinition,
+  GithubBindingDefinition,
+  GitlabBindingDefinition
+} from '../../types/alm-settings';
+
+export function mockAzureDefinition(
+  overrides: Partial<AzureBindingDefinition> = {}
+): AzureBindingDefinition {
+  return {
+    key: 'key',
+    personalAccessToken: 'asdf1234',
+    ...overrides
+  };
+}
+
+export function mockBitbucketDefinition(
+  overrides: Partial<BitbucketBindingDefinition> = {}
+): BitbucketBindingDefinition {
+  return {
+    key: 'key',
+    personalAccessToken: 'asdf1234',
+    url: 'http://bbs.enterprise.com',
+    ...overrides
+  };
+}
+
+export function mockGithubDefinition(
+  overrides: Partial<GithubBindingDefinition> = {}
+): GithubBindingDefinition {
+  return {
+    key: 'key',
+    url: 'http://github.enterprise.com',
+    appId: '123456',
+    privateKey: 'asdf1234',
+    ...overrides
+  };
+}
+
+export function mockGitlabDefinition(
+  overrides: Partial<GitlabBindingDefinition> = {}
+): GitlabBindingDefinition {
+  return {
+    key: 'foo',
+    personalAccessToken: 'foobar',
+    ...overrides
+  };
+}
index 8be68f8134605ee2f8dedcdc46d74b610cde1d8d..72821d2b85cdf2be67efe09b058bc363588fd205 100644 (file)
@@ -23,11 +23,6 @@ import { InjectedRouter } from 'react-router';
 import { createStore, Store } from 'redux';
 import { DocumentationEntry } from '../apps/documentation/utils';
 import { Exporter, Profile } from '../apps/quality-profiles/types';
-import {
-  AzureBindingDefinition,
-  BitbucketBindingDefinition,
-  GithubBindingDefinition
-} from '../types/alm-settings';
 
 export function mockAlmApplication(overrides: Partial<T.AlmApplication> = {}): T.AlmApplication {
   return {
@@ -55,39 +50,6 @@ export function mockAlmOrganization(overrides: Partial<T.AlmOrganization> = {}):
   };
 }
 
-export function mockAzureDefinition(
-  overrides: Partial<AzureBindingDefinition> = {}
-): AzureBindingDefinition {
-  return {
-    key: 'key',
-    personalAccessToken: 'asdf1234',
-    ...overrides
-  };
-}
-
-export function mockBitbucketDefinition(
-  overrides: Partial<BitbucketBindingDefinition> = {}
-): BitbucketBindingDefinition {
-  return {
-    key: 'key',
-    personalAccessToken: 'asdf1234',
-    url: 'http://bbs.enterprise.com',
-    ...overrides
-  };
-}
-
-export function mockGithubDefinition(
-  overrides: Partial<GithubBindingDefinition> = {}
-): GithubBindingDefinition {
-  return {
-    key: 'key',
-    url: 'http://github.enterprise.com',
-    appId: '123456',
-    privateKey: 'asdf1234',
-    ...overrides
-  };
-}
-
 export function mockAnalysis(overrides: Partial<T.Analysis> = {}): T.Analysis {
   return {
     date: '2017-03-01T09:36:01+0100',
index c7d6b5d6114f10e5139c5ec13e1b8cabe7f3b3ea..60d95f59284f67a5fdbb8879030a57350ed0a532 100644 (file)
@@ -20,7 +20,8 @@
 export const enum ALM_KEYS {
   AZURE = 'azure',
   BITBUCKET = 'bitbucket',
-  GITHUB = 'github'
+  GITHUB = 'github',
+  GITLAB = 'gitlab'
 }
 
 export interface AlmSettingsBinding {
@@ -37,6 +38,7 @@ export interface AlmSettingsBindingDefinitions {
   azure: AzureBindingDefinition[];
   bitbucket: BitbucketBindingDefinition[];
   github: GithubBindingDefinition[];
+  gitlab: GitlabBindingDefinition[];
 }
 
 export interface AzureBindingDefinition extends AlmSettingsBinding {
@@ -54,6 +56,10 @@ export interface GithubBindingDefinition extends AlmSettingsBinding {
   url: string;
 }
 
+export interface GitlabBindingDefinition extends AlmSettingsBinding {
+  personalAccessToken: string;
+}
+
 export interface ProjectAlmBinding {
   key: string;
   repository?: string;
@@ -77,3 +83,8 @@ export interface GithubProjectAlmBinding {
   project: string;
   repository: string;
 }
+
+export interface GitlabProjectAlmBinding {
+  almSetting: string;
+  project: string;
+}
index 0499a54c10c0781462170720ec0a2e2d22dc3979..d7338b776eefff7075de7203b65d44fc121530db 100644 (file)
@@ -940,10 +940,13 @@ settings.pr_decoration.manage_instances=Manage instances
 settings.pr_decoration.azure.info=Accounts that will be used to decorate Pull Requests need Code: Read & Write permission. {link}
 settings.pr_decoration.bitbucket.info=Accounts that will be used to decorate Pull Requests need write permission. {link}
 settings.pr_decoration.github.info=You need to install a GitHub App with specific settings and permissions to enable Pull Request Decoration on your Organization or Repository. {link}
+settings.pr_decoration.gitlab.info=Accounts that will be used to decorate Merge Requests need comment permissions on projects. The personal key needs the API scope permission. {link}
 settings.pr_decoration.table.title=Pull Request Decoration configurations
+settings.mr_decoration.table.title=Merge Request Decoration configurations
 settings.pr_decoration.table.empty.azure=Create your first Azure DevOps configuration to enable Pull Request Decoration on your projects.
 settings.pr_decoration.table.empty.bitbucket=Create your first Bitbucket configuration to enable Pull Request Decoration on your projects.
 settings.pr_decoration.table.empty.github=Create your first GitHub configuration to enable Pull Request Decoration on your organization or repository.
+settings.pr_decoration.table.empty.gitlab=Create your first GitLab configuration to enable Merge Request Decoration on your repository.
 settings.pr_decoration.table.create=Create configuration
 settings.pr_decoration.table.column.name=Name
 settings.pr_decoration.table.column.bitbucket.url=Bitbucket Server URL
@@ -956,13 +959,17 @@ settings.pr_decoration.delete.message=Are you sure you want to delete the {id} c
 settings.pr_decoration.delete.info={0} projects will no longer get Pull Request Decorations.
 settings.pr_decoration.delete.no_info=An unknown number of projects will no longer get Pull Request Decorations.
 settings.pr_decoration.form.header.create=Create a Pull Request Decoration configuration
+settings.mr_decoration.form.header.create=Create a Merge Request Decoration configuration
 settings.pr_decoration.form.header.edit=Edit the Pull Request Decoration configuration
+settings.mr_decoration.form.header.edit=Edit the Merge Request Decoration configuration
 settings.pr_decoration.form.name.azure=Configuration name
 settings.pr_decoration.form.name.azure.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured Azure instance for a project.
 settings.pr_decoration.form.name.bitbucket=Configuration name
 settings.pr_decoration.form.name.bitbucket.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured Bitbucket instance for a project.
 settings.pr_decoration.form.name.github=Configuration name
 settings.pr_decoration.form.name.github.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured GitHub App for a project.
+settings.pr_decoration.form.name.gitlab=Configuration name
+settings.pr_decoration.form.name.gitlab.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured GitLab instance for a project.
 settings.pr_decoration.form.url.bitbucket=Bitbucket Server URL
 settings.pr_decoration.form.url.bitbucket.help=Example: {example}
 settings.pr_decoration.form.url.github=GitHub URL
@@ -971,7 +978,8 @@ settings.pr_decoration.form.url.github.help2=If using GitHub.com:
 settings.pr_decoration.form.app_id=GitHub App ID
 settings.pr_decoration.form.private_key=Private Key
 settings.pr_decoration.form.personal_access_token=Personal Access token
-settings.pr_decoration.form.personal_access_token.help=Token of the user that will be used to decorate the pull requests. Needs authorized scope: "Code (read and write)".
+settings.pr_decoration.form.personal_access_token.azure.help=Token of the user that will be used to decorate the Pull Requests. Needs authorized scope: "Code (read and write)".
+settings.pr_decoration.form.personal_access_token.gitlab.help=Token of the user that will be used to decorate the Merge Requests. Needs API scope authorization.
 settings.pr_decoration.form.save=Save configuration
 settings.pr_decoration.form.cancel=Cancel