diff --git a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx index d8837a93c7..97be4e3e80 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.jsx @@ -50,8 +50,8 @@ function OrganizationAdd({ i18n }) { {({ me }) => ( )} diff --git a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx index c690c8b9f6..216100c51c 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationAdd/OrganizationAdd.test.jsx @@ -8,7 +8,7 @@ import { OrganizationsAPI } from '@api'; jest.mock('@api'); describe('', () => { - test('handleSubmit should post to api', async () => { + test('onSubmit should post to api', async () => { const updatedOrgData = { name: 'new name', description: 'new description', @@ -16,11 +16,7 @@ describe('', () => { }; await act(async () => { const wrapper = mountWithContexts(); - wrapper.find('OrganizationForm').prop('handleSubmit')( - updatedOrgData, - [], - [] - ); + wrapper.find('OrganizationForm').prop('onSubmit')(updatedOrgData, [], []); }); expect(OrganizationsAPI.create).toHaveBeenCalledWith(updatedOrgData); }); @@ -32,6 +28,9 @@ describe('', () => { wrapper = mountWithContexts(, { context: { router: { history } }, }); + }); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + await act(async () => { wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); }); expect(history.location.pathname).toEqual('/organizations'); @@ -71,16 +70,12 @@ describe('', () => { context: { router: { history } }, }); await waitForElement(wrapper, 'button[aria-label="Save"]'); - await wrapper.find('OrganizationForm').prop('handleSubmit')( - orgData, - [3], - [] - ); + await wrapper.find('OrganizationForm').prop('onSubmit')(orgData, [3], []); }); expect(history.location.pathname).toEqual('/organizations/5'); }); - test('handleSubmit should post instance groups', async () => { + test('onSubmit should post instance groups', async () => { const orgData = { name: 'new name', description: 'new description', @@ -100,15 +95,17 @@ describe('', () => { wrapper = mountWithContexts(); }); await waitForElement(wrapper, 'button[aria-label="Save"]'); - await wrapper.find('OrganizationForm').prop('handleSubmit')( - orgData, - [3], - [] - ); + await wrapper.find('OrganizationForm').prop('onSubmit')(orgData, [3], []); expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(5, 3); }); test('AnsibleSelect component renders if there are virtual environments', async () => { + const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; + OrganizationsAPI.readInstanceGroups.mockReturnValue({ + data: { + results: mockInstanceGroups, + }, + }); const config = { custom_virtualenvs: ['foo', 'bar'], }; @@ -116,8 +113,9 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(, { context: { config }, - }).find('AnsibleSelect'); + }); }); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(wrapper.find('FormSelect')).toHaveLength(1); expect(wrapper.find('FormSelectOption')).toHaveLength(3); expect( @@ -129,6 +127,12 @@ describe('', () => { }); test('AnsibleSelect component does not render if there are 0 virtual environments', async () => { + const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; + OrganizationsAPI.readInstanceGroups.mockReturnValue({ + data: { + results: mockInstanceGroups, + }, + }); const config = { custom_virtualenvs: [], }; @@ -136,8 +140,9 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(, { context: { config }, - }).find('AnsibleSelect'); + }); }); - expect(wrapper.find('FormSelect')).toHaveLength(0); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + expect(wrapper.find('AnsibleSelect FormSelect')).toHaveLength(0); }); }); diff --git a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx index ae38719d8b..55df19f59a 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.jsx @@ -46,8 +46,8 @@ function OrganizationEdit({ organization }) { {({ me }) => ( )} diff --git a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx index 2ecf914440..8defe7ccc6 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationEdit/OrganizationEdit.test.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { createMemoryHistory } from 'history'; import { OrganizationsAPI } from '@api'; -import { mountWithContexts } from '@testUtils/enzymeHelpers'; +import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import OrganizationEdit from './OrganizationEdit'; jest.mock('@api'); @@ -18,7 +18,7 @@ describe('', () => { }, }; - test('handleSubmit should call api update', async () => { + test('onSubmit should call api update', async () => { let wrapper; await act(async () => { wrapper = mountWithContexts(); @@ -29,16 +29,12 @@ describe('', () => { description: 'new description', custom_virtualenv: 'Buzz', }; - wrapper.find('OrganizationForm').prop('handleSubmit')( - updatedOrgData, - [], - [] - ); + wrapper.find('OrganizationForm').prop('onSubmit')(updatedOrgData, [], []); expect(OrganizationsAPI.update).toHaveBeenCalledWith(1, updatedOrgData); }); - test('handleSubmit associates and disassociates instance groups', async () => { + test('onSubmit associates and disassociates instance groups', async () => { let wrapper; await act(async () => { wrapper = mountWithContexts(); @@ -50,13 +46,12 @@ describe('', () => { custom_virtualenv: 'Buzz', }; await act(async () => { - wrapper.find('OrganizationForm').invoke('handleSubmit')( + wrapper.find('OrganizationForm').invoke('onSubmit')( updatedOrgData, [3, 4], [2] ); }); - expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 3); expect(OrganizationsAPI.associateInstanceGroup).toHaveBeenCalledWith(1, 4); expect(OrganizationsAPI.disassociateInstanceGroup).toHaveBeenCalledWith( @@ -66,6 +61,12 @@ describe('', () => { }); test('should navigate to organization detail when cancel is clicked', async () => { + const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; + OrganizationsAPI.readInstanceGroups.mockReturnValue({ + data: { + results: mockInstanceGroups, + }, + }); const history = createMemoryHistory({}); let wrapper; await act(async () => { @@ -74,9 +75,10 @@ describe('', () => { { context: { router: { history } } } ); }); - - wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); - + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + await act(async () => { + wrapper.find('button[aria-label="Cancel"]').invoke('onClick')(); + }); expect(history.location.pathname).toEqual('/organizations/1/details'); }); }); diff --git a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx index 2bc0dcdc4d..fea386879f 100644 --- a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx +++ b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.jsx @@ -1,206 +1,174 @@ -import React, { Component, Fragment } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { QuestionCircleIcon } from '@patternfly/react-icons'; - import { withRouter } from 'react-router-dom'; import { Formik, Field } from 'formik'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; - +import { QuestionCircleIcon } from '@patternfly/react-icons'; import { Tooltip, Form, FormGroup } from '@patternfly/react-core'; import { OrganizationsAPI } from '@api'; -import { Config } from '@contexts/Config'; +import { ConfigContext } from '@contexts/Config'; +import AnsibleSelect from '@components/AnsibleSelect'; +import ContentError from '@components/ContentError'; +import ContentLoading from '@components/ContentLoading'; import FormRow from '@components/FormRow'; import FormField from '@components/FormField'; import FormActionGroup from '@components/FormActionGroup/FormActionGroup'; -import AnsibleSelect from '@components/AnsibleSelect'; import { InstanceGroupsLookup } from '@components/Lookup/'; +import { getAddedAndRemoved } from '@util/lists'; import { required, minMaxValue } from '@util/validators'; -class OrganizationForm extends Component { - constructor(props) { - super(props); +function OrganizationForm({ organization, i18n, me, onCancel, onSubmit }) { + const defaultVenv = { + label: i18n._(t`Use Default Ansible Environment`), + value: '/venv/ansible/', + key: 'default', + }; + const { custom_virtualenvs } = useContext(ConfigContext); + const [contentError, setContentError] = useState(null); + const [hasContentLoading, setHasContentLoading] = useState(true); + const [initialInstanceGroups, setInitialInstanceGroups] = useState([]); + const [instanceGroups, setInstanceGroups] = useState([]); - this.getRelatedInstanceGroups = this.getRelatedInstanceGroups.bind(this); - this.handleInstanceGroupsChange = this.handleInstanceGroupsChange.bind( - this + const handleCancel = () => { + onCancel(); + }; + + const handleSubmit = values => { + const { added, removed } = getAddedAndRemoved( + initialInstanceGroups, + instanceGroups ); - this.handleSubmit = this.handleSubmit.bind(this); - - this.state = { - instanceGroups: [], - initialInstanceGroups: [], - formIsValid: true, - }; - } - - async componentDidMount() { - let instanceGroups = []; - - if (!this.isEditingNewOrganization()) { - try { - instanceGroups = await this.getRelatedInstanceGroups(); - } catch (err) { - this.setState({ error: err }); - } - } - - this.setState({ - instanceGroups, - initialInstanceGroups: [...instanceGroups], - }); - } - - async getRelatedInstanceGroups() { - const { - organization: { id }, - } = this.props; - const { data } = await OrganizationsAPI.readInstanceGroups(id); - return data.results; - } - - isEditingNewOrganization() { - const { organization } = this.props; - return !organization.id; - } - - handleInstanceGroupsChange(instanceGroups) { - this.setState({ instanceGroups }); - } - - handleSubmit(values) { - const { handleSubmit } = this.props; - const { instanceGroups, initialInstanceGroups } = this.state; - - const initialIds = initialInstanceGroups.map(ig => ig.id); - const updatedIds = instanceGroups.map(ig => ig.id); - const groupsToAssociate = [...updatedIds].filter( - x => !initialIds.includes(x) - ); - const groupsToDisassociate = [...initialIds].filter( - x => !updatedIds.includes(x) - ); - + const addedIds = added.map(({ id }) => id); + const removedIds = removed.map(({ id }) => id); if ( typeof values.max_hosts !== 'number' || values.max_hosts === 'undefined' ) { values.max_hosts = 0; } + onSubmit(values, addedIds, removedIds); + }; - handleSubmit(values, groupsToAssociate, groupsToDisassociate); + useEffect(() => { + (async () => { + const { id } = organization; + if (!id) { + setHasContentLoading(false); + return; + } + setContentError(null); + setHasContentLoading(true); + try { + const { + data: { results = [] }, + } = await OrganizationsAPI.readInstanceGroups(id); + setInitialInstanceGroups(results); + setInstanceGroups(results); + } catch (error) { + setContentError(error); + } finally { + setHasContentLoading(false); + } + })(); + }, [organization]); + + if (contentError) { + return ; } - render() { - const { organization, handleCancel, i18n, me } = this.props; - const { instanceGroups, formIsValid, error } = this.state; - const defaultVenv = { - label: i18n._(t`Use Default Ansible Environment`), - value: '/venv/ansible/', - key: 'default', - }; + if (hasContentLoading) { + return ; + } - return ( - ( -
- - - - - {i18n._(t`Max Hosts`)}{' '} - { - - - - } - - } - validate={minMaxValue(0, 2147483647, i18n)} - me={me || {}} - isDisabled={!me.is_superuser} - /> - - {({ custom_virtualenvs }) => - custom_virtualenvs && - custom_virtualenvs.length > 1 && ( - ( - - datum !== defaultVenv.value) - .map(datum => ({ - label: datum, - value: datum, - key: datum, - })), - ]} - {...field} - /> - - )} + return ( + ( + + + + + + {i18n._(t`Max Hosts`)}{' '} + + + + + } + validate={minMaxValue(0, Number.MAX_SAFE_INTEGER, i18n)} + me={me || {}} + isDisabled={!me.is_superuser} + /> + {custom_virtualenvs && custom_virtualenvs.length > 1 && ( + ( + + value !== defaultVenv.value) + .map(value => ({ value, label: value, key: value })), + ]} + {...field} /> - ) - } - - - - - {error ?
error
: null} - - )} - /> - ); - } + + )} + /> + )} + + + + + )} + /> + ); } FormField.propTypes = { @@ -209,8 +177,8 @@ FormField.propTypes = { OrganizationForm.propTypes = { organization: PropTypes.shape(), - handleSubmit: PropTypes.func.isRequired, - handleCancel: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, }; OrganizationForm.defaultProps = { diff --git a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.test.jsx b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.test.jsx index 9fda0b279d..6443493301 100644 --- a/awx/ui_next/src/screens/Organization/shared/OrganizationForm.test.jsx +++ b/awx/ui_next/src/screens/Organization/shared/OrganizationForm.test.jsx @@ -1,7 +1,6 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '@testUtils/enzymeHelpers'; -import { sleep } from '@testUtils/testUtils'; +import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers'; import { OrganizationsAPI } from '@api'; import OrganizationForm from './OrganizationForm'; @@ -25,18 +24,20 @@ describe('', () => { instance_groups: '/api/v2/organizations/1/instance_groups', }, }; + const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; afterEach(() => { jest.clearAllMocks(); }); test('should request related instance groups from api', async () => { + let wrapper; await act(async () => { - mountWithContexts( + wrapper = mountWithContexts( , { @@ -44,12 +45,11 @@ describe('', () => { } ); }); - + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalledTimes(1); }); test('componentDidMount should set instanceGroups to state', async () => { - const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; OrganizationsAPI.readInstanceGroups.mockReturnValue({ data: { results: mockInstanceGroups, @@ -60,8 +60,8 @@ describe('', () => { wrapper = mountWithContexts( , { @@ -70,84 +70,109 @@ describe('', () => { ); }); + await waitForElement( + wrapper, + 'InstanceGroupsLookup', + el => el.length === 1 + ); expect(OrganizationsAPI.readInstanceGroups).toHaveBeenCalled(); - expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual( - mockInstanceGroups - ); + expect(wrapper.find('InstanceGroupsLookup Chip span')).toHaveLength(2); }); - test('changing instance group successfully sets instanceGroups state', async () => { + test('Instance group is rendered when added', async () => { + OrganizationsAPI.readInstanceGroups.mockReturnValue({ + data: { results: [] }, + }); let wrapper; await act(async () => { wrapper = mountWithContexts( ); }); - - const lookup = wrapper.find('InstanceGroupsLookup'); + const lookup = await waitForElement( + wrapper, + 'InstanceGroupsLookup', + el => el.length === 1 + ); expect(lookup.length).toBe(1); - - lookup.prop('onChange')( - [ - { - id: 1, - name: 'foo', - }, - ], - 'instanceGroups' + expect(lookup.find('Chip span')).toHaveLength(0); + await act(async () => { + lookup.prop('onChange')( + [ + { + id: 1, + name: 'foo', + }, + ], + 'instanceGroups' + ); + }); + const group = await waitForElement( + wrapper, + 'InstanceGroupsLookup Chip span', + el => el.length === 1 ); - expect(wrapper.find('OrganizationForm').state().instanceGroups).toEqual([ - { - id: 1, - name: 'foo', - }, - ]); + expect(group.text()).toEqual('foo'); }); - test('changing inputs should update form values', async () => { + test('changing inputs and saving triggers expected callback', async () => { let wrapper; + const onSubmit = jest.fn(); await act(async () => { wrapper = mountWithContexts( ); }); - - const form = wrapper.find('Formik'); - wrapper.find('input#org-name').simulate('change', { - target: { value: 'new foo', name: 'name' }, + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + await act(async () => { + wrapper.find('input#org-name').simulate('change', { + target: { value: 'new foo', name: 'name' }, + }); + wrapper.find('input#org-description').simulate('change', { + target: { value: 'new bar', name: 'description' }, + }); + wrapper.find('input#org-max_hosts').simulate('change', { + target: { value: 134, name: 'max_hosts' }, + }); }); - expect(form.state('values').name).toEqual('new foo'); - wrapper.find('input#org-description').simulate('change', { - target: { value: 'new bar', name: 'description' }, + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); }); - expect(form.state('values').description).toEqual('new bar'); - wrapper.find('input#org-max_hosts').simulate('change', { - target: { value: '134', name: 'max_hosts' }, + expect(onSubmit).toHaveBeenCalledTimes(1); + expect(onSubmit.mock.calls[0][0]).toEqual({ + name: 'new foo', + description: 'new bar', + custom_virtualenv: 'Fizz', + max_hosts: 134, }); - expect(form.state('values').max_hosts).toEqual('134'); }); test('AnsibleSelect component renders if there are virtual environments', async () => { const config = { custom_virtualenvs: ['foo', 'bar'], }; + OrganizationsAPI.readInstanceGroups.mockReturnValue({ + data: { + results: mockInstanceGroups, + }, + }); let wrapper; await act(async () => { wrapper = mountWithContexts( , { @@ -155,6 +180,7 @@ describe('', () => { } ); }); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(wrapper.find('FormSelect')).toHaveLength(1); expect(wrapper.find('FormSelectOption')).toHaveLength(3); expect( @@ -165,36 +191,7 @@ describe('', () => { ).toEqual('/venv/ansible/'); }); - test('calls handleSubmit when form submitted', async () => { - const handleSubmit = jest.fn(); - let wrapper; - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - expect(handleSubmit).not.toHaveBeenCalled(); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(1); - expect(handleSubmit).toHaveBeenCalledWith( - { - name: 'Foo', - description: 'Bar', - max_hosts: 1, - custom_virtualenv: 'Fizz', - }, - [], - [] - ); - }); - - test('handleSubmit associates and disassociates instance groups', async () => { - const mockInstanceGroups = [{ name: 'One', id: 1 }, { name: 'Two', id: 2 }]; + test('onSubmit associates and disassociates instance groups', async () => { OrganizationsAPI.readInstanceGroups.mockReturnValue({ data: { results: mockInstanceGroups, @@ -206,7 +203,7 @@ describe('', () => { max_hosts: 1, custom_virtualenv: 'Fizz', }; - const handleSubmit = jest.fn(); + const onSubmit = jest.fn(); OrganizationsAPI.update.mockResolvedValue(1, mockDataForm); OrganizationsAPI.associateInstanceGroup.mockResolvedValue('done'); OrganizationsAPI.disassociateInstanceGroup.mockResolvedValue('done'); @@ -215,8 +212,8 @@ describe('', () => { wrapper = mountWithContexts( , { @@ -224,47 +221,21 @@ describe('', () => { } ); }); - wrapper.find('InstanceGroupsLookup').prop('onChange')( - [{ name: 'One', id: 1 }, { name: 'Three', id: 3 }], - 'instanceGroups' - ); - - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(0); - expect(handleSubmit).toHaveBeenCalledWith(mockDataForm, [3], [2]); - }); - - test('handleSubmit is called with max_hosts value if it is in range', async () => { - const handleSubmit = jest.fn(); - - let wrapper; + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); await act(async () => { - wrapper = mountWithContexts( - + wrapper.find('InstanceGroupsLookup').prop('onChange')( + [{ name: 'One', id: 1 }, { name: 'Three', id: 3 }], + 'instanceGroups' ); }); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(0); - expect(handleSubmit).toHaveBeenCalledWith( - { - name: 'Foo', - description: 'Bar', - max_hosts: 1, - custom_virtualenv: 'Fizz', - }, - [], - [] - ); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); + expect(onSubmit).toHaveBeenCalledWith(mockDataForm, [3], [2]); }); - test('handleSubmit does not get called if max_hosts value is out of range', async () => { - const handleSubmit = jest.fn(); - + test('onSubmit does not get called if max_hosts value is out of range', async () => { + const onSubmit = jest.fn(); // mount with negative value let wrapper1; const mockDataNegative = JSON.parse(JSON.stringify(mockData)); @@ -273,38 +244,41 @@ describe('', () => { wrapper1 = mountWithContexts( ); }); - wrapper1.find('button[aria-label="Save"]').simulate('click'); - await sleep(0); - expect(handleSubmit).not.toHaveBeenCalled(); + await waitForElement(wrapper1, 'ContentLoading', el => el.length === 0); + await act(async () => { + wrapper1.find('button[aria-label="Save"]').simulate('click'); + }); + expect(onSubmit).not.toHaveBeenCalled(); // mount with out of range value let wrapper2; - const mockDataOoR = JSON.parse(JSON.stringify(mockData)); - mockDataOoR.max_hosts = 999999999999; + const mockDataOutOfRange = JSON.parse(JSON.stringify(mockData)); + mockDataOutOfRange.max_hosts = 999999999999999999999; await act(async () => { wrapper2 = mountWithContexts( ); }); - wrapper2.find('button[aria-label="Save"]').simulate('click'); - await sleep(0); - expect(handleSubmit).not.toHaveBeenCalled(); + await waitForElement(wrapper2, 'ContentLoading', el => el.length === 0); + await act(async () => { + wrapper2.find('button[aria-label="Save"]').simulate('click'); + }); + expect(onSubmit).not.toHaveBeenCalled(); }); - test('handleSubmit is called and max_hosts value defaults to 0 if input is not a number', async () => { - const handleSubmit = jest.fn(); - + test('onSubmit is called and max_hosts value defaults to 0 if input is not a number', async () => { + const onSubmit = jest.fn(); // mount with String value (default to zero) const mockDataString = JSON.parse(JSON.stringify(mockData)); mockDataString.max_hosts = 'Bee'; @@ -313,15 +287,17 @@ describe('', () => { wrapper = mountWithContexts( ); }); - wrapper.find('button[aria-label="Save"]').simulate('click'); - await sleep(0); - expect(handleSubmit).toHaveBeenCalledWith( + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + await act(async () => { + wrapper.find('button[aria-label="Save"]').simulate('click'); + }); + expect(onSubmit).toHaveBeenCalledWith( { name: 'Foo', description: 'Bar', @@ -333,22 +309,22 @@ describe('', () => { ); }); - test('calls "handleCancel" when Cancel button is clicked', async () => { - const handleCancel = jest.fn(); - + test('calls "onCancel" when Cancel button is clicked', async () => { + const onCancel = jest.fn(); let wrapper; await act(async () => { wrapper = mountWithContexts( ); }); - expect(handleCancel).not.toHaveBeenCalled(); + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + expect(onCancel).not.toHaveBeenCalled(); wrapper.find('button[aria-label="Cancel"]').prop('onClick')(); - expect(handleCancel).toBeCalled(); + expect(onCancel).toBeCalled(); }); });