mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
Merge pull request #6235 from marshmalien/6142-inv-group-add-host-form
Add Inventory Group Host Add form Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -5,10 +5,15 @@ class Groups extends Base {
|
|||||||
super(http);
|
super(http);
|
||||||
this.baseUrl = '/api/v2/groups/';
|
this.baseUrl = '/api/v2/groups/';
|
||||||
|
|
||||||
|
this.createHost = this.createHost.bind(this);
|
||||||
this.readAllHosts = this.readAllHosts.bind(this);
|
this.readAllHosts = this.readAllHosts.bind(this);
|
||||||
this.disassociateHost = this.disassociateHost.bind(this);
|
this.disassociateHost = this.disassociateHost.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createHost(id, data) {
|
||||||
|
return this.http.post(`${this.baseUrl}${id}/hosts/`, data);
|
||||||
|
}
|
||||||
|
|
||||||
readAllHosts(id, params) {
|
readAllHosts(id, params) {
|
||||||
return this.http.get(`${this.baseUrl}${id}/all_hosts/`, { params });
|
return this.http.get(`${this.baseUrl}${id}/all_hosts/`, { params });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class Inventories extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setBreadCrumbConfig = (inventory, nestedResource) => {
|
setBreadCrumbConfig = (inventory, nested) => {
|
||||||
const { i18n } = this.props;
|
const { i18n } = this.props;
|
||||||
if (!inventory) {
|
if (!inventory) {
|
||||||
return;
|
return;
|
||||||
@@ -36,57 +36,42 @@ class Inventories extends Component {
|
|||||||
const inventoryKind =
|
const inventoryKind =
|
||||||
inventory.kind === 'smart' ? 'smart_inventory' : 'inventory';
|
inventory.kind === 'smart' ? 'smart_inventory' : 'inventory';
|
||||||
|
|
||||||
|
const inventoryPath = `/inventories/${inventoryKind}/${inventory.id}`;
|
||||||
|
const inventoryHostsPath = `/inventories/${inventoryKind}/${inventory.id}/hosts`;
|
||||||
|
const inventoryGroupsPath = `/inventories/${inventoryKind}/${inventory.id}/groups`;
|
||||||
|
|
||||||
const breadcrumbConfig = {
|
const breadcrumbConfig = {
|
||||||
'/inventories': i18n._(t`Inventories`),
|
'/inventories': i18n._(t`Inventories`),
|
||||||
'/inventories/inventory/add': i18n._(t`Create New Inventory`),
|
'/inventories/inventory/add': i18n._(t`Create New Inventory`),
|
||||||
'/inventories/smart_inventory/add': i18n._(t`Create New Smart Inventory`),
|
'/inventories/smart_inventory/add': i18n._(t`Create New Smart Inventory`),
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}`]: `${inventory.name}`,
|
|
||||||
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/access`]: i18n._(
|
[inventoryPath]: `${inventory.name}`,
|
||||||
t`Access`
|
[`${inventoryPath}/access`]: i18n._(t`Access`),
|
||||||
),
|
[`${inventoryPath}/completed_jobs`]: i18n._(t`Completed Jobs`),
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/completed_jobs`]: i18n._(
|
[`${inventoryPath}/details`]: i18n._(t`Details`),
|
||||||
|
[`${inventoryPath}/edit`]: i18n._(t`Edit Details`),
|
||||||
|
[`${inventoryPath}/sources`]: i18n._(t`Sources`),
|
||||||
|
|
||||||
|
[inventoryHostsPath]: i18n._(t`Hosts`),
|
||||||
|
[`${inventoryHostsPath}/add`]: i18n._(t`Create New Host`),
|
||||||
|
[`${inventoryHostsPath}/${nested?.id}`]: `${nested?.name}`,
|
||||||
|
[`${inventoryHostsPath}/${nested?.id}/edit`]: i18n._(t`Edit Details`),
|
||||||
|
[`${inventoryHostsPath}/${nested?.id}/details`]: i18n._(t`Host Details`),
|
||||||
|
[`${inventoryHostsPath}/${nested?.id}/completed_jobs`]: i18n._(
|
||||||
t`Completed Jobs`
|
t`Completed Jobs`
|
||||||
),
|
),
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/details`]: i18n._(
|
|
||||||
t`Details`
|
|
||||||
),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/edit`]: i18n._(
|
|
||||||
t`Edit Details`
|
|
||||||
),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups`]: i18n._(
|
|
||||||
t`Groups`
|
|
||||||
),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/hosts`]: i18n._(t`Hosts`),
|
|
||||||
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/sources`]: i18n._(
|
[inventoryGroupsPath]: i18n._(t`Groups`),
|
||||||
t`Sources`
|
[`${inventoryGroupsPath}/add`]: i18n._(t`Create New Group`),
|
||||||
|
[`${inventoryGroupsPath}/${nested?.id}`]: `${nested?.name}`,
|
||||||
|
[`${inventoryGroupsPath}/${nested?.id}/edit`]: i18n._(t`Edit Details`),
|
||||||
|
[`${inventoryGroupsPath}/${nested?.id}/details`]: i18n._(
|
||||||
|
t`Group Details`
|
||||||
),
|
),
|
||||||
|
[`${inventoryGroupsPath}/${nested?.id}/nested_hosts`]: i18n._(t`Hosts`),
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/hosts/add`]: i18n._(
|
[`${inventoryGroupsPath}/${nested?.id}/nested_hosts/add`]: i18n._(
|
||||||
t`Create New Host`
|
t`Create New Host`
|
||||||
),
|
),
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/hosts/${nestedResource &&
|
|
||||||
nestedResource.id}`]: `${nestedResource && nestedResource.name}`,
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/hosts/${nestedResource &&
|
|
||||||
nestedResource.id}/edit`]: i18n._(t`Edit Details`),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/hosts/${nestedResource &&
|
|
||||||
nestedResource.id}/details`]: i18n._(t`Host Details`),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/hosts/${nestedResource &&
|
|
||||||
nestedResource.id}/completed_jobs`]: i18n._(t`Completed Jobs`),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups/add`]: i18n._(
|
|
||||||
t`Create New Group`
|
|
||||||
),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups/${nestedResource &&
|
|
||||||
nestedResource.id}`]: `${nestedResource && nestedResource.name}`,
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups/${nestedResource &&
|
|
||||||
nestedResource.id}/edit`]: i18n._(t`Edit Details`),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups/${nestedResource &&
|
|
||||||
nestedResource.id}/details`]: i18n._(t`Group Details`),
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups/${nestedResource &&
|
|
||||||
nestedResource.id}`]: `${nestedResource && nestedResource.name}`,
|
|
||||||
[`/inventories/${inventoryKind}/${inventory.id}/groups/${nestedResource &&
|
|
||||||
nestedResource.id}/nested_hosts`]: i18n._(t`Hosts`),
|
|
||||||
};
|
};
|
||||||
this.setState({ breadcrumbConfig });
|
this.setState({ breadcrumbConfig });
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,70 +59,58 @@ function InventoryGroup({ i18n, setBreadcrumb, inventory }) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Details`),
|
name: i18n._(t`Details`),
|
||||||
link: `/inventories/inventory/${inventory.id}/groups/${inventoryGroup &&
|
link: `/inventories/inventory/${inventory.id}/groups/${inventoryGroup?.id}/details`,
|
||||||
inventoryGroup.id}/details`,
|
|
||||||
id: 0,
|
id: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Related Groups`),
|
name: i18n._(t`Related Groups`),
|
||||||
link: `/inventories/inventory/${inventory.id}/groups/${inventoryGroup &&
|
link: `/inventories/inventory/${inventory.id}/groups/${inventoryGroup?.id}/nested_groups`,
|
||||||
inventoryGroup.id}/nested_groups`,
|
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: i18n._(t`Hosts`),
|
name: i18n._(t`Hosts`),
|
||||||
link: `/inventories/inventory/${inventory.id}/groups/${inventoryGroup &&
|
link: `/inventories/inventory/${inventory.id}/groups/${inventoryGroup?.id}/nested_hosts`,
|
||||||
inventoryGroup.id}/nested_hosts`,
|
|
||||||
id: 2,
|
id: 2,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// In cases where a user manipulates the url such that they try to navigate to a
|
|
||||||
// Inventory Group that is not associated with the Inventory Id in the Url this
|
|
||||||
// Content Error is thrown. Inventory Groups have a 1:1 relationship to Inventories
|
|
||||||
// thus their Ids must corrolate.
|
|
||||||
|
|
||||||
if (contentLoading) {
|
if (contentLoading) {
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
inventoryGroup.summary_fields.inventory.id !== parseInt(inventoryId, 10)
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<ContentError>
|
|
||||||
{inventoryGroup && (
|
|
||||||
<Link to={`/inventories/inventory/${inventory.id}/groups`}>
|
|
||||||
{i18n._(t`View Inventory Groups`)}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</ContentError>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentError) {
|
if (contentError) {
|
||||||
return <ContentError error={contentError} />;
|
return <ContentError error={contentError} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cardHeader = null;
|
// In cases where a user manipulates the url such that they try to navigate to a
|
||||||
|
// Inventory Group that is not associated with the Inventory Id in the Url this
|
||||||
|
// Content Error is thrown. Inventory Groups have a 1:1 relationship to Inventories
|
||||||
|
// thus their Ids must corrolate.
|
||||||
|
|
||||||
if (
|
if (
|
||||||
location.pathname.includes('groups/') &&
|
inventoryGroup?.summary_fields?.inventory?.id !== parseInt(inventoryId, 10)
|
||||||
!location.pathname.endsWith('edit')
|
|
||||||
) {
|
) {
|
||||||
cardHeader = (
|
return (
|
||||||
<TabbedCardHeader>
|
<ContentError isNotFound>
|
||||||
<RoutedTabs tabsArray={tabsArray} />
|
<Link to={`/inventories/inventory/${inventory.id}/groups`}>
|
||||||
<CardActions>
|
{i18n._(t`View Inventory Groups`)}
|
||||||
<CardCloseButton
|
</Link>
|
||||||
linkTo={`/inventories/inventory/${inventory.id}/groups`}
|
</ContentError>
|
||||||
/>
|
|
||||||
</CardActions>
|
|
||||||
</TabbedCardHeader>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{cardHeader}
|
{['add', 'edit'].some(name => location.pathname.includes(name)) ? null : (
|
||||||
|
<TabbedCardHeader>
|
||||||
|
<RoutedTabs tabsArray={tabsArray} />
|
||||||
|
<CardActions>
|
||||||
|
<CardCloseButton
|
||||||
|
linkTo={`/inventories/inventory/${inventory.id}/groups`}
|
||||||
|
/>
|
||||||
|
</CardActions>
|
||||||
|
</TabbedCardHeader>
|
||||||
|
)}
|
||||||
<Switch>
|
<Switch>
|
||||||
<Redirect
|
<Redirect
|
||||||
from="/inventories/inventory/:id/groups/:groupId"
|
from="/inventories/inventory/:id/groups/:groupId"
|
||||||
@@ -139,17 +127,16 @@ function InventoryGroup({ i18n, setBreadcrumb, inventory }) {
|
|||||||
<Route
|
<Route
|
||||||
key="details"
|
key="details"
|
||||||
path="/inventories/inventory/:id/groups/:groupId/details"
|
path="/inventories/inventory/:id/groups/:groupId/details"
|
||||||
render={() => {
|
>
|
||||||
return <InventoryGroupDetail inventoryGroup={inventoryGroup} />;
|
<InventoryGroupDetail inventoryGroup={inventoryGroup} />
|
||||||
}}
|
</Route>,
|
||||||
/>,
|
<Route
|
||||||
|
key="hosts"
|
||||||
|
path="/inventories/inventory/:id/groups/:groupId/nested_hosts"
|
||||||
|
>
|
||||||
|
<InventoryGroupHosts inventoryGroup={inventoryGroup} />
|
||||||
|
</Route>,
|
||||||
]}
|
]}
|
||||||
<Route
|
|
||||||
key="hosts"
|
|
||||||
path="/inventories/inventory/:id/groups/:groupId/nested_hosts"
|
|
||||||
>
|
|
||||||
<InventoryGroupHosts />
|
|
||||||
</Route>
|
|
||||||
<Route
|
<Route
|
||||||
key="not-found"
|
key="not-found"
|
||||||
path="*"
|
path="*"
|
||||||
|
|||||||
@@ -8,6 +8,14 @@ import { createMemoryHistory } from 'history';
|
|||||||
import InventoryGroup from './InventoryGroup';
|
import InventoryGroup from './InventoryGroup';
|
||||||
|
|
||||||
jest.mock('@api');
|
jest.mock('@api');
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useParams: () => ({
|
||||||
|
id: 1,
|
||||||
|
groupId: 2,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
GroupsAPI.readDetail.mockResolvedValue({
|
GroupsAPI.readDetail.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -23,10 +31,12 @@ GroupsAPI.readDetail.mockResolvedValue({
|
|||||||
modified: '2020-04-25T01:23:45.678901Z',
|
modified: '2020-04-25T01:23:45.678901Z',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('<InventoryGroup />', () => {
|
describe('<InventoryGroup />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
let history;
|
let history;
|
||||||
const inventory = { id: 1, name: 'Foo' };
|
const inventory = { id: 1, name: 'Foo' };
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
history = createMemoryHistory({
|
history = createMemoryHistory({
|
||||||
initialEntries: ['/inventories/inventory/1/groups/1/details'],
|
initialEntries: ['/inventories/inventory/1/groups/1/details'],
|
||||||
@@ -39,29 +49,20 @@ describe('<InventoryGroup />', () => {
|
|||||||
<InventoryGroup setBreadcrumb={() => {}} inventory={inventory} />
|
<InventoryGroup setBreadcrumb={() => {}} inventory={inventory} />
|
||||||
)}
|
)}
|
||||||
/>,
|
/>,
|
||||||
{
|
{ context: { router: { history } } }
|
||||||
context: {
|
|
||||||
router: {
|
|
||||||
history,
|
|
||||||
route: {
|
|
||||||
location: history.location,
|
|
||||||
match: {
|
|
||||||
params: { id: 1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('renders successfully', async () => {
|
test('renders successfully', async () => {
|
||||||
expect(wrapper.length).toBe(1);
|
expect(wrapper.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('expect all tabs to exist, including Back to Groups', async () => {
|
test('expect all tabs to exist, including Back to Groups', async () => {
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('button[link="/inventories/inventory/1/groups"]').length
|
wrapper.find('button[link="/inventories/inventory/1/groups"]').length
|
||||||
@@ -70,4 +71,27 @@ describe('<InventoryGroup />', () => {
|
|||||||
expect(wrapper.find('button[aria-label="Related Groups"]').length).toBe(1);
|
expect(wrapper.find('button[aria-label="Related Groups"]').length).toBe(1);
|
||||||
expect(wrapper.find('button[aria-label="Hosts"]').length).toBe(1);
|
expect(wrapper.find('button[aria-label="Hosts"]').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should show content error when user attempts to navigate to erroneous route', async () => {
|
||||||
|
history = createMemoryHistory({
|
||||||
|
initialEntries: ['/inventories/inventory/1/groups/1/foobar'],
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<InventoryGroup setBreadcrumb={() => {}} inventory={inventory} />,
|
||||||
|
{ context: { router: { history } } }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show content error when api throws error on initial render', async () => {
|
||||||
|
GroupsAPI.readDetail.mockImplementationOnce(() =>
|
||||||
|
Promise.reject(new Error())
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<InventoryGroup inventory={inventory} />);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { CardBody } from '@components/Card';
|
||||||
|
import HostForm from '@components/HostForm';
|
||||||
|
|
||||||
|
import { GroupsAPI } from '@api';
|
||||||
|
|
||||||
|
function InventoryGroupHostAdd({ inventoryGroup }) {
|
||||||
|
const [formError, setFormError] = useState(null);
|
||||||
|
const baseUrl = `/inventories/inventory/${inventoryGroup.inventory}`;
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const handleSubmit = async formData => {
|
||||||
|
try {
|
||||||
|
const values = {
|
||||||
|
...formData,
|
||||||
|
inventory: inventoryGroup.inventory,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data: response } = await GroupsAPI.createHost(
|
||||||
|
inventoryGroup.id,
|
||||||
|
values
|
||||||
|
);
|
||||||
|
history.push(`${baseUrl}/hosts/${response.id}/details`);
|
||||||
|
} catch (error) {
|
||||||
|
setFormError(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
history.push(`${baseUrl}/groups/${inventoryGroup.id}/nested_hosts`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CardBody>
|
||||||
|
<HostForm
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
isInventoryVisible={false}
|
||||||
|
submitError={formError}
|
||||||
|
/>
|
||||||
|
</CardBody>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InventoryGroupHostAdd;
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
|
import InventoryGroupHostAdd from './InventoryGroupHostAdd';
|
||||||
|
import mockHost from '../shared/data.host.json';
|
||||||
|
import { GroupsAPI } from '@api';
|
||||||
|
|
||||||
|
jest.mock('@api');
|
||||||
|
|
||||||
|
GroupsAPI.createHost.mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
...mockHost,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('<InventoryGroupHostAdd />', () => {
|
||||||
|
let wrapper;
|
||||||
|
let history;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
history = createMemoryHistory();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<InventoryGroupHostAdd inventoryGroup={{ id: 123, inventory: 3 }} />,
|
||||||
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handleSubmit should post to api', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('HostForm').prop('handleSubmit')(mockHost);
|
||||||
|
});
|
||||||
|
expect(GroupsAPI.createHost).toHaveBeenCalledWith(123, mockHost);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should navigate to inventory group host list when cancel is clicked', () => {
|
||||||
|
wrapper.find('button[aria-label="Cancel"]').invoke('onClick')();
|
||||||
|
expect(history.location.pathname).toEqual(
|
||||||
|
'/inventories/inventory/3/groups/123/nested_hosts'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('successful form submission should trigger redirect', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('HostForm').invoke('handleSubmit')(mockHost);
|
||||||
|
});
|
||||||
|
expect(wrapper.find('FormSubmitError').length).toBe(0);
|
||||||
|
expect(history.location.pathname).toEqual(
|
||||||
|
'/inventories/inventory/3/hosts/2/details'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('failed form submission should show an error message', async () => {
|
||||||
|
const error = {
|
||||||
|
response: {
|
||||||
|
data: { detail: 'An error occurred' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
GroupsAPI.createHost.mockImplementationOnce(() => Promise.reject(error));
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('HostForm').invoke('handleSubmit')(mockHost);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './InventoryGroupHostAdd';
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Switch, Route } from 'react-router-dom';
|
import { Switch, Route } from 'react-router-dom';
|
||||||
|
import InventoryGroupHostAdd from '../InventoryGroupHostAdd';
|
||||||
import InventoryGroupHostList from './InventoryGroupHostList';
|
import InventoryGroupHostList from './InventoryGroupHostList';
|
||||||
|
|
||||||
function InventoryGroupHosts() {
|
function InventoryGroupHosts({ inventoryGroup }) {
|
||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
{/* Route to InventoryGroupHostAddForm */}
|
<Route path="/inventories/inventory/:id/groups/:groupId/nested_hosts/add">
|
||||||
|
<InventoryGroupHostAdd inventoryGroup={inventoryGroup} />
|
||||||
|
</Route>
|
||||||
<Route path="/inventories/inventory/:id/groups/:groupId/nested_hosts">
|
<Route path="/inventories/inventory/:id/groups/:groupId/nested_hosts">
|
||||||
<InventoryGroupHostList />
|
<InventoryGroupHostList />
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
Reference in New Issue
Block a user