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: '',