]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18931 Migrate to RTL for PRDecorationBinding
authorstanislavh <stanislav.honcharov@sonarsource.com>
Thu, 30 Mar 2023 09:02:59 +0000 (11:02 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 30 Mar 2023 20:03:07 +0000 (20:03 +0000)
18 files changed:
server/sonar-web/__mocks__/react-intl.tsx
server/sonar-web/src/main/js/api/mocks/AlmSettingsServiceMock.ts
server/sonar-web/src/main/js/app/components/nav/component/__tests__/Header-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-it.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-it.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/settings/encryption/__tests__/EncryptionApp-it.tsx
server/sonar-web/src/main/js/components/controls/Toggle.tsx
server/sonar-web/src/main/js/components/devops-platform/AlmSettingsInstanceSelector.tsx
server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts
server/sonar-web/src/main/js/types/alm-settings.ts

index b2ab27be3fb0936248862b67beaace7acbde8bd8..9e8c8170dd1f401c0970e089bfa345c3216e43b5 100644 (file)
@@ -30,5 +30,5 @@ module.exports = {
         ))}
       </>
     );
-  }
+  },
 };
index 4b15331071aa8dd84d8d5d962977dad519689003..864aac3a0f5f70efa4380f5437a3597fa1f90874 100644 (file)
@@ -24,10 +24,18 @@ import {
   AlmSettingsBindingDefinitions,
   AlmSettingsInstance,
   AzureBindingDefinition,
+  AzureProjectAlmBindingParams,
   BitbucketCloudBindingDefinition,
+  BitbucketCloudProjectAlmBindingParams,
+  BitbucketProjectAlmBindingParams,
   BitbucketServerBindingDefinition,
   GithubBindingDefinition,
+  GithubProjectAlmBindingParams,
   GitlabBindingDefinition,
+  GitlabProjectAlmBindingParams,
+  ProjectAlmBindingConfigurationErrors,
+  ProjectAlmBindingParams,
+  ProjectAlmBindingResponse,
 } from '../../types/alm-settings';
 import {
   countBoundProjects,
@@ -37,14 +45,22 @@ import {
   createGithubConfiguration,
   createGitlabConfiguration,
   deleteConfiguration,
+  deleteProjectAlmBinding,
   getAlmDefinitions,
   getAlmSettings,
+  getProjectAlmBinding,
+  setProjectAzureBinding,
+  setProjectBitbucketBinding,
+  setProjectBitbucketCloudBinding,
+  setProjectGithubBinding,
+  setProjectGitlabBinding,
   updateAzureConfiguration,
   updateBitbucketCloudConfiguration,
   updateBitbucketServerConfiguration,
   updateGithubConfiguration,
   updateGitlabConfiguration,
   validateAlmSettings,
+  validateProjectAlmBinding,
 } from '../alm-settings';
 
 const defaultAlmDefinitions = {
@@ -85,10 +101,20 @@ const defaultAlmSettings = [
   }),
 ];
 
