From bba7be1fdba424967ee1d1c78d9b4188142bfd14 Mon Sep 17 00:00:00 2001
From: Jannik Lehmann <jlehmann@gitlab.com>
Date: Fri, 23 Apr 2021 11:51:25 +0000
Subject: [PATCH] Refactor manage_via_mr Component

This commit solves https://gitlab.com/gitlab-org/gitlab/-/issues/326249.
The security Configuration Table for CE and EE has been refactored.
Both of those tables support the ability to enable scanners via Merge Request.
Previously this was handled separately leading to duplicate code.
---
 .../components/configuration_table.vue        |  15 +-
 .../{scanners_constants.js => constants.js}   |  16 +++
 .../components/manage_sast.vue                |  59 --------
 .../components/upgrade.vue                    |   2 +-
 .../components/manage_via_mr.vue              |  21 +--
 .../security_configuration}/provider.js       |   0
 .../security_configuration/components/app.vue |   2 +-
 .../components/constants.js                   |  18 ++-
 .../components/manage_feature.vue             |   2 +-
 .../security_configuration/index.js           |   4 +
 .../security/configuration/show.html.haml     |   1 +
 .../components/app_spec.js                    |   2 +-
 .../components/helpers.js                     |   2 +-
 .../components/manage_feature_spec.js         |   2 +-
 .../components/configuration_form_spec.js     |   4 +-
 locale/gitlab.pot                             |   6 -
 .../configuration_table_spec.js               |  16 ++-
 .../manage_sast_spec.js                       | 136 ------------------
 .../security_configuration/upgrade_spec.js    |   2 +-
 .../components/apollo_mocks.js                |   0
 .../components/manage_via_mr_spec.js          |  16 +--
 21 files changed, 79 insertions(+), 247 deletions(-)
 rename app/assets/javascripts/security_configuration/components/{scanners_constants.js => constants.js} (91%)
 delete mode 100644 app/assets/javascripts/security_configuration/components/manage_sast.vue
 rename {ee/app/assets/javascripts => app/assets/javascripts/vue_shared}/security_configuration/components/manage_via_mr.vue (75%)
 rename {ee/app/assets/javascripts/security_configuration/graphql => app/assets/javascripts/vue_shared/security_configuration}/provider.js (100%)
 delete mode 100644 spec/frontend/security_configuration/manage_sast_spec.js
 rename {ee/spec/frontend/security_configuration => spec/frontend/vue_shared/security_reports}/components/apollo_mocks.js (100%)
 rename {ee/spec/frontend/security_configuration => spec/frontend/vue_shared/security_reports}/components/manage_via_mr_spec.js (93%)

diff --git a/app/assets/javascripts/security_configuration/components/configuration_table.vue b/app/assets/javascripts/security_configuration/components/configuration_table.vue
index 4a3f988296cb..2110af1522b8 100644
--- a/app/assets/javascripts/security_configuration/components/configuration_table.vue
+++ b/app/assets/javascripts/security_configuration/components/configuration_table.vue
@@ -1,6 +1,7 @@
 <script>
 import { GlLink, GlTable, GlAlert } from '@gitlab/ui';
 import { s__, sprintf } from '~/locale';
