diff --git a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx index 49b2f36d7c..360a195048 100644 --- a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx +++ b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx @@ -164,6 +164,7 @@ class OrganizationForm extends Component { label={i18n._(t`Ansible Environment`)} > ', () => { ).toEqual(true); expect(wrapper.find('input#template-name').text()).toBe(defaultProps.name); - expect(wrapper.find('input#template-playbook').text()).toBe( - defaultProps.playbook + expect(wrapper.find('AnsibleSelect[name="playbook"]').text()).toBe( + 'Choose a playbook' ); expect(wrapper.find('ProjectLookup').prop('value')).toBe(null); done(); @@ -78,7 +79,18 @@ describe('', () => { }); const wrapper = mountWithContexts(); await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); - wrapper.find('JobTemplateForm').prop('handleSubmit')(jobTemplateData); + const formik = wrapper.find('Formik').instance(); + const changeState = new Promise(resolve => { + formik.setState( + { + values: jobTemplateData, + }, + () => resolve() + ); + }); + await changeState; + wrapper.find('form').simulate('submit'); + await sleep(1); expect(JobTemplatesAPI.create).toHaveBeenCalledWith(jobTemplateData); done(); }); @@ -107,6 +119,7 @@ describe('', () => { }); await wrapper.find('JobTemplateForm').prop('handleSubmit')(jobTemplateData); + await sleep(0); expect(history.push).toHaveBeenCalledWith( '/templates/job_template/1/details' ); diff --git a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx index 9f8f71c100..51e5a3ff65 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.jsx @@ -106,13 +106,14 @@ class JobTemplateEdit extends Component { template: { id }, history, } = this.props; + const { newLabels, removedLabels } = values; + delete values.newLabels; + delete values.removedLabels; this.setState({ formSubmitError: null }); try { - await JobTemplatesAPI.update(id, { ...values }); - await Promise.all([ - this.submitLabels(values.newLabels, values.removedLabels), - ]); + await JobTemplatesAPI.update(id, values); + await Promise.all([this.submitLabels(newLabels, removedLabels)]); history.push(this.detailsUrl); } catch (formSubmitError) { this.setState({ formSubmitError }); diff --git a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx index 0b9ce09b83..682d29b01a 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateEdit/JobTemplateEdit.test.jsx @@ -1,6 +1,7 @@ import React from 'react'; -import { JobTemplatesAPI, LabelsAPI } from '@api'; +import { sleep } from '@testUtils/testUtils'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; +import { JobTemplatesAPI, LabelsAPI, ProjectsAPI } from '@api'; import JobTemplateEdit from './JobTemplateEdit'; jest.mock('@api'); @@ -74,9 +75,29 @@ const mockRelatedCredentials = { ], }; +const mockRelatedProjectPlaybooks = [ + 'check.yml', + 'debug-50.yml', + 'debug.yml', + 'debug2.yml', + 'debug_extra_vars.yml', + 'dynamic_inventory.yml', + 'environ_test.yml', + 'fail_unless.yml', + 'pass_unless.yml', + 'pause.yml', + 'ping-20.yml', + 'ping.yml', + 'setfact_50.yml', + 'vault.yml', +]; + JobTemplatesAPI.readCredentials.mockResolvedValue({ data: mockRelatedCredentials, }); +ProjectsAPI.readPlaybooks.mockResolvedValue({ + data: mockRelatedProjectPlaybooks, +}); LabelsAPI.read.mockResolvedValue({ data: { results: [] } }); describe('', () => { @@ -101,20 +122,38 @@ describe('', () => { const newLabels = [ { associate: true, id: 3 }, { associate: true, id: 3 }, - { name: 'Mapel', organization: 1 }, + { name: 'Maple', organization: 1 }, { name: 'Tree', organization: 1 }, ]; const removedLabels = [ { disassociate: true, id: 1 }, { disassociate: true, id: 2 }, ]; + JobTemplatesAPI.update.mockResolvedValue({ + data: { ...updatedTemplateData }, + }); + const formik = wrapper.find('Formik').instance(); + const changeState = new Promise(resolve => { + formik.setState( + { + values: { + ...mockJobTemplate, + ...updatedTemplateData, + newLabels, + removedLabels, + }, + }, + () => resolve() + ); + }); + await changeState; + wrapper.find('button[aria-label="Save"]').simulate('click'); + await sleep(0); - await wrapper.find('JobTemplateForm').prop('handleSubmit')( - updatedTemplateData, - newLabels, - removedLabels - ); - expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, updatedTemplateData); + expect(JobTemplatesAPI.update).toHaveBeenCalledWith(1, { + ...mockJobTemplate, + ...updatedTemplateData, + }); expect(JobTemplatesAPI.disassociateLabel).toHaveBeenCalledTimes(2); expect(JobTemplatesAPI.associateLabel).toHaveBeenCalledTimes(2); expect(JobTemplatesAPI.generateLabel).toHaveBeenCalledTimes(2); diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx index 30479e3871..d7a699c387 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx @@ -427,7 +427,7 @@ const FormikApp = withFormik({ const { name = '', description = '', - job_type = '', + job_type = 'run', inventory = '', playbook = '', project = '', diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.test.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.test.jsx index 428bfc928f..d9be14e148 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.test.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { sleep } from '@testUtils/testUtils'; -import { shallow } from 'enzyme'; import JobTemplateForm, { _JobTemplateForm } from './JobTemplateForm'; import { LabelsAPI } from '@api'; @@ -42,13 +41,11 @@ describe('', () => { test('initially renders successfully', async done => { const wrapper = mountWithContexts( - shallow( - - ).get(0) + ); await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); @@ -64,14 +61,13 @@ describe('', () => { test('should update form values on input changes', async done => { const wrapper = mountWithContexts( - shallow( - - ).get(0) + ); + await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); const form = wrapper.find('Formik'); wrapper.find('input#template-name').simulate('change', { @@ -136,6 +132,27 @@ describe('', () => { done(); }); + test('should call loadRelatedProjectPlaybooks when project value changes', async done => { + const loadRelatedProjectPlaybooks = jest.spyOn( + _JobTemplateForm.prototype, + 'loadRelatedProjectPlaybooks' + ); + const wrapper = mountWithContexts( + + ); + await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0); + wrapper.find('ProjectLookup').prop('onChange')({ + id: 10, + name: 'project', + }); + expect(loadRelatedProjectPlaybooks).toHaveBeenCalledWith(10); + done(); + }); + test('handleNewLabel should arrange new labels properly', async done => { const handleNewLabel = jest.spyOn( _JobTemplateForm.prototype,