diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx
index 078f17d1ff..65fe8656f6 100644
--- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx
@@ -85,7 +85,6 @@ function InventorySourceList({ i18n }) {
await handleDeleteSources();
setSelected([]);
};
-
const canAdd =
sourceChoicesOptions &&
Object.prototype.hasOwnProperty.call(sourceChoicesOptions, 'POST');
@@ -97,7 +96,7 @@ function InventorySourceList({ i18n }) {
hasContentLoading={isLoading || isDeleteLoading}
items={sources}
itemCount={sourceCount}
- pluralizedItemName={i18n._(t`Sources`)}
+ pluralizedItemName={i18n._(t`Inventory Sources`)}
qsConfig={QS_CONFIG}
renderToolbar={props => (
,
]}
/>
)}
renderItem={inventorySource => {
+ let label;
sourceChoices.forEach(([scMatch, scLabel]) => {
if (inventorySource.source === scMatch) {
- inventorySource.source = scLabel;
+ label = scLabel;
}
});
return (
@@ -132,6 +132,7 @@ function InventorySourceList({ i18n }) {
key={inventorySource.id}
source={inventorySource}
onSelect={() => handleSelect(inventorySource)}
+ label={label}
detailUrl={`${detailUrl}${inventorySource.id}`}
isSelected={selected.some(row => row.id === inventorySource.id)}
/>
@@ -140,6 +141,7 @@ function InventorySourceList({ i18n }) {
/>
{deletionError && (
', () => {
});
});
afterEach(() => {
+ wrapper.unmount();
jest.clearAllMocks();
});
test('should mount properly', async () => {
@@ -123,6 +124,97 @@ describe('', () => {
);
expect(InventorySourcesAPI.destroy).toHaveBeenCalledWith(1);
});
+ test('should throw error after deletion failure', async () => {
+ InventorySourcesAPI.destroy.mockRejectedValue(
+ new Error({
+ response: {
+ config: {
+ method: 'delete',
+ url: '/api/v2/inventory_sources/',
+ },
+ data: 'An error occurred',
+ status: 403,
+ },
+ })
+ );
+
+ await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0);
+
+ await act(async () =>
+ wrapper.find('DataListCheck').prop('onChange')({ id: 1 })
+ );
+ wrapper.update();
+
+ await act(async () =>
+ wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
+ );
+ wrapper.update();
+
+ await act(async () =>
+ wrapper.find('Button[aria-label="confirm delete"]').prop('onClick')()
+ );
+ wrapper.update();
+ expect(wrapper.find("AlertModal[aria-label='Delete Error']").length).toBe(
+ 1
+ );
+ });
+ test('displays error after unseccessful read sources fetch', async () => {
+ InventorySourcesAPI.readOptions.mockRejectedValue(
+ new Error({
+ response: {
+ config: {
+ method: 'get',
+ url: '/api/v2/inventories/inventory_sources/',
+ },
+ data: 'An error occurred',
+ status: 403,
+ },
+ })
+ );
+ InventoriesAPI.readSources.mockRejectedValue(
+ new Error({
+ response: {
+ config: {
+ method: 'get',
+ url: '/api/v2/inventories/inventory_sources/',
+ },
+ data: 'An error occurred',
+ status: 403,
+ },
+ })
+ );
+
+ await act(async () => {
+ wrapper = mountWithContexts();
+ });
+
+ await waitForElement(wrapper, 'ContentError', el => el.length > 0);
+
+ expect(wrapper.find('ContentError').length).toBe(1);
+ });
+
+ test('displays error after unseccessful read options fetch', async () => {
+ InventorySourcesAPI.readOptions.mockRejectedValue(
+ new Error({
+ response: {
+ config: {
+ method: 'options',
+ url: '/api/v2/inventory_sources/',
+ },
+ data: 'An error occurred',
+ status: 403,
+ },
+ })
+ );
+
+ await act(async () => {
+ wrapper = mountWithContexts();
+ });
+
+ await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0);
+
+ expect(wrapper.find('ContentError').length).toBe(1);
+ });
});
describe(' RBAC testing', () => {
@@ -156,12 +248,12 @@ describe(' RBAC testing', () => {
},
},
});
- let wrapper;
+ let newWrapper;
const history = createMemoryHistory({
- initialEntries: ['/inventories/inventory/1/sources'],
+ initialEntries: ['/inventories/inventory/2/sources'],
});
await act(async () => {
- wrapper = mountWithContexts(
+ newWrapper = mountWithContexts(
,
@@ -171,15 +263,20 @@ describe(' RBAC testing', () => {
history,
route: {
location: { search: '' },
- match: { params: { id: 1 } },
+ match: { params: { id: 2 } },
},
},
},
}
);
});
-
- await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0);
- expect(wrapper.find('ToolbarAddButton').length).toBe(0);
+ await waitForElement(
+ newWrapper,
+ 'InventorySourceList',
+ el => el.length > 0
+ );
+ expect(newWrapper.find('ToolbarAddButton').length).toBe(0);
+ newWrapper.unmount();
+ jest.clearAllMocks();
});
});
diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx
index acd140f439..153311bd13 100644
--- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React from 'react';
import { withI18n } from '@lingui/react';
import { Link } from 'react-router-dom';
import { t } from '@lingui/macro';
@@ -19,51 +19,49 @@ function InventorySourceListItem({
onSelect,
i18n,
detailUrl,
+ label,
}) {
-
return (
- <>
-
-
-
-
-
-
- {source.name}
-
-
- ,
-
- {source.source}
- ,
- ]}
- />
-
- {source.summary_fields.user_capabilities.edit && (
-
- )}
-
-
-
- >
+
+
+
+
+
+
+ {source.name}
+
+
+ ,
+
+ {label}
+ ,
+ ]}
+ />
+
+ {source.summary_fields.user_capabilities.edit && (
+
+ )}
+
+
+
);
}
export default withI18n()(InventorySourceListItem);
diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.test.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.test.jsx
index fb3c700033..c0555ced67 100644
--- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.test.jsx
+++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.test.jsx
@@ -1,5 +1,4 @@
import React from 'react';
-import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '@testUtils/enzymeHelpers';
import InventorySourceListItem from './InventorySourceListItem';
@@ -10,35 +9,34 @@ const source = {
summary_fields: { user_capabilities: { start: true, edit: true } },
};
describe('', () => {
+ let wrapper;
afterEach(() => {
+ wrapper.unmount();
jest.clearAllMocks();
});
- test('should mount properly', async () => {
- let wrapper;
+ test('should mount properly', () => {
const onSelect = jest.fn();
- await act(async () => {
- wrapper = mountWithContexts(
-
- );
- });
+ wrapper = mountWithContexts(
+
+ );
expect(wrapper.find('InventorySourceListItem').length).toBe(1);
});
- test('all buttons and text fields should render properly', async () => {
- let wrapper;
+
+ test('all buttons and text fields should render properly', () => {
const onSelect = jest.fn();
- await act(async () => {
- wrapper = mountWithContexts(
-
- );
- });
+ wrapper = mountWithContexts(
+
+ );
expect(wrapper.find('DataListCheck').length).toBe(1);
expect(
wrapper
@@ -55,37 +53,33 @@ describe('', () => {
expect(wrapper.find('PencilAltIcon').length).toBe(1);
});
- test('item should be checked', async () => {
- let wrapper;
+ test('item should be checked', () => {
const onSelect = jest.fn();
- await act(async () => {
- wrapper = mountWithContexts(
-
- );
- });
+ wrapper = mountWithContexts(
+
+ );
expect(wrapper.find('DataListCheck').length).toBe(1);
expect(wrapper.find('DataListCheck').prop('checked')).toBe(true);
});
- test(' should render edit buttons', async () => {
- let wrapper;
+ test(' should render edit buttons', () => {
const onSelect = jest.fn();
- await act(async () => {
- wrapper = mountWithContexts(
-
- );
- });
+ wrapper = mountWithContexts(
+
+ );
expect(wrapper.find('Button[aria-label="Edit Source"]').length).toBe(0);
});
});