mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 09:27:36 -02:30
Merge pull request #8483 from AlexSCorey/8261-AddRelatedGroups
Supports Associating Related Inventory Group Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -37,6 +37,23 @@ class Groups extends Base {
|
|||||||
readChildren(id, params) {
|
readChildren(id, params) {
|
||||||
return this.http.get(`${this.baseUrl}${id}/children/`, { params });
|
return this.http.get(`${this.baseUrl}${id}/children/`, { params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
associateChildGroup(id, childId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${id}/children/`, { id: childId });
|
||||||
|
}
|
||||||
|
|
||||||
|
disassociateChildGroup(id, childId) {
|
||||||
|
return this.http.post(`${this.baseUrl}${id}/children/`, {
|
||||||
|
disassociate: id,
|
||||||
|
id: childId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
readPotentialGroups(id, params) {
|
||||||
|
return this.http.get(`${this.baseUrl}${id}/potential_children/`, {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Groups;
|
export default Groups;
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ function Inventories({ i18n }) {
|
|||||||
[`${inventoryGroupsPath}/${nested?.id}/nested_hosts/add`]: i18n._(
|
[`${inventoryGroupsPath}/${nested?.id}/nested_hosts/add`]: i18n._(
|
||||||
t`Create new host`
|
t`Create new host`
|
||||||
),
|
),
|
||||||
|
[`${inventoryGroupsPath}/${nested?.id}/nested_groups`]: i18n._(
|
||||||
|
t`Groups`
|
||||||
|
),
|
||||||
|
[`${inventoryGroupsPath}/${nested?.id}/nested_groups/add`]: i18n._(
|
||||||
|
t`Create new group`
|
||||||
|
),
|
||||||
|
|
||||||
[`${inventorySourcesPath}`]: i18n._(t`Sources`),
|
[`${inventorySourcesPath}`]: i18n._(t`Sources`),
|
||||||
[`${inventorySourcesPath}/add`]: i18n._(t`Create new source`),
|
[`${inventorySourcesPath}/add`]: i18n._(t`Create new source`),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import ContentLoading from '../../../components/ContentLoading';
|
|||||||
import InventoryGroupEdit from '../InventoryGroupEdit/InventoryGroupEdit';
|
import InventoryGroupEdit from '../InventoryGroupEdit/InventoryGroupEdit';
|
||||||
import InventoryGroupDetail from '../InventoryGroupDetail/InventoryGroupDetail';
|
import InventoryGroupDetail from '../InventoryGroupDetail/InventoryGroupDetail';
|
||||||
import InventoryGroupHosts from '../InventoryGroupHosts';
|
import InventoryGroupHosts from '../InventoryGroupHosts';
|
||||||
import InventoryGroupsRelatedGroup from '../InventoryRelatedGroups';
|
import InventoryRelatedGroups from '../InventoryRelatedGroups';
|
||||||
|
|
||||||
import { GroupsAPI } from '../../../api';
|
import { GroupsAPI } from '../../../api';
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ function InventoryGroup({ i18n, setBreadcrumb, inventory }) {
|
|||||||
{
|
{
|
||||||
name: (
|
name: (
|
||||||
<>
|
<>
|
||||||
<CaretLeftIcon />
|
<CaretLeftIcon aria-label={i18n._(t`Back to Groups`)} />
|
||||||
{i18n._(t`Back to Groups`)}
|
{i18n._(t`Back to Groups`)}
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
@@ -134,7 +134,7 @@ function InventoryGroup({ i18n, setBreadcrumb, inventory }) {
|
|||||||
key="relatedGroups"
|
key="relatedGroups"
|
||||||
path="/inventories/inventory/:id/groups/:groupId/nested_groups"
|
path="/inventories/inventory/:id/groups/:groupId/nested_groups"
|
||||||
>
|
>
|
||||||
<InventoryGroupsRelatedGroup />
|
<InventoryRelatedGroups />
|
||||||
</Route>,
|
</Route>,
|
||||||
]}
|
]}
|
||||||
<Route key="not-found" path="*">
|
<Route key="not-found" path="*">
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
|
import { GroupsAPI } from '../../../api';
|
||||||
|
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
||||||
|
|
||||||
|
function InventoryRelatedGroupAdd() {
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const history = useHistory();
|
||||||
|
const { id, groupId } = useParams();
|
||||||
|
const associateInventoryGroup = async values => {
|
||||||
|
values.inventory = id;
|
||||||
|
try {
|
||||||
|
const { data } = await GroupsAPI.create(values);
|
||||||
|
await GroupsAPI.associateChildGroup(groupId, data.id);
|
||||||
|
history.push(`/inventories/inventory/${id}/groups/${data.id}/details`, {
|
||||||
|
prevGroupId: groupId,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
setError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
history.push(
|
||||||
|
`/inventories/inventory/${id}/groups/${groupId}/nested_groups`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<InventoryGroupForm
|
||||||
|
handleSubmit={associateInventoryGroup}
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InventoryRelatedGroupAdd;
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
|
import { GroupsAPI } from '../../../api';
|
||||||
|
import InventoryRelatedGroupAdd from './InventoryRelatedGroupAdd';
|
||||||
|
|
||||||
|
jest.mock('../../../api');
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useParams: () => ({
|
||||||
|
id: 1,
|
||||||
|
groupId: 2,
|
||||||
|
}),
|
||||||
|
useHistory: () => ({ push: jest.fn() }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('<InventoryRelatedGroupAdd/>', () => {
|
||||||
|
let wrapper;
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/inventories/inventory/1/groups/2/nested_groups'],
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = mountWithContexts(<InventoryRelatedGroupAdd />);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should render properly', () => {
|
||||||
|
expect(wrapper.find('InventoryRelatedGroupAdd').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should call api with proper data', async () => {
|
||||||
|
GroupsAPI.create.mockResolvedValue({ data: { id: 3 } });
|
||||||
|
await act(() =>
|
||||||
|
wrapper.find('InventoryGroupForm').prop('handleSubmit')({
|
||||||
|
name: 'foo',
|
||||||
|
description: 'bar',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(GroupsAPI.create).toBeCalledWith({
|
||||||
|
inventory: 1,
|
||||||
|
name: 'foo',
|
||||||
|
description: 'bar',
|
||||||
|
});
|
||||||
|
expect(GroupsAPI.associateChildGroup).toBeCalledWith(2, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cancel should navigate user to Inventory Groups List', async () => {
|
||||||
|
wrapper.find('button[aria-label="Cancel"]').simulate('click');
|
||||||
|
expect(history.location.pathname).toEqual(
|
||||||
|
'/inventories/inventory/1/groups/2/nested_groups'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw error on creation of group', async () => {
|
||||||
|
GroupsAPI.create.mockRejectedValue(
|
||||||
|
new Error({
|
||||||
|
response: {
|
||||||
|
config: {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/v2/groups/',
|
||||||
|
},
|
||||||
|
data: 'An error occurred',
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await act(() =>
|
||||||
|
wrapper.find('InventoryGroupForm').prop('handleSubmit')({
|
||||||
|
name: 'foo',
|
||||||
|
description: 'bar',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw error on association of group', async () => {
|
||||||
|
GroupsAPI.create.mockResolvedValue({ data: { id: 3 } });
|
||||||
|
GroupsAPI.associateChildGroup.mockRejectedValue(
|
||||||
|
new Error({
|
||||||
|
response: {
|
||||||
|
config: {
|
||||||
|
method: 'post',
|
||||||
|
url: '/api/v2/groups/',
|
||||||
|
},
|
||||||
|
data: 'An error occurred',
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await act(() =>
|
||||||
|
wrapper.find('InventoryGroupForm').prop('handleSubmit')({
|
||||||
|
name: 'foo',
|
||||||
|
description: 'bar',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(GroupsAPI.create).toBeCalledWith({
|
||||||
|
inventory: 1,
|
||||||
|
name: 'foo',
|
||||||
|
description: 'bar',
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('FormSubmitError').length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from './InventoryRelatedGroupAdd';
|
||||||
@@ -5,7 +5,7 @@ import { useParams, useLocation, Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import { DropdownItem } from '@patternfly/react-core';
|
import { DropdownItem } from '@patternfly/react-core';
|
||||||
import { GroupsAPI, InventoriesAPI } from '../../../api';
|
import { GroupsAPI, InventoriesAPI } from '../../../api';
|
||||||
import useRequest from '../../../util/useRequest';
|
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
||||||
import { getQSConfig, parseQueryString, mergeParams } from '../../../util/qs';
|
import { getQSConfig, parseQueryString, mergeParams } from '../../../util/qs';
|
||||||
import useSelected from '../../../util/useSelected';
|
import useSelected from '../../../util/useSelected';
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ import PaginatedDataList from '../../../components/PaginatedDataList';
|
|||||||
import InventoryGroupRelatedGroupListItem from './InventoryRelatedGroupListItem';
|
import InventoryGroupRelatedGroupListItem from './InventoryRelatedGroupListItem';
|
||||||
import AddDropDownButton from '../../../components/AddDropDownButton';
|
import AddDropDownButton from '../../../components/AddDropDownButton';
|
||||||
import AdHocCommands from '../../../components/AdHocCommands/AdHocCommands';
|
import AdHocCommands from '../../../components/AdHocCommands/AdHocCommands';
|
||||||
|
import AlertModal from '../../../components/AlertModal';
|
||||||
|
import ErrorDetail from '../../../components/ErrorDetail';
|
||||||
import AssociateModal from '../../../components/AssociateModal';
|
import AssociateModal from '../../../components/AssociateModal';
|
||||||
import DisassociateButton from '../../../components/DisassociateButton';
|
import DisassociateButton from '../../../components/DisassociateButton';
|
||||||
import { toTitleCase } from '../../../util/strings';
|
import { toTitleCase } from '../../../util/strings';
|
||||||
@@ -25,6 +27,8 @@ const QS_CONFIG = getQSConfig('group', {
|
|||||||
});
|
});
|
||||||
function InventoryRelatedGroupList({ i18n }) {
|
function InventoryRelatedGroupList({ i18n }) {
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [associateError, setAssociateError] = useState(null);
|
||||||
|
const [disassociateError, setDisassociateError] = useState(null);
|
||||||
const { id: inventoryId, groupId } = useParams();
|
const { id: inventoryId, groupId } = useParams();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
@@ -69,24 +73,58 @@ function InventoryRelatedGroupList({ i18n }) {
|
|||||||
|
|
||||||
const fetchGroupsToAssociate = useCallback(
|
const fetchGroupsToAssociate = useCallback(
|
||||||
params => {
|
params => {
|
||||||
return InventoriesAPI.readGroups(
|
return GroupsAPI.readPotentialGroups(
|
||||||
inventoryId,
|
groupId,
|
||||||
mergeParams(params, { not__id: inventoryId, not__parents: inventoryId })
|
mergeParams(params, { not__id: groupId, not__parents: groupId })
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[inventoryId]
|
[groupId]
|
||||||
);
|
);
|
||||||
|
|
||||||
const fetchGroupsOptions = useCallback(
|
const associateGroup = useCallback(
|
||||||
() => InventoriesAPI.readGroupsOptions(inventoryId),
|
async selectedGroups => {
|
||||||
[inventoryId]
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
selectedGroups.map(selected =>
|
||||||
|
GroupsAPI.associateChildGroup(groupId, selected.id)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
setAssociateError(err);
|
||||||
|
}
|
||||||
|
fetchRelated();
|
||||||
|
},
|
||||||
|
[groupId, fetchRelated]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
|
const { selected, isAllSelected, handleSelect, setSelected } = useSelected(
|
||||||
groups
|
groups
|
||||||
);
|
);
|
||||||
|
|
||||||
const addFormUrl = `/home`;
|
const disassociateGroups = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
selected.map(({ id: childId }) =>
|
||||||
|
GroupsAPI.disassociateChildGroup(parseInt(groupId, 10), childId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
setDisassociateError(err);
|
||||||
|
}
|
||||||
|
fetchRelated();
|
||||||
|
setSelected([]);
|
||||||
|
}, [groupId, selected, setSelected, fetchRelated]);
|
||||||
|
|
||||||
|
const fetchGroupsOptions = useCallback(
|
||||||
|
() => InventoriesAPI.readGroupsOptions(inventoryId),
|
||||||
|
[inventoryId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { error, dismissError } = useDismissableError(
|
||||||
|
associateError || disassociateError
|
||||||
|
);
|
||||||
|
|
||||||
|
const addFormUrl = `/inventories/inventory/${inventoryId}/groups/${groupId}/nested_groups/add`;
|
||||||
|
|
||||||
const addExistingGroup = toTitleCase(i18n._(t`Add Existing Group`));
|
const addExistingGroup = toTitleCase(i18n._(t`Add Existing Group`));
|
||||||
const addNewGroup = toTitleCase(i18n._(t`Add New Group`));
|
const addNewGroup = toTitleCase(i18n._(t`Add New Group`));
|
||||||
@@ -163,7 +201,7 @@ function InventoryRelatedGroupList({ i18n }) {
|
|||||||
/>,
|
/>,
|
||||||
<DisassociateButton
|
<DisassociateButton
|
||||||
key="disassociate"
|
key="disassociate"
|
||||||
onDisassociate={() => {}}
|
onDisassociate={disassociateGroups}
|
||||||
itemsToDisassociate={selected}
|
itemsToDisassociate={selected}
|
||||||
modalTitle={i18n._(t`Disassociate related group(s)?`)}
|
modalTitle={i18n._(t`Disassociate related group(s)?`)}
|
||||||
/>,
|
/>,
|
||||||
@@ -188,11 +226,24 @@ function InventoryRelatedGroupList({ i18n }) {
|
|||||||
fetchRequest={fetchGroupsToAssociate}
|
fetchRequest={fetchGroupsToAssociate}
|
||||||
optionsRequest={fetchGroupsOptions}
|
optionsRequest={fetchGroupsOptions}
|
||||||
isModalOpen={isModalOpen}
|
isModalOpen={isModalOpen}
|
||||||
onAssociate={() => {}}
|
onAssociate={associateGroup}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
title={i18n._(t`Select Groups`)}
|
title={i18n._(t`Select Groups`)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{error && (
|
||||||
|
<AlertModal
|
||||||
|
isOpen={error}
|
||||||
|
onClose={dismissError}
|
||||||
|
title={i18n._(t`Error!`)}
|
||||||
|
variant="error"
|
||||||
|
>
|
||||||
|
{associateError
|
||||||
|
? i18n._(t`Failed to associate.`)
|
||||||
|
: i18n._(t`Failed to disassociate one or more groups.`)}
|
||||||
|
<ErrorDetail error={error} />
|
||||||
|
</AlertModal>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,48 @@ jest.mock('react-router-dom', () => ({
|
|||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const mockGroups = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: 'group',
|
||||||
|
name: 'foo',
|
||||||
|
inventory: 1,
|
||||||
|
url: '/api/v2/groups/1',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
delete: true,
|
||||||
|
edit: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
type: 'group',
|
||||||
|
name: 'bar',
|
||||||
|
inventory: 1,
|
||||||
|
url: '/api/v2/groups/2',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
delete: true,
|
||||||
|
edit: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
type: 'group',
|
||||||
|
name: 'baz',
|
||||||
|
inventory: 1,
|
||||||
|
url: '/api/v2/groups/3',
|
||||||
|
summary_fields: {
|
||||||
|
user_capabilities: {
|
||||||
|
delete: false,
|
||||||
|
edit: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
describe('<InventoryRelatedGroupList />', () => {
|
describe('<InventoryRelatedGroupList />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
@@ -145,4 +187,38 @@ describe('<InventoryRelatedGroupList />', () => {
|
|||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
expect(wrapper.find('AddDropdown').length).toBe(0);
|
expect(wrapper.find('AddDropdown').length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should associate existing group', async () => {
|
||||||
|
GroupsAPI.readPotentialGroups.mockResolvedValue({
|
||||||
|
data: { count: mockGroups.length, results: mockGroups },
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<InventoryRelatedGroupList />);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
|
||||||
|
act(() => wrapper.find('Button[aria-label="Add"]').prop('onClick')());
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () =>
|
||||||
|
wrapper
|
||||||
|
.find('DropdownItem[aria-label="Add existing group"]')
|
||||||
|
.prop('onClick')()
|
||||||
|
);
|
||||||
|
expect(GroupsAPI.readPotentialGroups).toBeCalledWith(2, {
|
||||||
|
not__id: 2,
|
||||||
|
not__parents: 2,
|
||||||
|
order_by: 'name',
|
||||||
|
page: 1,
|
||||||
|
page_size: 5,
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
act(() =>
|
||||||
|
wrapper.find('CheckboxListItem[name="foo"]').prop('onSelect')({ id: 1 })
|
||||||
|
);
|
||||||
|
wrapper.update();
|
||||||
|
await act(() =>
|
||||||
|
wrapper.find('button[aria-label="Save"]').prop('onClick')()
|
||||||
|
);
|
||||||
|
expect(GroupsAPI.associateChildGroup).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Switch, Route } from 'react-router-dom';
|
||||||
|
import InventoryRelatedGroupList from './InventoryRelatedGroupList';
|
||||||
|
import InventoryRelatedGroupAdd from '../InventoryRelatedGroupAdd';
|
||||||
|
|
||||||
|
function InventoryRelatedGroups() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
key="addRelatedGroups"
|
||||||
|
path="/inventories/inventory/:id/groups/:groupId/nested_groups/add"
|
||||||
|
>
|
||||||
|
<InventoryRelatedGroupAdd />
|
||||||
|
</Route>
|
||||||
|
<Route
|
||||||
|
key="relatedGroups"
|
||||||
|
path="/inventories/inventory/:id/groups/:groupId/nested_groups"
|
||||||
|
>
|
||||||
|
<InventoryRelatedGroupList />
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default InventoryRelatedGroups;
|
||||||
@@ -1 +1 @@
|
|||||||
export { default } from './InventoryRelatedGroupList';
|
export { default } from './InventoryRelatedGroups';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Form, Card } from '@patternfly/react-core';
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import { CardBody } from '../../../components/Card';
|
import { CardBody } from '../../../components/Card';
|
||||||
import FormField from '../../../components/FormField';
|
import FormField, { FormSubmitError } from '../../../components/FormField';
|
||||||
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
||||||
import { VariablesField } from '../../../components/CodeMirrorInput';
|
import { VariablesField } from '../../../components/CodeMirrorInput';
|
||||||
import { required } from '../../../util/validators';
|
import { required } from '../../../util/validators';
|
||||||
@@ -59,7 +59,7 @@ function InventoryGroupForm({
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
onSubmit={formik.handleSubmit}
|
onSubmit={formik.handleSubmit}
|
||||||
/>
|
/>
|
||||||
{error ? <div>error</div> : null}
|
{error && <FormSubmitError error={error} />}
|
||||||
</FormColumnLayout>
|
</FormColumnLayout>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -30,4 +30,15 @@ describe('<InventoryGroupForm />', () => {
|
|||||||
expect(wrapper.find("FormGroup[label='Description']").length).toBe(1);
|
expect(wrapper.find("FormGroup[label='Description']").length).toBe(1);
|
||||||
expect(wrapper.find("VariablesField[label='Variables']").length).toBe(1);
|
expect(wrapper.find("VariablesField[label='Variables']").length).toBe(1);
|
||||||
});
|
});
|
||||||
|
test('should throw error properly', () => {
|
||||||
|
const newWrapper = mountWithContexts(
|
||||||
|
<InventoryGroupForm
|
||||||
|
handleSubmit={jest.fn()}
|
||||||
|
handleCancel={jest.fn()}
|
||||||
|
group={group}
|
||||||
|
error
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(newWrapper.find('FormSubmitError').length).toBe(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user