From 8c21a2aa9e5f94b5837e3c6d22fa65c463979c44 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Tue, 22 Feb 2022 15:32:38 -0500 Subject: [PATCH 1/2] Add schedule detail to job list expanded view --- .../components/DetailList/LaunchedByDetail.js | 39 +------ awx/ui/src/components/JobList/JobListItem.js | 12 ++ .../components/JobList/JobListItem.test.js | 13 +++ .../screens/Job/JobDetail/JobDetail.test.js | 1 + awx/ui/src/util/getScheduleUrl.js | 32 ++++++ awx/ui/src/util/getScheduleUrl.test.js | 103 ++++++++++++++++++ 6 files changed, 166 insertions(+), 34 deletions(-) create mode 100644 awx/ui/src/util/getScheduleUrl.js create mode 100644 awx/ui/src/util/getScheduleUrl.test.js diff --git a/awx/ui/src/components/DetailList/LaunchedByDetail.js b/awx/ui/src/components/DetailList/LaunchedByDetail.js index 099d2385a7..6f542231c5 100644 --- a/awx/ui/src/components/DetailList/LaunchedByDetail.js +++ b/awx/ui/src/components/DetailList/LaunchedByDetail.js @@ -1,45 +1,16 @@ import React from 'react'; import { Link } from 'react-router-dom'; import { t } from '@lingui/macro'; +import getScheduleUrl from 'util/getScheduleUrl'; import Detail from './Detail'; -function getScheduleURL(template, scheduleId, inventoryId = null) { - let scheduleUrl; - - switch (template.unified_job_type) { - case 'inventory_update': - scheduleUrl = - inventoryId && - `/inventories/inventory/${inventoryId}/sources/${template.id}/schedules/${scheduleId}/details`; - break; - case 'job': - scheduleUrl = `/templates/job_template/${template.id}/schedules/${scheduleId}/details`; - break; - case 'project_update': - scheduleUrl = `/projects/${template.id}/schedules/${scheduleId}/details`; - break; - case 'system_job': - scheduleUrl = `/management_jobs/${template.id}/schedules/${scheduleId}/details`; - break; - case 'workflow_job': - scheduleUrl = `/templates/workflow_job_template/${template.id}/schedules/${scheduleId}/details`; - break; - default: - break; - } - - return scheduleUrl; -} - -const getLaunchedByDetails = ({ summary_fields = {}, launch_type }) => { +const getLaunchedByDetails = (job) => { const { created_by: createdBy, job_template: jobTemplate, - unified_job_template: unifiedJT, workflow_job_template: workflowJT, - inventory, schedule, - } = summary_fields; + } = job.summary_fields; if (!createdBy && !schedule) { return {}; @@ -48,7 +19,7 @@ const getLaunchedByDetails = ({ summary_fields = {}, launch_type }) => { let link; let value; - switch (launch_type) { + switch (job.launch_type) { case 'webhook': value = t`Webhook`; link = @@ -58,7 +29,7 @@ const getLaunchedByDetails = ({ summary_fields = {}, launch_type }) => { break; case 'scheduled': value = schedule.name; - link = getScheduleURL(unifiedJT, schedule.id, inventory?.id); + link = getScheduleUrl(job); break; case 'manual': link = `/users/${createdBy.id}/details`; diff --git a/awx/ui/src/components/JobList/JobListItem.js b/awx/ui/src/components/JobList/JobListItem.js index 9967b1b154..93f398c741 100644 --- a/awx/ui/src/components/JobList/JobListItem.js +++ b/awx/ui/src/components/JobList/JobListItem.js @@ -8,6 +8,7 @@ import { RocketIcon } from '@patternfly/react-icons'; import styled from 'styled-components'; import { formatDateString } from 'util/dates'; import { isJobRunning } from 'util/jobs'; +import getScheduleUrl from 'util/getScheduleUrl'; import { ActionsTd, ActionItem, TdBreakWord } from '../PaginatedTable'; import { LaunchButton, ReLaunchDropDown } from '../LaunchButton'; import StatusLabel from '../StatusLabel'; @@ -167,6 +168,17 @@ function JobListItem({ /> )} + {job.launch_type === 'scheduled' && ( + + {job.summary_fields.schedule.name} + + } + /> + )} {job_template && ( ', () => { test('initially renders successfully', () => { expect(wrapper.find('JobListItem').length).toBe(1); + }); + + test('should display expected details', () => { assertDetail('Job Slice', '1/3'); + assertDetail('Schedule', 'mock schedule'); }); test('launch button shown to users with launch capabilities', () => { diff --git a/awx/ui/src/screens/Job/JobDetail/JobDetail.test.js b/awx/ui/src/screens/Job/JobDetail/JobDetail.test.js index da8b7048fe..33487f687d 100644 --- a/awx/ui/src/screens/Job/JobDetail/JobDetail.test.js +++ b/awx/ui/src/screens/Job/JobDetail/JobDetail.test.js @@ -189,6 +189,7 @@ describe('', () => { { + test('should return expected schedule URL for inventory update job', () => { + const invSrcJob = { + type: 'inventory_update', + summary_fields: { + inventory: { + id: 1, + name: 'mock inv', + }, + schedule: { + name: 'mock schedule', + id: 3, + }, + unified_job_template: { + unified_job_type: 'inventory_update', + name: 'mock inv src', + id: 2, + }, + }, + }; + expect(getScheduleUrl(invSrcJob)).toEqual( + '/inventories/inventory/1/sources/2/schedules/3/details' + ); + }); + test('should return expected schedule URL for job', () => { + const templateJob = { + type: 'job', + summary_fields: { + schedule: { + name: 'mock schedule', + id: 5, + }, + unified_job_template: { + unified_job_type: 'job', + name: 'mock job', + id: 4, + }, + }, + }; + expect(getScheduleUrl(templateJob)).toEqual( + '/templates/job_template/4/schedules/5/details' + ); + }); + test('should return expected schedule URL for project update job', () => { + const projectUpdateJob = { + type: 'project_update', + summary_fields: { + schedule: { + name: 'mock schedule', + id: 7, + }, + unified_job_template: { + unified_job_type: 'project_update', + name: 'mock job', + id: 6, + }, + }, + }; + expect(getScheduleUrl(projectUpdateJob)).toEqual( + '/projects/6/schedules/7/details' + ); + }); + test('should return expected schedule URL for system job', () => { + const systemJob = { + type: 'system_job', + summary_fields: { + schedule: { + name: 'mock schedule', + id: 10, + }, + unified_job_template: { + unified_job_type: 'system_job', + name: 'mock job', + id: 9, + }, + }, + }; + expect(getScheduleUrl(systemJob)).toEqual( + '/management_jobs/9/schedules/10/details' + ); + }); + test('should return expected schedule URL for workflow job', () => { + const workflowJob = { + type: 'workflow_job', + summary_fields: { + schedule: { + name: 'mock schedule', + id: 12, + }, + unified_job_template: { + unified_job_type: 'job', + name: 'mock job', + id: 11, + }, + }, + }; + expect(getScheduleUrl(workflowJob)).toEqual( + '/templates/workflow_job_template/11/schedules/12/details' + ); + }); +}); From 1aefd39782874c24c9e370a17e081c538381dde2 Mon Sep 17 00:00:00 2001 From: Marliana Lara Date: Mon, 28 Feb 2022 15:51:36 -0500 Subject: [PATCH 2/2] Show deleted detail for deleted schedules --- awx/ui/src/components/JobList/JobListItem.js | 31 ++++++++++++------- .../components/JobList/JobListItem.test.js | 19 ++++++++++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/awx/ui/src/components/JobList/JobListItem.js b/awx/ui/src/components/JobList/JobListItem.js index 93f398c741..364b4279c0 100644 --- a/awx/ui/src/components/JobList/JobListItem.js +++ b/awx/ui/src/components/JobList/JobListItem.js @@ -12,7 +12,12 @@ import getScheduleUrl from 'util/getScheduleUrl'; import { ActionsTd, ActionItem, TdBreakWord } from '../PaginatedTable'; import { LaunchButton, ReLaunchDropDown } from '../LaunchButton'; import StatusLabel from '../StatusLabel'; -import { DetailList, Detail, LaunchedByDetail } from '../DetailList'; +import { + DetailList, + Detail, + DeletedDetail, + LaunchedByDetail, +} from '../DetailList'; import ChipGroup from '../ChipGroup'; import CredentialChip from '../CredentialChip'; import ExecutionEnvironmentDetail from '../ExecutionEnvironmentDetail'; @@ -49,6 +54,7 @@ function JobListItem({ job_template, labels, project, + schedule, source_workflow_job, workflow_job_template, } = job.summary_fields; @@ -168,17 +174,18 @@ function JobListItem({ /> )} - {job.launch_type === 'scheduled' && ( - - {job.summary_fields.schedule.name} - - } - /> - )} + {job.launch_type === 'scheduled' && + (schedule ? ( + {schedule.name} + } + /> + ) : ( + + ))} {job_template && ( ', () => { expect(wrapper.find('Td[dataLabel="Type"]').length).toBe(1); }); + test('should not show schedule detail in expanded view', () => { + wrapper = mountWithContexts( + + + {}} + /> + +
+ ); + expect(wrapper.find('Detail[label="Schedule"] dt').length).toBe(1); + }); + test('should not display EE for canceled jobs', () => { wrapper = mountWithContexts(