diff --git a/ee/app/assets/javascripts/monitoring/components/alert_widget.vue b/ee/app/assets/javascripts/monitoring/components/alert_widget.vue
index 577cb72acbddf0f6e3b2fe81a414867c87883ce3..bd46e52f8344efef182f3dcbcedc2d109a4a0a42 100644
--- a/ee/app/assets/javascripts/monitoring/components/alert_widget.vue
+++ b/ee/app/assets/javascripts/monitoring/components/alert_widget.vue
@@ -54,18 +54,11 @@ export default {
         .map(this.formatAlertSummary)
         .join(', ');
     },
-    supportsComputedAlerts() {
-      return gon.features && gon.features.prometheusComputedAlerts;
-    },
   },
   created() {
     this.service = new AlertsService({ alertsEndpoint: this.alertsEndpoint });
     this.fetchAlertData();
   },
-  beforeDestroy() {
-    // clean up external event listeners
-    document.removeEventListener('click', this.handleOutsideClick);
-  },
   methods: {
     fetchAlertData() {
       this.isLoading = true;
diff --git a/ee/spec/frontend/monitoring/alert_widget_spec.js b/ee/spec/frontend/monitoring/alert_widget_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..334f8db3ef2f8d02246e8af36de81324d543fa3e
--- /dev/null
+++ b/ee/spec/frontend/monitoring/alert_widget_spec.js
@@ -0,0 +1,245 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import createFlash from '~/flash';
+import AlertWidget from 'ee/monitoring/components/alert_widget.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+const mockReadAlert = jest.fn();
+const mockCreateAlert = jest.fn();
+const mockUpdateAlert = jest.fn();
+const mockDeleteAlert = jest.fn();
+
+jest.mock('~/flash');
+jest.mock(
+  'ee/monitoring/services/alerts_service',
+  () =>
+    function AlertsServiceMock() {
+      return {
+        readAlert: mockReadAlert,
+        createAlert: mockCreateAlert,
+        updateAlert: mockUpdateAlert,
+        deleteAlert: mockDeleteAlert,
+      };
+    },
+);
+
+describe('AlertWidget', () => {
+  let wrapper;
+
+  const metricId = '5';
+  const alertPath = 'my/alert.json';
+  const relevantQueries = [{ metricId, label: 'alert-label', alert_path: alertPath }];
+
+  const defaultProps = {
+    alertsEndpoint: '',
+    relevantQueries,
+    alertsToManage: {},
+    modalId: 'alert-modal-1',
+  };
+
+  const propsWithAlert = {
+    relevantQueries,
+  };
+
+  const propsWithAlertData = {
+    relevantQueries,
+    alertsToManage: {
+      [alertPath]: { operator: '>', threshold: 42, alert_path: alertPath, metricId },
+    },
+  };
+
+  const createComponent = propsData => {
+    wrapper = shallowMount(AlertWidget, {
+      propsData: {
+        ...defaultProps,
+        ...propsData,
+      },
+      sync: false,
+    });
+  };
+  const findWidgetForm = () => wrapper.find({ ref: 'widgetForm' });
+  const findAlertErrorMessage = () => wrapper.find('.alert-error-message');
+  const findCurrentSettings = () => wrapper.find('.alert-current-setting');
+
+  afterEach(() => {
+    jest.clearAllMocks();
+    wrapper.destroy();
+    wrapper = null;
+  });
+
+  it('displays a loading spinner and disables form when fetching alerts', () => {
+    let resolveReadAlert;
+    mockReadAlert.mockReturnValue(
+      new Promise(resolve => {
+        resolveReadAlert = resolve;
+      }),
+    );
+    createComponent(defaultProps);
+    return wrapper.vm
+      .$nextTick()
+      .then(() => {
+        expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
+        expect(findWidgetForm().props('disabled')).toBe(true);
+
+        resolveReadAlert({ operator: '=', threshold: 42 });
+      })
+      .then(() => waitForPromises())
+      .then(() => {
+        expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(false);
+        expect(findWidgetForm().props('disabled')).toBe(false);
+      });
+  });
+
+  it('displays an error message when fetch fails', () => {
+    mockReadAlert.mockRejectedValue();
+    createComponent(propsWithAlert);
+
+    expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
+
+    return waitForPromises().then(() => {
+      expect(createFlash).toHaveBeenCalled();
+      expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(false);
+    });
+  });
+
+  it('displays an alert summary when there is a single alert', () => {
+    mockReadAlert.mockResolvedValue({ operator: '>', threshold: 42 });
+    createComponent(propsWithAlertData);
+
+    expect(wrapper.text()).toContain('alert-label > 42');
+  });
+
+  it('displays a combined alert summary when there are multiple alerts', () => {
+    mockReadAlert.mockResolvedValue({ operator: '>', threshold: 42 });
+    const propsWithManyAlerts = {
+      relevantQueries: relevantQueries.concat([
+        { metricId: '6', alert_path: 'my/alert2.json', label: 'alert-label2' },
+      ]),
+      alertsToManage: {
+        'my/alert.json': {
+          operator: '>',
+          threshold: 42,
+          alert_path: alertPath,
+          metricId,
+        },
+        'my/alert2.json': {
+          operator: '=',
+          threshold: 900,
+          alert_path: 'my/alert2.json',
+          metricId: '6',
+        },
+      },
+    };
+    createComponent(propsWithManyAlerts);
+
+    expect(findCurrentSettings().text()).toEqual('alert-label > 42, alert-label2 = 900');
+  });
+
+  it('creates an alert with an appropriate handler', () => {
+    const alertParams = {
+      operator: '<',
+      threshold: 4,
+      prometheus_metric_id: '5',
+    };
+    mockReadAlert.mockResolvedValue({ operator: '>', threshold: 42 });
+    const fakeAlertPath = 'foo/bar';
+    mockCreateAlert.mockResolvedValue({ alert_path: fakeAlertPath, ...alertParams });
+    createComponent({
+      alertsToManage: {
+        [fakeAlertPath]: {
+          alert_path: fakeAlertPath,
+          operator: '<',
+          threshold: 4,
+          prometheus_metric_id: '5',
+          metricId: '5',
+        },
+      },
+    });
+
+    findWidgetForm().vm.$emit('create', alertParams);
+
+    expect(mockCreateAlert).toHaveBeenCalledWith(alertParams);
+  });
+
+  it('updates an alert with an appropriate handler', () => {
+    const alertParams = { operator: '<', threshold: 4, alert_path: alertPath };
+    const newAlertParams = { operator: '=', threshold: 12 };
+    mockReadAlert.mockResolvedValue(alertParams);
+    mockUpdateAlert.mockResolvedValue({ ...alertParams, ...newAlertParams });
+    createComponent({
+      ...propsWithAlertData,
+      alertsToManage: {
+        [alertPath]: {
+          alert_path: alertPath,
+          operator: '=',
+          threshold: 12,
+          metricId: '5',
+        },
+      },
+    });
+
+    findWidgetForm().vm.$emit('update', {
+      alert: alertPath,
+      ...newAlertParams,
+      prometheus_metric_id: '5',
+    });
+
+    expect(mockUpdateAlert).toHaveBeenCalledWith(alertPath, newAlertParams);
+  });
+
+  it('deletes an alert with an appropriate handler', () => {
+    const alertParams = { alert_path: alertPath, operator: '>', threshold: 42 };
+    mockReadAlert.mockResolvedValue(alertParams);
+    mockDeleteAlert.mockResolvedValue({});
+    createComponent({
+      ...propsWithAlert,
+      alertsToManage: {
+        [alertPath]: {
+          alert_path: alertPath,
+          operator: '>',
+          threshold: 42,
+          metricId: '5',
+        },
+      },
+    });
+
+    findWidgetForm().vm.$emit('delete', { alert: alertPath });
+
+    expect(mockDeleteAlert).toHaveBeenCalledWith(alertPath);
+    expect(findAlertErrorMessage().exists()).toBe(false);
+  });
+
+  describe('when delete fails', () => {
+    beforeEach(() => {
+      const alertParams = { alert_path: alertPath, operator: '>', threshold: 42 };
+      mockReadAlert.mockResolvedValue(alertParams);
+      mockDeleteAlert.mockRejectedValue();
+
+      createComponent({
+        ...propsWithAlert,
+        alertsToManage: {
+          [alertPath]: {
+            alert_path: alertPath,
+            operator: '>',
+            threshold: 42,
+            metricId: '5',
+          },
+        },
+      });
+
+      findWidgetForm().vm.$emit('delete', { alert: alertPath });
+    });
+
+    it('shows error message', () => {
+      expect(findAlertErrorMessage().text()).toEqual('Error deleting alert');
+    });
+
+    it('dismisses error message on cancel', () => {
+      findWidgetForm().vm.$emit('cancel');
+
+      return wrapper.vm.$nextTick().then(() => {
+        expect(findAlertErrorMessage().exists()).toBe(false);
+      });
+    });
+  });
+});
diff --git a/ee/spec/javascripts/monitoring/alert_widget_spec.js b/ee/spec/javascripts/monitoring/alert_widget_spec.js
deleted file mode 100644
index e46c8a0dfbc4bd215652c966253eec4147db141b..0000000000000000000000000000000000000000
--- a/ee/spec/javascripts/monitoring/alert_widget_spec.js
+++ /dev/null
@@ -1,219 +0,0 @@
-import Vue from 'vue';
-import AlertWidget from 'ee/monitoring/components/alert_widget.vue';
-import AlertsService from 'ee/monitoring/services/alerts_service';
-import mountComponent from 'spec/helpers/vue_mount_component_helper';
-import waitForPromises from 'spec/helpers/wait_for_promises';
-
-describe('AlertWidget', () => {
-  let AlertWidgetComponent;
-  let vm;
-
-  const metricId = '5';
-  const alertPath = 'my/alert.json';
-  const relevantQueries = [{ metricId, label: 'alert-label', alert_path: alertPath }];
-
-  const props = {
-    alertsEndpoint: '',
-    relevantQueries,
-    alertsToManage: {},
-    modalId: 'alert-modal-1',
-  };
-
-  const propsWithAlert = {
-    ...props,
-    relevantQueries,
-  };
-
-  const propsWithAlertData = {
-    ...props,
-    relevantQueries,
-    alertsToManage: {
-      [alertPath]: { operator: '>', threshold: 42, alert_path: alertPath, metricId },
-    },
-  };
-
-  const mockSetAlerts = (path, data) => {
-    const alerts = data ? { [path]: data } : {};
-    Vue.set(vm, 'alertsToManage', alerts);
-  };
-
-  beforeAll(() => {
-    AlertWidgetComponent = Vue.extend(AlertWidget);
-  });
-
-  beforeEach(() => {
-    setFixtures('<div id="alert-widget"></div>');
-  });
-
-  afterEach(() => {
-    if (vm) vm.$destroy();
-  });
-
-  it('displays a loading spinner when fetching alerts', done => {
-    let resolveReadAlert;
-
-    spyOn(AlertsService.prototype, 'readAlert').and.returnValue(
-      new Promise(resolve => {
-        resolveReadAlert = resolve;
-      }),
-    );
-    vm = mountComponent(AlertWidgetComponent, propsWithAlert, '#alert-widget');
-
-    // expect loading spinner to exist during fetch
-    expect(vm.isLoading).toBeTruthy();
-    expect(vm.$refs.widgetForm.$props.disabled).toBe(true);
-
-    expect(vm.$el.querySelector('.loading-container')).toBeVisible();
-
-    resolveReadAlert({ operator: '=', threshold: 42 });
-
-    // expect loading spinner to go away after fetch
-    setTimeout(() =>
-      vm.$nextTick(() => {
-        expect(vm.isLoading).toEqual(false);
-        expect(vm.$el.querySelector('.loading-container')).toBeHidden();
-        expect(vm.$refs.widgetForm.$props.disabled).toBe(false);
-        done();
-      }),
-    );
-  });
-
-  it('displays an error message when fetch fails', done => {
-    const spy = spyOnDependency(AlertWidget, 'createFlash');
-    spyOn(AlertsService.prototype, 'readAlert').and.returnValue(Promise.reject());
-    vm = mountComponent(AlertWidgetComponent, propsWithAlert, '#alert-widget');
-
-    setTimeout(() =>
-      vm.$nextTick(() => {
-        expect(vm.isLoading).toEqual(false);
-        expect(spy).toHaveBeenCalled();
-        done();
-      }),
-    );
-  });
-
-  it('displays an alert summary when there is a single alert', () => {
-    spyOn(AlertsService.prototype, 'readAlert').and.returnValue(
-      Promise.resolve({ operator: '>', threshold: 42 }),
-    );
-    vm = mountComponent(AlertWidgetComponent, propsWithAlertData, '#alert-widget');
-
-    expect(vm.alertSummary).toBe('alert-label > 42');
-    expect(vm.$el.querySelector('.alert-current-setting')).toBeVisible();
-  });
-
-  it('displays a combined alert summary when there are multiple alerts', () => {
-    spyOn(AlertsService.prototype, 'readAlert').and.returnValue(
-      Promise.resolve({ operator: '>', threshold: 42 }),
-    );
-    const propsWithManyAlerts = {
-      ...props,
-      relevantQueries: relevantQueries.concat([
-        { metricId: '6', alert_path: 'my/alert2.json', label: 'alert-label2' },
-      ]),
-      alertsToManage: {
-        'my/alert.json': {
-          operator: '>',
-          threshold: 42,
-          alert_path: alertPath,
-          metricId,
-        },
-        'my/alert2.json': {
-          operator: '=',
-          threshold: 900,
-          alert_path: 'my/alert2.json',
-          metricId: '6',
-        },
-      },
-    };
-    vm = mountComponent(AlertWidgetComponent, propsWithManyAlerts, '#alert-widget');
-
-    expect(vm.alertSummary).toBe('alert-label > 42, alert-label2 = 900');
-    expect(vm.$el.querySelector('.alert-current-setting')).toBeVisible();
-  });
-
-  it('creates an alert with an appropriate handler', done => {
-    const alertParams = {
-      operator: '<',
-      threshold: 4,
-      prometheus_metric_id: '5',
-    };
-
-    spyOn(AlertsService.prototype, 'createAlert').and.returnValue(
-      Promise.resolve({ alert_path: 'foo/bar', ...alertParams }),
-    );
-
-    vm = mountComponent(AlertWidgetComponent, props);
-    vm.$on('setAlerts', mockSetAlerts);
-
-    vm.$refs.widgetForm.$emit('create', alertParams);
-
-    expect(AlertsService.prototype.createAlert).toHaveBeenCalledWith(alertParams);
-
-    waitForPromises()
-      .then(() => {
-        expect(vm.isLoading).toEqual(false);
-        done();
-      })
-      .catch(done.fail);
-  });
-
-  it('dismisses error message when action is cancelled', () => {
-    vm = mountComponent(AlertWidgetComponent, props);
-    vm.$on('setAlerts', mockSetAlerts);
-    vm.errorMessage = 'Mock error message.';
-
-    // widget modal is dismissed
-    vm.$refs.widgetForm.$emit('cancel');
-
-    expect(vm.errorMessage).toBe(null);
-  });
-
-  it('updates an alert with an appropriate handler', done => {
-    const alertParams = { operator: '<', threshold: 4, alert_path: alertPath };
-    const newAlertParams = { operator: '=', threshold: 12 };
-
-    spyOn(AlertsService.prototype, 'readAlert').and.returnValue(Promise.resolve(alertParams));
-    spyOn(AlertsService.prototype, 'updateAlert').and.returnValue(
-      Promise.resolve({ ...alertParams, ...newAlertParams }),
-    );
-
-    vm = mountComponent(AlertWidgetComponent, propsWithAlertData);
-    vm.$on('setAlerts', mockSetAlerts);
-
-    vm.$refs.widgetForm.$emit('update', {
-      alert: alertPath,
-      ...newAlertParams,
-      prometheus_metric_id: '5',
-    });
-
-    expect(AlertsService.prototype.updateAlert).toHaveBeenCalledWith(alertPath, newAlertParams);
-    waitForPromises()
-      .then(() => {
-        expect(vm.isLoading).toEqual(false);
-        done();
-      })
-      .catch(done.fail);
-  });
-
-  it('deletes an alert with an appropriate handler', done => {
-    const alertParams = { alert_path: alertPath, operator: '>', threshold: 42 };
-
-    spyOn(AlertsService.prototype, 'readAlert').and.returnValue(Promise.resolve(alertParams));
-    spyOn(AlertsService.prototype, 'deleteAlert').and.returnValue(Promise.resolve({}));
-
-    vm = mountComponent(AlertWidgetComponent, propsWithAlert);
-    vm.$on('setAlerts', mockSetAlerts);
-
-    vm.$refs.widgetForm.$emit('delete', { alert: alertPath });
-
-    expect(AlertsService.prototype.deleteAlert).toHaveBeenCalledWith(alertPath);
-    waitForPromises()
-      .then(() => {
-        expect(vm.isLoading).toEqual(false);
-        expect(vm.alertSummary).toBeFalsy();
-        done();
-      })
-      .catch(done.fail);
-  });
-});