mirror of
https://github.com/ansible/awx.git
synced 2026-02-28 16:28:43 -03:30
update JobTemplateForm tests
This commit is contained in:
@@ -16,6 +16,7 @@ function CheckboxField({ id, name, label, tooltip, validate, ...rest }) {
|
|||||||
validate={validate}
|
validate={validate}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
aria-label={label}
|
||||||
label={
|
label={
|
||||||
<span>
|
<span>
|
||||||
{label}
|
{label}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ FormField.propTypes = {
|
|||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
validate: PropTypes.func,
|
validate: PropTypes.func,
|
||||||
isRequired: PropTypes.bool,
|
isRequired: PropTypes.bool,
|
||||||
tooltip: PropTypes.string,
|
tooltip: PropTypes.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
FormField.defaultProps = {
|
FormField.defaultProps = {
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ const getInstanceGroups = async params => InstanceGroupsAPI.read(params);
|
|||||||
|
|
||||||
class InstanceGroupsLookup extends React.Component {
|
class InstanceGroupsLookup extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { value, tooltip, onChange, i18n } = this.props;
|
const { value, tooltip, onChange, className, i18n } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
className={className}
|
||||||
label={
|
label={
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{i18n._(t`Instance Groups`)}{' '}
|
{i18n._(t`Instance Groups`)}{' '}
|
||||||
|
|||||||
@@ -92,6 +92,32 @@ const mockRelatedProjectPlaybooks = [
|
|||||||
'vault.yml',
|
'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({
|
JobTemplatesAPI.readCredentials.mockResolvedValue({
|
||||||
data: mockRelatedCredentials,
|
data: mockRelatedCredentials,
|
||||||
});
|
});
|
||||||
@@ -101,12 +127,25 @@ ProjectsAPI.readPlaybooks.mockResolvedValue({
|
|||||||
LabelsAPI.read.mockResolvedValue({ data: { results: [] } });
|
LabelsAPI.read.mockResolvedValue({ data: { results: [] } });
|
||||||
|
|
||||||
describe('<JobTemplateEdit />', () => {
|
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(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateEdit template={mockJobTemplate} />
|
<JobTemplateEdit template={mockJobTemplate} />
|
||||||
);
|
);
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleSubmit should call api update', async done => {
|
test('handleSubmit should call api update', async done => {
|
||||||
|
|||||||
@@ -157,23 +157,6 @@ class JobTemplateForm extends Component {
|
|||||||
newLabel => newLabel.name !== label
|
newLabel => newLabel.name !== label
|
||||||
);
|
);
|
||||||
this.setState({ newLabels: filteredLabels });
|
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 {
|
} else {
|
||||||
setFieldValue('newLabels', [
|
setFieldValue('newLabels', [
|
||||||
...newLabels,
|
...newLabels,
|
||||||
@@ -182,7 +165,12 @@ class JobTemplateForm extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
newLabels: [
|
newLabels: [
|
||||||
...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: '3', key: '3', label: i18n._(t`3 (Debug)`) },
|
||||||
{ value: '4', key: '4', label: i18n._(t`4 (Connection 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) {
|
if (hasContentLoading) {
|
||||||
return (
|
return (
|
||||||
@@ -477,6 +471,7 @@ class JobTemplateForm extends Component {
|
|||||||
id="template-forks"
|
id="template-forks"
|
||||||
name="forks"
|
name="forks"
|
||||||
type="number"
|
type="number"
|
||||||
|
min="0"
|
||||||
label={i18n._(t`Forks`)}
|
label={i18n._(t`Forks`)}
|
||||||
tooltip={
|
tooltip={
|
||||||
<span>
|
<span>
|
||||||
@@ -568,6 +563,7 @@ class JobTemplateForm extends Component {
|
|||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<InstanceGroupsLookup
|
<InstanceGroupsLookup
|
||||||
|
css="margin-top: 20px"
|
||||||
value={relatedInstanceGroups}
|
value={relatedInstanceGroups}
|
||||||
onChange={this.handleInstanceGroupsChange}
|
onChange={this.handleInstanceGroupsChange}
|
||||||
tooltip={i18n._(
|
tooltip={i18n._(
|
||||||
@@ -579,6 +575,7 @@ class JobTemplateForm extends Component {
|
|||||||
render={({ field, form }) => (
|
render={({ field, form }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={i18n._(t`Job Tags`)}
|
label={i18n._(t`Job Tags`)}
|
||||||
|
css="margin-top: 20px"
|
||||||
fieldId="template-job-tags"
|
fieldId="template-job-tags"
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -603,6 +600,7 @@ class JobTemplateForm extends Component {
|
|||||||
render={({ field, form }) => (
|
render={({ field, form }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={i18n._(t`Skip Tags`)}
|
label={i18n._(t`Skip Tags`)}
|
||||||
|
css="margin-top: 20px"
|
||||||
fieldId="template-skip-tags"
|
fieldId="template-skip-tags"
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -622,7 +620,12 @@ class JobTemplateForm extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<GridFormGroup isInline label={i18n._(t`Options`)}>
|
<GridFormGroup
|
||||||
|
fieldId="template-option-checkboxes"
|
||||||
|
isInline
|
||||||
|
label={i18n._(t`Options`)}
|
||||||
|
css="margin-top: 20px"
|
||||||
|
>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
id="option-privilege-escalation"
|
id="option-privilege-escalation"
|
||||||
name="become_enabled"
|
name="become_enabled"
|
||||||
@@ -641,7 +644,7 @@ class JobTemplateForm extends Component {
|
|||||||
position="right"
|
position="right"
|
||||||
content={i18n._(
|
content={i18n._(
|
||||||
t`Enables creation of a provisioning callback URL. Using
|
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.`
|
configuration update using this job template.`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -677,19 +680,22 @@ class JobTemplateForm extends Component {
|
|||||||
<div
|
<div
|
||||||
css={`
|
css={`
|
||||||
${allowCallbacks ? '' : 'display: none'}
|
${allowCallbacks ? '' : 'display: none'}
|
||||||
|
margin-top: 20px;
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
<FormGroup
|
{callbackUrl && (
|
||||||
label={i18n._(t`Provisioning Callback URL`)}
|
<FormGroup
|
||||||
fieldId="template-callback-url"
|
label={i18n._(t`Provisioning Callback URL`)}
|
||||||
>
|
fieldId="template-callback-url"
|
||||||
<TextInput
|
>
|
||||||
id="template-callback-url"
|
<TextInput
|
||||||
isDisabled
|
id="template-callback-url"
|
||||||
value={`${document.location.origin}${template.related.callback}`}
|
isDisabled
|
||||||
/>
|
value={callbackUrl}
|
||||||
</FormGroup>
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
<FormField
|
<FormField
|
||||||
id="template-host-config-key"
|
id="template-host-config-key"
|
||||||
name="host_config_key"
|
name="host_config_key"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import { sleep } from '@testUtils/testUtils';
|
import { sleep } from '@testUtils/testUtils';
|
||||||
import JobTemplateForm, { _JobTemplateForm } from './JobTemplateForm';
|
import JobTemplateForm, { _JobTemplateForm } from './JobTemplateForm';
|
||||||
import { LabelsAPI } from '@api';
|
import { LabelsAPI, JobTemplatesAPI } from '@api';
|
||||||
|
|
||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
|
|
||||||
@@ -29,17 +29,45 @@ describe('<JobTemplateForm />', () => {
|
|||||||
labels: { results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }] },
|
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(() => {
|
beforeEach(() => {
|
||||||
LabelsAPI.read.mockReturnValue({
|
LabelsAPI.read.mockReturnValue({
|
||||||
data: mockData.summary_fields.labels,
|
data: mockData.summary_fields.labels,
|
||||||
});
|
});
|
||||||
|
JobTemplatesAPI.readInstanceGroups.mockReturnValue({
|
||||||
|
data: { results: mockInstanceGroups },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('initially renders successfully', async done => {
|
test('should render labels MultiSelect', async () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateForm
|
<JobTemplateForm
|
||||||
template={mockData}
|
template={mockData}
|
||||||
@@ -47,19 +75,18 @@ describe('<JobTemplateForm />', () => {
|
|||||||
handleCancel={jest.fn()}
|
handleCancel={jest.fn()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
await waitForElement(wrapper, 'Form', el => el.length === 0);
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
|
||||||
expect(LabelsAPI.read).toHaveBeenCalled();
|
expect(LabelsAPI.read).toHaveBeenCalled();
|
||||||
|
expect(JobTemplatesAPI.readInstanceGroups).toHaveBeenCalled();
|
||||||
|
wrapper.update();
|
||||||
expect(
|
expect(
|
||||||
wrapper
|
wrapper
|
||||||
.find('FormGroup[fieldId="template-labels"] MultiSelect Chip')
|
.find('FormGroup[fieldId="template-labels"] MultiSelect')
|
||||||
.first()
|
.prop('associatedItems')
|
||||||
.text()
|
).toEqual(mockData.summary_fields.labels.results);
|
||||||
).toEqual('Sushi');
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should update form values on input changes', async done => {
|
test('should update form values on input changes', async () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateForm
|
<JobTemplateForm
|
||||||
template={mockData}
|
template={mockData}
|
||||||
@@ -96,10 +123,9 @@ describe('<JobTemplateForm />', () => {
|
|||||||
target: { value: 'new baz type', name: 'playbook' },
|
target: { value: 'new baz type', name: 'playbook' },
|
||||||
});
|
});
|
||||||
expect(form.state('values').playbook).toEqual('new baz type');
|
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 handleSubmit = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateForm
|
<JobTemplateForm
|
||||||
@@ -113,10 +139,9 @@ describe('<JobTemplateForm />', () => {
|
|||||||
wrapper.find('button[aria-label="Save"]').simulate('click');
|
wrapper.find('button[aria-label="Save"]').simulate('click');
|
||||||
await sleep(1);
|
await sleep(1);
|
||||||
expect(handleSubmit).toBeCalled();
|
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 handleCancel = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateForm
|
<JobTemplateForm
|
||||||
@@ -129,10 +154,9 @@ describe('<JobTemplateForm />', () => {
|
|||||||
expect(handleCancel).not.toHaveBeenCalled();
|
expect(handleCancel).not.toHaveBeenCalled();
|
||||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
||||||
expect(handleCancel).toBeCalled();
|
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(
|
const loadRelatedProjectPlaybooks = jest.spyOn(
|
||||||
_JobTemplateForm.prototype,
|
_JobTemplateForm.prototype,
|
||||||
'loadRelatedProjectPlaybooks'
|
'loadRelatedProjectPlaybooks'
|
||||||
@@ -150,15 +174,10 @@ describe('<JobTemplateForm />', () => {
|
|||||||
name: 'project',
|
name: 'project',
|
||||||
});
|
});
|
||||||
expect(loadRelatedProjectPlaybooks).toHaveBeenCalledWith(10);
|
expect(loadRelatedProjectPlaybooks).toHaveBeenCalledWith(10);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handleNewLabel should arrange new labels properly', async done => {
|
test('handleNewLabel should arrange new labels properly', async () => {
|
||||||
const handleNewLabel = jest.spyOn(
|
const event = { key: 'Enter' };
|
||||||
_JobTemplateForm.prototype,
|
|
||||||
'handleNewLabel'
|
|
||||||
);
|
|
||||||
const event = { key: 'Enter', preventDefault: () => {} };
|
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateForm
|
<JobTemplateForm
|
||||||
template={mockData}
|
template={mockData}
|
||||||
@@ -167,22 +186,25 @@ describe('<JobTemplateForm />', () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
await waitForElement(wrapper, 'EmptyStateBody', el => el.length === 0);
|
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');
|
const component = wrapper.find('JobTemplateForm');
|
||||||
|
|
||||||
wrapper.setState({ newLabels: [], loadedLabels: [], removedLabels: [] });
|
wrapper.setState({ newLabels: [], loadedLabels: [], removedLabels: [] });
|
||||||
multiSelect.setState({ input: 'Foo' });
|
multiSelect.setState({ input: 'Foo' });
|
||||||
component.find('input[aria-label="labels"]').prop('onKeyDown')(event);
|
component
|
||||||
expect(handleNewLabel).toHaveBeenCalledWith('Foo');
|
.find('FormGroup[fieldId="template-labels"] input[aria-label="labels"]')
|
||||||
|
.prop('onKeyDown')(event);
|
||||||
|
|
||||||
component.instance().handleNewLabel({ name: 'Bar', id: 2 });
|
component.instance().handleNewLabel({ name: 'Bar', id: 2 });
|
||||||
expect(component.state().newLabels).toEqual([
|
const newLabels = component.state('newLabels');
|
||||||
{ name: 'Foo', organization: 1 },
|
expect(newLabels).toHaveLength(2);
|
||||||
{ associate: true, id: 2, name: 'Bar' },
|
expect(newLabels[0].name).toEqual('Foo');
|
||||||
]);
|
expect(newLabels[0].organization).toEqual(1);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
test('disassociateLabel should arrange new labels properly', async done => {
|
|
||||||
|
test('disassociateLabel should arrange new labels properly', async () => {
|
||||||
const wrapper = mountWithContexts(
|
const wrapper = mountWithContexts(
|
||||||
<JobTemplateForm
|
<JobTemplateForm
|
||||||
template={mockData}
|
template={mockData}
|
||||||
@@ -203,6 +225,5 @@ describe('<JobTemplateForm />', () => {
|
|||||||
component.instance().removeLabel({ name: 'Sushi', id: 1 });
|
component.instance().removeLabel({ name: 'Sushi', id: 1 });
|
||||||
expect(component.state().newLabels.length).toBe(0);
|
expect(component.state().newLabels.length).toBe(0);
|
||||||
expect(component.state().removedLabels.length).toBe(1);
|
expect(component.state().removedLabels.length).toBe(1);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user