diff --git a/awx/ui_next/src/components/DetailList/LaunchedByDetail.jsx b/awx/ui_next/src/components/DetailList/LaunchedByDetail.jsx index 7708feadc9..ce7ca5d75e 100644 --- a/awx/ui_next/src/components/DetailList/LaunchedByDetail.jsx +++ b/awx/ui_next/src/components/DetailList/LaunchedByDetail.jsx @@ -3,13 +3,43 @@ import { Link } from 'react-router-dom'; import { t } from '@lingui/macro'; import Detail from './Detail'; -const getLaunchedByDetails = ({ summary_fields = {}, related = {} }) => { +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 }, i18n) => { const { created_by: createdBy, job_template: jobTemplate, + unified_job_template: unifiedJT, + workflow_job_template: workflowJT, + inventory, schedule, } = summary_fields; - const { schedule: relatedSchedule } = related; if (!createdBy && !schedule) { return {}; @@ -18,15 +48,26 @@ const getLaunchedByDetails = ({ summary_fields = {}, related = {} }) => { let link; let value; - if (createdBy) { - link = `/users/${createdBy.id}`; - value = createdBy.username; - } else if (relatedSchedule && jobTemplate) { - link = `/templates/job_template/${jobTemplate.id}/schedules/${schedule.id}`; - value = schedule.name; - } else { - link = null; - value = schedule.name; + switch (launch_type) { + case 'webhook': + value = i18n._(t`Webhook`); + link = + (jobTemplate && `/templates/job_template/${jobTemplate.id}/details`) || + (workflowJT && + `/templates/workflow_job_template/${workflowJT.id}/details`); + break; + case 'scheduled': + value = schedule.name; + link = getScheduleURL(unifiedJT, schedule.id, inventory?.id); + break; + case 'manual': + link = `/users/${createdBy.id}/details`; + value = createdBy.username; + break; + default: + link = createdBy && `/users/${createdBy.id}/details`; + value = createdBy && createdBy.username; + break; } return { link, value }; @@ -34,7 +75,7 @@ const getLaunchedByDetails = ({ summary_fields = {}, related = {} }) => { export default function LaunchedByDetail({ job }) { const { value: launchedByValue, link: launchedByLink } = - getLaunchedByDetails(job) || {}; + getLaunchedByDetails(job, i18n) || {}; return ( ', () => { ).toHaveLength(1); }); + test('should show schedule that launched workflow job', async () => { + wrapper = mountWithContexts( + + ); + const launchedByDetail = wrapper.find('Detail[label="Launched By"] dd'); + expect(launchedByDetail).toHaveLength(1); + expect(launchedByDetail.text()).toBe('mock wf schedule'); + expect( + launchedByDetail.find( + 'a[href="/templates/workflow_job_template/888/schedules/999/details"]' + ) + ).toHaveLength(1); + }); + + test('should hide "Launched By" detail for JT launched from a workflow launched by a schedule', async () => { + wrapper = mountWithContexts( + + ); + expect(wrapper.find('Detail[label="Launched By"] dt')).toHaveLength(0); + expect(wrapper.find('Detail[label="Launched By"] dd')).toHaveLength(0); + }); + test('should properly delete job', async () => { wrapper = mountWithContexts(); wrapper.find('button[aria-label="Delete"]').simulate('click');