From 7b8b657c56e3dd63bb080f9a1ea68a021f49c93a Mon Sep 17 00:00:00 2001
From: Kyle Wiebers <kwiebers@gitlab.com>
Date: Fri, 23 Apr 2021 15:55:43 +0000
Subject: [PATCH] Revert "Merge branch
 'jnnkl-refactor-manage-via-mr-to-shared-components' into 'master'"

This reverts merge request !59001
---
 .../components/configuration_table.vue        |  15 +-
 .../components/manage_sast.vue                |  59 ++++++++
 .../{constants.js => scanners_constants.js}   |  16 ---
 .../components/upgrade.vue                    |   2 +-
 .../security_configuration/components/app.vue |   2 +-
 .../components/constants.js                   |  18 +--
 .../components/manage_feature.vue             |   2 +-
 .../components/manage_via_mr.vue              |  21 ++-
 .../graphql}/provider.js                      |   0
 .../security_configuration/index.js           |   4 -
 .../security/configuration/show.html.haml     |   1 -
 .../components/apollo_mocks.js                |   0
 .../components/app_spec.js                    |   2 +-
 .../components/helpers.js                     |   2 +-
 .../components/manage_feature_spec.js         |   2 +-
 .../components/manage_via_mr_spec.js          |  16 +--
 .../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 +-
 21 files changed, 247 insertions(+), 79 deletions(-)
 create mode 100644 app/assets/javascripts/security_configuration/components/manage_sast.vue
 rename app/assets/javascripts/security_configuration/components/{constants.js => scanners_constants.js} (91%)
 rename {app/assets/javascripts/vue_shared => ee/app/assets/javascripts}/security_configuration/components/manage_via_mr.vue (75%)
 rename {app/assets/javascripts/vue_shared/security_configuration => ee/app/assets/javascripts/security_configuration/graphql}/provider.js (100%)
 rename {spec/frontend/vue_shared/security_reports => ee/spec/frontend/security_configuration}/components/apollo_mocks.js (100%)
 rename {spec/frontend/vue_shared/security_reports => ee/spec/frontend/security_configuration}/components/manage_via_mr_spec.js (93%)
 create mode 100644 spec/frontend/security_configuration/manage_sast_spec.js

diff --git a/app/assets/javascripts/security_configuration/components/configuration_table.vue b/app/assets/javascripts/security_configuration/components/configuration_table.vue
index 2110af1522b8..4a3f988296cb 100644
--- a/app/assets/javascripts/security_configuration/components/configuration_table.vue
+++ b/app/assets/javascripts/security_configuration/components/configuration_table.vue
@@ -1,7 +1,6 @@
 <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,
@@ -12,8 +11,8 @@ import {
   REPORT_TYPE_API_FUZZING,
   REPORT_TYPE_LICENSE_COMPLIANCE,
 } from '~/vue_shared/security_reports/constants';
-
-import { scanners } from './constants';
+import ManageSast from './manage_sast.vue';
+import { scanners } from './scanners_constants';
 import Upgrade from './upgrade.vue';
 
 const borderClasses = 'gl-border-b-1! gl-border-b-solid! gl-border-gray-100!';
@@ -41,7 +40,7 @@ export default {
     },
     getComponentForItem(item) {
       const COMPONENTS = {
-        [REPORT_TYPE_SAST]: ManageViaMR,
+        [REPORT_TYPE_SAST]: ManageSast,
         [REPORT_TYPE_DAST]: Upgrade,
         [REPORT_TYPE_DAST_PROFILES]: Upgrade,
         [REPORT_TYPE_DEPENDENCY_SCANNING]: Upgrade,
@@ -50,6 +49,7 @@ export default {
         [REPORT_TYPE_API_FUZZING]: Upgrade,
         [REPORT_TYPE_LICENSE_COMPLIANCE]: Upgrade,
       };
+
       return COMPONENTS[item.type];
     },
   },
