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');