diff --git a/app/assets/javascripts/ci/constants.js b/app/assets/javascripts/ci/constants.js
index 138a44a8dd08441ad55cd7495c8de198425e05b1..53c7f87b441d735c004a8409ea6bb82eb30817cc 100644
--- a/app/assets/javascripts/ci/constants.js
+++ b/app/assets/javascripts/ci/constants.js
@@ -4,6 +4,7 @@ export const forwardDeploymentFailureModalId = 'forward-deployment-failure';
 
 export const BUTTON_TOOLTIP_RETRY = __('Retry all failed or cancelled jobs');
 export const BUTTON_TOOLTIP_CANCEL = __('Cancel the running pipeline');
+export const BUTTON_TOOLTIP_DELETE = __('Delete the pipeline');
 
 export const FILTER_TAG_IDENTIFIER = 'tag';
 
diff --git a/app/assets/javascripts/ci/pipeline_details/header/components/header_actions.vue b/app/assets/javascripts/ci/pipeline_details/header/components/header_actions.vue
new file mode 100644
index 0000000000000000000000000000000000000000..c9d358f509cf70e4e930b1b7b148736f3d722b2c
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_details/header/components/header_actions.vue
@@ -0,0 +1,128 @@
+<script>
+import { GlButton, GlModal, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
+import { BUTTON_TOOLTIP_CANCEL, BUTTON_TOOLTIP_DELETE, BUTTON_TOOLTIP_RETRY } from '~/ci/constants';
+import { __ } from '~/locale';
+import { DELETE_MODAL_ID } from '../constants';
+
+export default {
+  name: 'HeaderActions',
+  BUTTON_TOOLTIP_CANCEL,
+  BUTTON_TOOLTIP_DELETE,
+  BUTTON_TOOLTIP_RETRY,
+  modal: {
+    id: DELETE_MODAL_ID,
+    actionPrimary: {
+      text: __('Delete pipeline'),
+      attributes: {
+        variant: 'danger',
+      },
+    },
+    actionCancel: {
+      text: __('Cancel'),
+    },
+  },
+  components: {
+    GlButton,
+    GlModal,
+  },
+  directives: {
+    GlModal: GlModalDirective,
+    GlTooltip: GlTooltipDirective,
+  },
+  props: {
+    pipeline: {
+      type: Object,
+      required: true,
+    },
+    isRetrying: {
+      type: Boolean,
+      required: true,
+    },
+    isDeleting: {
+      type: Boolean,
+      required: true,
+    },
+    isCanceling: {
+      type: Boolean,
+      required: true,
+    },
+  },
+  computed: {
+    canRetryPipeline() {
+      const { retryable, userPermissions } = this.pipeline;
+
+      return retryable && userPermissions.updatePipeline;
+    },
+    canCancelPipeline() {
+      const { cancelable, userPermissions } = this.pipeline;
+
+      return cancelable && userPermissions.cancelPipeline;
+    },
+  },
+};
+</script>
+
+<template>
+  <div>
+    <div class="gl-mt-5 gl-lg-mt-0 gl-display-flex gl-align-items-flex-start gl-gap-3">
+      <gl-button
+        v-if="canRetryPipeline"
+        v-gl-tooltip
+        :aria-label="$options.BUTTON_TOOLTIP_RETRY"
+        :title="$options.BUTTON_TOOLTIP_RETRY"
+        :loading="isRetrying"
+        :disabled="isRetrying"
+        variant="confirm"
+        data-testid="retry-pipeline"
+        @click="$emit('retryPipeline', pipeline.id)"
+      >
+        {{ __('Retry') }}
+      </gl-button>
+
+      <gl-button
+        v-if="canCancelPipeline"
+        v-gl-tooltip
+        :aria-label="$options.BUTTON_TOOLTIP_CANCEL"
+        :title="$options.BUTTON_TOOLTIP_CANCEL"
+        :loading="isCanceling"
+        :disabled="isCanceling"
+        variant="danger"
+        data-testid="cancel-pipeline"
+        @click="$emit('cancelPipeline', pipeline.id)"
+      >
+        {{ __('Cancel pipeline') }}
+      </gl-button>
+
+      <gl-button
+        v-if="pipeline.userPermissions.destroyPipeline"
+        v-gl-tooltip
+        v-gl-modal="$options.modal.id"
+        :aria-label="$options.BUTTON_TOOLTIP_DELETE"
+        :title="$options.BUTTON_TOOLTIP_DELETE"
+        :loading="isDeleting"
+        :disabled="isDeleting"
+        variant="danger"
+        category="secondary"
+        data-testid="delete-pipeline"
+      >
+        {{ __('Delete') }}
+      </gl-button>
+    </div>
+
+    <gl-modal
+      :modal-id="$options.modal.id"
+      :title="__('Delete pipeline')"
+      :action-primary="$options.modal.actionPrimary"
+      :action-cancel="$options.modal.actionCancel"
+      @primary="$emit('deletePipeline', pipeline.id)"
+    >
+      <p>
+        {{
+          __(
+            'Are you sure you want to delete this pipeline? Doing so will expire all pipeline caches and delete all related objects, such as builds, logs, artifacts, and triggers. This action cannot be undone.',
+          )
+        }}
+      </p>
+    </gl-modal>
+  </div>
+</template>
diff --git a/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue b/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue
index 87fd12c09f8ebdb39433d93f35158fd3d82a5797..114d3d0babd1512e97e5fc6143b7768f99ec0210 100644
--- a/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue
+++ b/app/assets/javascripts/ci/pipeline_details/header/pipeline_details_header.vue
@@ -2,12 +2,9 @@
 import {
   GlAlert,
   GlBadge,
-  GlButton,
   GlIcon,
   GlLink,
   GlLoadingIcon,
-  GlModal,
-  GlModalDirective,
   GlSprintf,
   GlTooltipDirective,
 } from '@gitlab/ui';
@@ -25,9 +22,9 @@ import cancelPipelineMutation from '../graphql/mutations/cancel_pipeline.mutatio
 import deletePipelineMutation from '../graphql/mutations/delete_pipeline.mutation.graphql';
 import retryPipelineMutation from '../graphql/mutations/retry_pipeline.mutation.graphql';
 import { getQueryHeaders } from '../graph/utils';
+import HeaderActions from './components/header_actions.vue';
 import getPipelineQuery from './graphql/queries/get_pipeline_header_data.query.graphql';
 import {
-  DELETE_MODAL_ID,
   POLL_INTERVAL,
   DETACHED_EVENT_TYPE,
   AUTO_DEVOPS_SOURCE,
@@ -48,16 +45,14 @@ export default {
     ClipboardButton,
     GlAlert,
     GlBadge,
-    GlButton,
     GlIcon,
     GlLink,
     GlLoadingIcon,
-    GlModal,
     GlSprintf,
+    HeaderActions,
     TimeAgoTooltip,
   },
   directives: {
-    GlModal: GlModalDirective,
     GlTooltip: GlTooltipDirective,
     SafeHtml,
   },
@@ -94,9 +89,6 @@ export default {
     stuckBadgeTooltip: s__('Pipelines|This pipeline is stuck'),
     computeMinutesTooltip: s__('Pipelines|Total amount of compute minutes used for the pipeline'),
     totalJobsTooltip: s__('Pipelines|Total number of jobs for the pipeline'),
-    retryPipelineText: __('Retry'),
-    cancelPipelineText: __('Cancel pipeline'),
-    deletePipelineText: __('Delete'),
     clipboardTooltip: __('Copy commit SHA'),
     finishedText: s__('Pipelines|finished'),
   },
@@ -106,22 +98,6 @@ export default {
     [DELETE_FAILURE]: __('An error occurred while deleting the pipeline.'),
     [DEFAULT]: __('An unknown error occurred.'),
   },
-  modal: {
-    id: DELETE_MODAL_ID,
-    title: __('Delete pipeline'),
-    deleteConfirmationText: __(
-      'Are you sure you want to delete this pipeline? Doing so will expire all pipeline caches and delete all related objects, such as builds, logs, artifacts, and triggers. This action cannot be undone.',
-    ),
-    actionPrimary: {
-      text: __('Delete pipeline'),
-      attributes: {
-        variant: 'danger',
-      },
-    },
-    actionCancel: {
-      text: __('Cancel'),
-    },
-  },
   inject: {
     graphqlResourceEtag: {
       default: '',
@@ -277,16 +253,6 @@ export default {
         queuedDuration: formatNumber(this.queuedDuration),
       });
     },
-    canRetryPipeline() {
-      const { retryable, userPermissions } = this.pipeline;
-
-      return retryable && userPermissions.updatePipeline;
-    },
-    canCancelPipeline() {
-      const { cancelable, userPermissions } = this.pipeline;
-
-      return cancelable && userPermissions.cancelPipeline;
-    },
     computeMinutes() {
       return this.pipeline?.computeMinutes;
     },
@@ -347,7 +313,7 @@ export default {
       this.failureType = errorType;
       this.failureMessages = errorMessages;
     },
-    async postPipelineAction(name, mutation) {
+    async postPipelineAction(name, mutation, id) {
       try {
         const {
           data: {
@@ -355,7 +321,7 @@ export default {
           },
         } = await this.$apollo.mutate({
           mutation,
-          variables: { id: this.pipeline.id },
+          variables: { id },
         });
 
         if (errors.length > 0) {
@@ -374,15 +340,15 @@ export default {
         this.reportFailure(POST_FAILURE);
       }
     },
-    cancelPipeline() {
+    cancelPipeline(id) {
       this.isCanceling = true;
-      this.postPipelineAction(this.$options.pipelineCancel, cancelPipelineMutation);
+      this.postPipelineAction(this.$options.pipelineCancel, cancelPipelineMutation, id);
     },
-    retryPipeline() {
+    retryPipeline(id) {
       this.isRetrying = true;
-      this.postPipelineAction(this.$options.pipelineRetry, retryPipelineMutation);
+      this.postPipelineAction(this.$options.pipelineRetry, retryPipelineMutation, id);
     },
-    async deletePipeline() {
+    async deletePipeline(id) {
       this.isDeleting = true;
       this.$apollo.queries.pipeline.stopPolling();
 
@@ -394,7 +360,7 @@ export default {
         } = await this.$apollo.mutate({
           mutation: deletePipelineMutation,
           variables: {
-            id: this.pipeline.id,
+            id,
           },
         });
 
@@ -627,59 +593,16 @@ export default {
           </div>
         </div>
       </div>
-      <div class="gl-mt-5 gl-lg-mt-0 gl-display-flex gl-align-items-flex-start gl-gap-3">
-        <gl-button
-          v-if="canRetryPipeline"
-          v-gl-tooltip
-          :aria-label="$options.BUTTON_TOOLTIP_RETRY"
-          :title="$options.BUTTON_TOOLTIP_RETRY"
-          :loading="isRetrying"
-          :disabled="isRetrying"
-          variant="confirm"
-          data-testid="retry-pipeline"
-          class="js-retry-button"
-          @click="retryPipeline()"
-        >
-          {{ $options.i18n.retryPipelineText }}
-        </gl-button>
 
-        <gl-button
-          v-if="canCancelPipeline"
-          v-gl-tooltip
-          :aria-label="$options.BUTTON_TOOLTIP_CANCEL"
-          :title="$options.BUTTON_TOOLTIP_CANCEL"
-          :loading="isCanceling"
-          :disabled="isCanceling"
-          variant="danger"
-          data-testid="cancel-pipeline"
-          @click="cancelPipeline()"
-        >
-          {{ $options.i18n.cancelPipelineText }}
-        </gl-button>
-
-        <gl-button
-          v-if="pipeline.userPermissions.destroyPipeline"
-          v-gl-modal="$options.modal.id"
-          :loading="isDeleting"
-          :disabled="isDeleting"
-          variant="danger"
-          category="secondary"
-          data-testid="delete-pipeline"
-        >
-          {{ $options.i18n.deletePipelineText }}
-        </gl-button>
-      </div>
+      <header-actions
+        :pipeline="pipeline"
+        :is-retrying="isRetrying"
+        :is-canceling="isCanceling"
+        :is-deleting="isDeleting"
+        @retryPipeline="retryPipeline($event)"
+        @cancelPipeline="cancelPipeline($event)"
+        @deletePipeline="deletePipeline($event)"
+      />
     </div>
-    <gl-modal
-      :modal-id="$options.modal.id"
-      :title="$options.modal.title"
-      :action-primary="$options.modal.actionPrimary"
-      :action-cancel="$options.modal.actionCancel"
-      @primary="deletePipeline()"
-    >
-      <p>
-        {{ $options.modal.deleteConfirmationText }}
-      </p>
-    </gl-modal>
   </div>
 </template>
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d43a57ae4967eb0c32a49132c0936645ee8d1ae8..14dcb70f6b55cbb4da79b6ef9aa01eef1e310d2c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16853,6 +16853,9 @@ msgstr ""
 msgid "Delete table"
 msgstr ""
 
+msgid "Delete the pipeline"
+msgstr ""
+
 msgid "Delete this attachment"
 msgstr ""
 
diff --git a/spec/frontend/ci/pipeline_details/header/components/header_actions_spec.js b/spec/frontend/ci/pipeline_details/header/components/header_actions_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a96dd61c542a62b849cdd04e169b4a1001cec2a0
--- /dev/null
+++ b/spec/frontend/ci/pipeline_details/header/components/header_actions_spec.js
@@ -0,0 +1,117 @@
+import { GlModal } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import HeaderActions from '~/ci/pipeline_details/header/components/header_actions.vue';
+import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, BUTTON_TOOLTIP_DELETE } from '~/ci/constants';
+import {
+  pipelineHeaderRunning,
+  pipelineHeaderRunningNoPermissions,
+  pipelineHeaderFailed,
+  pipelineHeaderFailedNoPermissions,
+} from '../../mock_data';
+
+describe('Header actions', () => {
+  let wrapper;
+  let glModalDirective;
+
+  const findModal = () => wrapper.findComponent(GlModal);
+  const findRetryButton = () => wrapper.findByTestId('retry-pipeline');
+  const findCancelButton = () => wrapper.findByTestId('cancel-pipeline');
+  const findDeleteButton = () => wrapper.findByTestId('delete-pipeline');
+
+  const defaultProps = {
+    isRetrying: false,
+    isCanceling: false,
+    isDeleting: false,
+  };
+
+  const createComponent = (props) => {
+    glModalDirective = jest.fn();
+
+    wrapper = shallowMountExtended(HeaderActions, {
+      propsData: {
+        ...props,
+        ...defaultProps,
+      },
+      directives: {
+        glModal: {
+          bind(_, { value }) {
+            glModalDirective(value);
+          },
+        },
+      },
+    });
+  };
+
+  describe('confirmation modal', () => {
+    it('should display modal when delete button is clicked', () => {
+      createComponent({ pipeline: pipelineHeaderFailed.data.project.pipeline });
+
+      findDeleteButton().vm.$emit('click');
+
+      expect(findModal().props('modalId')).toBe('pipeline-delete-modal');
+      expect(glModalDirective).toHaveBeenCalledWith('pipeline-delete-modal');
+    });
+  });
+
+  describe('events', () => {
+    it('emits the cancelPipeline event', () => {
+      createComponent({ pipeline: pipelineHeaderRunning.data.project.pipeline });
+
+      findCancelButton().vm.$emit('click');
+
+      expect(wrapper.emitted()).toEqual({
+        cancelPipeline: [[pipelineHeaderRunning.data.project.pipeline.id]],
+      });
+    });
+
+    it('emits the deletePipeline event', () => {
+      createComponent({ pipeline: pipelineHeaderFailed.data.project.pipeline });
+
+      findModal().vm.$emit('primary');
+
+      expect(wrapper.emitted()).toEqual({
+        deletePipeline: [[pipelineHeaderFailed.data.project.pipeline.id]],
+      });
+    });
+
+    it('emits the retryPipeline event', () => {
+      createComponent({ pipeline: pipelineHeaderFailed.data.project.pipeline });
+
+      findRetryButton().vm.$emit('click');
+
+      expect(wrapper.emitted()).toEqual({
+        retryPipeline: [[pipelineHeaderFailed.data.project.pipeline.id]],
+      });
+    });
+  });
+
+  describe('tooltips', () => {
+    it('displays retry and delete tooltip', () => {
+      createComponent({ pipeline: pipelineHeaderFailed.data.project.pipeline });
+
+      expect(findRetryButton().attributes('title')).toBe(BUTTON_TOOLTIP_RETRY);
+      expect(findDeleteButton().attributes('title')).toBe(BUTTON_TOOLTIP_DELETE);
+    });
+
+    it('displays cancel tooltip', () => {
+      createComponent({ pipeline: pipelineHeaderRunning.data.project.pipeline });
+
+      expect(findCancelButton().attributes('title')).toBe(BUTTON_TOOLTIP_CANCEL);
+    });
+  });
+
+  describe('permissions', () => {
+    it('cancel button is not visible', () => {
+      createComponent({ pipeline: pipelineHeaderRunningNoPermissions.data.project.pipeline });
+
+      expect(findCancelButton().exists()).toBe(false);
+    });
+
+    it('retry button and delete button are not visible', () => {
+      createComponent({ pipeline: pipelineHeaderFailedNoPermissions.data.project.pipeline });
+
+      expect(findRetryButton().exists()).toBe(false);
+      expect(findDeleteButton().exists()).toBe(false);
+    });
+  });
+});
diff --git a/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js b/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js
index d36d16d5de861bbd60bab5f1588428ad5687b9ae..86efa6c66e90046f83dbb409240d8e31acd5a9a9 100644
--- a/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js
+++ b/spec/frontend/ci/pipeline_details/header/pipeline_details_header_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert, GlBadge, GlLoadingIcon, GlModal, GlSprintf } from '@gitlab/ui';
+import { GlAlert, GlBadge, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
 import Vue, { nextTick } from 'vue';
 import VueApollo from 'vue-apollo';
 import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -6,17 +6,16 @@ import createMockApollo from 'helpers/mock_apollo_helper';
 import waitForPromises from 'helpers/wait_for_promises';
 import { getIdFromGraphQLId } from '~/graphql_shared/utils';
 import PipelineDetailsHeader from '~/ci/pipeline_details/header/pipeline_details_header.vue';
-import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL } from '~/ci/constants';
 import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
 import cancelPipelineMutation from '~/ci/pipeline_details/graphql/mutations/cancel_pipeline.mutation.graphql';
 import deletePipelineMutation from '~/ci/pipeline_details/graphql/mutations/delete_pipeline.mutation.graphql';
 import retryPipelineMutation from '~/ci/pipeline_details/graphql/mutations/retry_pipeline.mutation.graphql';
+import HeaderActions from '~/ci/pipeline_details/header/components/header_actions.vue';
 import getPipelineDetailsQuery from '~/ci/pipeline_details/header/graphql/queries/get_pipeline_header_data.query.graphql';
 import {
   pipelineHeaderSuccess,
   pipelineHeaderTrigger,
   pipelineHeaderRunning,
-  pipelineHeaderRunningNoPermissions,
   pipelineHeaderRunningWithDuration,
   pipelineHeaderFailed,
   pipelineRetryMutationResponseSuccess,
@@ -31,14 +30,10 @@ Vue.use(VueApollo);
 
 describe('Pipeline details header', () => {
   let wrapper;
-  let glModalDirective;
 
   const successHandler = jest.fn().mockResolvedValue(pipelineHeaderSuccess);
   const triggerHandler = jest.fn().mockResolvedValue(pipelineHeaderTrigger);
   const runningHandler = jest.fn().mockResolvedValue(pipelineHeaderRunning);
-  const runningHandlerNoPermissions = jest
-    .fn()
-    .mockResolvedValue(pipelineHeaderRunningNoPermissions);
   const runningHandlerWithDuration = jest.fn().mockResolvedValue(pipelineHeaderRunningWithDuration);
   const failedHandler = jest.fn().mockResolvedValue(pipelineHeaderFailed);
 
@@ -65,7 +60,7 @@ describe('Pipeline details header', () => {
   const findStatus = () => wrapper.findComponent(CiIcon);
   const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
   const findAllBadges = () => wrapper.findAllComponents(GlBadge);
-  const findDeleteModal = () => wrapper.findComponent(GlModal);
+  const findHeaderActions = () => wrapper.findComponent(HeaderActions);
   const findCreatedTimeAgo = () => wrapper.findByTestId('pipeline-created-time-ago');
   const findFinishedTimeAgo = () => wrapper.findByTestId('pipeline-finished-time-ago');
   const findFinishedCreatedTimeAgo = () =>
@@ -77,12 +72,13 @@ describe('Pipeline details header', () => {
   const findCommitCopyButton = () => wrapper.findByTestId('commit-copy-sha');
   const findPipelineRunningText = () => wrapper.findByTestId('pipeline-running-text').text();
   const findPipelineRefText = () => wrapper.findByTestId('pipeline-ref-text').text();
-  const findRetryButton = () => wrapper.findByTestId('retry-pipeline');
-  const findCancelButton = () => wrapper.findByTestId('cancel-pipeline');
-  const findDeleteButton = () => wrapper.findByTestId('delete-pipeline');
   const findPipelineUserLink = () => wrapper.findByTestId('pipeline-user-link');
   const findPipelineDuration = () => wrapper.findByTestId('pipeline-duration-text');
 
+  const clickActionButton = (action, id) => {
+    findHeaderActions().vm.$emit(action, id);
+  };
+
   const defaultHandlers = [[getPipelineDetailsQuery, successHandler]];
 
   const defaultProvideOptions = {
@@ -98,19 +94,10 @@ describe('Pipeline details header', () => {
   };
 
   const createComponent = (handlers = defaultHandlers) => {
-    glModalDirective = jest.fn();
-
     wrapper = shallowMountExtended(PipelineDetailsHeader, {
       provide: {
         ...defaultProvideOptions,
       },
-      directives: {
-        glModal: {
-          bind(_, { value }) {
-            glModalDirective(value);
-          },
-        },
-      },
       stubs: { GlSprintf },
       apolloProvider: createMockApolloProvider(handlers),
     });
@@ -281,6 +268,22 @@ describe('Pipeline details header', () => {
   });
 
   describe('actions', () => {
+    it('passes correct props to the header actions component', async () => {
+      createComponent([
+        [getPipelineDetailsQuery, failedHandler],
+        [retryPipelineMutation, retryMutationHandlerSuccess],
+      ]);
+
+      await waitForPromises();
+
+      expect(findHeaderActions().props()).toEqual({
+        isCanceling: false,
+        isDeleting: false,
+        isRetrying: false,
+        pipeline: pipelineHeaderFailed.data.project.pipeline,
+      });
+    });
+
     describe('retry action', () => {
       beforeEach(async () => {
         createComponent([
@@ -292,17 +295,13 @@ describe('Pipeline details header', () => {
       });
 
       it('should call retryPipeline Mutation with pipeline id', () => {
-        findRetryButton().vm.$emit('click');
+        clickActionButton('retryPipeline', pipelineHeaderFailed.data.project.pipeline.id);
 
         expect(retryMutationHandlerSuccess).toHaveBeenCalledWith({
           id: pipelineHeaderFailed.data.project.pipeline.id,
         });
         expect(findAlert().exists()).toBe(false);
       });
-
-      it('should render retry action tooltip', () => {
-        expect(findRetryButton().attributes('title')).toBe(BUTTON_TOOLTIP_RETRY);
-      });
     });
 
     describe('retry action failed', () => {
@@ -316,7 +315,7 @@ describe('Pipeline details header', () => {
       });
 
       it('should display error message on failure', async () => {
-        findRetryButton().vm.$emit('click');
+        clickActionButton('retryPipeline', pipelineHeaderFailed.data.project.pipeline.id);
 
         await waitForPromises();
 
@@ -324,15 +323,15 @@ describe('Pipeline details header', () => {
       });
 
       it('retry button loading state should reset on error', async () => {
-        findRetryButton().vm.$emit('click');
+        clickActionButton('retryPipeline', pipelineHeaderFailed.data.project.pipeline.id);
 
         await nextTick();
 
-        expect(findRetryButton().props('loading')).toBe(true);
+        expect(findHeaderActions().props('isRetrying')).toBe(true);
 
         await waitForPromises();
 
-        expect(findRetryButton().props('loading')).toBe(false);
+        expect(findHeaderActions().props('isRetrying')).toBe(false);
       });
     });
 
@@ -346,7 +345,7 @@ describe('Pipeline details header', () => {
 
           await waitForPromises();
 
-          findCancelButton().vm.$emit('click');
+          clickActionButton('cancelPipeline', pipelineHeaderRunning.data.project.pipeline.id);
 
           expect(cancelMutationHandlerSuccess).toHaveBeenCalledWith({
             id: pipelineHeaderRunning.data.project.pipeline.id,
@@ -354,17 +353,6 @@ describe('Pipeline details header', () => {
           expect(findAlert().exists()).toBe(false);
         });
 
-        it('should render cancel action tooltip', async () => {
-          createComponent([
-            [getPipelineDetailsQuery, runningHandler],
-            [cancelPipelineMutation, cancelMutationHandlerSuccess],
-          ]);
-
-          await waitForPromises();
-
-          expect(findCancelButton().attributes('title')).toBe(BUTTON_TOOLTIP_CANCEL);
-        });
-
         it('should display error message on failure', async () => {
           createComponent([
             [getPipelineDetailsQuery, runningHandler],
@@ -373,44 +361,16 @@ describe('Pipeline details header', () => {
 
           await waitForPromises();
 
-          findCancelButton().vm.$emit('click');
+          clickActionButton('cancelPipeline', pipelineHeaderRunning.data.project.pipeline.id);
 
           await waitForPromises();
 
           expect(findAlert().exists()).toBe(true);
         });
       });
-
-      describe('without permissions', () => {
-        it('should not display cancel pipeline button', async () => {
-          createComponent([[getPipelineDetailsQuery, runningHandlerNoPermissions]]);
-
-          await waitForPromises();
-
-          expect(findCancelButton().exists()).toBe(false);
-        });
-      });
     });
 
     describe('delete action', () => {
-      it('displays delete modal when clicking on delete and does not call the delete action', async () => {
-        createComponent([
-          [getPipelineDetailsQuery, successHandler],
-          [deletePipelineMutation, deleteMutationHandlerSuccess],
-        ]);
-
-        await waitForPromises();
-
-        findDeleteButton().vm.$emit('click');
-
-        const modalId = 'pipeline-delete-modal';
-
-        expect(findDeleteModal().props('modalId')).toBe(modalId);
-        expect(glModalDirective).toHaveBeenCalledWith(modalId);
-        expect(deleteMutationHandlerSuccess).not.toHaveBeenCalled();
-        expect(findAlert().exists()).toBe(false);
-      });
-
       it('should call deletePipeline Mutation with pipeline id when modal is submitted', async () => {
         createComponent([
           [getPipelineDetailsQuery, successHandler],
@@ -419,7 +379,7 @@ describe('Pipeline details header', () => {
 
         await waitForPromises();
 
-        findDeleteModal().vm.$emit('primary');
+        clickActionButton('deletePipeline', pipelineHeaderSuccess.data.project.pipeline.id);
 
         expect(deleteMutationHandlerSuccess).toHaveBeenCalledWith({
           id: pipelineHeaderSuccess.data.project.pipeline.id,
@@ -434,7 +394,7 @@ describe('Pipeline details header', () => {
 
         await waitForPromises();
 
-        findDeleteModal().vm.$emit('primary');
+        clickActionButton('deletePipeline', pipelineHeaderSuccess.data.project.pipeline.id);
 
         await waitForPromises();
 
diff --git a/spec/frontend/ci/pipeline_details/mock_data.js b/spec/frontend/ci/pipeline_details/mock_data.js
index b53f9e94deeb9e02544e7deab8bdc02b48eeae64..e93debcf56494f106349ca4ad20f28fc136ccf7f 100644
--- a/spec/frontend/ci/pipeline_details/mock_data.js
+++ b/spec/frontend/ci/pipeline_details/mock_data.js
@@ -111,6 +111,22 @@ export const mockRunningPipelineHeaderData = {
   },
 };
 
+export const pipelineHeaderFailedNoPermissions = {
+  data: {
+    project: {
+      id: '1',
+      pipeline: {
+        ...pipelineHeaderFailed.data.project.pipeline,
+        userPermissions: {
+          destroyPipeline: false,
+          cancelPipeline: false,
+          updatePipeline: false,
+        },
+      },
+    },
+  },
+};
+
 export const users = [
   {
     id: 1,