From 204af9ec91aa4bf22e1121aabc29431741af408a Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Tue, 8 Dec 2020 16:35:42 -0800 Subject: [PATCH] update tests for PaginatedTable lists --- .../src/components/ListHeader/ListHeader.jsx | 3 +- .../components/PaginatedTable/ActionsTd.jsx | 3 +- .../PaginatedTable/PaginatedTable.jsx | 8 - .../components/StatusLabel/StatusLabel.jsx | 4 + .../Inventory/InventoryList/InventoryList.jsx | 1 - .../InventoryList/InventoryListItem.jsx | 23 +- .../InventoryList/InventoryListItem.test.jsx | 298 ++++++++++-------- .../OrganizationList/OrganizationList.jsx | 2 - .../OrganizationList.test.jsx | 12 +- .../OrganizationList/OrganizationListItem.jsx | 2 +- .../OrganizationListItem.test.jsx | 122 +++---- 11 files changed, 259 insertions(+), 219 deletions(-) diff --git a/awx/ui_next/src/components/ListHeader/ListHeader.jsx b/awx/ui_next/src/components/ListHeader/ListHeader.jsx index f383c24130..6e1f9cb4e9 100644 --- a/awx/ui_next/src/components/ListHeader/ListHeader.jsx +++ b/awx/ui_next/src/components/ListHeader/ListHeader.jsx @@ -147,13 +147,14 @@ ListHeader.propTypes = { searchColumns: SearchColumns.isRequired, searchableKeys: PropTypes.arrayOf(PropTypes.string), relatedSearchableKeys: PropTypes.arrayOf(PropTypes.string), - sortColumns: SortColumns.isRequired, + sortColumns: SortColumns, renderToolbar: PropTypes.func, }; ListHeader.defaultProps = { renderToolbar: props => , searchableKeys: [], + sortColumns: null, relatedSearchableKeys: [], }; diff --git a/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx b/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx index 7b6199d56f..8ed5f74344 100644 --- a/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx +++ b/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx @@ -16,7 +16,7 @@ const ActionsGrid = styled.div` }} `; -export default function ActionsTd({ children }) { +export default function ActionsTd({ children, ...props }) { const numActions = children.length || 1; const width = numActions * 40; return ( @@ -25,6 +25,7 @@ export default function ActionsTd({ children }) { text-align: right; --pf-c-table--cell--Width: ${width}px; `} + {...props} > {React.Children.map(children, (child, i) => diff --git a/awx/ui_next/src/components/PaginatedTable/PaginatedTable.jsx b/awx/ui_next/src/components/PaginatedTable/PaginatedTable.jsx index 912773e573..9d5964cd9f 100644 --- a/awx/ui_next/src/components/PaginatedTable/PaginatedTable.jsx +++ b/awx/ui_next/src/components/PaginatedTable/PaginatedTable.jsx @@ -36,15 +36,9 @@ function PaginatedTable({ showPageSizeOptions, i18n, renderToolbar, - // onRowClick, }) { const history = useHistory(); - // const handleListItemSelect = (id = 0) => { - // const match = items.find(item => item.id === Number(id)); - // onRowClick(match); - // }; - const pushHistoryState = params => { const { pathname } = history.location; const encodedParams = encodeNonDefaultQueryString(qsConfig, params); @@ -174,7 +168,6 @@ PaginatedTable.propTypes = { renderToolbar: PropTypes.func, hasContentLoading: PropTypes.bool, contentError: PropTypes.shape(), - // onRowClick: PropTypes.func, }; PaginatedTable.defaultProps = { @@ -187,7 +180,6 @@ PaginatedTable.defaultProps = { showPageSizeOptions: true, renderRow: item => , renderToolbar: props => , - // onRowClick: () => null, }; export { PaginatedTable as _PaginatedTable }; diff --git a/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx b/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx index 31917b9597..bd0466f5ce 100644 --- a/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx +++ b/awx/ui_next/src/components/StatusLabel/StatusLabel.jsx @@ -8,6 +8,7 @@ import { SyncAltIcon, ExclamationTriangleIcon, ClockIcon, + MinusCircleIcon, } from '@patternfly/react-icons'; import styled, { keyframes } from 'styled-components'; @@ -32,6 +33,7 @@ const colors = { running: 'blue', pending: 'blue', waiting: 'grey', + disabled: 'grey', canceled: 'orange', }; const icons = { @@ -42,6 +44,7 @@ const icons = { running: RunningIcon, pending: ClockIcon, waiting: ClockIcon, + disabled: MinusCircleIcon, canceled: ExclamationTriangleIcon, }; @@ -66,6 +69,7 @@ StatusLabel.propTypes = { 'running', 'pending', 'waiting', + 'disabled', 'canceled', ]).isRequired, }; diff --git a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx index a66900da17..ce3d936743 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryList.jsx @@ -162,7 +162,6 @@ function InventoryList({ i18n }) { itemCount={itemCount} pluralizedItemName={i18n._(t`Inventories`)} qsConfig={QS_CONFIG} - onRowClick={handleSelect} toolbarSearchColumns={[ { name: i18n._(t`Name`), diff --git a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx index b641e66b7d..fbb6e46249 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx @@ -56,15 +56,16 @@ function InventoryListItem({ } return ( - + - + {inventory.pending_deletion ? ( {inventory.name} ) : ( @@ -73,30 +74,32 @@ function InventoryListItem({ )} - + {inventory.kind !== 'smart' && } - + {inventory.kind === 'smart' ? i18n._(t`Smart Inventory`) : i18n._(t`Inventory`)} - + {inventory.summary_fields.organization.name} - {inventory.total_groups} - {inventory.total_hosts} - {inventory.total_inventory_sources} + {inventory.total_groups} + {inventory.total_hosts} + + {inventory.total_inventory_sources} + {inventory.pending_deletion ? ( - + ) : ( - + ', () => { test('initially renders succesfully', () => { mountWithContexts( - + + {}} - /> + name: 'Inventory', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: true, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); }); test('should render prompt list item data', () => { const wrapper = mountWithContexts( - + + {}} - /> + name: 'Inventory', + kind: '', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: true, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); - expect(wrapper.find('SyncStatusIndicator').length).toBe(1); + expect(wrapper.find('StatusLabel').length).toBe(1); expect( wrapper - .find('DataListCell') + .find('Td') .at(1) .text() ).toBe('Inventory'); expect( wrapper - .find('DataListCell') + .find('Td') .at(2) .text() + ).toBe('Disabled'); + expect( + wrapper + .find('Td') + .at(3) + .text() ).toBe('Inventory'); expect( wrapper - .find('DataListCell') - .at(3) - .text() - ).toBe('OrganizationDefault'); - expect( - wrapper - .find('DataListCell') + .find('Td') .at(4) .text() - ).toBe('GroupsHostsSources'); + ).toBe('Default'); }); test('edit button shown to users with edit capabilities', () => { const wrapper = mountWithContexts( - + + {}} - /> + name: 'Inventory', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: true, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy(); }); + test('edit button hidden from users without edit capabilities', () => { const wrapper = mountWithContexts( - + + {}} - /> + name: 'Inventory', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: false, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); }); + test('should call api to copy inventory', async () => { InventoriesAPI.copy.mockResolvedValue(); const wrapper = mountWithContexts( - + + {}} - /> + name: 'Inventory', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: false, + copy: true, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); await act(async () => @@ -161,25 +183,29 @@ describe('', () => { InventoriesAPI.copy.mockRejectedValue(new Error()); const wrapper = mountWithContexts( - + + {}} - /> + name: 'Inventory', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: false, + copy: true, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); await act(async () => wrapper.find('Button[aria-label="Copy"]').prop('onClick')() @@ -191,25 +217,29 @@ describe('', () => { test('should not render copy button', async () => { const wrapper = mountWithContexts( - + + {}} - /> + name: 'Inventory', + summary_fields: { + organization: { + id: 1, + name: 'Default', + }, + user_capabilities: { + edit: false, + copy: false, + }, + }, + }} + detailUrl="/inventories/inventory/1" + isSelected + onSelect={() => {}} + /> + + ); expect(wrapper.find('CopyButton').length).toBe(0); }); diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx index 05d66f2ada..49ab977167 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationList.jsx @@ -122,14 +122,12 @@ function OrganizationsList({ i18n }) { ', () => { }); test('Item appears selected after selecting it', async () => { - const itemCheckboxInput = 'input#select-organization-1'; + const itemCheckboxInput = 'tr#org-row-1 input[type="checkbox"]'; await act(async () => { wrapper = mountWithContexts(); }); @@ -115,7 +115,6 @@ describe('', () => { await act(async () => { wrapper .find(itemCheckboxInput) - .closest('DataListCheck') .props() .onChange(); }); @@ -128,9 +127,9 @@ describe('', () => { test('All items appear selected after select-all and unselected after unselect-all', async () => { const itemCheckboxInputs = [ - 'input#select-organization-1', - 'input#select-organization-2', - 'input#select-organization-3', + 'tr#org-row-1 input[type="checkbox"]', + 'tr#org-row-2 input[type="checkbox"]', + 'tr#org-row-3 input[type="checkbox"]', ]; await act(async () => { wrapper = mountWithContexts(); @@ -227,7 +226,7 @@ describe('', () => { }); test('Error dialog shown for failed deletion', async () => { - const itemCheckboxInput = 'input#select-organization-1'; + const itemCheckboxInput = 'tr#org-row-1 input[type="checkbox"]'; OrganizationsAPI.destroy.mockRejectedValue( new Error({ response: { @@ -250,7 +249,6 @@ describe('', () => { await act(async () => { wrapper .find(itemCheckboxInput) - .closest('DataListCheck') .props() .onChange(); }); diff --git a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx index 9bf440e165..1db1d7e859 100644 --- a/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx +++ b/awx/ui_next/src/screens/Organization/OrganizationList/OrganizationListItem.jsx @@ -20,7 +20,7 @@ function OrganizationListItem({ }) { const labelId = `check-action-${organization.id}`; return ( - + ', () => { mountWithContexts( - {}} - /> + + + {}} + /> + +
); }); + test('edit button shown to users with edit capabilities', () => { const wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeTruthy(); }); + test('edit button hidden from users without edit capabilities', () => { const wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
);