mirror of
https://github.com/ansible/awx.git
synced 2026-05-16 05:47:38 -02:30
Moves inventoryGroupForm into shared directory
Updates InventoryGroups tests Adds ContentError functionalist to catch a case where a use might navigate to an Inventory that isn't associated to the shown inventoryGroup.
This commit is contained in:
@@ -15,7 +15,7 @@ import { getAddedAndRemoved } from '../../../util/lists';
|
|||||||
function InventoryEdit({ history, i18n, inventory }) {
|
function InventoryEdit({ history, i18n, inventory }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [associatedInstanceGroups, setInstanceGroups] = useState(null);
|
const [associatedInstanceGroups, setInstanceGroups] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [contentLoading, setContentLoading] = useState(true);
|
||||||
const [credentialTypeId, setCredentialTypeId] = useState(null);
|
const [credentialTypeId, setCredentialTypeId] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -39,11 +39,11 @@ function InventoryEdit({ history, i18n, inventory }) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err);
|
setError(err);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setContentLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
loadData();
|
loadData();
|
||||||
}, [inventory.id, isLoading, inventory, credentialTypeId]);
|
}, [inventory.id, contentLoading, inventory, credentialTypeId]);
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
history.push('/inventories');
|
history.push('/inventories');
|
||||||
@@ -85,7 +85,7 @@ function InventoryEdit({ history, i18n, inventory }) {
|
|||||||
history.push(`${url}`);
|
history.push(`${url}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (isLoading) {
|
if (contentLoading) {
|
||||||
return <ContentLoading />;
|
return <ContentLoading />;
|
||||||
}
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import InventoryGroupDetail from '../InventoryGroupDetail/InventoryGroupDetail';
|
|||||||
|
|
||||||
function InventoryGroups({ i18n, match, setBreadcrumb, inventory, history }) {
|
function InventoryGroups({ i18n, match, setBreadcrumb, inventory, history }) {
|
||||||
const [inventoryGroup, setInventoryGroup] = useState(null);
|
const [inventoryGroup, setInventoryGroup] = useState(null);
|
||||||
const [hasContentLoading, setHasContentLoading] = useState(true);
|
const [contentLoading, setContentLoading] = useState(true);
|
||||||
const [contentError, setHasContentError] = useState(null);
|
const [contentError, setContentError] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
@@ -24,9 +24,9 @@ function InventoryGroups({ i18n, match, setBreadcrumb, inventory, history }) {
|
|||||||
setInventoryGroup(data);
|
setInventoryGroup(data);
|
||||||
setBreadcrumb(inventory, data);
|
setBreadcrumb(inventory, data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setHasContentError(err);
|
setContentError(err);
|
||||||
} finally {
|
} finally {
|
||||||
setHasContentLoading(false);
|
setContentLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,12 +64,32 @@ function InventoryGroups({ i18n, match, setBreadcrumb, inventory, history }) {
|
|||||||
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) {
|
||||||
|
return <ContentLoading />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
inventoryGroup.summary_fields.inventory.id !== parseInt(match.params.id, 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} />;
|
||||||
}
|
}
|
||||||
if (hasContentLoading) {
|
|
||||||
return <ContentLoading />;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cardHeader = null;
|
let cardHeader = null;
|
||||||
if (
|
if (
|
||||||
@@ -80,12 +100,11 @@ function InventoryGroups({ i18n, match, setBreadcrumb, inventory, history }) {
|
|||||||
<CardHeader style={{ padding: 0 }}>
|
<CardHeader style={{ padding: 0 }}>
|
||||||
<RoutedTabs history={history} tabsArray={tabsArray} />
|
<RoutedTabs history={history} tabsArray={tabsArray} />
|
||||||
<CardCloseButton
|
<CardCloseButton
|
||||||
linkTo={`/inventories/inventory/${inventory.id}/group`}
|
linkTo={`/inventories/inventory/${inventory.id}/groups`}
|
||||||
/>
|
/>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{cardHeader}
|
{cardHeader}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { GroupsAPI } from '@api';
|
import { GroupsAPI } from '@api';
|
||||||
|
import { Route } from 'react-router-dom';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
@@ -14,6 +15,7 @@ GroupsAPI.readDetail.mockResolvedValue({
|
|||||||
description: 'Bar',
|
description: 'Bar',
|
||||||
variables: 'bizz: buzz',
|
variables: 'bizz: buzz',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
|
inventory: { id: 1 },
|
||||||
created_by: { id: 1, name: 'Athena' },
|
created_by: { id: 1, name: 'Athena' },
|
||||||
modified_by: { id: 1, name: 'Apollo' },
|
modified_by: { id: 1, name: 'Apollo' },
|
||||||
},
|
},
|
||||||
@@ -29,7 +31,12 @@ describe('<InventoryGroup />', () => {
|
|||||||
});
|
});
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<InventoryGroup inventory={inventory} setBreadcrumb={() => {}} />,
|
<Route
|
||||||
|
path="/inventories/inventory/:id/groups"
|
||||||
|
component={() => (
|
||||||
|
<InventoryGroup setBreadcrumb={() => {}} inventory={inventory} />
|
||||||
|
)}
|
||||||
|
/>,
|
||||||
{
|
{
|
||||||
context: {
|
context: {
|
||||||
router: {
|
router: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { withRouter } from 'react-router-dom';
|
|||||||
import { GroupsAPI } from '@api';
|
import { GroupsAPI } from '@api';
|
||||||
import { Card } from '@patternfly/react-core';
|
import { Card } from '@patternfly/react-core';
|
||||||
|
|
||||||
import InventoryGroupForm from '../InventoryGroupForm/InventoryGroupForm';
|
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
||||||
|
|
||||||
function InventoryGroupsAdd({ history, inventory, setBreadcrumb }) {
|
function InventoryGroupsAdd({ history, inventory, setBreadcrumb }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ function InventoryGroupDetail({ i18n, history, match, inventoryGroup }) {
|
|||||||
summary_fields: { created_by, modified_by },
|
summary_fields: { created_by, modified_by },
|
||||||
created,
|
created,
|
||||||
modified,
|
modified,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
variables,
|
||||||
} = inventoryGroup;
|
} = inventoryGroup;
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
@@ -78,16 +81,13 @@ function InventoryGroupDetail({ i18n, history, match, inventoryGroup }) {
|
|||||||
return (
|
return (
|
||||||
<CardBody css="padding-top: 20px">
|
<CardBody css="padding-top: 20px">
|
||||||
<DetailList gutter="sm">
|
<DetailList gutter="sm">
|
||||||
<Detail label={i18n._(t`Name`)} value={inventoryGroup.name} />
|
<Detail label={i18n._(t`Name`)} value={name} />
|
||||||
<Detail
|
<Detail label={i18n._(t`Description`)} value={description} />
|
||||||
label={i18n._(t`Description`)}
|
|
||||||
value={inventoryGroup.description}
|
|
||||||
/>
|
|
||||||
</DetailList>
|
</DetailList>
|
||||||
<VariablesInput
|
<VariablesInput
|
||||||
id="inventoryGroup-variables"
|
id="inventoryGroup-variables"
|
||||||
readOnly
|
readOnly
|
||||||
value={inventoryGroup.variables}
|
value={variables}
|
||||||
rows={4}
|
rows={4}
|
||||||
label={i18n._(t`Variables`)}
|
label={i18n._(t`Variables`)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { withI18n } from '@lingui/react';
|
|||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { GroupsAPI } from '@api';
|
import { GroupsAPI } from '@api';
|
||||||
|
|
||||||
import InventoryGroupForm from '../InventoryGroupForm/InventoryGroupForm';
|
import InventoryGroupForm from '../shared/InventoryGroupForm';
|
||||||
|
|
||||||
function InventoryGroupEdit({ history, inventoryGroup, inventory, match }) {
|
function InventoryGroupEdit({ history, inventoryGroup, inventory, match }) {
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export { default } from './InventoryGroupForm';
|
|
||||||
@@ -1,81 +1,25 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
|
||||||
import { Route } from 'react-router-dom';
|
import { Route } from 'react-router-dom';
|
||||||
|
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
import InventoryGroups from './InventoryGroups';
|
||||||
import { InventoriesAPI, GroupsAPI } from '@api';
|
|
||||||
import InventoryGroupsList from './InventoryGroupsList';
|
|
||||||
|
|
||||||
jest.mock('@api');
|
describe('<InventoryGroups />', () => {
|
||||||
|
test('initially renders successfully', async () => {
|
||||||
const mockGroups = [
|
let wrapper;
|
||||||
{
|
|
||||||
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('<InventoryGroupsList />', () => {
|
|
||||||
let wrapper;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
InventoriesAPI.readGroups.mockResolvedValue({
|
|
||||||
data: {
|
|
||||||
count: mockGroups.length,
|
|
||||||
results: mockGroups,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
InventoriesAPI.readGroupsOptions.mockResolvedValue({
|
|
||||||
data: {
|
|
||||||
actions: {
|
|
||||||
GET: {},
|
|
||||||
POST: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const history = createMemoryHistory({
|
const history = createMemoryHistory({
|
||||||
initialEntries: ['/inventories/inventory/3/groups'],
|
initialEntries: ['/inventories/inventory/1/groups'],
|
||||||
});
|
});
|
||||||
|
const inventory = { id: 1, name: 'Foo' };
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<Route
|
<Route
|
||||||
path="/inventories/inventory/:id/groups"
|
path="/inventories/inventory/:id/groups"
|
||||||
component={() => <InventoryGroupsList />}
|
component={() => (
|
||||||
|
<InventoryGroups setBreadcrumb={() => {}} inventory={inventory} />
|
||||||
|
)}
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
context: {
|
context: {
|
||||||
@@ -84,134 +28,30 @@ describe('<InventoryGroupsList />', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
expect(wrapper.length).toBe(1);
|
||||||
});
|
|
||||||
|
|
||||||
test('initially renders successfully', () => {
|
|
||||||
expect(wrapper.find('InventoryGroupsList').length).toBe(1);
|
expect(wrapper.find('InventoryGroupsList').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
test('test that InventoryGroupsAdd renders', async () => {
|
||||||
test('should fetch groups from api and render them in the list', async () => {
|
const history = createMemoryHistory({
|
||||||
expect(InventoriesAPI.readGroups).toHaveBeenCalled();
|
initialEntries: ['/inventories/inventory/1/groups/add'],
|
||||||
expect(wrapper.find('InventoryGroupItem').length).toBe(3);
|
});
|
||||||
});
|
const inventory = { id: 1, name: 'Foo' };
|
||||||
|
let wrapper;
|
||||||
test('should check and uncheck the row item', async () => {
|
|
||||||
expect(
|
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked
|
|
||||||
).toBe(false);
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(
|
wrapper = mountWithContexts(
|
||||||
true
|
<Route
|
||||||
);
|
path="/inventories/inventory/:id/groups/add"
|
||||||
});
|
component={() => (
|
||||||
wrapper.update();
|
<InventoryGroups setBreadcrumb={() => {}} inventory={inventory} />
|
||||||
expect(
|
)}
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked
|
/>,
|
||||||
).toBe(true);
|
{
|
||||||
|
context: {
|
||||||
await act(async () => {
|
router: { history, route: { location: history.location } },
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(
|
|
||||||
false
|
|
||||||
);
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
expect(
|
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked
|
|
||||||
).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should check all row items when select all is checked', async () => {
|
|
||||||
wrapper.find('PFDataListCheck').forEach(el => {
|
|
||||||
expect(el.props().checked).toBe(false);
|
|
||||||
});
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Checkbox#select-all').invoke('onChange')(true);
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
wrapper.find('PFDataListCheck').forEach(el => {
|
|
||||||
expect(el.props().checked).toBe(true);
|
|
||||||
});
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Checkbox#select-all').invoke('onChange')(false);
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
wrapper.find('PFDataListCheck').forEach(el => {
|
|
||||||
expect(el.props().checked).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show content error when api throws error on initial render', async () => {
|
|
||||||
InventoriesAPI.readGroupsOptions.mockImplementation(() =>
|
|
||||||
Promise.reject(new Error())
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper = mountWithContexts(<InventoryGroupsList />);
|
|
||||||
});
|
|
||||||
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show content error if groups are not successfully fetched from api', async () => {
|
|
||||||
InventoriesAPI.readGroups.mockImplementation(() =>
|
|
||||||
Promise.reject(new Error())
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')();
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')();
|
|
||||||
});
|
|
||||||
await waitForElement(
|
|
||||||
wrapper,
|
|
||||||
'InventoryGroupsDeleteModal',
|
|
||||||
el => el.props().isModalOpen === true
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper
|
|
||||||
.find('ModalBoxFooter Button[aria-label="Delete"]')
|
|
||||||
.invoke('onClick')();
|
|
||||||
});
|
|
||||||
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show error modal when group is not successfully deleted from api', async () => {
|
|
||||||
GroupsAPI.destroy.mockRejectedValue(
|
|
||||||
new Error({
|
|
||||||
response: {
|
|
||||||
config: {
|
|
||||||
method: 'delete',
|
|
||||||
url: '/api/v2/groups/1',
|
|
||||||
},
|
},
|
||||||
data: 'An error occurred',
|
}
|
||||||
},
|
);
|
||||||
})
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')();
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')();
|
|
||||||
});
|
|
||||||
await waitForElement(
|
|
||||||
wrapper,
|
|
||||||
'InventoryGroupsDeleteModal',
|
|
||||||
el => el.props().isModalOpen === true
|
|
||||||
);
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
|
|
||||||
});
|
|
||||||
wrapper.update();
|
|
||||||
await act(async () => {
|
|
||||||
wrapper
|
|
||||||
.find('ModalBoxFooter Button[aria-label="Delete"]')
|
|
||||||
.invoke('onClick')();
|
|
||||||
});
|
|
||||||
await waitForElement(wrapper, { title: 'Error!', variant: 'danger' });
|
|
||||||
await act(async () => {
|
|
||||||
wrapper.find('ModalBoxCloseButton').invoke('onClose')();
|
|
||||||
});
|
});
|
||||||
|
expect(wrapper.find('InventoryGroupsAdd').length).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import { Route } from 'react-router-dom';
|
||||||
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { mountWithContexts, waitForElement } from '@testUtils/enzymeHelpers';
|
||||||
|
import { InventoriesAPI, GroupsAPI } from '@api';
|
||||||
|
import InventoryGroupsList from './InventoryGroupsList';
|
||||||
|
|
||||||
|
jest.mock('@api');
|
||||||
|
|
||||||
|
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('<InventoryGroupsList />', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
InventoriesAPI.readGroups.mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
count: mockGroups.length,
|
||||||
|
results: mockGroups,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
InventoriesAPI.readGroupsOptions.mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
actions: {
|
||||||
|
GET: {},
|
||||||
|
POST: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/inventories/inventory/3/groups'],
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Route
|
||||||
|
path="/inventories/inventory/:id/groups"
|
||||||
|
component={() => <InventoryGroupsList />}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
router: { history, route: { location: history.location } },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initially renders successfully', () => {
|
||||||
|
expect(wrapper.find('InventoryGroupsList').length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should fetch groups from api and render them in the list', async () => {
|
||||||
|
expect(InventoriesAPI.readGroups).toHaveBeenCalled();
|
||||||
|
expect(wrapper.find('InventoryGroupItem').length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should check and uncheck the row item', async () => {
|
||||||
|
expect(
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked
|
||||||
|
).toBe(false);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')(
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').props().checked
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should check all row items when select all is checked', async () => {
|
||||||
|
wrapper.find('PFDataListCheck').forEach(el => {
|
||||||
|
expect(el.props().checked).toBe(false);
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Checkbox#select-all').invoke('onChange')(true);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
wrapper.find('PFDataListCheck').forEach(el => {
|
||||||
|
expect(el.props().checked).toBe(true);
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Checkbox#select-all').invoke('onChange')(false);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
wrapper.find('PFDataListCheck').forEach(el => {
|
||||||
|
expect(el.props().checked).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show content error when api throws error on initial render', async () => {
|
||||||
|
InventoriesAPI.readGroupsOptions.mockImplementation(() =>
|
||||||
|
Promise.reject(new Error())
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<InventoryGroupsList />);
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show content error if groups are not successfully fetched from api', async () => {
|
||||||
|
InventoriesAPI.readGroups.mockImplementation(() =>
|
||||||
|
Promise.reject(new Error())
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')();
|
||||||
|
});
|
||||||
|
await waitForElement(
|
||||||
|
wrapper,
|
||||||
|
'InventoryGroupsDeleteModal',
|
||||||
|
el => el.props().isModalOpen === true
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper
|
||||||
|
.find('ModalBoxFooter Button[aria-label="Delete"]')
|
||||||
|
.invoke('onClick')();
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'ContentError', el => el.length === 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show error modal when group is not successfully deleted from api', async () => {
|
||||||
|
GroupsAPI.destroy.mockRejectedValue(
|
||||||
|
new Error({
|
||||||
|
response: {
|
||||||
|
config: {
|
||||||
|
method: 'delete',
|
||||||
|
url: '/api/v2/groups/1',
|
||||||
|
},
|
||||||
|
data: 'An error occurred',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('PFDataListCheck[id="select-group-1"]').invoke('onChange')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Toolbar Button[aria-label="Delete"]').invoke('onClick')();
|
||||||
|
});
|
||||||
|
await waitForElement(
|
||||||
|
wrapper,
|
||||||
|
'InventoryGroupsDeleteModal',
|
||||||
|
el => el.props().isModalOpen === true
|
||||||
|
);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('Radio[id="radio-delete"]').invoke('onChange')();
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
await act(async () => {
|
||||||
|
wrapper
|
||||||
|
.find('ModalBoxFooter Button[aria-label="Delete"]')
|
||||||
|
.invoke('onClick')();
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, { title: 'Error!', variant: 'danger' });
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('ModalBoxCloseButton').invoke('onClose')();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user