diff --git a/awx/ui_next/src/components/JobList/JobList.jsx b/awx/ui_next/src/components/JobList/JobList.jsx index c92096ad68..0ae8b38397 100644 --- a/awx/ui_next/src/components/JobList/JobList.jsx +++ b/awx/ui_next/src/components/JobList/JobList.jsx @@ -152,6 +152,8 @@ function JobList({ i18n, defaultParams, showTypeColumn = false }) { } }; + const cannotDeleteItems = selected.filter(job => isJobRunning(job.status)); + return ( <> @@ -238,6 +240,17 @@ function JobList({ i18n, defaultParams, showTypeColumn = false }) { onDelete={handleJobDelete} itemsToDelete={selected} pluralizedItemName={i18n._(t`Jobs`)} + cannotDelete={item => + isJobRunning(item.status) || + !item.summary_fields.user_capabilities.delete + } + errorMessage={i18n.plural({ + value: cannotDeleteItems.length, + one: + 'The selected job cannot be deleted due to insufficient permission or a running job status', + other: + 'The selected jobs cannot be deleted due to insufficient permissions or a running job status', + })} />, ', () => { let debug; beforeEach(() => { + UnifiedJobsAPI.read.mockResolvedValue({ + data: { count: 6, results: mockResults }, + }); debug = global.console.debug; // eslint-disable-line prefer-destructuring global.console.debug = () => {}; }); @@ -262,6 +265,72 @@ describe('', () => { jest.restoreAllMocks(); }); + test('should display message about job running status', async () => { + UnifiedJobsAPI.read.mockResolvedValue({ + data: { + count: 2, + results: [ + { + id: 1, + url: '/api/v2/project_updates/1', + name: 'job 1', + type: 'project_update', + status: 'running', + related: { + cancel: '/api/v2/project_updates/1/cancel', + }, + summary_fields: { + user_capabilities: { + delete: true, + start: true, + }, + }, + }, + { + id: 2, + url: '/api/v2/jobs/2', + name: 'job 2', + type: 'job', + status: 'running', + related: { + cancel: '/api/v2/jobs/2/cancel', + }, + summary_fields: { + user_capabilities: { + delete: true, + start: true, + }, + }, + }, + ], + }, + }); + + let wrapper; + await act(async () => { + wrapper = mountWithContexts(); + }); + await waitForLoaded(wrapper); + + act(() => { + wrapper.find('DataListToolbar').invoke('onSelectAll')(true); + }); + wrapper.update(); + wrapper.find('JobListItem'); + expect( + wrapper.find('ToolbarDeleteButton').prop('itemsToDelete') + ).toHaveLength(2); + + wrapper.update(); + + const deleteButton = wrapper.find('ToolbarDeleteButton'); + expect(deleteButton.find('Tooltip').prop('content').props.children).toEqual( + 'The selected jobs cannot be deleted due to insufficient permissions or a running job status: job 1, job 2' + ); + + jest.restoreAllMocks(); + }); + test('error is shown when job not successfully deleted from api', async () => { JobsAPI.destroy.mockImplementation(() => { throw new Error({ @@ -298,7 +367,7 @@ describe('', () => { ); }); - test('should send all corresponding delete API requests', async () => { + test('should send all corresponding cancel API requests', async () => { JobsAPI.cancel = jest.fn(); let wrapper; await act(async () => {