Commit 013a90e5 authored by PaytonBurdette's avatar PaytonBurdette Committed by Payton Burdette

Disable action buttons on click

After first click disable action buttons
for jobs table. On next fetch they are
enabled again. This will prevent extra
api requests.
parent 9f4a28ae
...@@ -58,6 +58,14 @@ export default { ...@@ -58,6 +58,14 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
retryBtnDisabled: false,
cancelBtnDisabled: false,
playManualBtnDisabled: false,
unscheduleBtnDisabled: false,
};
},
computed: { computed: {
hasArtifacts() { hasArtifacts() {
return this.job.artifacts.nodes.find((artifact) => artifact.fileType === FILE_TYPE_ARCHIVE); return this.job.artifacts.nodes.find((artifact) => artifact.fileType === FILE_TYPE_ARCHIVE);
...@@ -132,15 +140,23 @@ export default { ...@@ -132,15 +140,23 @@ export default {
}); });
}, },
cancelJob() { cancelJob() {
this.cancelBtnDisabled = true;
this.postJobAction(this.$options.jobCancel, cancelJobMutation); this.postJobAction(this.$options.jobCancel, cancelJobMutation);
}, },
retryJob() { retryJob() {
this.retryBtnDisabled = true;
this.postJobAction(this.$options.jobRetry, retryJobMutation); this.postJobAction(this.$options.jobRetry, retryJobMutation);
}, },
playJob() { playJob() {
this.playManualBtnDisabled = true;
this.postJobAction(this.$options.jobPlay, playJobMutation); this.postJobAction(this.$options.jobPlay, playJobMutation);
}, },
unscheduleJob() { unscheduleJob() {
this.unscheduleBtnDisabled = true;
this.postJobAction(this.$options.jobUnschedule, unscheduleJobMutation); this.postJobAction(this.$options.jobUnschedule, unscheduleJobMutation);
}, },
}, },
...@@ -155,6 +171,7 @@ export default { ...@@ -155,6 +171,7 @@ export default {
data-testid="cancel-button" data-testid="cancel-button"
icon="cancel" icon="cancel"
:title="$options.CANCEL" :title="$options.CANCEL"
:disabled="cancelBtnDisabled"
@click="cancelJob()" @click="cancelJob()"
/> />
<template v-else-if="isScheduled"> <template v-else-if="isScheduled">
...@@ -179,6 +196,7 @@ export default { ...@@ -179,6 +196,7 @@ export default {
<gl-button <gl-button
icon="time-out" icon="time-out"
:title="$options.ACTIONS_UNSCHEDULE" :title="$options.ACTIONS_UNSCHEDULE"
:disabled="unscheduleBtnDisabled"
data-testid="unschedule" data-testid="unschedule"
@click="unscheduleJob()" @click="unscheduleJob()"
/> />
...@@ -189,6 +207,7 @@ export default { ...@@ -189,6 +207,7 @@ export default {
v-if="manualJobPlayable" v-if="manualJobPlayable"
icon="play" icon="play"
:title="$options.ACTIONS_PLAY" :title="$options.ACTIONS_PLAY"
:disabled="playManualBtnDisabled"
data-testid="play" data-testid="play"
@click="playJob()" @click="playJob()"
/> />
...@@ -197,6 +216,7 @@ export default { ...@@ -197,6 +216,7 @@ export default {
icon="repeat" icon="repeat"
:title="$options.ACTIONS_RETRY" :title="$options.ACTIONS_RETRY"
:method="currentJobMethod" :method="currentJobMethod"
:disabled="retryBtnDisabled"
data-testid="retry" data-testid="retry"
@click="retryJob()" @click="retryJob()"
/> />
......
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import waitForPromises from 'helpers/wait_for_promises';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ActionsCell from '~/jobs/components/table/cells/actions_cell.vue'; import ActionsCell from '~/jobs/components/table/cells/actions_cell.vue';
import JobPlayMutation from '~/jobs/components/table/graphql/mutations/job_play.mutation.graphql'; import JobPlayMutation from '~/jobs/components/table/graphql/mutations/job_play.mutation.graphql';
import JobRetryMutation from '~/jobs/components/table/graphql/mutations/job_retry.mutation.graphql'; import JobRetryMutation from '~/jobs/components/table/graphql/mutations/job_retry.mutation.graphql';
import JobUnscheduleMutation from '~/jobs/components/table/graphql/mutations/job_unschedule.mutation.graphql'; import JobUnscheduleMutation from '~/jobs/components/table/graphql/mutations/job_unschedule.mutation.graphql';
import JobCancelMutation from '~/jobs/components/table/graphql/mutations/job_cancel.mutation.graphql';
import { import {
playableJob, playableJob,
retryableJob, retryableJob,
cancelableJob,
scheduledJob, scheduledJob,
cannotRetryJob, cannotRetryJob,
cannotPlayJob, cannotPlayJob,
...@@ -20,6 +23,7 @@ describe('Job actions cell', () => { ...@@ -20,6 +23,7 @@ describe('Job actions cell', () => {
const findRetryButton = () => wrapper.findByTestId('retry'); const findRetryButton = () => wrapper.findByTestId('retry');
const findPlayButton = () => wrapper.findByTestId('play'); const findPlayButton = () => wrapper.findByTestId('play');
const findCancelButton = () => wrapper.findByTestId('cancel-button');
const findDownloadArtifactsButton = () => wrapper.findByTestId('download-artifacts'); const findDownloadArtifactsButton = () => wrapper.findByTestId('download-artifacts');
const findCountdownButton = () => wrapper.findByTestId('countdown'); const findCountdownButton = () => wrapper.findByTestId('countdown');
const findPlayScheduledJobButton = () => wrapper.findByTestId('play-scheduled'); const findPlayScheduledJobButton = () => wrapper.findByTestId('play-scheduled');
...@@ -32,6 +36,7 @@ describe('Job actions cell', () => { ...@@ -32,6 +36,7 @@ describe('Job actions cell', () => {
data: { JobUnscheduleMutation: { jobId: scheduledJob.id } }, data: { JobUnscheduleMutation: { jobId: scheduledJob.id } },
}; };
const MUTATION_SUCCESS_PLAY = { data: { JobPlayMutation: { jobId: playableJob.id } } }; const MUTATION_SUCCESS_PLAY = { data: { JobPlayMutation: { jobId: playableJob.id } } };
const MUTATION_SUCCESS_CANCEL = { data: { JobCancelMutation: { jobId: cancelableJob.id } } };
const $toast = { const $toast = {
show: jest.fn(), show: jest.fn(),
...@@ -88,6 +93,7 @@ describe('Job actions cell', () => { ...@@ -88,6 +93,7 @@ describe('Job actions cell', () => {
${findPlayButton} | ${'play'} | ${playableJob} ${findPlayButton} | ${'play'} | ${playableJob}
${findRetryButton} | ${'retry'} | ${retryableJob} ${findRetryButton} | ${'retry'} | ${retryableJob}
${findDownloadArtifactsButton} | ${'download artifacts'} | ${playableJob} ${findDownloadArtifactsButton} | ${'download artifacts'} | ${playableJob}
${findCancelButton} | ${'cancel'} | ${cancelableJob}
`('displays the $action button', ({ button, jobType }) => { `('displays the $action button', ({ button, jobType }) => {
createComponent(jobType); createComponent(jobType);
...@@ -98,6 +104,7 @@ describe('Job actions cell', () => { ...@@ -98,6 +104,7 @@ describe('Job actions cell', () => {
button | mutationResult | action | jobType | mutationFile button | mutationResult | action | jobType | mutationFile
${findPlayButton} | ${MUTATION_SUCCESS_PLAY} | ${'play'} | ${playableJob} | ${JobPlayMutation} ${findPlayButton} | ${MUTATION_SUCCESS_PLAY} | ${'play'} | ${playableJob} | ${JobPlayMutation}
${findRetryButton} | ${MUTATION_SUCCESS} | ${'retry'} | ${retryableJob} | ${JobRetryMutation} ${findRetryButton} | ${MUTATION_SUCCESS} | ${'retry'} | ${retryableJob} | ${JobRetryMutation}
${findCancelButton} | ${MUTATION_SUCCESS_CANCEL} | ${'cancel'} | ${cancelableJob} | ${JobCancelMutation}
`('performs the $action mutation', ({ button, mutationResult, jobType, mutationFile }) => { `('performs the $action mutation', ({ button, mutationResult, jobType, mutationFile }) => {
createComponent(jobType, mutationResult); createComponent(jobType, mutationResult);
...@@ -111,6 +118,24 @@ describe('Job actions cell', () => { ...@@ -111,6 +118,24 @@ describe('Job actions cell', () => {
}); });
}); });
it.each`
button | action | jobType
${findPlayButton} | ${'play'} | ${playableJob}
${findRetryButton} | ${'retry'} | ${retryableJob}
${findCancelButton} | ${'cancel'} | ${cancelableJob}
${findUnscheduleButton} | ${'unschedule'} | ${scheduledJob}
`('disables the $action button after first request', async ({ button, jobType }) => {
createComponent(jobType);
expect(button().props('disabled')).toBe(false);
button().vm.$emit('click');
await waitForPromises();
expect(button().props('disabled')).toBe(true);
});
describe('Scheduled Jobs', () => { describe('Scheduled Jobs', () => {
const today = () => new Date('2021-08-31'); const today = () => new Date('2021-08-31');
......
...@@ -1653,6 +1653,65 @@ export const retryableJob = { ...@@ -1653,6 +1653,65 @@ export const retryableJob = {
__typename: 'CiJob', __typename: 'CiJob',
}; };
export const cancelableJob = {
artifacts: {
nodes: [],
__typename: 'CiJobArtifactConnection',
},
allowFailure: false,
status: 'PENDING',
scheduledAt: null,
manualJob: false,
triggered: null,
createdByTag: false,
detailedStatus: {
id: 'pending-1305-1305',
detailsPath: '/root/lots-of-jobs-project/-/jobs/1305',
group: 'pending',
icon: 'status_pending',
label: 'pending',
text: 'pending',
tooltip: 'pending',
action: {
id: 'Ci::Build-pending-1305',
buttonTitle: 'Cancel this job',
icon: 'cancel',
method: 'post',
path: '/root/lots-of-jobs-project/-/jobs/1305/cancel',
title: 'Cancel',
__typename: 'StatusAction',
},
__typename: 'DetailedStatus',
},
id: 'gid://gitlab/Ci::Build/1305',
refName: 'main',
refPath: '/root/lots-of-jobs-project/-/commits/main',
tags: [],
shortSha: '750605f2',
commitPath: '/root/lots-of-jobs-project/-/commit/750605f29530778cf0912779eba6d073128962a5',
stage: {
id: 'gid://gitlab/Ci::Stage/181',
name: 'deploy',
__typename: 'CiStage',
},
name: 'job_212',
duration: null,
finishedAt: null,
coverage: null,
retryable: false,
playable: false,
cancelable: true,
active: true,
stuck: false,
userPermissions: {
readBuild: true,
readJobArtifacts: true,
updateBuild: true,
__typename: 'JobPermissions',
},
__typename: 'CiJob',
};
export const cannotRetryJob = { export const cannotRetryJob = {
...retryableJob, ...retryableJob,
userPermissions: { readBuild: true, updateBuild: false, __typename: 'JobPermissions' }, userPermissions: { readBuild: true, updateBuild: false, __typename: 'JobPermissions' },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment