update JobTemplateForm tests

This commit is contained in:
Keith Grant 2019-09-03 14:35:04 -07:00
parent 4f546be87a
commit 8b1ca12d8f
6 changed files with 135 additions and 67 deletions

View File

@ -16,6 +16,7 @@ function CheckboxField({ id, name, label, tooltip, validate, ...rest }) {
validate={validate}
render={({ field }) => (
<Checkbox
aria-label={label}
label={
<span>
{label}

View File

@ -57,7 +57,7 @@ FormField.propTypes = {
type: PropTypes.string,
validate: PropTypes.func,
isRequired: PropTypes.bool,
tooltip: PropTypes.string,
tooltip: PropTypes.node,
};
FormField.defaultProps = {

View File

@ -12,10 +12,11 @@ const getInstanceGroups = async params => InstanceGroupsAPI.read(params);
class InstanceGroupsLookup extends React.Component {
render() {
const { value, tooltip, onChange, i18n } = this.props;
const { value, tooltip, onChange, className, i18n } = this.props;
return (
<FormGroup
className={className}
label={
<Fragment>
{i18n._(t`Instance Groups`)}{' '}

View File

@ -92,6 +92,32 @@ const mockRelatedProjectPlaybooks = [
'vault.yml',
];
const mockInstanceGroups = [
{
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: 'tower',
capacity: 59,
committed_capacity: 0,
consumed_capacity: 0,
percent_capacity_remaining: 100.0,
jobs_running: 0,
jobs_total: 3,
instances: 1,
controller: null,
is_controller: false,
is_isolated: false,
policy_instance_percentage: 100,
policy_instance_minimum: 0,
policy_instance_list: [],
},
];
JobTemplatesAPI.readCredentials.mockResolvedValue({
data: mockRelatedCredentials,
});
@ -101,12 +127,25 @@ ProjectsAPI.readPlaybooks.mockResolvedValue({
LabelsAPI.read.mockResolvedValue({ data: { results: [] } });
describe('<JobTemplateEdit />', () => {
test('initially renders successfully', async done => {
beforeEach(() => {
LabelsAPI.read.mockResolvedValue({ data: { results: [] } });
JobTemplatesAPI.readCredentials.mockResolvedValue({
data: mockRelatedCredentials,
});
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
data: { results: mockInstanceGroups },
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders successfully', async () => {
const wrapper = mountWithContexts(
<JobTemplateEdit template={mockJobTemplate} />
);
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
done();
});
test('handleSubmit should call api update', async done => {

View File

@ -157,23 +157,6 @@ class JobTemplateForm extends Component {
newLabel => newLabel.name !== label
);
this.setState({ newLabels: filteredLabels });
} else if (typeof label === 'string') {
setFieldValue('newLabels', [
...newLabels,
{
name: label,
organization: template.summary_fields.inventory.organization_id,
},
]);
this.setState({
newLabels: [
...newLabels,
{
name: label,
organization: template.summary_fields.inventory.organization_id,
},
],
});
} else {
setFieldValue('newLabels', [
...newLabels,
@ -182,7 +165,12 @@ class JobTemplateForm extends Component {
this.setState({
newLabels: [
...newLabels,
{ name: label.name, associate: true, id: label.id },
{
name: label.name,
associate: true,
id: label.id,
organization: template.summary_fields.inventory.organization_id,
},
],
});
}
@ -311,6 +299,12 @@ class JobTemplateForm extends Component {
{ value: '3', key: '3', label: i18n._(t`3 (Debug)`) },
{ value: '4', key: '4', label: i18n._(t`4 (Connection Debug)`) },
];
let callbackUrl;
if (template && template.related) {
const { origin } = document.location;
const path = template.related.callback || `${template.url}callback`;
callbackUrl = `${origin}${path}`;
}
if (hasContentLoading) {
return (
@ -477,6 +471,7 @@ class JobTemplateForm extends Component {
id="template-forks"
name="forks"
type="number"
min="0"
label={i18n._(t`Forks`)}
tooltip={
<span>
@ -568,6 +563,7 @@ class JobTemplateForm extends Component {
/>
</FormRow>
<InstanceGroupsLookup
css="margin-top: 20px"
value={relatedInstanceGroups}
onChange={this.handleInstanceGroupsChange}
tooltip={i18n._(
@ -579,6 +575,7 @@ class JobTemplateForm extends Component {
render={({ field, form }) => (
<FormGroup
label={i18n._(t`Job Tags`)}
css="margin-top: 20px"
fieldId="template-job-tags"
>
<Tooltip
@ -603,6 +600,7 @@ class JobTemplateForm extends Component {
render={({ field, form }) => (
<FormGroup
label={i18n._(t`Skip Tags`)}
css="margin-top: 20px"
fieldId="template-skip-tags"
>
<Tooltip
@ -622,7 +620,12 @@ class JobTemplateForm extends Component {
</FormGroup>
)}
/>
<GridFormGroup isInline label={i18n._(t`Options`)}>
<GridFormGroup
fieldId="template-option-checkboxes"
isInline
label={i18n._(t`Options`)}
css="margin-top: 20px"
>
<CheckboxField
id="option-privilege-escalation"
name="become_enabled"
@ -641,7 +644,7 @@ class JobTemplateForm extends Component {
position="right"
content={i18n._(
t`Enables creation of a provisioning callback URL. Using
the URL a host can contact {{BRAND_NAME}} and request a
the URL a host can contact BRAND_NAME and request a
configuration update using this job template.`
)}
>
@ -677,19 +680,22 @@ class JobTemplateForm extends Component {
<div
css={`
${allowCallbacks ? '' : 'display: none'}
margin-top: 20px;
`}
>
<FormRow>
<FormGroup
label={i18n._(t`Provisioning Callback URL`)}
fieldId="template-callback-url"
>
<TextInput
id="template-callback-url"
isDisabled
value={`${document.location.origin}${template.related.callback}`}
/>
</FormGroup>
{callbackUrl && (
<FormGroup
label={i18n._(t`Provisioning Callback URL`)}
fieldId="template-callback-url"
>
<TextInput
id="template-callback-url"
isDisabled
value={callbackUrl}
/>
</FormGroup>
)}
<FormField
id="template-host-config-key"
name="host_config_key"

View File

@ -2,7 +2,7 @@ import React from 'react';
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
import { sleep } from '@testUtils/testUtils';
import JobTemplateForm, { _JobTemplateForm } from './JobTemplateForm';
import { LabelsAPI } from '@api';
import { LabelsAPI, JobTemplatesAPI } from '@api';
jest.mock('@api');
@ -29,17 +29,45 @@ describe('<JobTemplateForm />', () => {
labels: { results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }] },
},
};
const mockInstanceGroups = [
{
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: 'tower',
capacity: 59,
committed_capacity: 0,
consumed_capacity: 0,
percent_capacity_remaining: 100.0,
jobs_running: 0,
jobs_total: 3,
instances: 1,
controller: null,
is_controller: false,
is_isolated: false,
policy_instance_percentage: 100,
policy_instance_minimum: 0,
policy_instance_list: [],
},
];
beforeEach(() => {
LabelsAPI.read.mockReturnValue({
data: mockData.summary_fields.labels,
});
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
data: { results: mockInstanceGroups },
});
});
afterEach(() => {
jest.clearAllMocks();
});
test('initially renders successfully', async done => {
test('should render labels MultiSelect', async () => {
const wrapper = mountWithContexts(
<JobTemplateForm
template={mockData}
@ -47,19 +75,18 @@ describe('<JobTemplateForm />', () => {
handleCancel={jest.fn()}
/>
);
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
await waitForElement(wrapper, 'Form', el => el.length === 0);
expect(LabelsAPI.read).toHaveBeenCalled();
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalled();
wrapper.update();
expect(
wrapper
.find('FormGroup[fieldId="template-labels"] MultiSelect Chip')
.first()
.text()
).toEqual('Sushi');
done();
.find('FormGroup[fieldId="template-labels"] MultiSelect')
.prop('associatedItems')
).toEqual(mockData.summary_fields.labels.results);
});
test('should update form values on input changes', async done => {
test('should update form values on input changes', async () => {
const wrapper = mountWithContexts(
<JobTemplateForm
template={mockData}
@ -96,10 +123,9 @@ describe('<JobTemplateForm />', () => {
target: { value: 'new baz type', name: 'playbook' },
});
expect(form.state('values').playbook).toEqual('new baz type');
done();
});
test('should call handleSubmit when Submit button is clicked', async done => {
test('should call handleSubmit when Submit button is clicked', async () => {
const handleSubmit = jest.fn();
const wrapper = mountWithContexts(
<JobTemplateForm
@ -113,10 +139,9 @@ describe('<JobTemplateForm />', () => {
wrapper.find('button[aria-label="Save"]').simulate('click');
await sleep(1);
expect(handleSubmit).toBeCalled();
done();
});
test('should call handleCancel when Cancel button is clicked', async done => {
test('should call handleCancel when Cancel button is clicked', async () => {
const handleCancel = jest.fn();
const wrapper = mountWithContexts(
<JobTemplateForm
@ -129,10 +154,9 @@ describe('<JobTemplateForm />', () => {
expect(handleCancel).not.toHaveBeenCalled();
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
expect(handleCancel).toBeCalled();
done();
});
test('should call loadRelatedProjectPlaybooks when project value changes', async done => {
test('should call loadRelatedProjectPlaybooks when project value changes', async () => {
const loadRelatedProjectPlaybooks = jest.spyOn(
_JobTemplateForm.prototype,
'loadRelatedProjectPlaybooks'
@ -150,15 +174,10 @@ describe('<JobTemplateForm />', () => {
name: 'project',
});
expect(loadRelatedProjectPlaybooks).toHaveBeenCalledWith(10);
done();
});
test('handleNewLabel should arrange new labels properly', async done => {
const handleNewLabel = jest.spyOn(
_JobTemplateForm.prototype,
'handleNewLabel'
);
const event = { key: 'Enter', preventDefault: () => {} };
test('handleNewLabel should arrange new labels properly', async () => {
const event = { key: 'Enter' };
const wrapper = mountWithContexts(
<JobTemplateForm
template={mockData}
@ -167,22 +186,25 @@ describe('<JobTemplateForm />', () => {
/>
);
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
const multiSelect = wrapper.find('MultiSelect');
const multiSelect = wrapper.find(
'FormGroup[fieldId="template-labels"] MultiSelect'
);
const component = wrapper.find('JobTemplateForm');
wrapper.setState({ newLabels: [], loadedLabels: [], removedLabels: [] });
multiSelect.setState({ input: 'Foo' });
component.find('input[aria-label="labels"]').prop('onKeyDown')(event);
expect(handleNewLabel).toHaveBeenCalledWith('Foo');
component
.find('FormGroup[fieldId="template-labels"] input[aria-label="labels"]')
.prop('onKeyDown')(event);
component.instance().handleNewLabel({ name: 'Bar', id: 2 });
expect(component.state().newLabels).toEqual([
{ name: 'Foo', organization: 1 },
{ associate: true, id: 2, name: 'Bar' },
]);
done();
const newLabels = component.state('newLabels');
expect(newLabels).toHaveLength(2);
expect(newLabels[0].name).toEqual('Foo');
expect(newLabels[0].organization).toEqual(1);
});
test('disassociateLabel should arrange new labels properly', async done => {
test('disassociateLabel should arrange new labels properly', async () => {
const wrapper = mountWithContexts(
<JobTemplateForm
template={mockData}
@ -203,6 +225,5 @@ describe('<JobTemplateForm />', () => {
component.instance().removeLabel({ name: 'Sushi', id: 1 });
expect(component.state().newLabels.length).toBe(0);
expect(component.state().removedLabels.length).toBe(1);
done();
});
});