diff --git a/awx/ui/src/components/LabelSelect/LabelSelect.test.js b/awx/ui/src/components/LabelSelect/LabelSelect.test.js
index 6e45148abd..53b1a18701 100644
--- a/awx/ui/src/components/LabelSelect/LabelSelect.test.js
+++ b/awx/ui/src/components/LabelSelect/LabelSelect.test.js
@@ -63,7 +63,7 @@ describe('', () => {
const selectOptions = wrapper.find('SelectOption');
expect(selectOptions).toHaveLength(4);
});
- test('Generate a label ', async () => {
+ test('Generate a label', async () => {
let wrapper;
const onChange = jest.fn();
LabelsAPI.read.mockReturnValue({
@@ -79,4 +79,33 @@ describe('', () => {
await wrapper.find('Select').invoke('onSelect')({}, 'foo');
expect(onChange).toBeCalledWith([{ id: 'foo', name: 'foo' }]);
});
+ test('should handle read-only labels', async () => {
+ let wrapper;
+ const onChange = jest.fn();
+ LabelsAPI.read.mockReturnValue({
+ data: {
+ results: [
+ { id: 1, name: 'read only' },
+ { id: 2, name: 'not read only' },
+ ],
+ },
+ });
+ await act(async () => {
+ wrapper = mount(
+ {}}
+ onChange={onChange}
+ />
+ );
+ });
+ wrapper.find('SelectToggle').simulate('click');
+ const selectOptions = wrapper.find('SelectOption');
+ expect(selectOptions).toHaveLength(2);
+ expect(selectOptions.at(0).prop('isDisabled')).toBe(true);
+ expect(selectOptions.at(1).prop('isDisabled')).toBe(false);
+ });
});
diff --git a/awx/ui/src/components/LaunchButton/LaunchButton.test.js b/awx/ui/src/components/LaunchButton/LaunchButton.test.js
index 61ea5fd923..fcd7b155c9 100644
--- a/awx/ui/src/components/LaunchButton/LaunchButton.test.js
+++ b/awx/ui/src/components/LaunchButton/LaunchButton.test.js
@@ -37,6 +37,12 @@ describe('LaunchButton', () => {
ask_variables_on_launch: false,
ask_limit_on_launch: false,
ask_scm_branch_on_launch: false,
+ ask_execution_environment_on_launch: false,
+ ask_labels_on_launch: false,
+ ask_forks_on_launch: false,
+ ask_job_slice_count_on_launch: false,
+ ask_timeout_on_launch: false,
+ ask_instance_groups_on_launch: false,
survey_enabled: false,
variables_needed_to_start: [],
},
diff --git a/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js b/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js
index 53ea395af8..07563e1a2b 100644
--- a/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js
+++ b/awx/ui/src/components/LaunchPrompt/LaunchPrompt.test.js
@@ -20,6 +20,7 @@ import OtherPromptsStep from './steps/OtherPromptsStep';
import PreviewStep from './steps/PreviewStep';
import ExecutionEnvironmentStep from './steps/ExecutionEnvironmentStep';
import InstanceGroupsStep from './steps/InstanceGroupsStep';
+import SurveyStep from './steps/SurveyStep';
jest.mock('../../api/models/Inventories');
jest.mock('../../api/models/ExecutionEnvironments');
@@ -378,4 +379,46 @@ describe('LaunchPrompt', () => {
expect(isElementOfType(steps[0].component, OtherPromptsStep)).toEqual(true);
expect(isElementOfType(steps[1].component, PreviewStep)).toEqual(true);
});
+
+ test('should add survey step', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+ );
+ });
+ const wizard = await waitForElement(wrapper, 'Wizard');
+ const steps = wizard.prop('steps');
+
+ expect(steps).toHaveLength(2);
+ expect(steps[0].name.props.children).toEqual('Survey');
+ expect(isElementOfType(steps[0].component, SurveyStep)).toEqual(true);
+ expect(isElementOfType(steps[1].component, PreviewStep)).toEqual(true);
+ });
});
diff --git a/awx/ui/src/components/LaunchPrompt/steps/ExecutionEnvironmentStep.test.js b/awx/ui/src/components/LaunchPrompt/steps/ExecutionEnvironmentStep.test.js
new file mode 100644
index 0000000000..208195ffec
--- /dev/null
+++ b/awx/ui/src/components/LaunchPrompt/steps/ExecutionEnvironmentStep.test.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { Formik } from 'formik';
+import { ExecutionEnvironmentsAPI } from 'api';
+import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
+import ExecutionEnvironmentStep from './ExecutionEnvironmentStep';
+
+jest.mock('../../../api/models/ExecutionEnvironments');
+
+const execution_environments = [
+ { id: 1, name: 'ee one', url: '/execution_environments/1' },
+ { id: 2, name: 'ee two', url: '/execution_environments/2' },
+ { id: 3, name: 'ee three', url: '/execution_environments/3' },
+];
+
+describe('ExecutionEnvironmentStep', () => {
+ beforeEach(() => {
+ ExecutionEnvironmentsAPI.read.mockResolvedValue({
+ data: {
+ results: execution_environments,
+ count: 3,
+ },
+ });
+
+ ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({
+ data: {
+ actions: {
+ GET: {},
+ POST: {},
+ },
+ related_search_fields: [],
+ },
+ });
+ });
+
+ test('should load execution environments', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+ wrapper.update();
+
+ expect(ExecutionEnvironmentsAPI.read).toHaveBeenCalled();
+ expect(wrapper.find('OptionsList').prop('options')).toEqual(
+ execution_environments
+ );
+ });
+});
diff --git a/awx/ui/src/components/LaunchPrompt/steps/InstanceGroupsStep.test.js b/awx/ui/src/components/LaunchPrompt/steps/InstanceGroupsStep.test.js
new file mode 100644
index 0000000000..b260f1ff3c
--- /dev/null
+++ b/awx/ui/src/components/LaunchPrompt/steps/InstanceGroupsStep.test.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { Formik } from 'formik';
+import { InstanceGroupsAPI } from 'api';
+import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
+import InstanceGroupsStep from './InstanceGroupsStep';
+
+jest.mock('../../../api/models/InstanceGroups');
+
+const instance_groups = [
+ { id: 1, name: 'ig one', url: '/instance_groups/1' },
+ { id: 2, name: 'ig two', url: '/instance_groups/2' },
+ { id: 3, name: 'ig three', url: '/instance_groups/3' },
+];
+
+describe('InstanceGroupsStep', () => {
+ beforeEach(() => {
+ InstanceGroupsAPI.read.mockResolvedValue({
+ data: {
+ results: instance_groups,
+ count: 3,
+ },
+ });
+
+ InstanceGroupsAPI.readOptions.mockResolvedValue({
+ data: {
+ actions: {
+ GET: {},
+ POST: {},
+ },
+ related_search_fields: [],
+ },
+ });
+ });
+
+ test('should load instance groups', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+ wrapper.update();
+
+ expect(InstanceGroupsAPI.read).toHaveBeenCalled();
+ expect(wrapper.find('OptionsList').prop('options')).toEqual(
+ instance_groups
+ );
+ });
+});
diff --git a/awx/ui/src/components/LaunchPrompt/steps/OtherPromptsStep.test.js b/awx/ui/src/components/LaunchPrompt/steps/OtherPromptsStep.test.js
index fc33c3d03a..cdaeb41995 100644
--- a/awx/ui/src/components/LaunchPrompt/steps/OtherPromptsStep.test.js
+++ b/awx/ui/src/components/LaunchPrompt/steps/OtherPromptsStep.test.js
@@ -58,6 +58,81 @@ describe('OtherPromptsStep', () => {
);
});
+ test('should render timeout field', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+
+ expect(wrapper.find('FormField#prompt-timeout')).toHaveLength(1);
+ expect(wrapper.find('FormField#prompt-timeout input').prop('name')).toEqual(
+ 'timeout'
+ );
+ });
+
+ test('should render forks field', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+
+ expect(wrapper.find('FormField#prompt-forks')).toHaveLength(1);
+ expect(wrapper.find('FormField#prompt-forks input').prop('name')).toEqual(
+ 'forks'
+ );
+ });
+
+ test('should render job slicing field', async () => {
+ let wrapper;
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+
+ expect(wrapper.find('FormField#prompt-job-slicing')).toHaveLength(1);
+ expect(
+ wrapper.find('FormField#prompt-job-slicing input').prop('name')
+ ).toEqual('job_slice_count');
+ });
+
test('should render source control branch field', async () => {
let wrapper;
await act(async () => {
diff --git a/awx/ui/src/components/Lookup/ExecutionEnvironmentLookup.test.js b/awx/ui/src/components/Lookup/ExecutionEnvironmentLookup.test.js
index ea6c82df81..e1f020deff 100644
--- a/awx/ui/src/components/Lookup/ExecutionEnvironmentLookup.test.js
+++ b/awx/ui/src/components/Lookup/ExecutionEnvironmentLookup.test.js
@@ -66,6 +66,9 @@ describe('ExecutionEnvironmentLookup', () => {
expect(
wrapper.find('FormGroup[label="Execution Environment"]').length
).toBe(1);
+ expect(wrapper.find('Checkbox[aria-label="Prompt on launch"]').length).toBe(
+ 0
+ );
});
test('should fetch execution environments', async () => {
@@ -132,4 +135,25 @@ describe('ExecutionEnvironmentLookup', () => {
page_size: 5,
});
});
+
+ test('should render prompt on launch checkbox when necessary', async () => {
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+ {}}
+ projectId={12}
+ globallyAvailable
+ isPromptableField
+ promptId="ee-prompt"
+ promptName="ask_execution_environment_on_launch"
+ />
+
+ );
+ });
+ expect(wrapper.find('Checkbox[aria-label="Prompt on launch"]').length).toBe(
+ 1
+ );
+ });
});
diff --git a/awx/ui/src/components/Lookup/InstanceGroupsLookup.test.js b/awx/ui/src/components/Lookup/InstanceGroupsLookup.test.js
new file mode 100644
index 0000000000..b6acdb4ed9
--- /dev/null
+++ b/awx/ui/src/components/Lookup/InstanceGroupsLookup.test.js
@@ -0,0 +1,111 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { Formik } from 'formik';
+import { InstanceGroupsAPI } from 'api';
+import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
+import InstanceGroupsLookup from './InstanceGroupsLookup';
+
+jest.mock('../../api');
+
+const mockedInstanceGroups = {
+ count: 1,
+ results: [
+ {
+ id: 2,
+ name: 'Foo',
+ image: 'quay.io/ansible/awx-ee',
+ pull: 'missing',
+ },
+ ],
+};
+
+const instanceGroups = [
+ {
+ id: 1,
+ type: 'instance_group',
+ url: '/api/v2/instance_groups/1/',
+ related: {
+ jobs: '/api/v2/instance_groups/1/jobs/',
+ instances: '/api/v2/instance_groups/1/instances/',
+ },
+ name: 'controlplane',
+ created: '2022-09-13T15:44:54.870579Z',
+ modified: '2022-09-13T15:44:54.886047Z',
+ capacity: 59,
+ consumed_capacity: 0,
+ percent_capacity_remaining: 100.0,
+ jobs_running: 0,
+ jobs_total: 40,
+ instances: 1,
+ is_container_group: false,
+ credential: null,
+ policy_instance_percentage: 100,
+ policy_instance_minimum: 0,
+ policy_instance_list: [],
+ pod_spec_override: '',
+ summary_fields: {
+ user_capabilities: {
+ edit: true,
+ delete: false,
+ },
+ },
+ },
+];
+
+describe('InstanceGroupsLookup', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ InstanceGroupsAPI.read.mockResolvedValue({
+ data: mockedInstanceGroups,
+ });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ test('should render successfully', async () => {
+ InstanceGroupsAPI.readOptions.mockReturnValue({
+ data: {
+ actions: {
+ GET: {},
+ POST: {},
+ },
+ related_search_fields: [],
+ },
+ });
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+ {}} />
+
+ );
+ });
+ wrapper.update();
+ expect(InstanceGroupsAPI.read).toHaveBeenCalledTimes(1);
+ expect(wrapper.find('InstanceGroupsLookup')).toHaveLength(1);
+ expect(wrapper.find('FormGroup[label="Instance Groups"]').length).toBe(1);
+ expect(wrapper.find('Checkbox[aria-label="Prompt on launch"]').length).toBe(
+ 0
+ );
+ });
+ test('should render prompt on launch checkbox when necessary', async () => {
+ await act(async () => {
+ wrapper = mountWithContexts(
+
+ {}}
+ isPromptableField
+ promptId="ig-prompt"
+ promptName="ask_instance_groups_on_launch"
+ />
+
+ );
+ });
+ expect(wrapper.find('Checkbox[aria-label="Prompt on launch"]').length).toBe(
+ 1
+ );
+ });
+});
diff --git a/awx/ui/src/components/PromptDetail/PromptDetail.js b/awx/ui/src/components/PromptDetail/PromptDetail.js
index d52767fb0b..7122e6a63d 100644
--- a/awx/ui/src/components/PromptDetail/PromptDetail.js
+++ b/awx/ui/src/components/PromptDetail/PromptDetail.js
@@ -35,6 +35,9 @@ function formatTimeout(timeout) {
if (typeof timeout === 'undefined' || timeout === null) {
return null;
}
+ if (typeof timeout === 'string') {
+ return timeout;
+ }
const minutes = Math.floor(timeout / 60);
const seconds = timeout - Math.floor(timeout / 60) * 60;
return (
@@ -348,7 +351,10 @@ function PromptDetail({
/>
)}
{launchConfig.ask_timeout_on_launch && (
-
+
)}
{launchConfig.ask_diff_mode_on_launch && (
{
assertDetail('Limit', 'localhost');
assertDetail('Verbosity', '3 (Debug)');
assertDetail('Show Changes', 'Off');
+ assertDetail('Timeout', '1 min 40 sec');
+ assertDetail('Forks', '1');
+ assertDetail('Job Slicing', '1');
expect(wrapper.find('VariablesDetail').prop('value')).toEqual(
'---foo: bar'
);
+ expect(
+ wrapper
+ .find('Detail[label="Labels"]')
+ .containsAllMatchingElements([
+ L_91o2,
+ L_91o3,
+ ])
+ ).toEqual(true);
expect(
wrapper
.find('Detail[label="Credentials"]')
@@ -151,6 +172,19 @@ describe('PromptDetail', () => {
job_type: 'check',
scm_branch: 'Bar branch',
diff_mode: true,
+ forks: 2,
+ job_slice_count: 2,
+ timeout: 160,
+ labels: [
+ { name: 'foo', id: 1 },
+ { name: 'bar', id: 2 },
+ ],
+ instance_groups: [
+ {
+ id: 1,
+ name: 'controlplane',
+ },
+ ],
};
beforeAll(() => {
@@ -182,9 +216,17 @@ describe('PromptDetail', () => {
assertDetail('Limit', 'otherlimit');
assertDetail('Verbosity', '0 (Normal)');
assertDetail('Show Changes', 'On');
+ assertDetail('Timeout', '2 min 40 sec');
+ assertDetail('Forks', '2');
+ assertDetail('Job Slicing', '2');
expect(wrapper.find('VariablesDetail').prop('value')).toEqual(
'---one: two\nbar: baz'
);
+ expect(
+ wrapper
+ .find('Detail[label="Labels"]')
+ .containsAllMatchingElements([foo, bar])
+ ).toEqual(true);
expect(
wrapper
.find('Detail[label="Credentials"]')
diff --git a/awx/ui/src/components/PromptDetail/data.job_template.json b/awx/ui/src/components/PromptDetail/data.job_template.json
index 5bbc5b26a8..620bbb9f96 100644
--- a/awx/ui/src/components/PromptDetail/data.job_template.json
+++ b/awx/ui/src/components/PromptDetail/data.job_template.json
@@ -3,159 +3,163 @@
"type": "job_template",
"url": "/api/v2/job_templates/7/",
"related": {
- "named_url": "/api/v2/job_templates/MockJT/",
- "created_by": "/api/v2/users/1/",
- "modified_by": "/api/v2/users/1/",
- "labels": "/api/v2/job_templates/7/labels/",
- "inventory": "/api/v2/inventories/1/",
- "project": "/api/v2/projects/6/",
- "credentials": "/api/v2/job_templates/7/credentials/",
- "last_job": "/api/v2/jobs/12/",
- "jobs": "/api/v2/job_templates/7/jobs/",
- "schedules": "/api/v2/job_templates/7/schedules/",
- "activity_stream": "/api/v2/job_templates/7/activity_stream/",
- "launch": "/api/v2/job_templates/7/launch/",
- "webhook_key": "/api/v2/job_templates/7/webhook_key/",
- "webhook_receiver": "/api/v2/job_templates/7/github/",
- "notification_templates_started": "/api/v2/job_templates/7/notification_templates_started/",
- "notification_templates_success": "/api/v2/job_templates/7/notification_templates_success/",
- "notification_templates_error": "/api/v2/job_templates/7/notification_templates_error/",
- "access_list": "/api/v2/job_templates/7/access_list/",
- "survey_spec": "/api/v2/job_templates/7/survey_spec/",
- "object_roles": "/api/v2/job_templates/7/object_roles/",
- "instance_groups": "/api/v2/job_templates/7/instance_groups/",
- "slice_workflow_jobs": "/api/v2/job_templates/7/slice_workflow_jobs/",
- "copy": "/api/v2/job_templates/7/copy/",
- "callback": "/api/v2/job_templates/7/callback/",
- "webhook_credential": "/api/v2/credentials/8/"
+ "named_url": "/api/v2/job_templates/MockJT/",
+ "created_by": "/api/v2/users/1/",
+ "modified_by": "/api/v2/users/1/",
+ "labels": "/api/v2/job_templates/7/labels/",
+ "inventory": "/api/v2/inventories/1/",
+ "project": "/api/v2/projects/6/",
+ "credentials": "/api/v2/job_templates/7/credentials/",
+ "last_job": "/api/v2/jobs/12/",
+ "jobs": "/api/v2/job_templates/7/jobs/",
+ "schedules": "/api/v2/job_templates/7/schedules/",
+ "activity_stream": "/api/v2/job_templates/7/activity_stream/",
+ "launch": "/api/v2/job_templates/7/launch/",
+ "webhook_key": "/api/v2/job_templates/7/webhook_key/",
+ "webhook_receiver": "/api/v2/job_templates/7/github/",
+ "notification_templates_started": "/api/v2/job_templates/7/notification_templates_started/",
+ "notification_templates_success": "/api/v2/job_templates/7/notification_templates_success/",
+ "notification_templates_error": "/api/v2/job_templates/7/notification_templates_error/",
+ "access_list": "/api/v2/job_templates/7/access_list/",
+ "survey_spec": "/api/v2/job_templates/7/survey_spec/",
+ "object_roles": "/api/v2/job_templates/7/object_roles/",
+ "instance_groups": "/api/v2/job_templates/7/instance_groups/",
+ "slice_workflow_jobs": "/api/v2/job_templates/7/slice_workflow_jobs/",
+ "copy": "/api/v2/job_templates/7/copy/",
+ "callback": "/api/v2/job_templates/7/callback/",
+ "webhook_credential": "/api/v2/credentials/8/"
},
"summary_fields": {
- "inventory": {
- "id": 1,
- "name": "Demo Inventory",
- "description": "",
- "has_active_failures": false,
- "total_hosts": 1,
- "hosts_with_active_failures": 0,
- "total_groups": 0,
- "groups_with_active_failures": 0,
- "has_inventory_sources": false,
- "total_inventory_sources": 0,
- "inventory_sources_with_failures": 0,
- "organization_id": 1,
- "kind": ""
- },
- "execution_environment": {
- "id": 1,
- "name": "Default EE",
- "description": "",
- "image": "quay.io/ansible/awx-ee"
- },
- "project": {
- "id": 6,
- "name": "Mock Project",
- "description": "",
- "status": "successful",
- "scm_type": "git"
- },
- "last_job": {
- "id": 12,
- "name": "Mock JT",
- "description": "",
- "finished": "2019-10-01T14:34:35.142483Z",
- "status": "successful",
- "failed": false
- },
- "last_update": {
- "id": 12,
- "name": "Mock JT",
- "description": "",
- "status": "successful",
- "failed": false
- },
- "webhook_credential": {
- "id": 8,
- "name": "GitHub Cred",
- "description": "",
- "kind": "github_token",
- "cloud": false,
- "credential_type_id": 12
- },
- "created_by": {
- "id": 1,
- "username": "admin",
- "first_name": "",
- "last_name": ""
- },
- "modified_by": {
- "id": 1,
- "username": "admin",
- "first_name": "",
- "last_name": ""
- },
- "object_roles": {
- "admin_role": {
- "description": "Can manage all aspects of the job template",
- "name": "Admin",
- "id": 24
- },
- "execute_role": {
- "description": "May run the job template",
- "name": "Execute",
- "id": 25
- },
- "read_role": {
- "description": "May view settings for the job template",
- "name": "Read",
- "id": 26
- }
- },
- "user_capabilities": {
- "edit": true,
- "delete": true,
- "start": true,
- "schedule": true,
- "copy": true
- },
- "labels": {
- "count": 1,
- "results": [
- {
- "id": 91,
- "name": "L_91o2"
- },
- {
- "id": 92,
- "name": "L_91o3"
- }
- ]
+ "inventory": {
+ "id": 1,
+ "name": "Demo Inventory",
+ "description": "",
+ "has_active_failures": false,
+ "total_hosts": 1,
+ "hosts_with_active_failures": 0,
+ "total_groups": 0,
+ "groups_with_active_failures": 0,
+ "has_inventory_sources": false,
+ "total_inventory_sources": 0,
+ "inventory_sources_with_failures": 0,
+ "organization_id": 1,
+ "kind": ""
},
- "survey": {
- "title": "",
- "description": ""
+ "execution_environment": {
+ "id": 1,
+ "name": "Default EE",
+ "description": "",
+ "image": "quay.io/ansible/awx-ee"
+ },
+ "project": {
+ "id": 6,
+ "name": "Mock Project",
+ "description": "",
+ "status": "successful",
+ "scm_type": "git"
+ },
+ "last_job": {
+ "id": 12,
+ "name": "Mock JT",
+ "description": "",
+ "finished": "2019-10-01T14:34:35.142483Z",
+ "status": "successful",
+ "failed": false
+ },
+ "last_update": {
+ "id": 12,
+ "name": "Mock JT",
+ "description": "",
+ "status": "successful",
+ "failed": false
+ },
+ "webhook_credential": {
+ "id": 8,
+ "name": "GitHub Cred",
+ "description": "",
+ "kind": "github_token",
+ "cloud": false,
+ "credential_type_id": 12
+ },
+ "created_by": {
+ "id": 1,
+ "username": "admin",
+ "first_name": "",
+ "last_name": ""
+ },
+ "modified_by": {
+ "id": 1,
+ "username": "admin",
+ "first_name": "",
+ "last_name": ""
+ },
+ "object_roles": {
+ "admin_role": {
+ "description": "Can manage all aspects of the job template",
+ "name": "Admin",
+ "id": 24
},
- "recent_jobs": [
- {
- "id": 12,
- "status": "successful",
- "finished": "2019-10-01T14:34:35.142483Z",
- "type": "job"
- },
- {
- "id": 13,
- "status": "successful",
- "finished": "2019-10-01T14:34:35.142483Z",
- "type": "job"
- }
- ],
- "credentials": [
+ "execute_role": {
+ "description": "May run the job template",
+ "name": "Execute",
+ "id": 25
+ },
+ "read_role": {
+ "description": "May view settings for the job template",
+ "name": "Read",
+ "id": 26
+ }
+ },
+ "user_capabilities": {
+ "edit": true,
+ "delete": true,
+ "start": true,
+ "schedule": true,
+ "copy": true
+ },
+ "labels": {
+ "count": 1,
+ "results": [
{
- "id": 1, "kind": "ssh" , "name": "Credential 1"
+ "id": 91,
+ "name": "L_91o2"
},
{
- "id": 2, "kind": "awx" , "name": "Credential 2"
+ "id": 92,
+ "name": "L_91o3"
}
]
+ },
+ "survey": {
+ "title": "",
+ "description": ""
+ },
+ "recent_jobs": [
+ {
+ "id": 12,
+ "status": "successful",
+ "finished": "2019-10-01T14:34:35.142483Z",
+ "type": "job"
+ },
+ {
+ "id": 13,
+ "status": "successful",
+ "finished": "2019-10-01T14:34:35.142483Z",
+ "type": "job"
+ }
+ ],
+ "credentials": [
+ {
+ "id": 1,
+ "kind": "ssh",
+ "name": "Credential 1"
+ },
+ {
+ "id": 2,
+ "kind": "awx",
+ "name": "Credential 2"
+ }
+ ]
},
"created": "2019-09-30T16:18:34.564820Z",
"modified": "2019-10-01T14:47:31.818431Z",
diff --git a/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.js b/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.js
index cfaf4de14b..bbad60baa3 100644
--- a/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.js
+++ b/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.js
@@ -270,9 +270,11 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
const showExecutionEnvironmentDetail =
ask_execution_environment_on_launch && execution_environment;
const showLabelsDetail = ask_labels_on_launch && labels && labels.length > 0;
- const showForksDetail = ask_forks_on_launch;
- const showJobSlicingDetail = ask_job_slice_count_on_launch;
- const showTimeoutDetail = ask_timeout_on_launch;
+ const showForksDetail = ask_forks_on_launch && typeof forks === 'number';
+ const showJobSlicingDetail =
+ ask_job_slice_count_on_launch && typeof job_slice_count === 'number';
+ const showTimeoutDetail =
+ ask_timeout_on_launch && typeof timeout === 'number';
const showInstanceGroupsDetail =
ask_instance_groups_on_launch && instanceGroups.length > 0;
@@ -551,17 +553,17 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
value={
- {summary_fields.labels.results.map((l) => (
+ {labels.map((l) => (
{l.name}
))}
}
- isEmpty={summary_fields.labels.results.length === 0}
+ isEmpty={labels.length === 0}
/>
)}
{showTagsDetail && (
diff --git a/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.js b/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.js
index 786767895e..9b36f07b5a 100644
--- a/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.js
+++ b/awx/ui/src/components/Schedule/ScheduleDetail/ScheduleDetail.test.js
@@ -23,6 +23,12 @@ const allPrompts = {
ask_tags_on_launch: true,
ask_variables_on_launch: true,
ask_verbosity_on_launch: true,
+ ask_execution_environment_on_launch: true,
+ ask_labels_on_launch: true,
+ ask_forks_on_launch: true,
+ ask_job_slice_count_on_launch: true,
+ ask_timeout_on_launch: true,
+ ask_instance_groups_on_launch: true,
survey_enabled: true,
inventory_needed_to_start: true,
},
@@ -40,6 +46,12 @@ const noPrompts = {
ask_tags_on_launch: false,
ask_variables_on_launch: false,
ask_verbosity_on_launch: false,
+ ask_execution_environment_on_launch: false,
+ ask_labels_on_launch: false,
+ ask_forks_on_launch: false,
+ ask_job_slice_count_on_launch: false,
+ ask_timeout_on_launch: false,
+ ask_instance_groups_on_launch: false,
survey_enabled: false,
},
};
@@ -91,6 +103,10 @@ const schedule = {
limit: null,
diff_mode: null,
verbosity: null,
+ execution_environment: null,
+ forks: null,
+ job_slice_count: null,
+ timeout: null,
};
const scheduleWithPrompts = {
@@ -104,6 +120,10 @@ const scheduleWithPrompts = {
diff_mode: true,
verbosity: 1,
extra_data: { foo: 'fii' },
+ execution_environment: 1,
+ forks: 1,
+ job_slice_count: 1,
+ timeout: 100,
};
describe('', () => {
@@ -182,6 +202,14 @@ describe('', () => {
expect(wrapper.find('Detail[label="Credentials"]').length).toBe(0);
expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(0);
expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Timeout"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Job Slicing"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Forks"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Labels"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Instance Groups"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Execution Environment"]').length).toBe(
+ 0
+ );
expect(wrapper.find('VariablesDetail').length).toBe(0);
});
test('details should render with the proper values with prompts', async () => {
@@ -200,6 +228,28 @@ describe('', () => {
],
},
});
+ SchedulesAPI.readInstanceGroups.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [
+ {
+ id: 1,
+ name: 'IG 1',
+ },
+ ],
+ },
+ });
+ SchedulesAPI.readAllLabels.mockResolvedValue({
+ data: {
+ count: 1,
+ results: [
+ {
+ id: 1,
+ name: 'Label 1',
+ },
+ ],
+ },
+ });
JobTemplatesAPI.readLaunch.mockResolvedValueOnce(allPrompts);
await act(async () => {
wrapper = mountWithContexts(
@@ -254,6 +304,14 @@ describe('', () => {
expect(wrapper.find('Detail[label="Credentials"]').length).toBe(1);
expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(1);
expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Timeout"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Job Slicing"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Forks"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Labels"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Instance Groups"]').length).toBe(1);
+ expect(wrapper.find('Detail[label="Execution Environment"]').length).toBe(
+ 1
+ );
expect(wrapper.find('VariablesDetail').length).toBe(1);
});
test('prompt values section should be hidden if no overrides are present on the schedule but ask_ options are all true', async () => {
@@ -263,6 +321,18 @@ describe('', () => {
results: [],
},
});
+ SchedulesAPI.readInstanceGroups.mockResolvedValue({
+ data: {
+ count: 0,
+ results: [],
+ },
+ });
+ SchedulesAPI.readAllLabels.mockResolvedValue({
+ data: {
+ count: 0,
+ results: [],
+ },
+ });
JobTemplatesAPI.readLaunch.mockResolvedValueOnce(allPrompts);
await act(async () => {
wrapper = mountWithContexts(
@@ -296,6 +366,14 @@ describe('', () => {
expect(wrapper.find('Detail[label="Credentials"]').length).toBe(0);
expect(wrapper.find('Detail[label="Job Tags"]').length).toBe(0);
expect(wrapper.find('Detail[label="Skip Tags"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Timeout"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Job Slicing"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Forks"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Labels"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Instance Groups"]').length).toBe(0);
+ expect(wrapper.find('Detail[label="Execution Environment"]').length).toBe(
+ 0
+ );
expect(wrapper.find('VariablesDetail').length).toBe(0);
});
test('prompt values section should be hidden if overrides are present on the schedule but ask_ options are all false', async () => {
@@ -469,6 +547,18 @@ describe('', () => {
results: [],
},
});
+ SchedulesAPI.readInstanceGroups.mockResolvedValue({
+ data: {
+ count: 0,
+ results: [],
+ },
+ });
+ SchedulesAPI.readAllLabels.mockResolvedValue({
+ data: {
+ count: 0,
+ results: [],
+ },
+ });
JobTemplatesAPI.readLaunch.mockResolvedValueOnce(allPrompts);
await act(async () => {
wrapper = mountWithContexts(
diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.js b/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.js
index 54e958324a..8d6e7badc4 100644
--- a/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.js
+++ b/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.js
@@ -22,12 +22,16 @@ function WorkflowJobTemplateAdd() {
webhook_credential,
webhook_key,
limit,
+ job_tags,
+ skip_tags,
...templatePayload
} = values;
templatePayload.inventory = inventory?.id;
templatePayload.organization = organization?.id;
templatePayload.webhook_credential = webhook_credential?.id;
templatePayload.limit = limit === '' ? null : limit;
+ templatePayload.job_tags = job_tags === '' ? null : job_tags;
+ templatePayload.skip_tags = skip_tags === '' ? null : skip_tags;
const organizationId =
organization?.id || inventory?.summary_fields?.organization.id;
try {
diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js b/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js
index 8a0c55cd06..8eaab645fc 100644
--- a/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js
+++ b/awx/ui/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.js
@@ -116,11 +116,11 @@ describe('', () => {
description: '',
extra_vars: '---',
inventory: undefined,
- job_tags: '',
+ job_tags: null,
limit: null,
organization: undefined,
scm_branch: '',
- skip_tags: '',
+ skip_tags: null,
webhook_credential: undefined,
webhook_service: '',
webhook_url: '',
diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.js b/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.js
index 88565297a4..6357f4adf2 100644
--- a/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.js
+++ b/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.js
@@ -23,12 +23,16 @@ function WorkflowJobTemplateEdit({ template }) {
webhook_credential,
webhook_key,
limit,
+ job_tags,
+ skip_tags,
...templatePayload
} = values;
templatePayload.inventory = inventory?.id || null;
templatePayload.organization = organization?.id || null;
templatePayload.webhook_credential = webhook_credential?.id || null;
templatePayload.limit = limit === '' ? null : limit;
+ templatePayload.job_tags = job_tags === '' ? null : job_tags;
+ templatePayload.skip_tags = skip_tags === '' ? null : skip_tags;
const formOrgId =
organization?.id || inventory?.summary_fields?.organization.id || null;
diff --git a/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.js b/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.js
index 56c99782c1..4ef1a6cce0 100644
--- a/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.js
+++ b/awx/ui/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.js
@@ -178,8 +178,8 @@ describe('', () => {
ask_labels_on_launch: false,
ask_skip_tags_on_launch: false,
ask_tags_on_launch: false,
- job_tags: '',
- skip_tags: '',
+ job_tags: null,
+ skip_tags: null,
});
wrapper.update();
await expect(WorkflowJobTemplatesAPI.disassociateLabel).toBeCalledWith(6, {
@@ -288,12 +288,12 @@ describe('', () => {
description: 'bar',
extra_vars: '---',
inventory: 1,
- job_tags: '',
+ job_tags: null,
limit: '5000',
name: 'Foo',
organization: 1,
scm_branch: 'devel',
- skip_tags: '',
+ skip_tags: null,
webhook_credential: null,
webhook_service: '',
webhook_url: '',