+interface EnhancedProjectAlmBindingParam extends ProjectAlmBindingParams {
+  projectName?: string;
+  repositoryName?: string;
+  repository?: string;
+  slug?: string;
+  summaryCommentEnabled?: boolean;
+}
+
 export default class AlmSettingsServiceMock {
   #almDefinitions: AlmSettingsBindingDefinitions;
   #almSettings: AlmSettingsInstance[];
   #definitionError = '';
+  #projectsBindings: { [key: string]: ProjectAlmBindingResponse | undefined } = {};
+  #projectBindingConfigurationErrors: ProjectAlmBindingConfigurationErrors | undefined = undefined;
 
   constructor() {
     this.#almSettings = cloneDeep(defaultAlmSettings);
@@ -116,6 +142,18 @@ export default class AlmSettingsServiceMock {
     jest
       .mocked(updateBitbucketCloudConfiguration)
       .mockImplementation(this.handleUpdateBitbucketCloudConfiguration);
+    jest.mocked(getProjectAlmBinding).mockImplementation(this.handleGetProjectBinding);
+    jest.mocked(deleteProjectAlmBinding).mockImplementation(this.handleDeleteProjectAlmBinding);
+    jest.mocked(setProjectAzureBinding).mockImplementation(this.handleSetProjectAzureBinding);
+    jest
+      .mocked(setProjectBitbucketBinding)
+      .mockImplementation(this.handleSetProjectBitbucketBinding);
+    jest
+      .mocked(setProjectBitbucketCloudBinding)
+      .mockImplementation(this.handleSetProjectBitbucketCloudBinding);
+    jest.mocked(setProjectGithubBinding).mockImplementation(this.handleSetProjectGithubBinding);
+    jest.mocked(setProjectGitlabBinding).mockImplementation(this.handleSetProjectGitlabBinding);
+    jest.mocked(validateProjectAlmBinding).mockImplementation(this.handleValidateProjectAlmBinding);
   }
 
   handleGetAlmDefinitions = () => {
@@ -234,9 +272,71 @@ export default class AlmSettingsServiceMock {
     return this.reply(undefined);
   };
 
+  handleGetProjectBinding = (project: string) => {
+    const projectBinding = this.#projectsBindings[project];
+
+    if (projectBinding === undefined) {
+      return Promise.reject(
+        new Response('', {
+          status: 404,
+        })
+      );
+    }
+
+    return this.reply(projectBinding);
+  };
+
+  handleSetProjectBinding = (alm: AlmKeys, data: EnhancedProjectAlmBindingParam) => {
+    this.#projectsBindings[data.project] = {
+      alm,
+      key: data.almSetting,
+      repository: data.repositoryName ?? (data.repository as string),
+      monorepo: data.monorepo,
+      slug: data.projectName ?? data.slug,
+      summaryCommentEnabled: data.summaryCommentEnabled ?? false,
+      url: 'https://company.com/project',
+    };
+    return this.reply(undefined);
+  };
+
+  handleSetProjectAzureBinding = (data: AzureProjectAlmBindingParams) => {
+    return this.handleSetProjectBinding(AlmKeys.Azure, data);
+  };
+
+  handleSetProjectBitbucketBinding = (data: BitbucketProjectAlmBindingParams) => {
+    return this.handleSetProjectBinding(AlmKeys.BitbucketServer, data);
+  };
+
+  handleSetProjectBitbucketCloudBinding = (data: BitbucketCloudProjectAlmBindingParams) => {
+    return this.handleSetProjectBinding(AlmKeys.BitbucketCloud, data);
+  };
+
+  handleSetProjectGithubBinding = (data: GithubProjectAlmBindingParams) => {
+    return this.handleSetProjectBinding(AlmKeys.GitHub, data);
+  };
+
+  handleSetProjectGitlabBinding = (data: GitlabProjectAlmBindingParams) => {
+    return this.handleSetProjectBinding(AlmKeys.GitLab, data);
+  };
+
+  handleValidateProjectAlmBinding = () => {
+    return this.reply(this.#projectBindingConfigurationErrors);
+  };
+
+  setProjectBindingConfigurationErrors = (errors?: ProjectAlmBindingConfigurationErrors) => {
+    this.#projectBindingConfigurationErrors = errors;
+  };
+
+  handleDeleteProjectAlmBinding = (project: string) => {
+    this.#projectsBindings[project] = undefined;
+    return this.reply(undefined);
+  };
+
   reset = () => {
     this.#almSettings = cloneDeep(defaultAlmSettings);
     this.#almDefinitions = cloneDeep(defaultAlmDefinitions);
+    this.#projectsBindings = {};
+    this.#projectBindingConfigurationErrors = undefined;
     this.setDefinitionErrorMessage('');
   };
 
index 04286b17cd1a586545af9bdb0f7279db2e121946..4dbfe4e9a128de947c99f1fe53b94e925b1ebbe3 100644 (file)
@@ -20,6 +20,7 @@
 import { screen } from '@testing-library/react';
 import userEvent from '@testing-library/user-event';
 import * as React from 'react';
+import { mockProjectAlmBindingResponse } from '../../../../../helpers/mocks/alm-settings';
 import {
   mockMainBranch,
   mockPullRequest,
@@ -145,7 +146,11 @@ it('should show the correct help tooltip when branch support is not enabled', ()
   renderHeader(
     {
       currentUser: mockLoggedInUser(),
-      projectBinding: { alm: AlmKeys.GitLab, key: 'key', monorepo: true },
+      projectBinding: mockProjectAlmBindingResponse({
+        alm: AlmKeys.GitLab,
+        key: 'key',
+        monorepo: true,
+      }),
     },
     []
   );
index 4c5391703e33e3ac203aeac1045b56f761e3aafa..1cb65f52ee6b7b806692b0a45a9eca692aac8137 100644 (file)
@@ -40,41 +40,15 @@ afterEach(() => {
   almSettings.reset();
 });
 
-const ui = {
-  almHeading: byRole('heading', { name: 'settings.almintegration.title' }),
-  emptyIntro: (almKey: AlmKeys) => byText(`settings.almintegration.empty.${almKey}`),
-  createConfigurationButton: byRole('button', { name: 'settings.almintegration.create' }),
-  tab: (almKey: AlmKeys) =>
-    byRole('tab', { name: `${almKey} settings.almintegration.tab.${almKey}` }),
-  bitbucketConfiguration: (almKey: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer) =>
-    byRole('button', { name: `alm.${almKey}.long` }),
-  configurationInput: (id: string) =>
-    byRole('textbox', { name: `settings.almintegration.form.${id}` }),
-  updateSecretValueButton: (key: string) =>
-    byRole('button', {
-      name: `settings.almintegration.form.secret.update_field_x.settings.almintegration.form.${key}`,
-    }),
-  saveConfigurationButton: byRole('button', { name: 'settings.almintegration.form.save' }),
-  editConfigurationButton: (key: string) =>
-    byRole('button', { name: `settings.almintegration.edit_configuration.${key}` }),
-  deleteConfigurationButton: (key: string) =>
-    byRole('button', { name: `settings.almintegration.delete_configuration.${key}` }),
-  cancelButton: byRole('button', { name: 'cancel' }),
-  confirmDelete: byRole('button', { name: 'delete' }),
-  checkConfigurationButton: (key: string) =>
-    byRole('button', { name: `settings.almintegration.check_configuration_x.${key}` }),
-  validationErrorMessage: byRole('alert'),
-  validationSuccessMessage: byRole('status'),
-};
-
 describe('github tab', () => {
   it('can create/edit/delete new configuration', async () => {
+    const { ui } = getPageObjects();
     const { rerender } = renderAlmIntegration();
     expect(await ui.almHeading.find()).toBeInTheDocument();
     expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
 
     // Create new configuration
-    await createConfiguration('Name', {
+    await ui.createConfiguration('Name', {
       'name.github': 'Name',
       'url.github': 'https://api.github.com',
       app_id: 'Github App ID',
@@ -83,14 +57,14 @@ describe('github tab', () => {
       private_key: 'Key',
     });
 
-    await editConfiguration('New Name', 'Name', 'client_secret.github', AlmKeys.GitHub);
+    await ui.editConfiguration('New Name', 'Name', 'client_secret.github', AlmKeys.GitHub);
 
-    await checkConfiguration('New Name');
+    await ui.checkConfiguration('New Name');
 
     rerender(<AlmIntegration />);
     expect(await screen.findByRole('heading', { name: 'New Name' })).toBeInTheDocument();
 
-    await deleteConfiguration('New Name');
+    await ui.deleteConfiguration('New Name');
     expect(ui.emptyIntro(AlmKeys.GitHub).get()).toBeInTheDocument();
   });
 });
@@ -99,6 +73,8 @@ describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
   '%s tab',
   (almKey: AlmKeys.Azure | AlmKeys.GitLab) => {
     it('can create/edit/delete new configuration', async () => {
+      const { ui } = getPageObjects();
+
       renderAlmIntegration();
       expect(await ui.almHeading.find()).toBeInTheDocument();
 
@@ -106,7 +82,7 @@ describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
       expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
 
       // Create new configuration
-      await createConfiguration('Name', {
+      await ui.createConfiguration('Name', {
         [`name.${almKey}`]: 'Name',
         [`url.${almKey}`]: 'https://api.alm.com',
         personal_access_token: 'Access Token',
@@ -115,11 +91,11 @@ describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
       // Cannot create another configuration without Multiple Alm feature
       expect(ui.createConfigurationButton.get()).toBeDisabled();
 
-      await editConfiguration('New Name', 'Name', 'personal_access_token', almKey);
+      await ui.editConfiguration('New Name', 'Name', 'personal_access_token', almKey);
 
-      await checkConfiguration('New Name');
+      await ui.checkConfiguration('New Name');
 
-      await deleteConfiguration('New Name');
+      await ui.deleteConfiguration('New Name');
       expect(ui.emptyIntro(almKey).get()).toBeInTheDocument();
     });
   }
@@ -127,6 +103,7 @@ describe.each([AlmKeys.GitLab, AlmKeys.Azure])(
 
 describe('bitbucket tab', () => {
   it('can create/edit/delete new configuration', async () => {
+    const { ui } = getPageObjects();
     renderAlmIntegration([Feature.MultipleAlm]);
     expect(await ui.almHeading.find()).toBeInTheDocument();
 
@@ -134,7 +111,7 @@ describe('bitbucket tab', () => {
     expect(ui.emptyIntro(AlmKeys.BitbucketServer).get()).toBeInTheDocument();
 
     // Create new Bitbucket Server configuration
-    await createConfiguration(
+    await ui.createConfiguration(
       'Name',
       {
         'name.bitbucket': 'Name',
@@ -145,7 +122,7 @@ describe('bitbucket tab', () => {
     );
 
     // Create new Bitbucket Cloud configuration
-    await createConfiguration(
+    await ui.createConfiguration(
       'Name Cloud',
       {
         'name.bitbucket': 'Name Cloud',
@@ -157,77 +134,124 @@ describe('bitbucket tab', () => {
     );
 
     // Edit, check delete Bitbucket Server configuration
-    await editConfiguration('New Name', 'Name', 'personal_access_token', AlmKeys.BitbucketServer);
+    await ui.editConfiguration(
+      'New Name',
+      'Name',
+      'personal_access_token',
+      AlmKeys.BitbucketServer
+    );
 
-    await checkConfiguration('New Name');
+    await ui.checkConfiguration('New Name');
 
-    await deleteConfiguration('New Name');
+    await ui.deleteConfiguration('New Name');
 
     // Cloud configuration still exists
     expect(screen.getByRole('heading', { name: 'Name Cloud' })).toBeInTheDocument();
   });
 });
 
-async function createConfiguration(
-  name: string,
-  params: { [key: string]: string },
-  almKey?: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer
-) {
-  await userEvent.click(ui.createConfigurationButton.get());
-  expect(ui.saveConfigurationButton.get()).toBeDisabled();
-
-  if (almKey) {
-    await userEvent.click(ui.bitbucketConfiguration(almKey).get());
+function getPageObjects() {
+  const user = userEvent.setup();
+
+  const ui = {
+    almHeading: byRole('heading', { name: 'settings.almintegration.title' }),
+    emptyIntro: (almKey: AlmKeys) => byText(`settings.almintegration.empty.${almKey}`),
+    createConfigurationButton: byRole('button', { name: 'settings.almintegration.create' }),
+    tab: (almKey: AlmKeys) =>
+      byRole('tab', { name: `${almKey} settings.almintegration.tab.${almKey}` }),
+    bitbucketConfiguration: (almKey: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer) =>
+      byRole('button', { name: `alm.${almKey}.long` }),
+    configurationInput: (id: string) =>
+      byRole('textbox', { name: `settings.almintegration.form.${id}` }),
+    updateSecretValueButton: (key: string) =>
+      byRole('button', {
+        name: `settings.almintegration.form.secret.update_field_x.settings.almintegration.form.${key}`,
+      }),
+    saveConfigurationButton: byRole('button', { name: 'settings.almintegration.form.save' }),
+    editConfigurationButton: (key: string) =>
+      byRole('button', { name: `settings.almintegration.edit_configuration.${key}` }),
+    deleteConfigurationButton: (key: string) =>
+      byRole('button', { name: `settings.almintegration.delete_configuration.${key}` }),
+    cancelButton: byRole('button', { name: 'cancel' }),
+    confirmDelete: byRole('button', { name: 'delete' }),
+    checkConfigurationButton: (key: string) =>
+      byRole('button', { name: `settings.almintegration.check_configuration_x.${key}` }),
+    validationErrorMessage: byRole('alert'),
+    validationSuccessMessage: byRole('status'),
+  };
+
+  async function createConfiguration(
+    name: string,
+    params: { [key: string]: string },
+    almKey?: AlmKeys.BitbucketCloud | AlmKeys.BitbucketServer
+  ) {
+    await userEvent.click(ui.createConfigurationButton.get());
+    expect(ui.saveConfigurationButton.get()).toBeDisabled();
+
+    if (almKey) {
+      await userEvent.click(ui.bitbucketConfiguration(almKey).get());
+    }
+
+    for (const [key, value] of Object.entries(params)) {
+      // eslint-disable-next-line no-await-in-loop
+      await userEvent.type(ui.configurationInput(key).get(), value);
+    }
+    expect(ui.saveConfigurationButton.get()).toBeEnabled();
+    await userEvent.click(ui.saveConfigurationButton.get());
+
+    // New configuration is created
+    expect(screen.getByRole('heading', { name })).toBeInTheDocument();
   }
 
-  for (const [key, value] of Object.entries(params)) {
-    // eslint-disable-next-line no-await-in-loop
-    await userEvent.type(ui.configurationInput(key).get(), value);
+  async function editConfiguration(
+    newName: string,
+    currentName: string,
+    secretId: string,
+    almKey: AlmKeys
+  ) {
+    almSettings.setDefinitionErrorMessage('Something is wrong');
+    await userEvent.click(ui.editConfigurationButton(currentName).get());
+    expect(ui.configurationInput(secretId).query()).not.toBeInTheDocument();
+    await userEvent.click(ui.updateSecretValueButton(secretId).get());
+    await userEvent.type(ui.configurationInput(secretId).get(), 'New Secret Value');
+    await userEvent.clear(ui.configurationInput(`name.${almKey}`).get());
+    await userEvent.type(ui.configurationInput(`name.${almKey}`).get(), newName);
+    await userEvent.click(ui.saveConfigurationButton.get());
+
+    // Existing configuration is edited
+    expect(screen.queryByRole('heading', { name: currentName })).not.toBeInTheDocument();
+    expect(screen.getByRole('heading', { name: newName })).toBeInTheDocument();
+    expect(ui.validationErrorMessage.get()).toHaveTextContent('Something is wrong');
   }
-  expect(ui.saveConfigurationButton.get()).toBeEnabled();
-  await userEvent.click(ui.saveConfigurationButton.get());
-
-  // New configuration is created
-  expect(screen.getByRole('heading', { name })).toBeInTheDocument();
-}
 
-async function editConfiguration(
-  newName: string,
-  currentName: string,
-  secretId: string,
-  almKey: AlmKeys
-) {
-  almSettings.setDefinitionErrorMessage('Something is wrong');
-  await userEvent.click(ui.editConfigurationButton(currentName).get());
-  expect(ui.configurationInput(secretId).query()).not.toBeInTheDocument();
-  await userEvent.click(ui.updateSecretValueButton(secretId).get());
-  await userEvent.type(ui.configurationInput(secretId).get(), 'New Secret Value');
-  await userEvent.clear(ui.configurationInput(`name.${almKey}`).get());
-  await userEvent.type(ui.configurationInput(`name.${almKey}`).get(), newName);
-  await userEvent.click(ui.saveConfigurationButton.get());
-
-  // Existing configuration is edited
-  expect(screen.queryByRole('heading', { name: currentName })).not.toBeInTheDocument();
-  expect(screen.getByRole('heading', { name: newName })).toBeInTheDocument();
-  expect(ui.validationErrorMessage.get()).toHaveTextContent('Something is wrong');
-}
+  async function checkConfiguration(name: string) {
+    almSettings.setDefinitionErrorMessage('');
+    await userEvent.click(ui.checkConfigurationButton(name).get());
+    expect(ui.validationSuccessMessage.getAll()[0]).toHaveTextContent(
+      'alert.tooltip.successsettings.almintegration.configuration_valid'
+    );
+  }
 
-async function checkConfiguration(name: string) {
-  almSettings.setDefinitionErrorMessage('');
-  await userEvent.click(ui.checkConfigurationButton(name).get());
-  expect(ui.validationSuccessMessage.getAll()[0]).toHaveTextContent(
-    'alert.tooltip.successsettings.almintegration.configuration_valid'
-  );
-}
+  async function deleteConfiguration(name: string) {
+    await userEvent.click(ui.deleteConfigurationButton(name).get());
+    await userEvent.click(ui.cancelButton.get());
+    expect(screen.getByRole('heading', { name })).toBeInTheDocument();
 
-async function deleteConfiguration(name: string) {
-  await userEvent.click(ui.deleteConfigurationButton(name).get());
-  await userEvent.click(ui.cancelButton.get());
-  expect(screen.getByRole('heading', { name })).toBeInTheDocument();
+    await userEvent.click(ui.deleteConfigurationButton(name).get());
+    await userEvent.click(ui.confirmDelete.get());
+    expect(screen.queryByRole('heading', { name })).not.toBeInTheDocument();
+  }
 
-  await userEvent.click(ui.deleteConfigurationButton(name).get());
-  await userEvent.click(ui.confirmDelete.get());
-  expect(screen.queryByRole('heading', { name })).not.toBeInTheDocument();
+  return {
+    ui: {
+      ...ui,
+      createConfiguration,
+      editConfiguration,
+      deleteConfiguration,
+      checkConfiguration,
+    },
+    user,
+  };
 }
 
 function renderAlmIntegration(features: Feature[] = []) {
index 9a833ae0e0102b464023953421f4adc03c63dcfc..0c02ad50e6bdce0bc59abb70ff4f92c9f96fd718 100644 (file)
@@ -73,7 +73,7 @@ function renderFieldWrapper(
   );
 }
 
-function renderHelp({ help, helpExample, helpParams, id }: CommonFieldProps) {
+function renderHelp({ help, helpExample, helpParams = {}, id }: CommonFieldProps) {
   return (
     help && (
       <>
@@ -113,7 +113,7 @@ function renderBooleanField(
     renderLabel({ ...props, optional: true }),
     <div className="display-flex-center big-spacer-top">
       <div className="display-inline-block text-top">
-        <Toggle name={id} onChange={(v) => onFieldChange(propKey, v)} value={value} />
+        <Toggle id={id} name={id} onChange={(v) => onFieldChange(propKey, v)} value={value} />
         {value == null && <span className="spacer-left note">{translate('settings.not_set')}</span>}
       </div>
       {inputExtra}
index cdf09eecf4c275287493fe11c2adca1b079d3b3e..aaf2ec9d9838d4fe12b8e060ef242ee9c0454b1a 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { cloneDeep } from 'lodash';
 import * as React from 'react';
 import {
   deleteProjectAlmBinding,
@@ -75,10 +76,12 @@ const REQUIRED_FIELDS_BY_ALM: {
   [AlmKeys.GitLab]: ['repository'],
 };
 
+const INITIAL_FORM_DATA = { key: '', repository: '', monorepo: false };
+
 export class PRDecorationBinding extends React.PureComponent<Props, State> {
   mounted = false;
   state: State = {
-    formData: { key: '', monorepo: false },
+    formData: cloneDeep(INITIAL_FORM_DATA),
     instances: [],
     isChanged: false,
     isConfigured: false,
@@ -169,23 +172,14 @@ export class PRDecorationBinding extends React.PureComponent<Props, State> {
   submitProjectAlmBinding(
     alm: AlmKeys,
     key: string,
-    almSpecificFields?: Omit<FormData, 'key'>
+    almSpecificFields: Omit<FormData, 'key'>
   ): Promise<void> {
     const almSetting = key;
+    const { repository, slug = '', monorepo = false } = almSpecificFields;
     const project = this.props.component.key;
-    const repository = almSpecificFields?.repository;
-    const slug = almSpecificFields?.slug;
-    const monorepo = almSpecificFields?.monorepo ?? false;
-
-    if (!repository) {
-      return Promise.reject();
-    }
 
     switch (alm) {
       case AlmKeys.Azure: {
-        if (!slug) {
-          return Promise.reject();
-        }
         return setProjectAzureBinding({
           almSetting,
           project,
@@ -195,9 +189,6 @@ export class PRDecorationBinding extends React.PureComponent<Props, State> {
         });
       }
       case AlmKeys.BitbucketServer: {
-        if (!slug) {
-          return Promise.reject();
-        }
         return setProjectBitbucketBinding({
           almSetting,
           project,
@@ -314,7 +305,7 @@ export class PRDecorationBinding extends React.PureComponent<Props, State> {
       return {
         formData: newFormData,
         isValid: this.validateForm(newFormData),
-        isChanged: !this.isDataSame(newFormData, originalData || { key: '', monorepo: false }),
+        isChanged: !this.isDataSame(newFormData, originalData || cloneDeep(INITIAL_FORM_DATA)),
         successfullyUpdated: false,
       };
     });
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
deleted file mode 100644 (file)
index ef972b6..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 { mockAlmSettingsInstance } from '../../../../../helpers/mocks/alm-settings';
-import { AlmKeys, AlmSettingsInstance } from '../../../../../types/alm-settings';
-import { AlmSpecificForm, AlmSpecificFormProps } from '../AlmSpecificForm';
-
-it.each([
-  [AlmKeys.Azure],
-  [AlmKeys.BitbucketServer],
-  [AlmKeys.BitbucketCloud],
-  [AlmKeys.GitHub],
-  [AlmKeys.GitLab],
-])('should render correctly for %s', (alm) => {
-  expect(shallowRender(alm)).toMatchSnapshot();
-});
-
-it.each([
-  [
-    AlmKeys.BitbucketServer,
-    [mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer, url: 'http://bbs.example.com' })],
-  ],
-  [AlmKeys.GitHub, [mockAlmSettingsInstance({ url: 'http://example.com/api/v3' })]],
-  [AlmKeys.GitHub, [mockAlmSettingsInstance({ url: 'http://api.github.com' })]],
-])(
-  'should render correctly for %s if an instance URL is provided',
-  (alm: AlmKeys, instances: AlmSettingsInstance[]) => {
-    expect(shallowRender(alm, { instances })).toMatchSnapshot();
-  }
-);
-
-it('should render the monorepo field when the feature is supported', () => {
-  expect(shallowRender(AlmKeys.Azure, { hasFeature: jest.fn(() => true) })).toMatchSnapshot();
-});
-
-function shallowRender(alm: AlmKeys, props: Partial<AlmSpecificFormProps> = {}) {
-  return shallow(
-    <AlmSpecificForm
-      alm={alm}
-      instances={[]}
-      formData={{
-        key: '',
-        repository: '',
-        slug: '',
-        monorepo: false,
-      }}
-      onFieldChange={jest.fn()}
-      hasFeature={jest.fn()}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-it.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-it.tsx
new file mode 100644 (file)
index 0000000..6120dab
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 userEvent from '@testing-library/user-event';
+import React from 'react';
+import selectEvent from 'react-select-event';
+import { byRole } from 'testing-library-selector';
+import AlmSettingsServiceMock from '../../../../../api/mocks/AlmSettingsServiceMock';
+import CurrentUserContextProvider from '../../../../../app/components/current-user/CurrentUserContextProvider';
+import { mockComponent } from '../../../../../helpers/mocks/component';
+import { mockCurrentUser } from '../../../../../helpers/testMocks';
+import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
+import {
+  AlmKeys,
+  ProjectAlmBindingConfigurationErrorScope,
+} from '../../../../../types/alm-settings';
+import { Component } from '../../../../../types/types';
+import { CurrentUser } from '../../../../../types/users';
+import PRDecorationBinding from '../PRDecorationBinding';
+
+jest.mock('../../../../../api/alm-settings');
+
+let almSettings: AlmSettingsServiceMock;
+
+beforeAll(() => {
+  almSettings = new AlmSettingsServiceMock();
+});
+
+afterEach(() => {
+  almSettings.reset();
+});
+
+const inputsList = {
+  [AlmKeys.GitLab]: { 'gitlab.repository': 'Repository', monorepo: false },
+  [AlmKeys.GitHub]: {
+    'github.repository': 'Repository',
+    'github.summary_comment_setting': false,
+    monorepo: false,
+  },
+  [AlmKeys.Azure]: {
+    'azure.repository': 'Repository',
+    'azure.project': 'Project',
+    monorepo: false,
+  },
+  [AlmKeys.BitbucketCloud]: { 'bitbucketcloud.repository': 'Repository', monorepo: false },
+  [AlmKeys.BitbucketServer]: {
+    'bitbucket.repository': 'Repository',
+    'bitbucket.slug': 'Slug',
+    monorepo: false,
+  },
+};
+
+it.each([
+  {
+    key: 'conf-final-1',
+    alm: AlmKeys.GitLab,
+  },
+  {
+    key: 'conf-github-1',
+    alm: AlmKeys.GitHub,
+  },
+  {
+    key: 'conf-azure-1',
+    alm: AlmKeys.Azure,
+  },
+  {
+    key: 'conf-bitbucketcloud-1',
+    alm: AlmKeys.BitbucketCloud,
+  },
+  {
+    key: 'conf-bitbucketserver-1',
+    alm: AlmKeys.BitbucketServer,
+  },
+])(
+  'should get, set, delete and validate binding for $alm',
+  async ({ key, alm }: { key: string; alm: AlmKeys }) => {
+    const { ui, user } = getPageObjects();
+    almSettings.setProjectBindingConfigurationErrors({
+      scope: ProjectAlmBindingConfigurationErrorScope.Global,
+      errors: [{ msg: 'cute error' }],
+    });
+    const { rerender } = renderPRDecorationBinding();
+    expect(await ui.mainTitle.find()).toBeInTheDocument();
+
+    // Set form data
+    await selectEvent.select(ui.input('name', 'combobox').get(), (content) =>
+      content.includes(key)
+    );
+
+    const list = inputsList[alm];
+    for (const [inputId, value] of Object.entries(list)) {
+      // eslint-disable-next-line no-await-in-loop
+      await ui.setInput(inputId, value);
+    }
+    // Save form and check for errors
+    await user.click(ui.saveButton.get());
+    expect(ui.validationErrorMsg.get()).toHaveTextContent('cute error');
+
+    // Check validation with errors
+    await user.click(ui.validateButton.get());
+    expect(ui.validationErrorMsg.get()).toHaveTextContent('cute error');
+
+    // Save form and check for errors
+    almSettings.setProjectBindingConfigurationErrors(undefined);
+    await ui.setInput(
+      Object.keys(list).find((key) => key.endsWith('.repository')) as string,
+      'Anything'
+    );
+    await user.click(ui.saveButton.get());
+    expect(await ui.validationSuccessMsg.find()).toHaveTextContent(
+      'settings.pr_decoration.binding.check_configuration.success'
+    );
+
+    await user.click(ui.validateButton.get());
+    expect(ui.validationSuccessMsg.get()).toHaveTextContent(
+      'settings.pr_decoration.binding.check_configuration.success'
+    );
+
+    // Rerender and verify that validation is done for binding
+    rerender(
+      <MockedPRDecorationBinding component={mockComponent()} currentUser={mockCurrentUser()} />
+    );
+    expect(await ui.validationSuccessMsg.find()).toHaveTextContent(
+      'settings.pr_decoration.binding.check_configuration.success'
+    );
+    expect(ui.saveButton.query()).not.toBeInTheDocument();
+
+    // Reset binding
+    await user.click(ui.resetButton.get());
+    expect(ui.input('', 'textbox').query()).not.toBeInTheDocument();
+    expect(ui.input('', 'switch').query()).not.toBeInTheDocument();
+  }
+);
+
+function getPageObjects() {
+  const user = userEvent.setup();
+
+  async function setInput(inputId: string, value: string | boolean) {
+    if (typeof value === 'boolean') {
+      if (value) {
+        await user.click(ui.input(inputId, 'switch').get());
+      }
+    } else {
+      const input = ui.input(inputId, 'textbox').get();
+      await user.clear(input);
+      await user.type(input, value);
+    }
+  }
+
+  const ui = {
+    mainTitle: byRole('heading', { name: 'settings.pr_decoration.binding.title' }),
+    input: (id: string, role: 'combobox' | 'switch' | 'textbox') =>
+      byRole(role, { name: new RegExp(`settings.pr_decoration.binding.form.${id}`) }),
+    saveButton: byRole('button', { name: 'save' }),
+    resetButton: byRole('button', { name: 'reset_verb' }),
+    validateButton: byRole('button', {
+      name: 'settings.pr_decoration.binding.check_configuration',
+    }),
+    validationErrorMsg: byRole('alert'),
+    validationSuccessMsg: byRole('status'),
+    setInput,
+  };
+
+  return {
+    ui,
+    user,
+  };
+}
+
+function MockedPRDecorationBinding({
+  component,
+  currentUser,
+}: {
+  component: Component;
+  currentUser: CurrentUser;
+}) {
+  return (
+    <CurrentUserContextProvider currentUser={currentUser}>
+      <PRDecorationBinding component={component} />
+    </CurrentUserContextProvider>
+  );
+}
+
+function renderPRDecorationBinding(
+  component: Component = mockComponent(),
+  currentUser: CurrentUser = mockCurrentUser()
+) {
+  return renderComponent(
+    <MockedPRDecorationBinding component={component} currentUser={currentUser} />
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx
deleted file mode 100644 (file)
index ec17a27..0000000
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 {
-  deleteProjectAlmBinding,
-  getAlmSettings,
-  getProjectAlmBinding,
-  setProjectAzureBinding,
-  setProjectBitbucketBinding,
-  setProjectBitbucketCloudBinding,
-  setProjectGithubBinding,
-  setProjectGitlabBinding,
-  validateProjectAlmBinding,
-} from '../../../../../api/alm-settings';
-import {
-  mockAlmSettingsInstance,
-  mockProjectAlmBindingConfigurationErrors,
-  mockProjectAlmBindingResponse,
-} from '../../../../../helpers/mocks/alm-settings';
-import { mockComponent } from '../../../../../helpers/mocks/component';
-import { mockCurrentUser } from '../../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../../helpers/testUtils';
-import { AlmKeys, AlmSettingsInstance } from '../../../../../types/alm-settings';
-import { PRDecorationBinding } from '../PRDecorationBinding';
-import PRDecorationBindingRenderer from '../PRDecorationBindingRenderer';
-
-jest.mock('../../../../../api/alm-settings', () => ({
-  getAlmSettings: jest.fn().mockResolvedValue([]),
-  getProjectAlmBinding: jest.fn().mockResolvedValue(undefined),
-  setProjectAzureBinding: jest.fn().mockResolvedValue(undefined),
-  setProjectBitbucketBinding: jest.fn().mockResolvedValue(undefined),
-  setProjectGithubBinding: jest.fn().mockResolvedValue(undefined),
-  setProjectGitlabBinding: jest.fn().mockResolvedValue(undefined),
-  setProjectBitbucketCloudBinding: jest.fn().mockResolvedValue(undefined),
-  deleteProjectAlmBinding: jest.fn().mockResolvedValue(undefined),
-  validateProjectAlmBinding: jest.fn().mockResolvedValue(undefined),
-}));
-
-const PROJECT_KEY = 'project-key';
-
-beforeEach(() => {
-  jest.clearAllMocks();
-});
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should fill selects and fill formdata', async () => {
-  const url = 'github.com';
-  const instances = [{ key: 'instance1', url, alm: AlmKeys.GitHub }];
-  const formdata = {
-    key: 'instance1',
-    repository: 'account/repo',
-  };
-  (getAlmSettings as jest.Mock).mockResolvedValueOnce(instances);
-  (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce(formdata);
-
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-
-  expect(wrapper.state().loading).toBe(false);
-  expect(wrapper.state().formData).toEqual(formdata);
-  expect(wrapper.state().isChanged).toBe(false);
-});
-
-it('should handle reset', async () => {
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-  wrapper.setState({
-    formData: {
-      key: 'whatever',
-      repository: 'something/else',
-      monorepo: false,
-    },
-  });
-
-  wrapper.instance().handleReset();
-  await waitAndUpdate(wrapper);
-
-  expect(deleteProjectAlmBinding).toHaveBeenCalledWith(PROJECT_KEY);
-  expect(wrapper.state().formData).toEqual({ key: '', repository: '', slug: '', monorepo: false });
-  expect(wrapper.state().isChanged).toBe(false);
-});
-
-describe('handleSubmit', () => {
-  const instances: AlmSettingsInstance[] = [
-    { key: 'github', alm: AlmKeys.GitHub },
-    { key: 'azure', alm: AlmKeys.Azure },
-    { key: 'bitbucket', alm: AlmKeys.BitbucketServer },
-    { key: 'gitlab', alm: AlmKeys.GitLab },
-    { key: 'bitbucketcloud', alm: AlmKeys.BitbucketCloud },
-  ];
-
-  it('should work for github', async () => {
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-    const githubKey = 'github';
-    const repository = 'repo/path';
-    const summaryCommentEnabled = true;
-    const monorepo = true;
-    wrapper.setState({
-      formData: { key: githubKey, repository, summaryCommentEnabled, monorepo },
-      instances,
-    });
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-
-    expect(setProjectGithubBinding).toHaveBeenCalledWith({
-      almSetting: githubKey,
-      project: PROJECT_KEY,
-      repository,
-      summaryCommentEnabled,
-      monorepo,
-    });
-    expect(wrapper.state().successfullyUpdated).toBe(true);
-  });
-
-  it('should work for azure', async () => {
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-    const azureKey = 'azure';
-    const repository = 'az-rep';
-    const slug = 'az-project';
-    const monorepo = true;
-    wrapper.setState({
-      formData: { key: azureKey, repository, slug, monorepo },
-      instances,
-    });
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-
-    expect(setProjectAzureBinding).toHaveBeenCalledWith({
-      almSetting: azureKey,
-      project: PROJECT_KEY,
-      projectName: slug,
-      repositoryName: repository,
-      monorepo,
-    });
-    expect(wrapper.state().successfullyUpdated).toBe(true);
-  });
-
-  it('should work for bitbucket', async () => {
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-    const bitbucketKey = 'bitbucket';
-    const repository = 'repoKey';
-    const slug = 'repoSlug';
-    const monorepo = true;
-    wrapper.setState({ formData: { key: bitbucketKey, repository, slug, monorepo }, instances });
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-
-    expect(setProjectBitbucketBinding).toHaveBeenCalledWith({
-      almSetting: bitbucketKey,
-      project: PROJECT_KEY,
-      repository,
-      slug,
-      monorepo,
-    });
-    expect(wrapper.state().successfullyUpdated).toBe(true);
-  });
-
-  it('should work for gitlab', async () => {
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-    const gitlabKey = 'gitlab';
-    const repository = 'repo';
-    const monorepo = true;
-    wrapper.setState({
-      formData: { key: gitlabKey, repository, monorepo },
-      instances,
-    });
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-
-    expect(setProjectGitlabBinding).toHaveBeenCalledWith({
-      almSetting: gitlabKey,
-      project: PROJECT_KEY,
-      repository,
-      monorepo,
-    });
-    expect(wrapper.state().successfullyUpdated).toBe(true);
-  });
-
-  it('should work for bitbucket cloud', async () => {
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-    const bitbucketKey = 'bitbucketcloud';
-    const repository = 'repoKey';
-    const monorepo = true;
-    wrapper.setState({ formData: { key: bitbucketKey, repository, monorepo }, instances: [] });
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-    expect(setProjectBitbucketCloudBinding).not.toHaveBeenCalled();
-
-    wrapper.setState({ formData: { key: bitbucketKey, repository, monorepo }, instances });
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-
-    expect(setProjectBitbucketCloudBinding).toHaveBeenCalledWith({
-      almSetting: bitbucketKey,
-      project: PROJECT_KEY,
-      repository,
-      monorepo,
-    });
-    expect(wrapper.state().successfullyUpdated).toBe(true);
-  });
-});
-
-describe.each([[500], [404]])('For status %i', (status) => {
-  it('should handle failures gracefully', async () => {
-    const newFormData = {
-      key: 'whatever',
-      repository: 'something/else',
-      monorepo: false,
-    };
-
-    (getProjectAlmBinding as jest.Mock).mockRejectedValueOnce({ status });
-    (setProjectGithubBinding as jest.Mock).mockRejectedValueOnce({ status });
-    (deleteProjectAlmBinding as jest.Mock).mockRejectedValueOnce({ status });
-
-    const wrapper = shallowRender();
-    await waitAndUpdate(wrapper);
-    wrapper.setState({
-      formData: newFormData,
-      originalData: undefined,
-    });
-
-    wrapper.instance().handleSubmit();
-    await waitAndUpdate(wrapper);
-    expect(wrapper.instance().state.originalData).toBeUndefined();
-    wrapper.instance().handleReset();
-    await waitAndUpdate(wrapper);
-    expect(wrapper.instance().state.formData).toEqual(newFormData);
-  });
-});
-
-it('should handle field changes', async () => {
-  const url = 'git.enterprise.com';
-  const repository = 'my/repo';
-  const instances: AlmSettingsInstance[] = [
-    { key: 'instance1', url, alm: AlmKeys.GitHub },
-    { key: 'instance2', url, alm: AlmKeys.GitHub },
-    { key: 'instance3', url: 'otherurl', alm: AlmKeys.GitHub },
-  ];
-  (getAlmSettings as jest.Mock).mockResolvedValueOnce(instances);
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-
-  wrapper.instance().handleFieldChange('key', 'instance2');
-  await waitAndUpdate(wrapper);
-  expect(wrapper.state().formData).toEqual({
-    key: 'instance2',
-    monorepo: false,
-  });
-
-  wrapper.instance().handleFieldChange('repository', repository);
-  await waitAndUpdate(wrapper);
-  expect(wrapper.state().formData).toEqual({
-    key: 'instance2',
-    repository,
-    monorepo: false,
-  });
-
-  wrapper.instance().handleFieldChange('summaryCommentEnabled', true);
-  await waitAndUpdate(wrapper);
-  expect(wrapper.state().formData).toEqual({
-    key: 'instance2',
-    monorepo: false,
-    repository,
-    summaryCommentEnabled: true,
-  });
-
-  wrapper.instance().handleFieldChange('monorepo', true);
-  await waitAndUpdate(wrapper);
-  expect(wrapper.state().formData).toEqual({
-    key: 'instance2',
-    repository,
-    summaryCommentEnabled: true,
-    monorepo: true,
-  });
-});
-
-it.each([
-  [AlmKeys.Azure, { monorepo: false }],
-  [AlmKeys.Azure, { slug: 'test', monorepo: false }],
-  [AlmKeys.Azure, { repository: 'test', monorepo: false }],
-  [AlmKeys.BitbucketServer, { monorepo: false }],
-  [AlmKeys.BitbucketServer, { slug: 'test', monorepo: false }],
-  [AlmKeys.BitbucketServer, { repository: 'test', monorepo: false }],
-  [AlmKeys.BitbucketCloud, { monorepo: false }],
-  [AlmKeys.GitHub, { monorepo: false }],
-  [AlmKeys.GitLab, { monorepo: false }],
-])(
-  'should properly reject promise for %s & %s',
-  async (almKey: AlmKeys, params: { monorepo: boolean }) => {
-    const wrapper = shallowRender();
-
-    expect.assertions(1);
-    await expect(
-      wrapper.instance().submitProjectAlmBinding(almKey, 'binding', params)
-    ).rejects.toBeUndefined();
-  }
-);
-
-it('should validate form', async () => {
-  const wrapper = shallowRender();
-  await waitAndUpdate(wrapper);
-
-  const validateMethod = wrapper.instance().validateForm;
-
-  expect(validateMethod({ key: '', repository: '', monorepo: false })).toBe(false);
-  expect(validateMethod({ key: '', repository: 'c', monorepo: false })).toBe(false);
-
-  wrapper.setState({
-    instances: [
-      { key: 'azure', alm: AlmKeys.Azure },
-      { key: 'bitbucket', alm: AlmKeys.BitbucketServer },
-      { key: 'bitbucketcloud', alm: AlmKeys.BitbucketCloud },
-      { key: 'github', alm: AlmKeys.GitHub },
-      { key: 'gitlab', alm: AlmKeys.GitLab },
-    ],
-  });
-
-  [
-    { values: { key: 'azure', monorepo: false, repository: 'rep' }, result: false },
-    { values: { key: 'azure', monorepo: false, slug: 'project' }, result: false },
-    {
-      values: { key: 'azure', monorepo: false, repository: 'repo', slug: 'project' },
-      result: true,
-    },
-    { values: { key: 'github', monorepo: false, repository: '' }, result: false },
-    { values: { key: 'github', monorepo: false, repository: 'asdf' }, result: true },
-    { values: { key: 'bitbucket', monorepo: false, repository: 'key' }, result: false },
-    {
-      values: { key: 'bitbucket', monorepo: false, repository: 'key', slug: 'slug' },
-      result: true,
-    },
-    { values: { key: 'bitbucketcloud', monorepo: false, repository: '' }, result: false },
-    { values: { key: 'bitbucketcloud', monorepo: false, repository: 'key' }, result: true },
-    { values: { key: 'gitlab', monorepo: false }, result: false },
-    { values: { key: 'gitlab', monorepo: false, repository: 'key' }, result: true },
-  ].forEach(({ values, result }) => {
-    expect(validateMethod(values)).toBe(result);
-  });
-});
-
-it('should call the validation WS and store errors', async () => {
-  (getAlmSettings as jest.Mock).mockResolvedValueOnce(mockAlmSettingsInstance());
-  (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce(
-    mockProjectAlmBindingResponse({ key: 'key' })
-  );
-
-  const errors = mockProjectAlmBindingConfigurationErrors();
-  (validateProjectAlmBinding as jest.Mock).mockRejectedValueOnce(errors);
-
-  const wrapper = shallowRender();
-
-  wrapper.find(PRDecorationBindingRenderer).props().onCheckConfiguration();
-
-  await waitAndUpdate(wrapper);
-
-  expect(validateProjectAlmBinding).toHaveBeenCalledWith(PROJECT_KEY);
-  expect(wrapper.state().configurationErrors).toBe(errors);
-});
-
-it('should call the validation WS after loading', async () => {
-  (getAlmSettings as jest.Mock).mockResolvedValueOnce([mockAlmSettingsInstance()]);
-  (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce(
-    mockProjectAlmBindingResponse({ key: 'key ' })
-  );
-
-  const wrapper = shallowRender();
-
-  await waitAndUpdate(wrapper);
-
-  expect(validateProjectAlmBinding).toHaveBeenCalled();
-});
-
-it('should call the validation WS upon saving', async () => {
-  (getAlmSettings as jest.Mock).mockResolvedValueOnce([mockAlmSettingsInstance()]);
-  (getProjectAlmBinding as jest.Mock).mockResolvedValueOnce(
-    mockProjectAlmBindingResponse({ key: 'key ' })
-  );
-
-  const wrapper = shallowRender();
-
-  wrapper.instance().handleFieldChange('key', 'key');
-  wrapper.instance().handleSubmit();
-
-  await waitAndUpdate(wrapper);
-
-  expect(validateProjectAlmBinding).toHaveBeenCalled();
-});
-
-function shallowRender(props: Partial<PRDecorationBinding['props']> = {}) {
-  return shallow<PRDecorationBinding>(
-    <PRDecorationBinding
-      currentUser={mockCurrentUser()}
-      component={mockComponent({ key: PROJECT_KEY })}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx
deleted file mode 100644 (file)
index 99acf51..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 {
-  AlmKeys,
-  AlmSettingsInstance,
-  ProjectAlmBindingConfigurationErrors,
-  ProjectAlmBindingConfigurationErrorScope,
-} from '../../../../../types/alm-settings';
-import PRDecorationBindingRenderer, {
-  PRDecorationBindingRendererProps,
-} from '../PRDecorationBindingRenderer';
-
-const urls = ['http://github.enterprise.com', 'http://bbs.enterprise.com'];
-const instances: AlmSettingsInstance[] = [
-  {
-    alm: AlmKeys.GitHub,
-    key: 'i1',
-    url: urls[0],
-  },
-  {
-    alm: AlmKeys.GitHub,
-    key: 'i2',
-    url: urls[0],
-  },
-  {
-    alm: AlmKeys.BitbucketServer,
-    key: 'i3',
-    url: urls[1],
-  },
-  {
-    alm: AlmKeys.Azure,
-    key: 'i4',
-  },
-];
-const configurationErrors: ProjectAlmBindingConfigurationErrors = {
-  scope: ProjectAlmBindingConfigurationErrorScope.Global,
-  errors: [{ msg: 'Test' }, { msg: 'tesT' }],
-};
-
-it.each([
-  ['when loading', { loading: true }],
-  ['with no ALM instances (admin user)', { isSysAdmin: true }],
-  ['with no ALM instances (non-admin user)', {}],
-  ['with a single ALM instance', { instances: [instances[0]] }],
-  ['with an empty form', { instances }],
-  [
-    'with a valid and saved form',
-    {
-      formData: {
-        key: 'i1',
-        repository: 'account/repo',
-        monorepo: false,
-      },
-      isChanged: false,
-      isConfigured: true,
-      instances,
-    },
-  ],
-  [
-    'when there are configuration errors (non-admin user)',
-    { instances, isConfigured: true, configurationErrors },
-  ],
-  [
-    'when there are configuration errors (admin user)',
-    {
-      formData: {
-        key: 'i1',
-        repository: 'account/repo',
-        monorepo: false,
-      },
-      instances,
-      isConfigured: true,
-      configurationErrors,
-      isSysAdmin: true,
-    },
-  ],
-  [
-    'when there are configuration errors (admin user) and error are at PROJECT level',
-    {
-      instances,
-      isConfigured: true,
-      configurationErrors: {
-        ...configurationErrors,
-        scope: ProjectAlmBindingConfigurationErrorScope.Project,
-      },
-      isSysAdmin: true,
-    },
-  ],
-])('should render correctly', (name: string, props: PRDecorationBindingRendererProps) => {
-  expect(shallowRender(props)).toMatchSnapshot(name);
-});
-
-it.each([
-  ['updating', { updating: true }],
-  ['update is successfull', { successfullyUpdated: true }],
-  ['form is valid', { isValid: true }],
-  ['configuration is saved', { isConfigured: true }],
-  ['configuration check is in progress', { isConfigured: true, checkingConfiguration: true }],
-])(
-  'should display action section correctly when',
-  (name: string, props: PRDecorationBindingRendererProps) => {
-    expect(shallowRender({ ...props, instances }).find('.action-section')).toMatchSnapshot(name);
-  }
-);
-
-function shallowRender(props: Partial<PRDecorationBindingRendererProps> = {}) {
-  return shallow(
-    <PRDecorationBindingRenderer
-      formData={{
-        key: '',
-        repository: '',
-        monorepo: false,
-      }}
-      instances={[]}
-      isChanged={false}
-      isConfigured={false}
-      isValid={false}
-      loading={false}
-      onFieldChange={jest.fn()}
-      onReset={jest.fn()}
-      onSubmit={jest.fn()}
-      updating={false}
-      successfullyUpdated={false}
-      checkingConfiguration={false}
-      onCheckConfiguration={jest.fn()}
-      isSysAdmin={false}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap
deleted file mode 100644 (file)
index 555d867..0000000
+++ /dev/null
@@ -1,834 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly for azure 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="azure.project"
-      >
-        settings.pr_decoration.binding.form.azure.project
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
-          id="settings.pr_decoration.binding.form.azure.project.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            <strong>
-              My Project
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="azure.project"
-        maxLength={256}
-        name="azure.project"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="azure.repository"
-      >
-        settings.pr_decoration.binding.form.azure.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
-          id="settings.pr_decoration.binding.form.azure.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            <strong>
-              My Repository
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="azure.repository"
-        maxLength={256}
-        name="azure.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for bitbucket 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="bitbucket.repository"
-      >
-        settings.pr_decoration.binding.form.bitbucket.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
-          id="settings.pr_decoration.binding.form.bitbucket.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            https://bb.company.com/projects/
-            <strong>
-              MY_PROJECT_KEY
-            </strong>
-            /repos/my-repository-slug/browse
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="bitbucket.repository"
-        maxLength={256}
-        name="bitbucket.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="bitbucket.slug"
-      >
-        settings.pr_decoration.binding.form.bitbucket.slug
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
-          id="settings.pr_decoration.binding.form.bitbucket.slug.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            https://bb.company.com/projects/MY_PROJECT_KEY/repos/
-            <strong>
-              my-repository-slug
-            </strong>
-            /browse
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="bitbucket.slug"
-        maxLength={256}
-        name="bitbucket.slug"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for bitbucket if an instance URL is provided 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="bitbucket.repository"
-      >
-        settings.pr_decoration.binding.form.bitbucket.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
-          id="settings.pr_decoration.binding.form.bitbucket.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            http://bbs.example.com/projects/
-            <strong>
-              MY_PROJECT_KEY
-            </strong>
-            /repos/my-repository-slug/browse
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="bitbucket.repository"
-        maxLength={256}
-        name="bitbucket.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="bitbucket.slug"
-      >
-        settings.pr_decoration.binding.form.bitbucket.slug
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
-          id="settings.pr_decoration.binding.form.bitbucket.slug.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            http://bbs.example.com/projects/MY_PROJECT_KEY/repos/
-            <strong>
-              my-repository-slug
-            </strong>
-            /browse
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="bitbucket.slug"
-        maxLength={256}
-        name="bitbucket.slug"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for bitbucketcloud 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="bitbucketcloud.repository"
-      >
-        settings.pr_decoration.binding.form.bitbucketcloud.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
-          id="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            https://bitbucket.org/my-workspace/
-            <strong>
-              my-repository-slug
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="bitbucketcloud.repository"
-        maxLength={256}
-        name="bitbucketcloud.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for github 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="github.repository"
-      >
-        settings.pr_decoration.binding.form.github.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
-          id="settings.pr_decoration.binding.form.github.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            https://github.com/
-            <strong>
-              sonarsource/sonarqube
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="github.repository"
-        maxLength={256}
-        name="github.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="github.summary_comment_setting"
-      >
-        settings.pr_decoration.binding.form.github.summary_comment_setting
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-          id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-        />
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <div
-        className="display-flex-center big-spacer-top"
-      >
-        <div
-          className="display-inline-block text-top"
-        >
-          <Toggle
-            name="github.summary_comment_setting"
-            onChange={[Function]}
-            value={true}
-          />
-        </div>
-      </div>
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for github if an instance URL is provided 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="github.repository"
-      >
-        settings.pr_decoration.binding.form.github.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
-          id="settings.pr_decoration.binding.form.github.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            http://example.com/
-            <strong>
-              sonarsource/sonarqube
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="github.repository"
-        maxLength={256}
-        name="github.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="github.summary_comment_setting"
-      >
-        settings.pr_decoration.binding.form.github.summary_comment_setting
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-          id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-        />
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <div
-        className="display-flex-center big-spacer-top"
-      >
-        <div
-          className="display-inline-block text-top"
-        >
-          <Toggle
-            name="github.summary_comment_setting"
-            onChange={[Function]}
-            value={true}
-          />
-        </div>
-      </div>
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for github if an instance URL is provided 2`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="github.repository"
-      >
-        settings.pr_decoration.binding.form.github.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
-          id="settings.pr_decoration.binding.form.github.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            https://github.com/
-            <strong>
-              sonarsource/sonarqube
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="github.repository"
-        maxLength={256}
-        name="github.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="github.summary_comment_setting"
-      >
-        settings.pr_decoration.binding.form.github.summary_comment_setting
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-          id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
-        />
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <div
-        className="display-flex-center big-spacer-top"
-      >
-        <div
-          className="display-inline-block text-top"
-        >
-          <Toggle
-            name="github.summary_comment_setting"
-            onChange={[Function]}
-            value={true}
-          />
-        </div>
-      </div>
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render correctly for gitlab 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="gitlab.repository"
-      >
-        settings.pr_decoration.binding.form.gitlab.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.gitlab.repository.help"
-          id="settings.pr_decoration.binding.form.gitlab.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            <strong>
-              123456
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="gitlab.repository"
-        maxLength={256}
-        name="gitlab.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-</Fragment>
-`;
-
-exports[`should render the monorepo field when the feature is supported 1`] = `
-<Fragment>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="azure.project"
-      >
-        settings.pr_decoration.binding.form.azure.project
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
-          id="settings.pr_decoration.binding.form.azure.project.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            <strong>
-              My Project
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="azure.project"
-        maxLength={256}
-        name="azure.project"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="azure.repository"
-      >
-        settings.pr_decoration.binding.form.azure.repository
-        <MandatoryFieldMarker />
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
-          id="settings.pr_decoration.binding.form.azure.repository.help"
-        />
-        <div
-          className="spacer-top nowrap"
-        >
-          example
-          : 
-          <em>
-            <strong>
-              My Repository
-            </strong>
-          </em>
-        </div>
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <input
-        className="input-super-large big-spacer-top"
-        id="azure.repository"
-        maxLength={256}
-        name="azure.repository"
-        onChange={[Function]}
-        type="text"
-        value=""
-      />
-    </div>
-  </div>
-  <div
-    className="settings-definition"
-  >
-    <div
-      className="settings-definition-left"
-    >
-      <label
-        className="h3"
-        htmlFor="monorepo"
-      >
-        settings.pr_decoration.binding.form.monorepo
-      </label>
-      <div
-        className="markdown small spacer-top"
-      >
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.form.monorepo.help"
-          id="settings.pr_decoration.binding.form.monorepo.help"
-          values={
-            {
-              "doc_link": <withAppStateContext(DocLink)
-                to="/devops-platform-integration/azure-devops-integration/"
-              >
-                learn_more
-              </withAppStateContext(DocLink)>,
-            }
-          }
-        />
-      </div>
-    </div>
-    <div
-      className="settings-definition-right padded-top"
-    >
-      <div
-        className="display-flex-center big-spacer-top"
-      >
-        <div
-          className="display-inline-block text-top"
-        >
-          <Toggle
-            name="monorepo"
-            onChange={[Function]}
-            value={false}
-          />
-        </div>
-      </div>
-    </div>
-  </div>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBinding-test.tsx.snap
deleted file mode 100644 (file)
index 53dc364..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<PRDecorationBindingRenderer
-  checkingConfiguration={false}
-  formData={
-    {
-      "key": "",
-      "monorepo": false,
-    }
-  }
-  instances={[]}
-  isChanged={false}
-  isConfigured={false}
-  isSysAdmin={false}
-  isValid={false}
-  loading={true}
-  onCheckConfiguration={[Function]}
-  onFieldChange={[Function]}
-  onReset={[Function]}
-  onSubmit={[Function]}
-  successfullyUpdated={false}
-  updating={false}
-/>
-`;
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
deleted file mode 100644 (file)
index 0bd144c..0000000
+++ /dev/null
@@ -1,870 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display action section correctly when: configuration check is in progress 1`] = `
-<div
-  className="display-flex-center big-spacer-top action-section"
->
-  <Button
-    className="spacer-right"
-    onClick={[MockFunction]}
-  >
-    <span
-      data-test="project-settings__alm-reset"
-    >
-      reset_verb
-    </span>
-  </Button>
-  <Button
-    disabled={true}
-    onClick={[MockFunction]}
-  >
-    settings.pr_decoration.binding.check_configuration
-    <DeferredSpinner
-      className="spacer-left"
-      loading={true}
-    />
-  </Button>
-</div>
-`;
-
-exports[`should display action section correctly when: configuration is saved 1`] = `
-<div
-  className="display-flex-center big-spacer-top action-section"
->
-  <Button
-    className="spacer-right"
-    onClick={[MockFunction]}
-  >
-    <span
-      data-test="project-settings__alm-reset"
-    >
-      reset_verb
-    </span>
-  </Button>
-  <Button
-    disabled={false}
-    onClick={[MockFunction]}
-  >
-    settings.pr_decoration.binding.check_configuration
-    <DeferredSpinner
-      className="spacer-left"
-      loading={false}
-    />
-  </Button>
-</div>
-`;
-
-exports[`should display action section correctly when: form is valid 1`] = `
-<div
-  className="display-flex-center big-spacer-top action-section"
-/>
-`;
-
-exports[`should display action section correctly when: update is successfull 1`] = `
-<div
-  className="display-flex-center big-spacer-top action-section"
->
-  <span
-    className="text-success spacer-right"
-  >
-    <AlertSuccessIcon
-      className="spacer-right"
-    />
-    settings.state.saved
-  </span>
-</div>
-`;
-
-exports[`should display action section correctly when: updating 1`] = `
-<div
-  className="display-flex-center big-spacer-top action-section"
-/>
-`;
-
-exports[`should render correctly: when loading 1`] = `<DeferredSpinner />`;
-
-exports[`should render correctly: when there are configuration errors (admin user) 1`] = `
-<div>
-  <header
-    className="page-header"
-  >
-    <h1
-      className="page-title"
-    >
-      settings.pr_decoration.binding.title
-    </h1>
-  </header>
-  <div
-    className="markdown small spacer-top big-spacer-bottom"
-  >
-    settings.pr_decoration.binding.description
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <MandatoryFieldsExplanation
-      className="form-field"
-    />
-    <div
-      className="settings-definition big-spacer-bottom"
-    >
-      <div
-        className="settings-definition-left"
-      >
-        <label
-          className="h3"
-          htmlFor="name"
-        >
-          settings.pr_decoration.binding.form.name
-          <MandatoryFieldMarker
-            className="spacer-right"
-          />
-        </label>
-        <div
-          className="markdown small spacer-top"
-        >
-          settings.pr_decoration.binding.form.name.help
-        </div>
-      </div>
-      <div
-        className="settings-definition-right"
-      >
-        <AlmSettingsInstanceSelector
-          classNames="abs-width-400 big-spacer-top it__configuration-name-select"
-          initialValue="i1"
-          inputId="name"
-          instances={
-            [
-              {
-                "alm": "github",
-                "key": "i1",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "github",
-                "key": "i2",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "bitbucket",
-                "key": "i3",
-                "url": "http://bbs.enterprise.com",
-              },
-              {
-                "alm": "azure",
-                "key": "i4",
-              },
-            ]
-          }
-          onChange={[Function]}
-        />
-      </div>
-    </div>
-    <withAvailableFeaturesContext(AlmSpecificForm)
-      alm="github"
-      formData={
-        {
-          "key": "i1",
-          "monorepo": false,
-          "repository": "account/repo",
-        }
-      }
-      instances={
-        [
-          {
-            "alm": "github",
-            "key": "i1",
-            "url": "http://github.enterprise.com",
-          },
-          {
-            "alm": "github",
-            "key": "i2",
-            "url": "http://github.enterprise.com",
-          },
-          {
-            "alm": "bitbucket",
-            "key": "i3",
-            "url": "http://bbs.enterprise.com",
-          },
-          {
-            "alm": "azure",
-            "key": "i4",
-          },
-        ]
-      }
-      onFieldChange={[MockFunction]}
-    />
-    <div
-      className="display-flex-center big-spacer-top action-section"
-    >
-      <Button
-        className="spacer-right"
-        onClick={[MockFunction]}
-      >
-        <span
-          data-test="project-settings__alm-reset"
-        >
-          reset_verb
-        </span>
-      </Button>
-      <Button
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        settings.pr_decoration.binding.check_configuration
-        <DeferredSpinner
-          className="spacer-left"
-          loading={false}
-        />
-      </Button>
-    </div>
-    <Alert
-      className="big-spacer-top"
-      display="inline"
-      variant="error"
-    >
-      <p
-        className="spacer-bottom"
-      >
-        settings.pr_decoration.binding.check_configuration.failure
-      </p>
-      <ul
-        className="list-styled"
-      >
-        <li
-          key="0"
-        >
-          Test
-        </li>
-        <li
-          key="1"
-        >
-          tesT
-        </li>
-      </ul>
-      <p>
-        <FormattedMessage
-          defaultMessage="settings.pr_decoration.binding.check_configuration.failure.check_global_settings"
-          id="settings.pr_decoration.binding.check_configuration.failure.check_global_settings"
-          values={
-            {
-              "link": <ForwardRef(Link)
-                to={
-                  {
-                    "pathname": "/admin/settings",
-                    "search": "?category=almintegration&alm=github",
-                  }
-                }
-              >
-                settings.pr_decoration.binding.check_configuration.failure.check_global_settings.link
-              </ForwardRef(Link)>,
-            }
-          }
-        />
-      </p>
-    </Alert>
-  </form>
-</div>
-`;
-
-exports[`should render correctly: when there are configuration errors (admin user) and error are at PROJECT level 1`] = `
-<div>
-  <header
-    className="page-header"
-  >
-    <h1
-      className="page-title"
-    >
-      settings.pr_decoration.binding.title
-    </h1>
-  </header>
-  <div
-    className="markdown small spacer-top big-spacer-bottom"
-  >
-    settings.pr_decoration.binding.description
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <MandatoryFieldsExplanation
-      className="form-field"
-    />
-    <div
-      className="settings-definition big-spacer-bottom"
-    >
-      <div
-        className="settings-definition-left"
-      >
-        <label
-          className="h3"
-          htmlFor="name"
-        >
-          settings.pr_decoration.binding.form.name
-          <MandatoryFieldMarker
-            className="spacer-right"
-          />
-        </label>
-        <div
-          className="markdown small spacer-top"
-        >
-          settings.pr_decoration.binding.form.name.help
-        </div>
-      </div>
-      <div
-        className="settings-definition-right"
-      >
-        <AlmSettingsInstanceSelector
-          classNames="abs-width-400 big-spacer-top it__configuration-name-select"
-          initialValue=""
-          inputId="name"
-          instances={
-            [
-              {
-                "alm": "github",
-                "key": "i1",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "github",
-                "key": "i2",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "bitbucket",
-                "key": "i3",
-                "url": "http://bbs.enterprise.com",
-              },
-              {
-                "alm": "azure",
-                "key": "i4",
-              },
-            ]
-          }
-          onChange={[Function]}
-        />
-      </div>
-    </div>
-    <div
-      className="display-flex-center big-spacer-top action-section"
-    >
-      <Button
-        className="spacer-right"
-        onClick={[MockFunction]}
-      >
-        <span
-          data-test="project-settings__alm-reset"
-        >
-          reset_verb
-        </span>
-      </Button>
-      <Button
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        settings.pr_decoration.binding.check_configuration
-        <DeferredSpinner
-          className="spacer-left"
-          loading={false}
-        />
-      </Button>
-    </div>
-    <Alert
-      className="big-spacer-top"
-      display="inline"
-      variant="error"
-    >
-      <p
-        className="spacer-bottom"
-      >
-        settings.pr_decoration.binding.check_configuration.failure
-      </p>
-      <ul
-        className="list-styled"
-      >
-        <li
-          key="0"
-        >
-          Test
-        </li>
-        <li
-          key="1"
-        >
-          tesT
-        </li>
-      </ul>
-    </Alert>
-  </form>
-</div>
-`;
-
-exports[`should render correctly: when there are configuration errors (non-admin user) 1`] = `
-<div>
-  <header
-    className="page-header"
-  >
-    <h1
-      className="page-title"
-    >
-      settings.pr_decoration.binding.title
-    </h1>
-  </header>
-  <div
-    className="markdown small spacer-top big-spacer-bottom"
-  >
-    settings.pr_decoration.binding.description
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <MandatoryFieldsExplanation
-      className="form-field"
-    />
-    <div
-      className="settings-definition big-spacer-bottom"
-    >
-      <div
-        className="settings-definition-left"
-      >
-        <label
-          className="h3"
-          htmlFor="name"
-        >
-          settings.pr_decoration.binding.form.name
-          <MandatoryFieldMarker
-            className="spacer-right"
-          />
-        </label>
-        <div
-          className="markdown small spacer-top"
-        >
-          settings.pr_decoration.binding.form.name.help
-        </div>
-      </div>
-      <div
-        className="settings-definition-right"
-      >
-        <AlmSettingsInstanceSelector
-          classNames="abs-width-400 big-spacer-top it__configuration-name-select"
-          initialValue=""
-          inputId="name"
-          instances={
-            [
-              {
-                "alm": "github",
-                "key": "i1",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "github",
-                "key": "i2",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "bitbucket",
-                "key": "i3",
-                "url": "http://bbs.enterprise.com",
-              },
-              {
-                "alm": "azure",
-                "key": "i4",
-              },
-            ]
-          }
-          onChange={[Function]}
-        />
-      </div>
-    </div>
-    <div
-      className="display-flex-center big-spacer-top action-section"
-    >
-      <Button
-        className="spacer-right"
-        onClick={[MockFunction]}
-      >
-        <span
-          data-test="project-settings__alm-reset"
-        >
-          reset_verb
-        </span>
-      </Button>
-      <Button
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        settings.pr_decoration.binding.check_configuration
-        <DeferredSpinner
-          className="spacer-left"
-          loading={false}
-        />
-      </Button>
-    </div>
-    <Alert
-      className="big-spacer-top"
-      display="inline"
-      variant="error"
-    >
-      <p
-        className="spacer-bottom"
-      >
-        settings.pr_decoration.binding.check_configuration.failure
-      </p>
-      <ul
-        className="list-styled"
-      >
-        <li
-          key="0"
-        >
-          Test
-        </li>
-        <li
-          key="1"
-        >
-          tesT
-        </li>
-      </ul>
-      <p>
-        settings.pr_decoration.binding.check_configuration.contact_admin
-      </p>
-    </Alert>
-  </form>
-</div>
-`;
-
-exports[`should render correctly: with a single ALM instance 1`] = `
-<div>
-  <header
-    className="page-header"
-  >
-    <h1
-      className="page-title"
-    >
-      settings.pr_decoration.binding.title
-    </h1>
-  </header>
-  <div
-    className="markdown small spacer-top big-spacer-bottom"
-  >
-    settings.pr_decoration.binding.description
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <MandatoryFieldsExplanation
-      className="form-field"
-    />
-    <div
-      className="settings-definition big-spacer-bottom"
-    >
-      <div
-        className="settings-definition-left"
-      >
-        <label
-          className="h3"
-          htmlFor="name"
-        >
-          settings.pr_decoration.binding.form.name
-          <MandatoryFieldMarker
-            className="spacer-right"
-          />
-        </label>
-        <div
-          className="markdown small spacer-top"
-        >
-          settings.pr_decoration.binding.form.name.help
-        </div>
-      </div>
-      <div
-        className="settings-definition-right"
-      >
-        <AlmSettingsInstanceSelector
-          classNames="abs-width-400 big-spacer-top it__configuration-name-select"
-          initialValue=""
-          inputId="name"
-          instances={
-            [
-              {
-                "alm": "github",
-                "key": "i1",
-                "url": "http://github.enterprise.com",
-              },
-            ]
-          }
-          onChange={[Function]}
-        />
-      </div>
-    </div>
-    <div
-      className="display-flex-center big-spacer-top action-section"
-    />
-  </form>
-</div>
-`;
-
-exports[`should render correctly: with a valid and saved form 1`] = `
-<div>
-  <header
-    className="page-header"
-  >
-    <h1
-      className="page-title"
-    >
-      settings.pr_decoration.binding.title
-    </h1>
-  </header>
-  <div
-    className="markdown small spacer-top big-spacer-bottom"
-  >
-    settings.pr_decoration.binding.description
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <MandatoryFieldsExplanation
-      className="form-field"
-    />
-    <div
-      className="settings-definition big-spacer-bottom"
-    >
-      <div
-        className="settings-definition-left"
-      >
-        <label
-          className="h3"
-          htmlFor="name"
-        >
-          settings.pr_decoration.binding.form.name
-          <MandatoryFieldMarker
-            className="spacer-right"
-          />
-        </label>
-        <div
-          className="markdown small spacer-top"
-        >
-          settings.pr_decoration.binding.form.name.help
-        </div>
-      </div>
-      <div
-        className="settings-definition-right"
-      >
-        <AlmSettingsInstanceSelector
-          classNames="abs-width-400 big-spacer-top it__configuration-name-select"
-          initialValue="i1"
-          inputId="name"
-          instances={
-            [
-              {
-                "alm": "github",
-                "key": "i1",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "github",
-                "key": "i2",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "bitbucket",
-                "key": "i3",
-                "url": "http://bbs.enterprise.com",
-              },
-              {
-                "alm": "azure",
-                "key": "i4",
-              },
-            ]
-          }
-          onChange={[Function]}
-        />
-      </div>
-    </div>
-    <withAvailableFeaturesContext(AlmSpecificForm)
-      alm="github"
-      formData={
-        {
-          "key": "i1",
-          "monorepo": false,
-          "repository": "account/repo",
-        }
-      }
-      instances={
-        [
-          {
-            "alm": "github",
-            "key": "i1",
-            "url": "http://github.enterprise.com",
-          },
-          {
-            "alm": "github",
-            "key": "i2",
-            "url": "http://github.enterprise.com",
-          },
-          {
-            "alm": "bitbucket",
-            "key": "i3",
-            "url": "http://bbs.enterprise.com",
-          },
-          {
-            "alm": "azure",
-            "key": "i4",
-          },
-        ]
-      }
-      onFieldChange={[MockFunction]}
-    />
-    <div
-      className="display-flex-center big-spacer-top action-section"
-    >
-      <Button
-        className="spacer-right"
-        onClick={[MockFunction]}
-      >
-        <span
-          data-test="project-settings__alm-reset"
-        >
-          reset_verb
-        </span>
-      </Button>
-      <Button
-        disabled={false}
-        onClick={[MockFunction]}
-      >
-        settings.pr_decoration.binding.check_configuration
-        <DeferredSpinner
-          className="spacer-left"
-          loading={false}
-        />
-      </Button>
-    </div>
-    <Alert
-      className="big-spacer-top"
-      display="inline"
-      variant="success"
-    >
-      settings.pr_decoration.binding.check_configuration.success
-    </Alert>
-  </form>
-</div>
-`;
-
-exports[`should render correctly: with an empty form 1`] = `
-<div>
-  <header
-    className="page-header"
-  >
-    <h1
-      className="page-title"
-    >
-      settings.pr_decoration.binding.title
-    </h1>
-  </header>
-  <div
-    className="markdown small spacer-top big-spacer-bottom"
-  >
-    settings.pr_decoration.binding.description
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <MandatoryFieldsExplanation
-      className="form-field"
-    />
-    <div
-      className="settings-definition big-spacer-bottom"
-    >
-      <div
-        className="settings-definition-left"
-      >
-        <label
-          className="h3"
-          htmlFor="name"
-        >
-          settings.pr_decoration.binding.form.name
-          <MandatoryFieldMarker
-            className="spacer-right"
-          />
-        </label>
-        <div
-          className="markdown small spacer-top"
-        >
-          settings.pr_decoration.binding.form.name.help
-        </div>
-      </div>
-      <div
-        className="settings-definition-right"
-      >
-        <AlmSettingsInstanceSelector
-          classNames="abs-width-400 big-spacer-top it__configuration-name-select"
-          initialValue=""
-          inputId="name"
-          instances={
-            [
-              {
-                "alm": "github",
-                "key": "i1",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "github",
-                "key": "i2",
-                "url": "http://github.enterprise.com",
-              },
-              {
-                "alm": "bitbucket",
-                "key": "i3",
-                "url": "http://bbs.enterprise.com",
-              },
-              {
-                "alm": "azure",
-                "key": "i4",
-              },
-            ]
-          }
-          onChange={[Function]}
-        />
-      </div>
-    </div>
-    <div
-      className="display-flex-center big-spacer-top action-section"
-    />
-  </form>
-</div>
-`;
-
-exports[`should render correctly: with no ALM instances (admin user) 1`] = `
-<div>
-  <Alert
-    className="spacer-top huge-spacer-bottom"
-    variant="info"
-  >
-    <FormattedMessage
-      defaultMessage="settings.pr_decoration.binding.no_bindings.admin"
-      id="settings.pr_decoration.binding.no_bindings.admin"
-      values={
-        {
-          "link": <ForwardRef(Link)
-            to={
-              {
-                "pathname": "/admin/settings",
-                "search": "?category=almintegration",
-              }
-            }
-          >
-            settings.pr_decoration.binding.no_bindings.link
-          </ForwardRef(Link)>,
-        }
-      }
-    />
-  </Alert>
-</div>
-`;
-
-exports[`should render correctly: with no ALM instances (non-admin user) 1`] = `
-<div>
-  <Alert
-    className="spacer-top huge-spacer-bottom"
-    variant="info"
-  >
-    settings.pr_decoration.binding.no_bindings
-  </Alert>
-</div>
-`;
index e40db8f9ce162a6ae9cab428bcb437899541b721..40fad0bf43271f4a6907eb701ab7c1c8cec5d306 100644 (file)
@@ -47,24 +47,26 @@ const ui = {
 };
 
 it('should be able to generate new key', async () => {
+  const user = userEvent.setup();
   renderEncryptionApp();
 
   expect(await ui.appHeading.find()).toBeInTheDocument();
-  await userEvent.click(ui.generateSecretButton.get());
+  await user.click(ui.generateSecretButton.get());
   expect(ui.copyToClipboard.get()).toHaveAttribute('data-clipboard-text', 'secretKey');
 });
 
 it('should be able to encrypt property value when secret is registered', async () => {
+  const user = userEvent.setup();
   settingsMock.setSecretKeyAvailable(true);
   renderEncryptionApp();
 
   expect(await ui.appHeading.find()).toBeInTheDocument();
-  await userEvent.type(ui.encryptTextarea.get(), 'sonar.announcement.message');
-  await userEvent.click(ui.encryptButton.get());
+  await user.type(ui.encryptTextarea.get(), 'sonar.announcement.message');
+  await user.click(ui.encryptButton.get());
   expect(ui.copyToClipboard.get()).toHaveAttribute('data-clipboard-text', 'encryptedValue');
 
   // can generate new secret in view
-  await userEvent.click(ui.generateNewSecretButton.get());
+  await user.click(ui.generateNewSecretButton.get());
   expect(ui.copyToClipboard.get()).toHaveAttribute('data-clipboard-text', 'secretKey');
 });
 
index 008c393d26022fad4d8c46ceeb53c9d418206cf1..3134c7e24b573adafd2241ca6c7e92d68ca4dbea 100644 (file)
@@ -24,6 +24,7 @@ import { Button } from './buttons';
 import './Toggle.css';
 
 interface Props {
+  id?: string;
   ariaLabel?: string;
   disabled?: boolean;
   name?: string;
@@ -49,12 +50,13 @@ export default class Toggle extends React.PureComponent<Props> {
   };
 
   render() {
-    const { ariaLabel, disabled, name } = this.props;
+    const { ariaLabel, disabled, name, id } = this.props;
     const value = this.getValue();
     const className = classNames('boolean-toggle', { 'boolean-toggle-on': value });
 
     return (
       <Button
+        id={id}
         className={className}
         disabled={disabled}
         aria-label={ariaLabel}
index 88d013e2a73c99fc1770825522b79207f5e174df..04b764ef975423d4ac59780d6a799c64ee6419bb 100644 (file)
@@ -71,7 +71,7 @@ export default function AlmSettingsInstanceSelector(props: Props) {
       }}
       placeholder={translate('alm.configuration.selector.placeholder')}
       getOptionValue={(opt) => opt.key}
-      value={instances.find((inst) => inst.key === initialValue)}
+      value={instances.find((inst) => inst.key === initialValue) ?? null}
     />
   );
 }
index 6fc74c084ed965ba4aa92ff679310de9cbfdb11c..4e5f9ea022c73cbbb5d5263b3f1ef553bd8a5b76 100644 (file)
@@ -121,6 +121,7 @@ export function mockProjectAlmBindingResponse(
   return {
     alm: AlmKeys.GitHub,
     key: 'foo',
+    repository: 'repo',
     monorepo: false,
     ...overrides,
   };
index 4711986b0515544846120d7db84709f79c16e27f..f73833525a3de2c7084c3de92f090210c1dc5ac2 100644 (file)
@@ -70,38 +70,34 @@ export interface GitlabBindingDefinition extends AlmBindingDefinitionBase {
 export interface ProjectAlmBindingResponse {
   alm: AlmKeys;
   key: string;
-  repository?: string;
+  repository: string;
   slug?: string;
+  url?: string;
   summaryCommentEnabled?: boolean;
   monorepo: boolean;
 }
 
 export interface ProjectAzureBindingResponse extends ProjectAlmBindingResponse {
   alm: AlmKeys.Azure;
-  repository: string;
   slug: string;
   url: string;
 }
 
 export interface ProjectBitbucketBindingResponse extends ProjectAlmBindingResponse {
   alm: AlmKeys.BitbucketServer;
-  repository: string;
   slug: string;
 }
 
 export interface ProjectBitbucketCloudBindingResponse extends ProjectAlmBindingResponse {
   alm: AlmKeys.BitbucketCloud;
-  repository: string;
 }
 
 export interface ProjectGitHubBindingResponse extends ProjectAlmBindingResponse {
   alm: AlmKeys.GitHub;
-  repository: string;
 }
 
 export interface ProjectGitLabBindingResponse extends ProjectAlmBindingResponse {
   alm: AlmKeys.GitLab;
-  repository: string;
   url: string;
 }