@@ -95,12 +95,7 @@ export default {
       </template>
 
       <template #cell(manage)="{ item }">
-        <component
-          :is="getComponentForItem(item)"
-          :feature="item"
-          :data-testid="item.type"
-          @error="onError"
-        />
+        <component :is="getComponentForItem(item)" :data-testid="item.type" @error="onError" />
       </template>
     </gl-table>
   </div>
diff --git a/app/assets/javascripts/security_configuration/components/manage_sast.vue b/app/assets/javascripts/security_configuration/components/manage_sast.vue
new file mode 100644
index 000000000000..8a8827b41cd4
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/components/manage_sast.vue
@@ -0,0 +1,59 @@
+<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/constants.js b/app/assets/javascripts/security_configuration/components/scanners_constants.js
similarity index 91%
rename from app/assets/javascripts/security_configuration/components/constants.js
rename to app/assets/javascripts/security_configuration/components/scanners_constants.js
index 3cdcac4c0b40..9846df0b4bf7 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/scanners_constants.js
@@ -1,7 +1,6 @@
 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,
@@ -135,18 +134,3 @@ 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/upgrade.vue b/app/assets/javascripts/security_configuration/components/upgrade.vue
index 2541c29224af..518eb57731d0 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 './constants';
+import { UPGRADE_CTA } from './scanners_constants';
 
 export default {
   components: {
diff --git a/ee/app/assets/javascripts/security_configuration/components/app.vue b/ee/app/assets/javascripts/security_configuration/components/app.vue
index 4e63daad88a6..439fdc862d25 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/constants';
+import { scanners } from '~/security_configuration/components/scanners_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 0495d12d545e..c6fb66bbb082 100644
--- a/ee/app/assets/javascripts/security_configuration/components/constants.js
+++ b/ee/app/assets/javascripts/security_configuration/components/constants.js
@@ -1,5 +1,4 @@
 import { s__ } from '~/locale';
-import { featureToMutationMap as featureToMutationMapCE } from '~/security_configuration/components/constants';
 import {
   REPORT_TYPE_DEPENDENCY_SCANNING,
   REPORT_TYPE_SECRET_DETECTION,
@@ -24,21 +23,12 @@ export const CUSTOM_VALUE_MESSAGE = s__(
 );
 
 export const featureToMutationMap = {
-  ...featureToMutationMapCE,
   [REPORT_TYPE_DEPENDENCY_SCANNING]: {
-    mutationId: 'configureDependencyScanning',
-    getMutationPayload: (projectPath) => ({
-      mutation: configureDependencyScanningMutation,
-      variables: {
-        fullPath: projectPath,
-      },
-    }),
+    type: 'configureDependencyScanning',
+    mutation: configureDependencyScanningMutation,
   },
   [REPORT_TYPE_SECRET_DETECTION]: {
-    mutationId: 'configureSecretDetection',
-    getMutationPayload: (projectPath) => ({
-      mutation: configureSecretDetectionMutation,
-      variables: { fullPath: projectPath },
-    }),
+    type: 'configureSecretDetection',
+    mutation: configureSecretDetectionMutation,
   },
 };
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 43283bf2bafe..536492e4b7a1 100644
--- a/ee/app/assets/javascripts/security_configuration/components/manage_feature.vue
+++ b/ee/app/assets/javascripts/security_configuration/components/manage_feature.vue
@@ -1,7 +1,6 @@
 <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,
@@ -9,6 +8,7 @@ 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/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue b/ee/app/assets/javascripts/security_configuration/components/manage_via_mr.vue
similarity index 75%
rename from app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
rename to ee/app/assets/javascripts/security_configuration/components/manage_via_mr.vue
index de7374ff4b28..a0a3b1aa2ad2 100644
--- a/app/assets/javascripts/vue_shared/security_configuration/components/manage_via_mr.vue
+++ b/ee/app/assets/javascripts/security_configuration/components/manage_via_mr.vue
@@ -1,16 +1,21 @@
 <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 '../provider';
+import apolloProvider from '../graphql/provider';
+import { featureToMutationMap } from './constants';
 
 export default {
   apolloProvider,
   components: {
     GlButton,
   },
-  inject: ['projectPath'],
+  inject: {
+    projectPath: {
+      from: 'projectPath',
+      default: '',
+    },
+  },
   props: {
     feature: {
       type: Object,
@@ -31,9 +36,13 @@ export default {
     async mutate() {
       this.isLoading = true;
       try {
-        const mutation = this.featureSettings;
-        const { data } = await this.$apollo.mutate(mutation.getMutationPayload(this.projectPath));
-        const { errors, successPath } = data[mutation.mutationId];
+        const { data } = await this.$apollo.mutate({
+          mutation: this.featureSettings.mutation,
+          variables: {
+            fullPath: this.projectPath,
+          },
+        });
+        const { errors, successPath } = data[this.featureSettings.type];
 
         if (errors.length > 0) {
           throw new Error(errors[0]);
diff --git a/app/assets/javascripts/vue_shared/security_configuration/provider.js b/ee/app/assets/javascripts/security_configuration/graphql/provider.js
similarity index 100%
rename from app/assets/javascripts/vue_shared/security_configuration/provider.js
rename to ee/app/assets/javascripts/security_configuration/graphql/provider.js
diff --git a/ee/app/assets/javascripts/security_configuration/index.js b/ee/app/assets/javascripts/security_configuration/index.js
index 3dbdcc096690..ab15b0309d17 100644
--- a/ee/app/assets/javascripts/security_configuration/index.js
+++ b/ee/app/assets/javascripts/security_configuration/index.js
@@ -18,7 +18,6 @@ export const initSecurityConfiguration = (el) => {
     containerScanningHelpPath,
     dependencyScanningHelpPath,
     toggleAutofixSettingEndpoint,
-    projectPath,
     gitlabCiHistoryPath,
   } = el.dataset;
 
@@ -27,9 +26,6 @@ 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 42afe03b5c46..cf849bff30a7 100644
--- a/ee/app/views/projects/security/configuration/show.html.haml
+++ b/ee/app/views/projects/security/configuration/show.html.haml
@@ -6,7 +6,6 @@
 - 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/spec/frontend/vue_shared/security_reports/components/apollo_mocks.js b/ee/spec/frontend/security_configuration/components/apollo_mocks.js
similarity index 100%
rename from spec/frontend/vue_shared/security_reports/components/apollo_mocks.js
rename to ee/spec/frontend/security_configuration/components/apollo_mocks.js
diff --git a/ee/spec/frontend/security_configuration/components/app_spec.js b/ee/spec/frontend/security_configuration/components/app_spec.js
index d3dd47684f07..cebba404035e 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/constants';
+import { scanners } from '~/security_configuration/components/scanners_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 56cd4d80f94d..ec0db18a36a9 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/constants';
+import { scanners } from '~/security_configuration/components/scanners_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 0607c91db308..bed3633cd652 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 '~/vue_shared/security_configuration/components/manage_via_mr.vue';
+import ManageViaMr from 'ee/security_configuration/components/manage_via_mr.vue';
 import {
   REPORT_TYPE_DAST_PROFILES,
   REPORT_TYPE_DEPENDENCY_SCANNING,
diff --git a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js b/ee/spec/frontend/security_configuration/components/manage_via_mr_spec.js
similarity index 93%
rename from spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
rename to ee/spec/frontend/security_configuration/components/manage_via_mr_spec.js
index 29e0da7f6ed3..30a7414fba95 100644
--- a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
+++ b/ee/spec/frontend/security_configuration/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,12 +23,15 @@ describe('ManageViaMr component', () => {
   let wrapper;
 
   const findButton = () => wrapper.findComponent(GlButton);
+
   describe.each`
-    featureName              | featureType                        | mutation                               | mutationId
+    featureName              | featureType                        | mutation                               | mutationType
     ${'Dependency Scanning'} | ${REPORT_TYPE_DEPENDENCY_SCANNING} | ${configureDependencyScanningMutation} | ${'configureDependencyScanning'}
     ${'Secret Detection'}    | ${REPORT_TYPE_SECRET_DETECTION}    | ${configureSecretDetectionMutation}    | ${'configureSecretDetection'}
-  `('$featureType', ({ featureName, mutation, featureType, mutationId }) => {
-    const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(mutationId);
+  `('$featureType', ({ featureName, featureType, mutation, mutationType }) => {
+    const buildConfigureSecurityFeatureMock = buildConfigureSecurityFeatureMockFactory(
+      mutationType,
+    );
     const successHandler = async () => buildConfigureSecurityFeatureMock();
     const noSuccessPathHandler = async () =>
       buildConfigureSecurityFeatureMock({
@@ -50,14 +53,11 @@ describe('ManageViaMr component', () => {
       wrapper = extendedWrapper(
         mount(ManageViaMr, {
           apolloProvider: mockApollo,
-          provide: {
-            projectPath: 'testProjectPath',
-          },
           propsData: {
             feature: {
               name: featureName,
-              type: featureType,
               configured: isFeatureConfigured,
+              type: featureType,
             },
           },
         }),
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 cbe1b145d4ba..89929df64eb4 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,7 +10,9 @@ 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');
+jest.mock('~/lib/utils/url_utility', () => ({
+  redirectTo: jest.fn(),
+}));
 
 const projectPath = 'group/project';
 const sastAnalyzersDocumentationPath = '/help/sast/analyzers';
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ad604a97cfb2..7e3ac99655e4 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -28169,6 +28169,9 @@ 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 ""
 
@@ -28205,6 +28208,9 @@ 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 fbd72265c4b9..a1789052c929 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/constants';
+import { scanners, UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
 
 import {
   REPORT_TYPE_SAST,
@@ -12,13 +12,7 @@ describe('Configuration Table Component', () => {
   let wrapper;
 
   const createComponent = () => {
-    wrapper = extendedWrapper(
-      mount(ConfigurationTable, {
-        provide: {
-          projectPath: 'testProjectPath',
-        },
-      }),
-    );
+    wrapper = extendedWrapper(mount(ConfigurationTable, {}));
   };
 
   const findHelpLinks = () => wrapper.findAll('[data-testid="help-link"]');
@@ -36,10 +30,8 @@ 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).exists()).toBe(false);
-      } else {
+        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()).toMatchInterpolatedText(UPGRADE_CTA);
       }
     });
diff --git a/spec/frontend/security_configuration/manage_sast_spec.js b/spec/frontend/security_configuration/manage_sast_spec.js
new file mode 100644
index 000000000000..15a57210246e
--- /dev/null
+++ b/spec/frontend/security_configuration/manage_sast_spec.js
@@ -0,0 +1,136 @@
+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 20bb38aa4699..1f0cc795fc52 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/constants';
+import { UPGRADE_CTA } from '~/security_configuration/components/scanners_constants';
 import Upgrade from '~/security_configuration/components/upgrade.vue';
 
 const TEST_URL = 'http://www.example.test';
-- 
GitLab