+import ManageViaMR from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
 import {
   REPORT_TYPE_SAST,
   REPORT_TYPE_DAST,
@@ -11,8 +12,8 @@ import {
   REPORT_TYPE_API_FUZZING,
   REPORT_TYPE_LICENSE_COMPLIANCE,
 } from '~/vue_shared/security_reports/constants';
-import ManageSast from './manage_sast.vue';
-import { scanners } from './scanners_constants';
+
+import { scanners } from './constants';
 import Upgrade from './upgrade.vue';
 
 const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!';
@@ -40,7 +41,7 @@ export default {
     },
     getComponentForItem(item) {
       const COMPONENTS = {
-        [REPORT_TYPE_SAST]: ManageSast,
+        [REPORT_TYPE_SAST]: ManageViaMR,
         [REPORT_TYPE_DAST]: Upgrade,
         [REPORT_TYPE_DAST_PROFILES]: Upgrade,
         [REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade,
@@ -49,7 +50,6 @@ export default {
         [REPORT_TYPE_API_FUZZING]: Upgrade,
         [REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade,
       };
-
       return COMPONENTS[item.type];
     },
   },
@@ -95,7 +95,12 @@ export default {
       </template>
 
       <template #cell(manage)="{ item }">
-        <component :is="getComponentForItem(item)" :data-testid="item.type" @error="onError" />
+        <component
+          :is="getComponentForItem(item)"
+          :feature="item"
+          :data-testid="item.type"
+          @error="onError"
+        />
       </template>
     </gl-table>
   </div>
diff --git a/app/assets/javascripts/security_configuration/components/scanners_constants.js b/app/assets/javascripts/security_configuration/components/constants.js
similarity index 91%
rename from app/assets/javascripts/security_configuration/components/scanners_constants.js
rename to app/assets/javascripts/security_configuration/components/constants.js
index 9846df0b4bf7..3cdcac4c0b40 100644
--- a/app/assets/javascripts/security_configuration/components/scanners_constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -1,6 +1,7 @@
 import { helpPagePath } from '~/helpers/help_page_helper';
 import { __, s__ } from '~/locale';
 
+import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql';
 import {
   REPORT_TYPE_SAST,
   REPORT_TYPE_DAST,
@@ -134,3 +135,18 @@ export const scanners = [
     type: REPORT_TYPE_LICENSE_COMPLIANCE,
   },
 ];
+
+export const featureToMutationMap = {
+  [REPORT_TYPE_SAST]: {
+    mutationId: 'configureSast',
+    getMutationPayload: (projectPath) => ({
+      mutation: configureSastMutation,
+      variables: {
+        input: {
+          projectPath,
+          configuration: { global: [], pipeline: [], analyzers: [] },
+        },
+      },
+    }),
+  },
+};
diff --git a/app/assets/javascripts/security_configuration/components/manage_sast.vue b/app/assets/javascripts/security_configuration/components/manage_sast.vue
deleted file mode 100644
index 8a8827b41cd4..000000000000
--- a/app/assets/javascripts/security_configuration/components/manage_sast.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import { redirectTo } from '~/lib/utils/url_utility';
-import { s__ } from '~/locale';
-import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql';
-
-export default {
-  components: {
-    GlButton,
-  },
-  inject: {
-    projectPath: {
-      from: 'projectPath',
-      default: '',
-    },
-  },
-  data() {
-    return {
-      isLoading: false,
-    };
-  },
-  methods: {
-    async mutate() {
-      this.isLoading = true;
-      try {
-        const { data } = await this.$apollo.mutate({
-          mutation: configureSastMutation,
-          variables: {
-            input: {
-              projectPath: this.projectPath,
-              configuration: { global: [], pipeline: [], analyzers: [] },
-            },
-          },
-        });
-        const { errors, successPath } = data.configureSast;
-
-        if (errors.length > 0) {
-          throw new Error(errors[0]);
-        }
-
-        if (!successPath) {
-          throw new Error(s__('SecurityConfiguration|SAST merge request creation mutation failed'));
-        }
-
-        redirectTo(successPath);
-      } catch (e) {
-        this.$emit('error', e.message);
-        this.isLoading = false;
-      }
-    },
-  },
-};
-</script>
-
-<template>
-  <gl-button :loading="isLoading" variant="success" category="secondary" @click="mutate">{{
-    s__('SecurityConfiguration|Configure via merge request')
-  }}</gl-button>
-</template>
diff --git a/app/assets/javascripts/security_configuration/components/upgrade.vue b/app/assets/javascripts/security_configuration/components/upgrade.vue
index 518eb57731d0..2541c29224af 100644
--- a/app/assets/javascripts/security_configuration/components/upgrade.vue
+++ b/app/assets/javascripts/security_configuration/components/upgrade.vue
@@ -1,6 +1,6 @@
 <script>
 import { GlLink, GlSprintf } from '@gitlab/ui';
-import { UPGRADE_CTA } from './scanners_constants';
+import { UPGRADE_CTA } from './constants';
 
 export default {
   components: {
diff --git a/ee/app/assets/javascripts/security_configuration/components/manage_via_mr.vue b/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
similarity index 75%
rename from ee/app/assets/javascripts/security_configuration/components/manage_via_mr.vue
rename to app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
index a0a3b1aa2ad2..de7374ff4b28 100644
--- a/ee/app/assets/javascripts/security_configuration/components/manage_via_mr.vue
+++ b/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
@@ -1,21 +1,16 @@
 <script>
 import { GlButton } from '@gitlab/ui';
+import { featureToMutationMap } from 'ee_else_ce/security_configuration/components/constants';
 import { redirectTo } from '~/lib/utils/url_utility';
 import { sprintf, s__ } from '~/locale';
-import apolloProvider from '../graphql/provider';
-import { featureToMutationMap } from './constants';
+import apolloProvider from '../provider';
 
 export default {
   apolloProvider,
   components: {
     GlButton,
   },
-  inject: {
-    projectPath: {
-      from: 'projectPath',
-      default: '',
-    },
-  },
+  inject: ['projectPath'],
   props: {
     feature: {
       type: Object,
@@ -36,13 +31,9 @@ export default {
     async mutate() {
       this.isLoading = true;
       try {
-        const { data } = await this.$apollo.mutate({
-          mutation: this.featureSettings.mutation,
-          variables: {
-            fullPath: this.projectPath,
-          },
-        });
-        const { errors, successPath } = data[this.featureSettings.type];
+        const mutation = this.featureSettings;
+        const { data } = await this.$apollo.mutate(mutation.getMutationPayload(this.projectPath));
+        const { errors, successPath } = data[mutation.mutationId];
 
         if (errors.length > 0) {
           throw new Error(errors[0]);
diff --git a/ee/app/assets/javascripts/security_configuration/graphql/provider.js b/app/assets/javascripts/vue_shared/security_configuration/provider.js
similarity index 100%
rename from ee/app/assets/javascripts/security_configuration/graphql/provider.js
rename to app/assets/javascripts/vue_shared/security_configuration/provider.js
diff --git a/ee/app/assets/javascripts/security_configuration/components/app.vue b/ee/app/assets/javascripts/security_configuration/components/app.vue
index 439fdc862d25..4e63daad88a6 100644
--- a/ee/app/assets/javascripts/security_configuration/components/app.vue
+++ b/ee/app/assets/javascripts/security_configuration/components/app.vue
@@ -2,7 +2,7 @@
 import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
 import { parseBoolean } from '~/lib/utils/common_utils';
 import { s__, __ } from '~/locale';
-import { scanners } from '~/security_configuration/components/scanners_constants';
+import { scanners } from '~/security_configuration/components/constants';
 import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
 import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 import AutoFixSettings from './auto_fix_settings.vue';
diff --git a/ee/app/assets/javascripts/security_configuration/components/constants.js b/ee/app/assets/javascripts/security_configuration/components/constants.js
index c6fb66bbb082..0495d12d545e 100644
--- a/ee/app/assets/javascripts/security_configuration/components/constants.js
+++ b/ee/app/assets/javascripts/security_configuration/components/constants.js
@@ -1,4 +1,5 @@
 import { s__ } from '~/locale';
+import { featureToMutationMap as featureToMutationMapCE } from '~/security_configuration/components/constants';
 import {
   REPORT_TYPE_DEPENDENCY_SCANNING,
   REPORT_TYPE_SECRET_DETECTION,
@@ -23,12 +24,21 @@ export const CUSTOM_VALUE_MESSAGE = s__(
 );
 
 export const featureToMutationMap = {
+  ...featureToMutationMapCE,
   [REPORT_TYPE_DEPENDENCY_SCANNING]: {
-    type: 'configureDependencyScanning',
-    mutation: configureDependencyScanningMutation,
+    mutationId: 'configureDependencyScanning',
+    getMutationPayload: (projectPath) => ({
+      mutation: configureDependencyScanningMutation,
+      variables: {
+        fullPath: projectPath,
+      },
+    }),
   },
   [REPORT_TYPE_SECRET_DETECTION]: {
-    type: 'configureSecretDetection',
-    mutation: configureSecretDetectionMutation,
+    mutationId: 'configureSecretDetection',
+    getMutationPayload: (projectPath) => ({
+      mutation: configureSecretDetectionMutation,
+      variables: { fullPath: projectPath },
+    }),
   },
 };
diff --git a/ee/app/assets/javascripts/security_configuration/components/manage_feature.vue b/ee/app/assets/javascripts/security_configuration/components/manage_feature.vue
index 536492e4b7a1..43283bf2bafe 100644
--- a/ee/app/assets/javascripts/security_configuration/components/manage_feature.vue
+++ b/ee/app/assets/javascripts/security_configuration/components/manage_feature.vue
@@ -1,6 +1,7 @@
 <script>
 import { propsUnion } from '~/vue_shared/components/lib/utils/props_utils';
 import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
 import {
   REPORT_TYPE_DAST_PROFILES,
   REPORT_TYPE_DEPENDENCY_SCANNING,
@@ -8,7 +9,6 @@ import {
 } from '~/vue_shared/security_reports/constants';
 import ManageDastProfiles from './manage_dast_profiles.vue';
 import ManageGeneric from './manage_generic.vue';
-import ManageViaMr from './manage_via_mr.vue';
 
 const scannerComponentMap = {
   [REPORT_TYPE_DAST_PROFILES]: ManageDastProfiles,
diff --git a/ee/app/assets/javascripts/security_configuration/index.js b/ee/app/assets/javascripts/security_configuration/index.js
index ab15b0309d17..3dbdcc096690 100644
--- a/ee/app/assets/javascripts/security_configuration/index.js
+++ b/ee/app/assets/javascripts/security_configuration/index.js
@@ -18,6 +18,7 @@ export const initSecurityConfiguration = (el) => {
     containerScanningHelpPath,
     dependencyScanningHelpPath,
     toggleAutofixSettingEndpoint,
+    projectPath,
     gitlabCiHistoryPath,
   } = el.dataset;
 
@@ -26,6 +27,9 @@ export const initSecurityConfiguration = (el) => {
     components: {
       SecurityConfigurationApp,
     },
+    provide: {
+      projectPath,
+    },
     render(createElement) {
       return createElement(SecurityConfigurationApp, {
         props: {
diff --git a/ee/app/views/projects/security/configuration/show.html.haml b/ee/app/views/projects/security/configuration/show.html.haml
index cf849bff30a7..42afe03b5c46 100644
--- a/ee/app/views/projects/security/configuration/show.html.haml
+++ b/ee/app/views/projects/security/configuration/show.html.haml
@@ -6,6 +6,7 @@
 - else
   #js-security-configuration{ data: { **@configuration.to_html_data_attribute,
     auto_fix_help_path: '/',
+    project_path: @project.full_path,
     toggle_autofix_setting_endpoint: 'configuration/auto_fix',
     container_scanning_help_path: help_page_path('user/application_security/container_scanning/index'),
     dependency_scanning_help_path: help_page_path('user/application_security/dependency_scanning/index') } }
diff --git a/ee/spec/frontend/security_configuration/components/app_spec.js b/ee/spec/frontend/security_configuration/components/app_spec.js
index cebba404035e..d3dd47684f07 100644
--- a/ee/spec/frontend/security_configuration/components/app_spec.js
+++ b/ee/spec/frontend/security_configuration/components/app_spec.js
@@ -5,7 +5,7 @@ import SecurityConfigurationApp from 'ee/security_configuration/components/app.v
 import ConfigurationTable from 'ee/security_configuration/components/configuration_table.vue';
 import { useLocalStorageSpy } from 'helpers/local_storage_helper';
 import stubChildren from 'helpers/stub_children';
-import { scanners } from '~/security_configuration/components/scanners_constants';
+import { scanners } from '~/security_configuration/components/constants';
 import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
 import { generateFeatures } from './helpers';
 
diff --git a/ee/spec/frontend/security_configuration/components/helpers.js b/ee/spec/frontend/security_configuration/components/helpers.js
index ec0db18a36a9..56cd4d80f94d 100644
--- a/ee/spec/frontend/security_configuration/components/helpers.js
+++ b/ee/spec/frontend/security_configuration/components/helpers.js
@@ -1,4 +1,4 @@
-import { scanners } from '~/security_configuration/components/scanners_constants';
+import { scanners } from '~/security_configuration/components/constants';
 
 export const generateFeatures = (n, overrides = {}) => {
   return [...Array(n).keys()].map((i) => ({
diff --git a/ee/spec/frontend/security_configuration/components/manage_feature_spec.js b/ee/spec/frontend/security_configuration/components/manage_feature_spec.js
index bed3633cd652..0607c91db308 100644
--- a/ee/spec/frontend/security_configuration/components/manage_feature_spec.js
+++ b/ee/spec/frontend/security_configuration/components/manage_feature_spec.js
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
 import ManageDastProfiles from 'ee/security_configuration/components/manage_dast_profiles.vue';
 import ManageFeature from 'ee/security_configuration/components/manage_feature.vue';
 import ManageGeneric from 'ee/security_configuration/components/manage_generic.vue';
-import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue';
+import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
 import {
   REPORT_TYPE_DAST_PROFILES,
   REPORT_TYPE_DEPENDENCY_SCANNING,
diff --git a/ee/spec/frontend/security_configuration/sast/components/configuration_form_spec.js b/ee/spec/frontend/security_configuration/sast/components/configuration_form_spec.js
index 89929df64eb4..cbe1b145d4ba 100644
--- a/ee/spec/frontend/security_configuration/sast/components/configuration_form_spec.js
+++ b/ee/spec/frontend/security_configuration/sast/components/configuration_form_spec.js
@@ -10,9 +10,7 @@ import { redirectTo } from '~/lib/utils/url_utility';
 import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql';
 import { makeEntities, makeSastCiConfiguration } from '../../helpers';
 
-jest.mock('~/lib/utils/url_utility', () => ({
-  redirectTo: jest.fn(),
-}));
+jest.mock('~/lib/utils/url_utility');
 
 const projectPath = 'group/project';
 const sastAnalyzersDocumentationPath = '/help/sast/analyzers';
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index a21f161df98c..03aacd4e07f1 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -28067,9 +28067,6 @@ msgstr ""
 msgid "SecurityConfiguration|Configure via Merge Request"
 msgstr ""
 
-msgid "SecurityConfiguration|Configure via merge request"
-msgstr ""
-
 msgid "SecurityConfiguration|Could not retrieve configuration data. Please refresh the page, or try again later."
 msgstr ""
 
@@ -28106,9 +28103,6 @@ msgstr ""
 msgid "SecurityConfiguration|SAST Configuration"
 msgstr ""
 
-msgid "SecurityConfiguration|SAST merge request creation mutation failed"
-msgstr ""
-
 msgid "SecurityConfiguration|Security Control"
 msgstr ""
 
diff --git a/spec/frontend/security_configuration/configuration_table_spec.js b/spec/frontend/security_configuration/configuration_table_spec.js
index a1789052c929..fbd72265c4b9 100644
--- a/spec/frontend/security_configuration/configuration_table_spec.js
+++ b/spec/frontend/security_configuration/configuration_table_spec.js
@@ -1,7 +1,7 @@
 import { mount } from '@vue/test-utils';
 import { extendedWrapper } from 'helpers/vue_test_utils_helper';
 import ConfigurationTable from '~/security_configuration/components/configuration_table.vue';
-import { scanners, UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
+import { scanners, UPGRADE_CTA } from '~/security_configuration/components/constants';
 
 import {
   REPORT_TYPE_SAST,
@@ -12,7 +12,13 @@ describe('Configuration Table Component', () => {
   let wrapper;
 
   const createComponent = () => {
-    wrapper = extendedWrapper(mount(ConfigurationTable, {}));
+    wrapper = extendedWrapper(
+      mount(ConfigurationTable, {
+        provide: {
+          projectPath: 'testProjectPath',
+        },
+      }),
+    );
   };
 
   const findHelpLinks = () => wrapper.findAll('[data-testid="help-link"]');
@@ -30,8 +36,10 @@ describe('Configuration Table Component', () => {
       expect(wrapper.text()).toContain(scanner.name);
       expect(wrapper.text()).toContain(scanner.description);
       if (scanner.type === REPORT_TYPE_SAST) {
-        expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via merge request');
-      } else if (scanner.type !== REPORT_TYPE_SECRET_DETECTION) {
+        expect(wrapper.findByTestId(scanner.type).text()).toBe('Configure via Merge Request');
+      } else if (scanner.type === REPORT_TYPE_SECRET_DETECTION) {
+        expect(wrapper.findByTestId(scanner.type).exists()).toBe(false);
+      } else {
         expect(wrapper.findByTestId(scanner.type).text()).toMatchInterpolatedText(UPGRADE_CTA);
       }
     });
diff --git a/spec/frontend/security_configuration/manage_sast_spec.js b/spec/frontend/security_configuration/manage_sast_spec.js
deleted file mode 100644
index 15a57210246e..000000000000
--- a/spec/frontend/security_configuration/manage_sast_spec.js
+++ /dev/null
@@ -1,136 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { redirectTo } from '~/lib/utils/url_utility';
-import ManageSast from '~/security_configuration/components/manage_sast.vue';
-import configureSastMutation from '~/security_configuration/graphql/configure_sast.mutation.graphql';
-
-jest.mock('~/lib/utils/url_utility', () => ({
-  redirectTo: jest.fn(),
-}));
-
-Vue.use(VueApollo);
-
-describe('Manage Sast Component', () => {
-  let wrapper;
-
-  const findButton = () => wrapper.findComponent(GlButton);
-  const successHandler = async () => {
-    return {
-      data: {
-        configureSast: {
-          successPath: 'testSuccessPath',
-          errors: [],
-          __typename: 'ConfigureSastPayload',
-        },
-      },
-    };
-  };
-
-  const noSuccessPathHandler = async () => {
-    return {
-      data: {
-        configureSast: {
-          successPath: '',
-          errors: [],
-          __typename: 'ConfigureSastPayload',
-        },
-      },
-    };
-  };
-
-  const errorHandler = async () => {
-    return {
-      data: {
-        configureSast: {
-          successPath: 'testSuccessPath',
-          errors: ['foo'],
-          __typename: 'ConfigureSastPayload',
-        },
-      },
-    };
-  };
-
-  const pendingHandler = () => new Promise(() => {});
-
-  function createMockApolloProvider(handler) {
-    const requestHandlers = [[configureSastMutation, handler]];
-
-    return createMockApollo(requestHandlers);
-  }
-
-  function createComponent(options = {}) {
-    const { mockApollo } = options;
-    wrapper = extendedWrapper(
-      mount(ManageSast, {
-        apolloProvider: mockApollo,
-      }),
-    );
-  }
-
-  afterEach(() => {
-    wrapper.destroy();
-    wrapper = null;
-  });
-
-  it('should render Button with correct text', () => {
-    createComponent();
-    expect(findButton().text()).toContain('Configure via merge request');
-  });
-
-  describe('given a successful response', () => {
-    beforeEach(() => {
-      const mockApollo = createMockApolloProvider(successHandler);
-      createComponent({ mockApollo });
-    });
-
-    it('should call redirect helper with correct value', async () => {
-      await wrapper.trigger('click');
-      await waitForPromises();
-      expect(redirectTo).toHaveBeenCalledTimes(1);
-      expect(redirectTo).toHaveBeenCalledWith('testSuccessPath');
-      // This is done for UX reasons. If the loading prop is set to false
-      // on success, then there's a period where the button is clickable
-      // again. Instead, we want the button to display a loading indicator
-      // for the remainder of the lifetime of the page (i.e., until the
-      // browser can start painting the new page it's been redirected to).
-      expect(findButton().props().loading).toBe(true);
-    });
-  });
-
-  describe('given a pending response', () => {
-    beforeEach(() => {
-      const mockApollo = createMockApolloProvider(pendingHandler);
-      createComponent({ mockApollo });
-    });
-
-    it('renders spinner correctly', async () => {
-      expect(findButton().props('loading')).toBe(false);
-      await wrapper.trigger('click');
-      await waitForPromises();
-      expect(findButton().props('loading')).toBe(true);
-    });
-  });
-
-  describe.each`
-    handler                 | message
-    ${noSuccessPathHandler} | ${'SAST merge request creation mutation failed'}
-    ${errorHandler}         | ${'foo'}
-  `('given an error response', ({ handler, message }) => {
-    beforeEach(() => {
-      const mockApollo = createMockApolloProvider(handler);
-      createComponent({ mockApollo });
-    });
-
-    it('should catch and emit error', async () => {
-      await wrapper.trigger('click');
-      await waitForPromises();
-      expect(wrapper.emitted('error')).toEqual([[message]]);
-      expect(findButton().props('loading')).toBe(false);
-    });
-  });
-});
diff --git a/spec/frontend/security_configuration/upgrade_spec.js b/spec/frontend/security_configuration/upgrade_spec.js
index 1f0cc795fc52..20bb38aa4699 100644
--- a/spec/frontend/security_configuration/upgrade_spec.js
+++ b/spec/frontend/security_configuration/upgrade_spec.js
@@ -1,5 +1,5 @@
 import { mount } from '@vue/test-utils';
-import { UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
+import { UPGRADE_CTA } from '~/security_configuration/components/constants';
 import Upgrade from '~/security_configuration/components/upgrade.vue';
 
 const TEST_URL = 'http://www.example.test';
diff --git a/ee/spec/frontend/security_configuration/components/apollo_mocks.js b/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
similarity index 100%
rename from ee/spec/frontend/security_configuration/components/apollo_mocks.js
rename to spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
diff --git a/ee/spec/frontend/security_configuration/components/manage_via_mr_spec.js b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
similarity index 93%
rename from ee/spec/frontend/security_configuration/components/manage_via_mr_spec.js
rename to spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
index 30a7414fba95..29e0da7f6ed3 100644
--- a/ee/spec/frontend/security_configuration/components/manage_via_mr_spec.js
+++ b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
@@ -2,13 +2,13 @@ import { GlButton } from '@gitlab/ui';
 import { mount } from '@vue/test-utils';
 import Vue from 'vue';
 import VueApollo from 'vue-apollo';
-import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue';
 import configureDependencyScanningMutation from 'ee/security_configuration/graphql/configure_dependency_scanning.mutation.graphql';
 import configureSecretDetectionMutation from 'ee/security_configuration/graphql/configure_secret_detection.mutation.graphql';
 import createMockApollo from 'helpers/mock_apollo_helper';
 import { extendedWrapper } from 'helpers/vue_test_utils_helper';
 import waitForPromises from 'helpers/wait_for_promises';
 import { redirectTo } from '~/lib/utils/url_utility';
+import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
 import {
   REPORT_TYPE_DEPENDENCY_SCANNING,
   REPORT_TYPE_SECRET_DETECTION,
@@ -23,15 +23,12 @@ describe('ManageViaMr component', () => {
   let wrapper;
 
   const findButton = () => wrapper.findComponent(GlButton);
-
   describe.each`
-    featureName              | featureType                        | mutation                               | mutationType
+    featureName              | featureType                        | mutation                               | mutationId
     ${'Dependency Scanning'} | ${REPORT_TYPE_DEPENDENCY_SCANNING} | ${configureDependencyScanningMutation} | ${'configureDependencyScanning'}
     ${'Secret Detection'}    | ${REPORT_TYPE_SECRET_DETECTION}    | ${configureSecretDetectionMutation}    | ${'configureSecretDetection'}
-  `('$featureType', ({ featureName, featureType, mutation, mutationType }) => {
-    const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(
-      mutationType,
-    );
+  `('$featureType', ({ featureName, mutation, featureType, mutationId }) => {
+    const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(mutationId);
     const successHandler = async () => buildConfigureSecurityFeatureMock();
     const noSuccessPathHandler = async () =>
       buildConfigureSecurityFeatureMock({
@@ -53,11 +50,14 @@ describe('ManageViaMr component', () => {
       wrapper = extendedWrapper(
         mount(ManageViaMr, {
           apolloProvider: mockApollo,
+          provide: {
+            projectPath: 'testProjectPath',
+          },
           propsData: {
             feature: {
               name: featureName,
-              configured: isFeatureConfigured,
               type: featureType,
+              configured: isFeatureConfigured,
             },
           },
         }),
-- 
GitLab