From df1489bceed494a3b819ec615563297b7f638f97 Mon Sep 17 00:00:00 2001 From: Alex Corey Date: Thu, 23 Apr 2020 11:35:58 -0400 Subject: [PATCH] Addresses some object mutation issues and improves testing --- .../InventorySources/InventorySourceList.jsx | 10 +- .../InventorySourceList.test.jsx | 111 ++++++++++++++++-- .../InventorySourceListItem.jsx | 86 +++++++------- .../InventorySourceListItem.test.jsx | 90 +++++++------- 4 files changed, 194 insertions(+), 103 deletions(-) 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); }); });