mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 16:51:11 +03:00
Move organization form to functional component
This commit is contained in:
parent
2f9742e9de
commit
057320aed3
@ -50,8 +50,8 @@ function OrganizationAdd({ i18n }) {
|
||||
<Config>
|
||||
{({ me }) => (
|
||||
<OrganizationForm
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
me={me || {}}
|
||||
/>
|
||||
)}
|
||||
|
@ -8,7 +8,7 @@ import { OrganizationsAPI } from '@api';
|
||||
jest.mock('@api');
|
||||
|
||||
describe('<OrganizationAdd />', () => {
|
||||
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('<OrganizationAdd />', () => {
|
||||
};
|
||||
await act(async () => {
|
||||
const wrapper = mountWithContexts(<OrganizationAdd />);
|
||||
wrapper.find('OrganizationForm').prop('handleSubmit')(
|
||||
updatedOrgData,
|
||||
[],
|
||||
[]
|
||||
);
|
||||
wrapper.find('OrganizationForm').prop('onSubmit')(updatedOrgData, [], []);
|
||||
});
|
||||
expect(OrganizationsAPI.create).toHaveBeenCalledWith(updatedOrgData);
|
||||
});
|
||||
@ -32,6 +28,9 @@ describe('<OrganizationAdd />', () => {
|
||||
wrapper = mountWithContexts(<OrganizationAdd />, {
|
||||
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('<OrganizationAdd />', () => {
|
||||
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('<OrganizationAdd />', () => {
|
||||
wrapper = mountWithContexts(<OrganizationAdd />);
|
||||
});
|
||||
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('<OrganizationAdd />', () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<OrganizationAdd />, {
|
||||
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('<OrganizationAdd />', () => {
|
||||
});
|
||||
|
||||
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('<OrganizationAdd />', () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<OrganizationAdd />, {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
@ -46,8 +46,8 @@ function OrganizationEdit({ organization }) {
|
||||
{({ me }) => (
|
||||
<OrganizationForm
|
||||
organization={organization}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
me={me || {}}
|
||||
/>
|
||||
)}
|
||||
|
@ -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('<OrganizationEdit />', () => {
|
||||
},
|
||||
};
|
||||
|
||||
test('handleSubmit should call api update', async () => {
|
||||
test('onSubmit should call api update', async () => {
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<OrganizationEdit organization={mockData} />);
|
||||
@ -29,16 +29,12 @@ describe('<OrganizationEdit />', () => {
|
||||
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(<OrganizationEdit organization={mockData} />);
|
||||
@ -50,13 +46,12 @@ describe('<OrganizationEdit />', () => {
|
||||
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('<OrganizationEdit />', () => {
|
||||
});
|
||||
|
||||
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('<OrganizationEdit />', () => {
|
||||
{ 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');
|
||||
});
|
||||
});
|
||||
|
@ -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 <ContentError error={contentError} />;
|
||||
}
|
||||
|
||||
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 <ContentLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
name: organization.name,
|
||||
description: organization.description,
|
||||
custom_virtualenv: organization.custom_virtualenv || '',
|
||||
max_hosts: organization.max_hosts || '0',
|
||||
}}
|
||||
onSubmit={this.handleSubmit}
|
||||
render={formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormRow>
|
||||
<FormField
|
||||
id="org-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={i18n._(t`Name`)}
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="org-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={i18n._(t`Description`)}
|
||||
/>
|
||||
<FormField
|
||||
id="org-max_hosts"
|
||||
name="max_hosts"
|
||||
type="number"
|
||||
label={
|
||||
<Fragment>
|
||||
{i18n._(t`Max Hosts`)}{' '}
|
||||
{
|
||||
<Tooltip
|
||||
position="right"
|
||||
content={i18n._(t`The maximum number of hosts allowed
|
||||
to be managed by this organization. Value defaults to
|
||||
0 which means no limit. Refer to the Ansible
|
||||
documentation for more details.`)}
|
||||
>
|
||||
<QuestionCircleIcon />
|
||||
</Tooltip>
|
||||
}
|
||||
</Fragment>
|
||||
}
|
||||
validate={minMaxValue(0, 2147483647, i18n)}
|
||||
me={me || {}}
|
||||
isDisabled={!me.is_superuser}
|
||||
/>
|
||||
<Config>
|
||||
{({ custom_virtualenvs }) =>
|
||||
custom_virtualenvs &&
|
||||
custom_virtualenvs.length > 1 && (
|
||||
<Field
|
||||
name="custom_virtualenv"
|
||||
render={({ field }) => (
|
||||
<FormGroup
|
||||
fieldId="org-custom-virtualenv"
|
||||
label={i18n._(t`Ansible Environment`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
id="org-custom-virtualenv"
|
||||
data={[
|
||||
defaultVenv,
|
||||
...custom_virtualenvs
|
||||
.filter(datum => datum !== defaultVenv.value)
|
||||
.map(datum => ({
|
||||
label: datum,
|
||||
value: datum,
|
||||
key: datum,
|
||||
})),
|
||||
]}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
name: organization.name,
|
||||
description: organization.description,
|
||||
custom_virtualenv: organization.custom_virtualenv || '',
|
||||
max_hosts: organization.max_hosts || '0',
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
render={formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormRow>
|
||||
<FormField
|
||||
id="org-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={i18n._(t`Name`)}
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="org-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={i18n._(t`Description`)}
|
||||
/>
|
||||
<FormField
|
||||
id="org-max_hosts"
|
||||
name="max_hosts"
|
||||
type="number"
|
||||
label={
|
||||
<>
|
||||
{i18n._(t`Max Hosts`)}{' '}
|
||||
<Tooltip
|
||||
position="right"
|
||||
content={i18n._(
|
||||
t`The maximum number of hosts allowed to be managed by this organization.
|
||||
Value defaults to 0 which means no limit. Refer to the Ansible
|
||||
documentation for more details.`
|
||||
)}
|
||||
>
|
||||
<QuestionCircleIcon />
|
||||
</Tooltip>
|
||||
</>
|
||||
}
|
||||
validate={minMaxValue(0, Number.MAX_SAFE_INTEGER, i18n)}
|
||||
me={me || {}}
|
||||
isDisabled={!me.is_superuser}
|
||||
/>
|
||||
{custom_virtualenvs && custom_virtualenvs.length > 1 && (
|
||||
<Field
|
||||
name="custom_virtualenv"
|
||||
render={({ field }) => (
|
||||
<FormGroup
|
||||
fieldId="org-custom-virtualenv"
|
||||
label={i18n._(t`Ansible Environment`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
id="org-custom-virtualenv"
|
||||
data={[
|
||||
defaultVenv,
|
||||
...custom_virtualenvs
|
||||
.filter(value => value !== defaultVenv.value)
|
||||
.map(value => ({ value, label: value, key: value })),
|
||||
]}
|
||||
{...field}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Config>
|
||||
</FormRow>
|
||||
<InstanceGroupsLookup
|
||||
value={instanceGroups}
|
||||
onChange={this.handleInstanceGroupsChange}
|
||||
tooltip={i18n._(
|
||||
t`Select the Instance Groups for this Organization to run on.`
|
||||
)}
|
||||
/>
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
submitDisabled={!formIsValid}
|
||||
/>
|
||||
{error ? <div>error</div> : null}
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
</FormGroup>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</FormRow>
|
||||
<InstanceGroupsLookup
|
||||
value={instanceGroups}
|
||||
onChange={setInstanceGroups}
|
||||
tooltip={i18n._(
|
||||
t`Select the Instance Groups for this Organization to run on.`
|
||||
)}
|
||||
/>
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
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 = {
|
||||
|
@ -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('<OrganizationForm />', () => {
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={jest.fn()}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>,
|
||||
{
|
||||
@ -44,12 +45,11 @@ describe('<OrganizationForm />', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
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('<OrganizationForm />', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={jest.fn()}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>,
|
||||
{
|
||||
@ -70,84 +70,109 @@ describe('<OrganizationForm />', () => {
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={jest.fn()}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={jest.fn()}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>,
|
||||
{
|
||||
@ -155,6 +180,7 @@ describe('<OrganizationForm />', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
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('<OrganizationForm />', () => {
|
||||
).toEqual('/venv/ansible/');
|
||||
});
|
||||
|
||||
test('calls handleSubmit when form submitted', async () => {
|
||||
const handleSubmit = jest.fn();
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
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('<OrganizationForm />', () => {
|
||||
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('<OrganizationForm />', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>,
|
||||
{
|
||||
@ -224,47 +221,21 @@ describe('<OrganizationForm />', () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
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('<OrganizationForm />', () => {
|
||||
wrapper1 = mountWithContexts(
|
||||
<OrganizationForm
|
||||
organization={mockDataNegative}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockDataOoR}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={jest.fn()}
|
||||
organization={mockDataOutOfRange}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
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('<OrganizationForm />', () => {
|
||||
wrapper = mountWithContexts(
|
||||
<OrganizationForm
|
||||
organization={mockDataString}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={jest.fn()}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={jest.fn()}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
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('<OrganizationForm />', () => {
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
<OrganizationForm
|
||||
organization={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={handleCancel}
|
||||
onSubmit={jest.fn()}
|
||||
onCancel={onCancel}
|
||||
me={meConfig.me}
|
||||
/>
|
||||
);
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user