mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 19:10:07 -03:30
Add feature to edit instance group
Add feature to edit instance group. See: https://github.com/ansible/awx/issues/7767
This commit is contained in:
parent
fbb9998b68
commit
e3fe680d14
@ -1,13 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Card, PageSection } from '@patternfly/react-core';
|
||||
import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CardBody } from '../../../components/Card';
|
||||
import { InstanceGroupsAPI } from '../../../api';
|
||||
import InstanceGroupForm from '../shared/InstanceGroupForm';
|
||||
|
||||
function InstanceGroupEdit({ instanceGroup }) {
|
||||
const history = useHistory();
|
||||
const [submitError, setSubmitError] = useState(null);
|
||||
const detailsUrl = `/instance_groups/${instanceGroup.id}/details`;
|
||||
|
||||
const handleSubmit = async values => {
|
||||
try {
|
||||
await InstanceGroupsAPI.update(instanceGroup.id, values);
|
||||
history.push(detailsUrl);
|
||||
} catch (error) {
|
||||
setSubmitError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
history.push(detailsUrl);
|
||||
};
|
||||
|
||||
function InstanceGroupEdit() {
|
||||
return (
|
||||
<PageSection>
|
||||
<Card>
|
||||
<div>Edit instance group</div>
|
||||
</Card>
|
||||
</PageSection>
|
||||
<CardBody>
|
||||
<InstanceGroupForm
|
||||
instanceGroup={instanceGroup}
|
||||
onSubmit={handleSubmit}
|
||||
submitError={submitError}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,140 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { createMemoryHistory } from 'history';
|
||||
|
||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||
import { InstanceGroupsAPI } from '../../../api';
|
||||
|
||||
import InstanceGroupEdit from './InstanceGroupEdit';
|
||||
|
||||
jest.mock('../../../api');
|
||||
|
||||
const instanceGroupData = {
|
||||
id: 42,
|
||||
type: 'instance_group',
|
||||
url: '/api/v2/instance_groups/42/',
|
||||
related: {
|
||||
jobs: '/api/v2/instance_groups/42/jobs/',
|
||||
instances: '/api/v2/instance_groups/7/instances/',
|
||||
},
|
||||
name: 'Foo',
|
||||
created: '2020-07-21T18:41:02.818081Z',
|
||||
modified: '2020-07-24T20:32:03.121079Z',
|
||||
capacity: 24,
|
||||
committed_capacity: 0,
|
||||
consumed_capacity: 0,
|
||||
percent_capacity_remaining: 100.0,
|
||||
jobs_running: 0,
|
||||
jobs_total: 0,
|
||||
instances: 1,
|
||||
controller: null,
|
||||
is_controller: false,
|
||||
is_isolated: false,
|
||||
is_containerized: false,
|
||||
credential: null,
|
||||
policy_instance_percentage: 46,
|
||||
policy_instance_minimum: 12,
|
||||
policy_instance_list: [],
|
||||
pod_spec_override: '',
|
||||
summary_fields: {
|
||||
user_capabilities: {
|
||||
edit: true,
|
||||
delete: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const updatedInstanceGroup = {
|
||||
name: 'Bar',
|
||||
policy_instance_percentage: 42,
|
||||
};
|
||||
|
||||
describe('<InstanceGroupEdit>', () => {
|
||||
let wrapper;
|
||||
let history;
|
||||
|
||||
beforeAll(async () => {
|
||||
history = createMemoryHistory();
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<InstanceGroupEdit instanceGroup={instanceGroupData} />,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('tower instance group name can not be updated', async () => {
|
||||
let towerWrapper;
|
||||
await act(async () => {
|
||||
towerWrapper = mountWithContexts(
|
||||
<InstanceGroupEdit
|
||||
instanceGroup={{ ...instanceGroupData, name: 'tower' }}
|
||||
/>,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
expect(
|
||||
towerWrapper.find('input#instance-group-name').prop('disabled')
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
towerWrapper.find('input#instance-group-name').prop('value')
|
||||
).toEqual('tower');
|
||||
});
|
||||
|
||||
test('handleSubmit should call the api and redirect to details page', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('InstanceGroupForm').invoke('onSubmit')(
|
||||
updatedInstanceGroup
|
||||
);
|
||||
});
|
||||
expect(InstanceGroupsAPI.update).toHaveBeenCalledWith(
|
||||
42,
|
||||
updatedInstanceGroup
|
||||
);
|
||||
});
|
||||
|
||||
test('should navigate to instance group details when cancel is clicked', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/instance_groups/42/details');
|
||||
});
|
||||
|
||||
test('should navigate to instance group details after successful submission', async () => {
|
||||
await act(async () => {
|
||||
wrapper.find('InstanceGroupForm').invoke('onSubmit')(
|
||||
updatedInstanceGroup
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(0);
|
||||
expect(history.location.pathname).toEqual('/instance_groups/42/details');
|
||||
});
|
||||
|
||||
test('failed form submission should show an error message', async () => {
|
||||
const error = {
|
||||
response: {
|
||||
data: { detail: 'An error occurred' },
|
||||
},
|
||||
};
|
||||
InstanceGroupsAPI.update.mockImplementationOnce(() =>
|
||||
Promise.reject(error)
|
||||
);
|
||||
await act(async () => {
|
||||
wrapper.find('InstanceGroupForm').invoke('onSubmit')(
|
||||
updatedInstanceGroup
|
||||
);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { func, shape } from 'prop-types';
|
||||
import { Formik } from 'formik';
|
||||
import { Formik, useField } from 'formik';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Form } from '@patternfly/react-core';
|
||||
@ -11,21 +11,24 @@ import { required, minMaxValue } from '../../../util/validators';
|
||||
import { FormColumnLayout } from '../../../components/FormLayout';
|
||||
|
||||
function InstanceGroupFormFields({ i18n }) {
|
||||
const [instanceGroupNameField, ,] = useField('name');
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
name="name"
|
||||
id="instance-group-name"
|
||||
label={i18n._(t`Name`)}
|
||||
name="name"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
isDisabled={instanceGroupNameField.value === 'tower'}
|
||||
/>
|
||||
<FormField
|
||||
id="instance-group-policy-instance-minimum"
|
||||
label={i18n._(t`Policy instance minimum`)}
|
||||
name="policy_instance_minimum"
|
||||
type="number"
|
||||
min="0"
|
||||
validate={minMaxValue(0, 2147483647, i18n)}
|
||||
tooltip={i18n._(
|
||||
t`Minimum number of instances that will be automatically
|
||||
@ -37,6 +40,8 @@ function InstanceGroupFormFields({ i18n }) {
|
||||
label={i18n._(t`Policy instance percentage`)}
|
||||
name="policy_instance_percentage"
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
tooltip={i18n._(
|
||||
t`Minimum percentage of all instances that will be automatically
|
||||
assigned to this group when new instances come online.`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user