From 677bf84e6da0b3c6044bcaac0d849fbe21b85d17 Mon Sep 17 00:00:00 2001
From: Sascha Eggenberger <seggenberger@gitlab.com>
Date: Fri, 16 Feb 2024 08:30:40 +0000
Subject: [PATCH] Pipeline MiniGraph: Migrate dropdown to GlDisclosureDropdown

Changelog: changed
---
 .../common/private/job_action_component.vue   |  4 +-
 .../pipeline_mini_graph/legacy_job_item.vue   | 89 ++++++++++-------
 .../legacy_pipeline_stage.vue                 | 99 ++++++++++---------
 .../page_bundles/_pipeline_mixins.scss        | 10 +-
 .../stylesheets/page_bundles/pipelines.scss   |  2 +-
 ..._adds_merge_request_to_merge_train_spec.rb |  4 +-
 .../commit/mini_pipeline_graph_spec.rb        |  6 +-
 .../projects/pipelines/pipelines_spec.rb      | 60 ++++++++---
 .../legacy_pipeline_stage_spec.js             | 16 +--
 .../ci/pipelines_page/pipelines_spec.js       | 15 +--
 .../mr_widget_options_spec.js                 |  3 +
 11 files changed, 181 insertions(+), 127 deletions(-)

diff --git a/app/assets/javascripts/ci/common/private/job_action_component.vue b/app/assets/javascripts/ci/common/private/job_action_component.vue
index c266e061513c4..0c9d195aa8523 100644
--- a/app/assets/javascripts/ci/common/private/job_action_component.vue
+++ b/app/assets/javascripts/ci/common/private/job_action_component.vue
@@ -80,7 +80,9 @@ export default {
      * different apps it avoids repetition & complexity.
      *
      */
-    onClickAction() {
+    onClickAction(e) {
+      e.preventDefault();
+
       if (this.withConfirmationModal) {
         this.$emit('showActionConfirmationModal');
       } else {
diff --git a/app/assets/javascripts/ci/pipeline_mini_graph/legacy_job_item.vue b/app/assets/javascripts/ci/pipeline_mini_graph/legacy_job_item.vue
index 4238f0e3872fe..248b09c9d2a41 100644
--- a/app/assets/javascripts/ci/pipeline_mini_graph/legacy_job_item.vue
+++ b/app/assets/javascripts/ci/pipeline_mini_graph/legacy_job_item.vue
@@ -1,5 +1,5 @@
 <script>
-import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import { GlDisclosureDropdownItem, GlTooltipDirective } from '@gitlab/ui';
 import ActionComponent from '~/ci/common/private/job_action_component.vue';
 import JobNameComponent from '~/ci/common/private/job_name_component.vue';
 import { ICONS } from '~/ci/constants';
@@ -44,7 +44,7 @@ export default {
   components: {
     ActionComponent,
     JobNameComponent,
-    GlLink,
+    GlDisclosureDropdownItem,
   },
   directives: {
     GlTooltip: GlTooltipDirective,
@@ -72,8 +72,14 @@ export default {
     },
   },
   computed: {
-    boundary() {
-      return this.dropdownLength === 1 ? 'viewport' : 'scrollParent';
+    alternativeTooltipConfig() {
+      const boundary = this.dropdownLength === 1 ? 'viewport' : 'scrollParent';
+
+      return {
+        boundary,
+        placement: 'bottom',
+        customClass: 'gl-pointer-events-none',
+      };
     },
     detailsPath() {
       return this.status?.details_path;
@@ -81,9 +87,18 @@ export default {
     hasDetails() {
       return this.status?.has_details;
     },
+    item() {
+      return {
+        text: this.job.name,
+        href: this.hasDetails ? this.detailsPath : '',
+      };
+    },
     status() {
       return this.job?.status ? this.job.status : {};
     },
+    tooltipConfig() {
+      return this.hasDetails ? this.$options.tooltipConfig : this.alternativeTooltipConfig;
+    },
     tooltipText() {
       const textBuilder = [];
       const { name: jobName } = this.job;
@@ -123,6 +138,9 @@ export default {
         ? this.$options.i18n.runAgainTooltipText
         : title;
     },
+    testid() {
+      return this.hasDetails ? 'job-with-link' : 'job-without-link';
+    },
   },
   errorCaptured(err, _vm, info) {
     reportToSentry('pipelines_job_item', `pipelines_job_item error: ${err}, info: ${info}`);
@@ -130,37 +148,38 @@ export default {
 };
 </script>
 <template>
-  <div
-    class="ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between"
+  <gl-disclosure-dropdown-item
+    :item="item"
+    class="ci-job-component"
+    :class="[
+      cssClassJobName,
+      {
+        'js-pipeline-graph-job-link gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none': hasDetails,
+        'js-job-component-tooltip non-details-job-component': !hasDetails,
+      },
+    ]"
+    :data-testid="testid"
   >
-    <gl-link
-      v-if="hasDetails"
-      v-gl-tooltip="$options.tooltipConfig"
-      :href="detailsPath"
-      :title="tooltipText"
-      :class="cssClassJobName"
-      class="js-pipeline-graph-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none"
-      data-testid="job-with-link"
-    >
-      <job-name-component :name="job.name" :status="job.status" />
-    </gl-link>
-
-    <div
-      v-else
-      v-gl-tooltip="{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }"
-      :title="tooltipText"
-      :class="cssClassJobName"
-      class="js-job-component-tooltip non-details-job-component menu-item"
-      data-testid="job-without-link"
-    >
-      <job-name-component :name="job.name" :status="job.status" />
-    </div>
+    <template #list-item>
+      <div
+        class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-mt-n2 gl-mb-n2 gl-ml-n2"
+      >
+        <job-name-component
+          v-gl-tooltip="tooltipConfig"
+          :title="tooltipText"
+          :name="job.name"
+          :status="job.status"
+          data-testid="job-name"
+        />
 
-    <action-component
-      v-if="hasJobAction"
-      :tooltip-text="jobActionTooltipText"
-      :link="status.action.path"
-      :action-icon="status.action.icon"
-    />
-  </div>
+        <action-component
+          v-if="hasJobAction"
+          :tooltip-text="jobActionTooltipText"
+          :link="status.action.path"
+          :action-icon="status.action.icon"
+          class="gl-mt-n2 gl-mr-n2"
+        />
+      </div>
+    </template>
+  </gl-disclosure-dropdown-item>
 </template>
diff --git a/app/assets/javascripts/ci/pipeline_mini_graph/legacy_pipeline_stage.vue b/app/assets/javascripts/ci/pipeline_mini_graph/legacy_pipeline_stage.vue
index 38a071a031930..2c8cd23ae1cb8 100644
--- a/app/assets/javascripts/ci/pipeline_mini_graph/legacy_pipeline_stage.vue
+++ b/app/assets/javascripts/ci/pipeline_mini_graph/legacy_pipeline_stage.vue
@@ -12,7 +12,7 @@
  * 4. Commit widget
  */
 
-import { GlDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlDisclosureDropdown, GlButton, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
 import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
 import { createAlert } from '~/alert';
 import eventHub from '~/ci/event_hub';
@@ -28,14 +28,11 @@ export default {
     stage: __('Stage:'),
     viewStageLabel: __('View Stage: %{title}'),
   },
-  dropdownPopperOpts: {
-    placement: 'bottom',
-    positionFixed: true,
-  },
   components: {
     CiIcon,
     GlLoadingIcon,
-    GlDropdown,
+    GlDisclosureDropdown,
+    GlButton,
     LegacyJobItem,
   },
   directives: {
@@ -95,7 +92,7 @@ export default {
           this.isLoading = false;
         })
         .catch(() => {
-          this.$refs.dropdown.hide();
+          this.$refs.dropdown.close();
           this.isLoading = false;
 
           createAlert({
@@ -111,58 +108,66 @@ export default {
 </script>
 
 <template>
-  <gl-dropdown
+  <gl-disclosure-dropdown
     ref="dropdown"
-    v-gl-tooltip.hover.ds0
-    v-gl-tooltip="stage.title"
     data-testid="mini-pipeline-graph-dropdown"
+    class="mini-pipeline-graph-dropdown"
     variant="link"
     :aria-label="stageAriaLabel(stage.title)"
-    :lazy="true"
-    :popper-opts="$options.dropdownPopperOpts"
-    :toggle-class="['gl-rounded-full!']"
-    menu-class="mini-pipeline-graph-dropdown-menu"
-    @hide="onHideDropdown"
-    @show="onShowDropdown"
+    no-caret
+    @hidden="onHideDropdown"
+    @shown="onShowDropdown"
   >
-    <template #button-content>
-      <ci-icon :status="stage.status" :show-tooltip="false" :use-link="false" class="gl-mb-0!" />
+    <template #toggle>
+      <gl-button
+        v-gl-tooltip.ds0="isDropdownOpen ? '' : stage.title"
+        variant="link"
+        class="gl-rounded-full!"
+        data-testid="mini-pipeline-graph-dropdown-toggle"
+      >
+        <ci-icon :status="stage.status" :show-tooltip="false" :use-link="false" class="gl-mb-0!" />
+      </gl-button>
     </template>
-    <div v-if="isLoading" class="gl--flex-center gl-p-2" data-testid="pipeline-stage-loading-state">
+
+    <template #header>
+      <div
+        class="gl-display-flex gl-align-items-center gl-p-4! gl-min-h-8 gl-border-b-1 gl-border-b-solid gl-border-b-gray-200 gl-font-sm gl-font-weight-bold gl-line-height-1"
+      >
+        <span class="gl-mr-1">{{ $options.i18n.stage }}</span>
+        <span data-testid="pipeline-stage-dropdown-menu-title">{{ stageName }}</span>
+      </div>
+    </template>
+
+    <div
+      v-if="isLoading"
+      class="gl-display-flex gl-py-3 gl-px-4"
+      data-testid="pipeline-stage-loading-state"
+    >
       <gl-loading-icon size="sm" class="gl-mr-3" />
       <p class="gl-line-height-normal gl-mb-0">{{ $options.i18n.loadingText }}</p>
     </div>
     <ul
       v-else
-      class="js-builds-dropdown-list scrollable-menu"
+      class="mini-pipeline-graph-dropdown-menu gl-overflow-y-auto gl-m-0 gl-p-0"
       data-testid="mini-pipeline-graph-dropdown-menu-list"
     >
-      <div class="gl--flex-center gl-border-b gl-font-weight-bold gl-mb-3 gl-pb-3">
-        <span class="gl-mr-1">{{ $options.i18n.stage }}</span>
-        <span data-testid="pipeline-stage-dropdown-menu-title">{{ stageName }}</span>
-      </div>
-      <li v-for="job in dropdownContent" :key="job.id">
-        <legacy-job-item
-          :dropdown-length="dropdownContent.length"
-          :job="job"
-          css-class-job-name="pipeline-job-item"
-        />
-      </li>
-      <template v-if="isMergeTrain">
-        <li class="gl-dropdown-divider" role="presentation">
-          <hr role="separator" aria-orientation="horizontal" class="dropdown-divider" />
-        </li>
-        <li>
-          <div
-            class="gl-display-flex gl-align-items-center"
-            data-testid="warning-message-merge-trains"
-          >
-            <div class="menu-item gl-font-sm gl-text-gray-300!">
-              {{ $options.i18n.mergeTrainMessage }}
-            </div>
-          </div>
-        </li>
-      </template>
+      <legacy-job-item
+        v-for="job in dropdownContent"
+        :key="job.id"
+        :dropdown-length="dropdownContent.length"
+        :job="job"
+        css-class-job-name="pipeline-job-item"
+      />
     </ul>
-  </gl-dropdown>
+
+    <template #footer>
+      <div
+        v-if="!isLoading && isMergeTrain"
+        class="gl-font-sm gl-text-secondary gl-py-3 gl-px-4 gl-border-t"
+        data-testid="warning-message-merge-trains"
+      >
+        {{ $options.i18n.mergeTrainMessage }}
+      </div>
+    </template>
+  </gl-disclosure-dropdown>
 </template>
diff --git a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
index a904afa7337bd..83245e2659af2 100644
--- a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
@@ -21,15 +21,7 @@
  - mini graph in Commit widget pipeline
 */
 @mixin pipeline-graph-dropdown-menu() {
-  width: auto;
-  max-width: 400px;
-
-  // override dropdown.scss
-  &.dropdown-menu li button,
-  &.dropdown-menu li a.ci-action-icon-container {
-    padding: 0;
-    text-align: center;
-  }
+  max-height: $gl-max-dropdown-max-height;
 
   .ci-action-icon-container {
     position: absolute;
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index d61e3f859959e..a441b92da2954 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -78,7 +78,7 @@
       border-bottom: 2px solid $gray-200;
       position: absolute;
       right: -4px;
-      top: 11px;
+      top: 12px;
       width: 4px;
     }
   }
diff --git a/ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb b/ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb
index 3cd69e305d3a4..163a9da84bbd1 100644
--- a/ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb
+++ b/ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb
@@ -75,8 +75,8 @@
         expect(page).to have_selector('[data-testid="mini-pipeline-graph-dropdown"]')
       end
 
-      it 'does not allow retry for merge train pipeline' do
-        find('[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle').click
+      it 'does not allow retry for merge train pipeline', :js do
+        find_by_testid('mini-pipeline-graph-dropdown-toggle').click
         page.within '.ci-job-component' do
           expect(page).to have_selector('[data-testid="ci-icon"]')
           expect(page).not_to have_selector('.retry')
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index c0a8dabf648cc..a8f65399a3508 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -31,7 +31,7 @@
     end
   end
 
-  context 'when commit has pipelines and feature flag is disabled' do
+  context 'when commit has pipelines and feature flag is disabled', :js do
     let(:pipeline) do
       create(
         :ci_pipeline,
@@ -58,11 +58,11 @@
     it 'displays a mini pipeline graph' do
       expect(page).to have_selector('[data-testid="commit-box-pipeline-mini-graph"]')
 
-      first('[data-testid="mini-pipeline-graph-dropdown"]').click
+      find_by_testid('mini-pipeline-graph-dropdown-toggle').click
 
       wait_for_requests
 
-      page.within '.js-builds-dropdown-list' do
+      within_testid('mini-pipeline-graph-dropdown') do
         expect(page).to have_selector('[data-testid="status_running_borderless-icon"]')
         expect(page).to have_content(build.stage_name)
       end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 00bb9141aa042..a9e7a50136d76 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -269,7 +269,7 @@
         end
       end
 
-      context 'with manual actions' do
+      context 'with manual actions', :js do
         let!(:manual) do
           create(:ci_build, :manual,
             pipeline: pipeline,
@@ -286,7 +286,7 @@
         end
 
         it 'has link to the manual action' do
-          find('[data-testid="pipelines-manual-actions-dropdown"]').click
+          find_by_testid('pipelines-manual-actions-dropdown').click
 
           wait_for_requests
 
@@ -295,11 +295,13 @@
 
         context 'when manual action was played' do
           before do
-            find('[data-testid="pipelines-manual-actions-dropdown"] button').click
+            find_by_testid('pipelines-manual-actions-dropdown').click
 
             wait_for_requests
 
             click_button('manual build')
+
+            wait_for_all_requests
           end
 
           it 'enqueues manual action job', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/409984' do
@@ -325,8 +327,8 @@
           expect(page).to have_selector('[data-testid="pipelines-manual-actions-dropdown"] [data-testid="play-icon"]')
         end
 
-        it "has link to the delayed job's action" do
-          find('[data-testid="pipelines-manual-actions-dropdown"] button').click
+        it "has link to the delayed job's action", :js do
+          find_by_testid('pipelines-manual-actions-dropdown').click
 
           wait_for_requests
 
@@ -344,8 +346,8 @@
               stage: 'test')
           end
 
-          it "shows 00:00:00 as the remaining time" do
-            find('[data-testid="pipelines-manual-actions-dropdown"] button').click
+          it "shows 00:00:00 as the remaining time", :js do
+            find_by_testid('pipelines-manual-actions-dropdown').click
 
             wait_for_requests
 
@@ -535,32 +537,60 @@
           expect(page).to have_selector(dropdown_selector)
         end
 
-        context 'when clicking a stage badge' do
+        context 'when clicking a stage badge', :js do
           it 'opens a dropdown' do
-            find(dropdown_selector).click
+            find_by_testid('mini-pipeline-graph-dropdown-toggle').click
+
+            wait_for_requests
 
             expect(page).to have_link build.name
           end
 
           it 'is possible to cancel pending build' do
-            find(dropdown_selector).click
-            find('.js-ci-action').click
+            find_by_testid('mini-pipeline-graph-dropdown-toggle').click
+
+            wait_for_requests
+
+            find_by_testid('ci-action-button').click
             wait_for_requests
 
             expect(build.reload).to be_canceled
           end
+
+          context 'manual job', :js do
+            let!(:build) do
+              create(:ci_build, :manual, pipeline: pipeline, stage: 'build', name: 'manual-build')
+            end
+
+            it 'is possible to play manual build' do
+              find_by_testid('mini-pipeline-graph-dropdown-toggle').click
+
+              wait_for_requests
+
+              within first('[data-testid="job-with-link"]') do
+                expect(find_by_testid('play-icon')).to be_visible
+              end
+
+              find_by_testid('ci-action-button').click
+              wait_for_requests
+
+              expect(find('[data-testid="mini-pipeline-graph-dropdown-toggle"][aria-expanded="true"]')).to be_visible
+            end
+          end
         end
 
-        context 'for a failed pipeline' do
+        context 'for a failed pipeline', :js do
           let!(:build) do
             create(:ci_build, :failed, pipeline: pipeline, stage: 'build', name: 'build')
           end
 
           it 'displays the failure reason' do
-            find(dropdown_selector).click
+            find_by_testid('mini-pipeline-graph-dropdown-toggle').click
+
+            wait_for_requests
 
-            within('.js-builds-dropdown-list') do
-              build_element = page.find('.pipeline-job-item')
+            within_testid('mini-pipeline-graph-dropdown') do
+              build_element = page.find('.pipeline-job-item [data-testid="job-name"]')
               expect(build_element['title']).to eq('build - failed - (unknown failure)')
             end
           end
diff --git a/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js b/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js
index 95fa82adc9e5c..666d42e56b29f 100644
--- a/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js
+++ b/spec/frontend/ci/pipeline_mini_graph/legacy_pipeline_stage_spec.js
@@ -1,4 +1,4 @@
-import { GlDropdown } from '@gitlab/ui';
+import { GlDisclosureDropdown } from '@gitlab/ui';
 import { nextTick } from 'vue';
 import { mount } from '@vue/test-utils';
 import MockAdapter from 'axios-mock-adapter';
@@ -53,8 +53,9 @@ describe('Pipelines stage component', () => {
 
   const findCiActionBtn = () => wrapper.find('.js-ci-action');
   const findCiIcon = () => wrapper.findComponent(CiIcon);
-  const findDropdown = () => wrapper.findComponent(GlDropdown);
-  const findDropdownToggle = () => wrapper.find('button.dropdown-toggle');
+  const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
+  const findDropdownToggle = () =>
+    wrapper.find('[data-testid="mini-pipeline-graph-dropdown-toggle"]');
   const findDropdownMenu = () =>
     wrapper.find('[data-testid="mini-pipeline-graph-dropdown-menu-list"]');
   const findDropdownMenuTitle = () =>
@@ -78,8 +79,9 @@ describe('Pipelines stage component', () => {
     });
 
     it('displays loading state while jobs are being fetched', async () => {
-      jest.runOnlyPendingTimers();
-      await nextTick();
+      // eslint-disable-next-line no-restricted-syntax
+      wrapper.setData({ isLoading: true });
+      await waitForPromises();
 
       expect(findLoadingState().exists()).toBe(true);
       expect(findLoadingState().text()).toBe(LegacyPipelineStage.i18n.loadingText);
@@ -144,7 +146,7 @@ describe('Pipelines stage component', () => {
       await axios.waitForAll();
       await waitForPromises();
 
-      expect(findDropdown().classes('show')).toBe(false);
+      expect(findDropdownToggle().attributes('aria-expanded')).toBe('false');
     });
   });
 
@@ -197,7 +199,7 @@ describe('Pipelines stage component', () => {
       await clickCiAction();
       await waitForPromises();
 
-      expect(findDropdown().classes('show')).toBe(true);
+      expect(findDropdownToggle().attributes('aria-expanded')).toBe('true');
     });
   });
 
diff --git a/spec/frontend/ci/pipelines_page/pipelines_spec.js b/spec/frontend/ci/pipelines_page/pipelines_spec.js
index f3c28b17339ae..6a87e6a6dd5a1 100644
--- a/spec/frontend/ci/pipelines_page/pipelines_spec.js
+++ b/spec/frontend/ci/pipelines_page/pipelines_spec.js
@@ -96,7 +96,7 @@ describe('Pipelines', () => {
   const findCiLintButton = () => wrapper.findByTestId('ci-lint-button');
   const findCleanCacheButton = () => wrapper.findByTestId('clear-cache-button');
   const findStagesDropdownToggle = () =>
-    wrapper.find('[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle');
+    wrapper.find('.mini-pipeline-graph-dropdown [data-testid="base-dropdown-toggle"]');
   const findPipelineUrlLinks = () => wrapper.findAll('[data-testid="pipeline-url-link"]');
 
   const createComponent = ({ props = {}, withPermissions = true } = {}) => {
@@ -769,6 +769,11 @@ describe('Pipelines', () => {
           .onGet(mockPipelineWithStages.details.stages[0].dropdown_path)
           .reply(HTTP_STATUS_OK, stageReply);
 
+        // cancelMock is getting overwritten in pipelines_service.js#L29
+        // so we have to spy on it again here
+        cancelMock = { cancel: jest.fn() };
+        jest.spyOn(axios.CancelToken, 'source').mockReturnValue(cancelMock);
+
         createComponent();
 
         stopMock = jest.spyOn(window, 'clearTimeout');
@@ -789,13 +794,9 @@ describe('Pipelines', () => {
           await findStagesDropdownToggle().trigger('click');
           jest.runOnlyPendingTimers();
 
-          // cancelMock is getting overwritten in pipelines_service.js#L29
-          // so we have to spy on it again here
-          cancelMock = jest.spyOn(axios.CancelToken, 'source');
-
           await waitForPromises();
 
-          expect(cancelMock).toHaveBeenCalled();
+          expect(cancelMock.cancel).toHaveBeenCalled();
           expect(stopMock).toHaveBeenCalled();
           expect(restartMock).toHaveBeenCalledWith(
             `${mockPipelinesResponse.pipelines[0].path}/stage.json?stage=build`,
@@ -807,7 +808,7 @@ describe('Pipelines', () => {
           jest.runOnlyPendingTimers();
           await waitForPromises();
 
-          expect(cancelMock).not.toHaveBeenCalled();
+          expect(cancelMock.cancel).not.toHaveBeenCalled();
           expect(stopMock).toHaveBeenCalled();
           expect(restartMock).toHaveBeenCalledWith(
             `${mockPipelinesResponse.pipelines[0].path}/stage.json?stage=build`,
diff --git a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
index 0f30c850020c5..fe66aa315688f 100644
--- a/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js
@@ -38,6 +38,7 @@ import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_
 import getStateQuery from '~/vue_merge_request_widget/queries/get_state.query.graphql';
 import getStateSubscription from '~/vue_merge_request_widget/queries/get_state.subscription.graphql';
 import readyToMergeSubscription from '~/vue_merge_request_widget/queries/states/ready_to_merge.subscription.graphql';
+import securityReportMergeRequestDownloadPathsQuery from '~/vue_merge_request_widget/extensions/security_reports/graphql/security_report_merge_request_download_paths.query.graphql';
 import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql';
 import approvalsQuery from 'ee_else_ce/vue_merge_request_widget/components/approvals/queries/approvals.query.graphql';
 import approvedBySubscription from 'ee_else_ce/vue_merge_request_widget/components/approvals/queries/approvals.subscription.graphql';
@@ -123,6 +124,8 @@ describe('MrWidgetOptions', () => {
         conflictsStateQuery,
         jest.fn().mockResolvedValue({ data: { project: { mergeRequest: {} } } }),
       ],
+      [securityReportMergeRequestDownloadPathsQuery, jest.fn().mockResolvedValue(null)],
+      ...(options.apolloMock || []),
     ];
     const subscriptionHandlers = [
       [approvedBySubscription, () => mockedApprovalsSubscription],
-- 
GitLab