From 0ac6ba9c99097a0a7d175dba731d36ac205759c3 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Fri, 23 Apr 2021 11:00:16 -0700 Subject: [PATCH 01/11] convert inventory access/hosts lists to tables --- .../src/components/HostToggle/HostToggle.jsx | 6 +- .../ResourceAccessList/ResourceAccessList.jsx | 30 ++--- .../ResourceAccessListItem.jsx | 118 ++++++------------ .../InventoryHosts/InventoryHostItem.jsx | 108 ++++++---------- .../InventoryHosts/InventoryHostList.jsx | 49 +++----- 5 files changed, 117 insertions(+), 194 deletions(-) diff --git a/awx/ui_next/src/components/HostToggle/HostToggle.jsx b/awx/ui_next/src/components/HostToggle/HostToggle.jsx index 4a7b03d55a..4212fa5bdc 100644 --- a/awx/ui_next/src/components/HostToggle/HostToggle.jsx +++ b/awx/ui_next/src/components/HostToggle/HostToggle.jsx @@ -47,7 +47,11 @@ function HostToggle({ return ( - + - ( @@ -188,7 +175,15 @@ function ResourceAccessList({ apiModel, resource }) { } /> )} - renderItem={accessRecord => ( + headerRow={ + + {t`Username`} + {t`First name`} + {t`Last name`} + {t`Roles`} + + } + renderRow={(accessRecord, index) => ( )} /> diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx index 24b9f11fd4..9c913df54d 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx @@ -3,27 +3,14 @@ import React from 'react'; import { func } from 'prop-types'; import { t } from '@lingui/macro'; -import { - Chip, - DataListItem, - DataListItemRow, - DataListItemCells as PFDataListItemCells, - Text, - TextContent, - TextVariants, -} from '@patternfly/react-core'; +import { Chip } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; -import styled from 'styled-components'; -import DataListCell from '../DataListCell'; import ChipGroup from '../ChipGroup'; import { DetailList, Detail } from '../DetailList'; import { AccessRecord } from '../../types'; -const DataListItemCells = styled(PFDataListItemCells)` - align-items: start; -`; - function ResourceAccessListItem({ accessRecord, onRoleDelete }) { ResourceAccessListItem.propTypes = { accessRecord: AccessRecord.isRequired, @@ -67,70 +54,43 @@ function ResourceAccessListItem({ accessRecord, onRoleDelete }) { const [teamRoles, userRoles] = getRoleLists(); return ( - - - - {accessRecord.username && ( - - {accessRecord.id ? ( - - - {accessRecord.username} - - - ) : ( - - {accessRecord.username} - - )} - - )} - {accessRecord.first_name || accessRecord.last_name ? ( - - - - ) : null} - , - - - {userRoles.length > 0 && ( - - {userRoles.map(renderChip)} - - } - /> - )} - {teamRoles.length > 0 && ( - - {teamRoles.map(renderChip)} - - } - /> - )} - - , - ]} - /> - - + + + {accessRecord.id ? ( + + {accessRecord.username} + + ) : ( + accessRecord.username + )} + + {accessRecord.first_name} + {accessRecord.first_name} + + + {userRoles.length > 0 && ( + + {userRoles.map(renderChip)} + + } + /> + )} + {teamRoles.length > 0 && ( + + {teamRoles.map(renderChip)} + + } + /> + )} + + + ); } diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx index ee75b8cf1c..5b821f80f6 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx @@ -2,83 +2,55 @@ import React from 'react'; import { string, bool, func } from 'prop-types'; import { t } from '@lingui/macro'; -import { - Button, - DataListAction as _DataListAction, - DataListCheck, - DataListItem, - DataListItemCells, - DataListItemRow, - Tooltip, -} from '@patternfly/react-core'; +import { Button, Tooltip } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import styled from 'styled-components'; -import DataListCell from '../../../components/DataListCell'; +import { ActionsTd } from '../../../components/PaginatedTable'; import HostToggle from '../../../components/HostToggle'; -import Sparkline from '../../../components/Sparkline'; import { Host } from '../../../types'; -const DataListAction = styled(_DataListAction)` - align-items: center; - display: grid; - grid-gap: 24px; - grid-template-columns: min-content 40px; -`; - -function InventoryHostItem(props) { - const { detailUrl, editUrl, host, isSelected, onSelect } = props; - - const recentPlaybookJobs = host.summary_fields.recent_jobs.map(job => ({ - ...job, - type: 'job', - })); - +function InventoryHostItem({ + detailUrl, + editUrl, + host, + isSelected, + onSelect, + rowIndex, +}) { const labelId = `check-action-${host.id}`; return ( - - - - - - {host.name} - - , - - - , - ]} - /> - - - {host.summary_fields.user_capabilities?.edit && ( - - - - )} - - - + + + + + {host.name} + + + + + {host.summary_fields.user_capabilities?.edit && ( + + + + )} + + ); } diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx index da1ccef87b..75eac089b6 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.jsx @@ -8,7 +8,11 @@ import useRequest, { useDeleteItems } from '../../../util/useRequest'; import AlertModal from '../../../components/AlertModal'; import DataListToolbar from '../../../components/DataListToolbar'; import ErrorDetail from '../../../components/ErrorDetail'; -import PaginatedDataList, { +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; +import { ToolbarAddButton, ToolbarDeleteButton, } from '../../../components/PaginatedDataList'; @@ -105,35 +109,21 @@ function InventoryHostList() { return ( <> - + {t`Name`} + {t`Actions`} + + } renderToolbar={props => ( )} - renderItem={o => ( + renderRow={(host, index) => ( row.id === o.id)} - onSelect={() => handleSelect(o)} + key={host.id} + host={host} + detailUrl={`/inventories/inventory/${id}/hosts/${host.id}/details`} + editUrl={`/inventories/inventory/${id}/hosts/${host.id}/edit`} + isSelected={selected.some(row => row.id === host.id)} + onSelect={() => handleSelect(host)} + rowIndex={index} /> )} emptyStateControls={ From fe0ad302455c3bbc14902800fdcc3186d3caf9de Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Mon, 26 Apr 2021 15:50:58 -0700 Subject: [PATCH 02/11] fix inventory access/hosts lists tests --- .../components/PaginatedTable/ActionsTd.jsx | 8 +- .../ResourceAccessList.test.jsx | 6 +- .../ResourceAccessListItem.test.jsx | 12 ++- .../InventoryHosts/InventoryHostItem.jsx | 4 +- .../InventoryHosts/InventoryHostItem.test.jsx | 32 +++++--- .../InventoryHosts/InventoryHostList.test.jsx | 76 +++++++++++++------ 6 files changed, 89 insertions(+), 49 deletions(-) diff --git a/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx b/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx index 617f7cd51a..b620ca2fda 100644 --- a/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx +++ b/awx/ui_next/src/components/PaginatedTable/ActionsTd.jsx @@ -30,9 +30,11 @@ export default function ActionsTd({ children, gridColumns, ...props }) { > {React.Children.map(children, (child, i) => - React.cloneElement(child, { - column: i + 1, - }) + child + ? React.cloneElement(child, { + column: i + 1, + }) + : null )} diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx index 4a2b92d124..6bcbc2b391 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx @@ -137,10 +137,6 @@ describe('', () => { jest.clearAllMocks(); }); - test('initially renders successfully', () => { - expect(wrapper.find('PaginatedDataList')).toHaveLength(1); - }); - test('should fetch and display access records on mount', async () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect(OrganizationsAPI.readAccessList).toHaveBeenCalled(); @@ -203,7 +199,7 @@ describe('', () => { await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); expect( - wrapper.find('PaginatedDataList').prop('toolbarSearchColumns') + wrapper.find('PaginatedTable').prop('toolbarSearchColumns') ).toStrictEqual([ { isDefault: true, key: 'username__icontains', name: 'Username' }, { key: 'first_name__icontains', name: 'First Name' }, diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.test.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.test.jsx index d6eaee2c73..43673ac576 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.test.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.test.jsx @@ -38,10 +38,14 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx index 5b821f80f6..50795f7955 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx @@ -37,7 +37,7 @@ function InventoryHostItem({ - {host.summary_fields.user_capabilities?.edit && ( + {host.summary_fields.user_capabilities?.edit ? ( - )} + ) : null} ); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.test.jsx index a135aae9e6..ce047cdaea 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.test.jsx @@ -31,12 +31,16 @@ describe('', () => { beforeEach(() => { wrapper = mountWithContexts( - {}} - host={mockHost} - /> + + + {}} + host={mockHost} + /> + +
); }); @@ -52,12 +56,16 @@ describe('', () => { const copyMockHost = Object.assign({}, mockHost); copyMockHost.summary_fields.user_capabilities.edit = false; wrapper = mountWithContexts( - {}} - host={copyMockHost} - /> + + + {}} + host={copyMockHost} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx index 71f1b2eee3..3dc9292200 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx @@ -103,10 +103,6 @@ describe('', () => { jest.resetAllMocks(); }); - test('initially renders successfully', () => { - expect(wrapper.find('InventoryHostList').length).toBe(1); - }); - test('should fetch hosts from api and render them in the list', async () => { expect(InventoriesAPI.readHosts).toHaveBeenCalled(); expect(wrapper.find('InventoryHostItem').length).toBe(3); @@ -114,49 +110,66 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('DataListCheck[id="select-host-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); await act(async () => { - wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')( - true - ); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(true); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-host-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(true); await act(async () => { - wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')( - false - ); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(false); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-host-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); }); test('should check all row items when select all is checked', async () => { - wrapper.find('DataListCheck').forEach(el => { - expect(el.props().checked).toBe(false); + expect.assertions(9); + wrapper.find('.pf-c-table__check').forEach(el => { + expect(el.find('input').props().checked).toBe(false); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { - expect(el.props().checked).toBe(true); + wrapper.find('.pf-c-table__check').forEach(el => { + expect(el.find('input').props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { - expect(el.props().checked).toBe(false); + wrapper.find('.pf-c-table__check').forEach(el => { + expect(el.find('input').props().checked).toBe(false); }); }); @@ -196,7 +209,12 @@ describe('', () => { test('delete button is disabled if user does not have delete capabilities on a selected host', async () => { await act(async () => { - wrapper.find('DataListCheck[id="select-host-3"]').invoke('onChange')(); + // wrapper.find('DataListCheck[id="select-host-3"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check') + .at(2) + .find('input') + .invoke('onChange')(); }); wrapper.update(); expect(wrapper.find('ToolbarDeleteButton button').props().disabled).toBe( @@ -207,7 +225,11 @@ describe('', () => { test('should call api delete hosts for each selected host', async () => { HostsAPI.destroy = jest.fn(); await act(async () => { - wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -230,7 +252,11 @@ describe('', () => { }) ); await act(async () => { - wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(); }); wrapper.update(); await act(async () => { @@ -252,7 +278,11 @@ describe('', () => { Promise.reject(new Error()) ); await act(async () => { - wrapper.find('DataListCheck[id="select-host-1"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(); }); wrapper.update(); await act(async () => { From 83ceacf588bdef38e42e539aaf674de7d3a45da4 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Tue, 27 Apr 2021 11:38:46 -0700 Subject: [PATCH 03/11] convert inventory groups list to tables --- .../InventoryGroups/InventoryGroupItem.jsx | 88 ++++++++----------- .../InventoryGroupItem.test.jsx | 32 ++++--- .../InventoryGroups/InventoryGroupsList.jsx | 25 +++--- .../InventoryGroupsList.test.jsx | 62 ++++++++----- .../InventoryHosts/InventoryHostItem.jsx | 30 +++---- 5 files changed, 130 insertions(+), 107 deletions(-) diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx index 8ebe0d604d..8a0b95ae2f 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx @@ -1,67 +1,57 @@ import React from 'react'; import { bool, func, number, oneOfType, string } from 'prop-types'; - import { t } from '@lingui/macro'; -import { - Button, - DataListAction, - DataListCheck, - DataListItem, - DataListItemCells, - DataListItemRow, - Tooltip, -} from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import DataListCell from '../../../components/DataListCell'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import { Group } from '../../../types'; -function InventoryGroupItem({ group, inventoryId, isSelected, onSelect }) { +function InventoryGroupItem({ + group, + inventoryId, + isSelected, + onSelect, + rowIndex, +}) { const labelId = `check-action-${group.id}`; const detailUrl = `/inventories/inventory/${inventoryId}/groups/${group.id}/details`; const editUrl = `/inventories/inventory/${inventoryId}/groups/${group.id}/edit`; return ( - - - - - - {group.name} - - , - ]} - /> - + + + + {group.name} + + + + - {group.summary_fields.user_capabilities.edit && ( - - - - )} - - - + + + + ); } diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.test.jsx index db0b2a0bd4..3d60389513 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.test.jsx @@ -18,12 +18,16 @@ describe('', () => { beforeEach(() => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); }); @@ -40,12 +44,16 @@ describe('', () => { copyMockGroup.summary_fields.user_capabilities.edit = false; wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx index 70dc7ef5c6..c190c5b54c 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.jsx @@ -8,9 +8,11 @@ import useSelected from '../../../util/useSelected'; import useRequest from '../../../util/useRequest'; import { InventoriesAPI } from '../../../api'; import DataListToolbar from '../../../components/DataListToolbar'; -import PaginatedDataList, { - ToolbarAddButton, -} from '../../../components/PaginatedDataList'; +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; +import { ToolbarAddButton } from '../../../components/PaginatedDataList'; import InventoryGroupItem from './InventoryGroupItem'; import InventoryGroupsDeleteModal from '../shared/InventoryGroupsDeleteModal'; @@ -104,7 +106,7 @@ function InventoryGroupsList() { return ( <> - ( + headerRow={ + + {t`Name`} + {t`Actions`} + + } + renderRow={(item, index) => ( row.id === item.id)} onSelect={() => handleSelect(item)} + rowIndex={index} /> )} renderToolbar={props => ( diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx index 1c6268fbcf..1dbd23b030 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupsList.test.jsx @@ -93,15 +93,12 @@ describe('', () => { }); await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); }); + afterEach(() => { jest.clearAllMocks(); wrapper.unmount(); }); - 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); @@ -109,52 +106,71 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('DataListCheck[id="select-group-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); await act(async () => { - wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')( - true - ); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(true); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-group-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(true); await act(async () => { - wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')( - false - ); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(false); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-group-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); }); test('should check all row items when select all is checked', async () => { - wrapper.find('DataListCheck').forEach(el => { - expect(el.props().checked).toBe(false); + expect.assertions(9); + wrapper.find('.pf-c-table__check').forEach(el => { + expect(el.find('input').props().checked).toBe(false); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { - expect(el.props().checked).toBe(true); + wrapper.find('.pf-c-table__check').forEach(el => { + expect(el.find('input').props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { - expect(el.props().checked).toBe(false); + wrapper.find('.pf-c-table__check').forEach(el => { + expect(el.find('input').props().checked).toBe(false); }); }); }); + describe(' error handling', () => { let wrapper; + beforeEach(() => { InventoriesAPI.readGroups.mockResolvedValue({ data: { @@ -182,10 +198,12 @@ describe(' error handling', () => { }) ); }); + afterEach(() => { jest.clearAllMocks(); wrapper.unmount(); }); + test('should show content error when api throws error on initial render', async () => { InventoriesAPI.readGroupsOptions.mockImplementationOnce(() => Promise.reject(new Error()) @@ -213,7 +231,11 @@ describe(' error handling', () => { waitForElement(wrapper, 'ContentEmpty', el => el.length === 0); await act(async () => { - wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')(); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(); }); wrapper.update(); await act(async () => { diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx index 50795f7955..c9bc45a179 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx @@ -1,12 +1,11 @@ import React from 'react'; import { string, bool, func } from 'prop-types'; - import { t } from '@lingui/macro'; -import { Button, Tooltip } from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; import { Tr, Td } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import { ActionsTd } from '../../../components/PaginatedTable'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import HostToggle from '../../../components/HostToggle'; import { Host } from '../../../types'; @@ -37,18 +36,19 @@ function InventoryHostItem({ - {host.summary_fields.user_capabilities?.edit ? ( - - - - ) : null} + + + ); From cbe2a782879c059b9fa4be06f097f03660ef3cd4 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Wed, 28 Apr 2021 09:51:11 -0700 Subject: [PATCH 04/11] convert inventory source list to tables --- .../InventorySources/InventorySourceList.jsx | 30 ++-- .../InventorySourceList.test.jsx | 76 +++----- .../InventorySourceListItem.jsx | 149 +++++++--------- .../InventorySourceListItem.test.jsx | 168 +++++++++++------- 4 files changed, 216 insertions(+), 207 deletions(-) diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx index ccd71b2204..5147773c86 100644 --- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.jsx @@ -9,7 +9,11 @@ import useRequest, { } from '../../../util/useRequest'; import { getQSConfig, parseQueryString } from '../../../util/qs'; import { InventoriesAPI, InventorySourcesAPI } from '../../../api'; -import PaginatedDataList, { +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; +import { ToolbarAddButton, ToolbarDeleteButton, } from '../../../components/PaginatedDataList'; @@ -148,7 +152,7 @@ function InventorySourceList() { ); return ( <> - )} - renderItem={inventorySource => { - let label; - sourceChoices.forEach(([scMatch, scLabel]) => { - if (inventorySource.source === scMatch) { - label = scLabel; - } - }); + headerRow={ + + {t`Name`} + {t`Status`} + {t`Type`} + {t`Actions`} + + } + renderRow={(inventorySource, index) => { + const label = sourceChoices.find( + ([scMatch]) => inventorySource.source === scMatch + ); return ( handleSelect(inventorySource)} - label={label} + label={label[1]} detailUrl={`${listUrl}${inventorySource.id}`} isSelected={selected.some(row => row.id === inventorySource.id)} + rowIndex={index} /> ); }} diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.test.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.test.jsx index be37e47f7e..138fac4207 100644 --- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceList.test.jsx @@ -7,10 +7,7 @@ import { InventorySourcesAPI, WorkflowJobTemplateNodesAPI, } from '../../../api'; -import { - mountWithContexts, - waitForElement, -} from '../../../../testUtils/enzymeHelpers'; +import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import InventorySourceList from './InventorySourceList'; @@ -108,18 +105,15 @@ describe('', () => { } ); }); + wrapper.update(); }); + afterEach(() => { jest.clearAllMocks(); global.console.debug = debug; }); - test('should mount properly', async () => { - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); - }); - test('api calls should be made on mount', async () => { - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); expect(InventoriesAPI.readSources).toHaveBeenCalledWith('1', { not__source: '', order_by: 'name', @@ -136,23 +130,16 @@ describe('', () => { }); test('source data should render properly', async () => { - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); + expect(wrapper.find('InventorySourceListItem')).toHaveLength(2); expect( wrapper - .find("DataListItem[aria-labelledby='check-action-1']") - .find('PFDataListCell[aria-label="name"]') - .text() - ).toBe('Source Foo'); - expect( - wrapper - .find("DataListItem[aria-labelledby='check-action-1']") - .find('PFDataListCell[aria-label="type"]') - .text() - ).toBe('EC2'); + .find('InventorySourceListItem') + .first() + .prop('source') + ).toEqual(sources.data.results[0]); }); test('add button is not disabled and delete button is disabled', async () => { - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); const addButton = wrapper.find('ToolbarAddButton').find('Link'); const deleteButton = wrapper.find('ToolbarDeleteButton').find('Button'); expect(addButton.prop('aria-disabled')).toBe(false); @@ -162,14 +149,23 @@ describe('', () => { test('delete button becomes enabled and properly calls api to delete', async () => { const deleteButton = wrapper.find('ToolbarDeleteButton').find('Button'); - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); expect(deleteButton.prop('isDisabled')).toBe(true); await act(async () => - wrapper.find('DataListCheck#select-source-1').prop('onChange')({ id: 1 }) + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .prop('onChange')({ id: 1 }) ); wrapper.update(); - expect(wrapper.find('input#select-source-1').prop('checked')).toBe(true); + expect( + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .prop('checked') + ).toBe(true); await act(async () => wrapper.find('Button[aria-label="Delete"]').prop('onClick')() @@ -199,10 +195,12 @@ describe('', () => { }) ); - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); - await act(async () => - wrapper.find('DataListCheck#select-source-1').prop('onChange')({ id: 1 }) + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .prop('onChange')({ id: 1 }) ); wrapper.update(); @@ -249,8 +247,7 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); - - await waitForElement(wrapper, 'ContentError', el => el.length > 0); + wrapper.update(); expect(wrapper.find('ContentError').length).toBe(1); }); @@ -272,8 +269,7 @@ describe('', () => { await act(async () => { wrapper = mountWithContexts(); }); - - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); + wrapper.update(); expect(wrapper.find('ContentError').length).toBe(1); }); @@ -291,7 +287,6 @@ describe('', () => { }, }) ); - await waitForElement(wrapper, 'InventorySourceList', el => el.length > 0); await act(async () => wrapper.find('Button[aria-label="Sync all"]').prop('onClick')() ); @@ -301,11 +296,6 @@ describe('', () => { }); test('should render sync all button and make api call to start sync for all', async () => { - await waitForElement( - wrapper, - 'InventorySourceListItem', - el => el.length > 0 - ); const syncAllButton = wrapper.find('Button[aria-label="Sync all"]'); expect(syncAllButton.length).toBe(1); await act(async () => syncAllButton.prop('onClick')()); @@ -359,11 +349,7 @@ describe(' RBAC testing', () => { } ); }); - await waitForElement( - newWrapper, - 'InventorySourceList', - el => el.length > 0 - ); + newWrapper.update(); expect(newWrapper.find('ToolbarAddButton').length).toBe(0); jest.clearAllMocks(); }); @@ -398,11 +384,7 @@ describe(' RBAC testing', () => { } ); }); - await waitForElement( - newWrapper, - 'InventorySourceList', - el => el.length > 0 - ); + newWrapper.update(); expect(newWrapper.find('Button[aria-label="Sync All"]').length).toBe(0); 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 2ac1f21fc3..3ca06649a7 100644 --- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx @@ -1,23 +1,15 @@ import React from 'react'; - import { Link } from 'react-router-dom'; import { t } from '@lingui/macro'; -import { - Button, - DataListItem, - DataListItemRow, - DataListCheck, - DataListItemCells, - DataListCell, - DataListAction, - Tooltip, -} from '@patternfly/react-core'; +import { Button, Tooltip } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { ExclamationTriangleIcon as PFExclamationTriangleIcon, PencilAltIcon, } from '@patternfly/react-icons'; import styled from 'styled-components'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import StatusIcon from '../../../components/StatusIcon'; import InventorySourceSyncButton from '../shared/InventorySourceSyncButton'; @@ -30,9 +22,9 @@ function InventorySourceListItem({ source, isSelected, onSelect, - detailUrl, label, + rowIndex, }) { const generateLastJobTooltip = job => { return ( @@ -58,80 +50,67 @@ function InventorySourceListItem({ return ( <> - - - - - {source.summary_fields.last_job && ( - - - - - - )} - , - - - - {source.name} - - - {missingExecutionEnvironment && ( - - - - - - )} - , - - {label} - , - ]} - /> - - {source.summary_fields.user_capabilities.start && ( - - )} - {source.summary_fields.user_capabilities.edit && ( - - )} - - - + +
+ + )} + + + {source.summary_fields.last_job && ( + + + + + + )} + + {label} + + + + + + + + + ); } 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 6c0a9146a9..398f6febfb 100644 --- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.test.jsx @@ -26,15 +26,20 @@ describe('', () => { wrapper.unmount(); jest.clearAllMocks(); }); + test('should mount properly', () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); expect(wrapper.find('InventorySourceListItem').length).toBe(1); }); @@ -42,32 +47,35 @@ describe('', () => { test('all buttons and text fields should render properly', () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); expect(wrapper.find('StatusIcon').length).toBe(1); expect( wrapper .find('Link') - .at(0) + .at(1) .prop('to') ).toBe('/jobs/inventory/664'); - expect(wrapper.find('DataListCheck').length).toBe(1); - expect(); + expect(wrapper.find('.pf-c-table__check').length).toBe(1); expect( wrapper - .find('DataListCell') + .find('Td') .at(1) .text() ).toBe('Foo'); expect( wrapper - .find('DataListCell') - .at(2) + .find('Td') + .at(3) .text() ).toBe('Source Bar'); expect(wrapper.find('InventorySourceSyncButton').length).toBe(1); @@ -77,32 +85,46 @@ describe('', () => { test('item should be checked', () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); - expect(wrapper.find('DataListCheck').length).toBe(1); - expect(wrapper.find('DataListCheck').prop('checked')).toBe(true); + wrapper.update(); + expect(wrapper.find('.pf-c-table__check').length).toBe(1); + expect( + wrapper + .find('Td') + .first() + .prop('select').isSelected + ).toEqual(true); }); test('should not render status icon', () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); expect(wrapper.find('StatusIcon').length).toBe(0); }); @@ -110,14 +132,20 @@ describe('', () => { test('should not render sync buttons', async () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); expect(wrapper.find('InventorySourceSyncButton').length).toBe(0); expect(wrapper.find('Button[aria-label="Edit Source"]').length).toBe(1); @@ -126,15 +154,21 @@ describe('', () => { test('should not render edit buttons', async () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); expect(wrapper.find('Button[aria-label="Edit Source"]').length).toBe(0); expect(wrapper.find('InventorySourceSyncButton').length).toBe(1); @@ -143,16 +177,20 @@ describe('', () => { test('should render warning about missing execution environment', () => { const onSelect = jest.fn(); wrapper = mountWithContexts( - + + + + +
); expect( wrapper.find('.missing-execution-environment').prop('content') From 25a4a112b3e5c9cfcab59768128d643cf5ba179e Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Thu, 29 Apr 2021 16:21:56 -0700 Subject: [PATCH 05/11] remove unecessary i18n wrappers; fix HostToggle tooltip --- awx/ui_next/src/components/HostToggle/HostToggle.jsx | 6 +----- .../components/ResourceAccessList/ResourceAccessList.jsx | 1 - .../ResourceAccessList/ResourceAccessListItem.jsx | 1 - .../Inventory/InventoryGroups/InventoryGroupsList.jsx | 1 - .../screens/Inventory/InventoryHosts/InventoryHostList.jsx | 1 - 5 files changed, 1 insertion(+), 9 deletions(-) diff --git a/awx/ui_next/src/components/HostToggle/HostToggle.jsx b/awx/ui_next/src/components/HostToggle/HostToggle.jsx index 4212fa5bdc..4a7b03d55a 100644 --- a/awx/ui_next/src/components/HostToggle/HostToggle.jsx +++ b/awx/ui_next/src/components/HostToggle/HostToggle.jsx @@ -47,11 +47,7 @@ function HostToggle({ return ( - + Date: Thu, 29 Apr 2021 16:23:08 -0700 Subject: [PATCH 06/11] fix id string Co-authored-by: Marliana Lara --- .../screens/Inventory/InventoryGroups/InventoryGroupItem.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx index 8a0b95ae2f..bcc2cc3d8f 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryGroups/InventoryGroupItem.jsx @@ -22,7 +22,7 @@ function InventoryGroupItem({ const editUrl = `/inventories/inventory/${inventoryId}/groups/${group.id}/edit`; return ( - + Date: Thu, 29 Apr 2021 16:23:23 -0700 Subject: [PATCH 07/11] delete comment Co-authored-by: Marliana Lara --- .../screens/Inventory/InventoryHosts/InventoryHostList.test.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx index 3dc9292200..c1e6d14442 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHosts/InventoryHostList.test.jsx @@ -209,7 +209,6 @@ describe('', () => { test('delete button is disabled if user does not have delete capabilities on a selected host', async () => { await act(async () => { - // wrapper.find('DataListCheck[id="select-host-3"]').invoke('onChange')(); wrapper .find('.pf-c-table__check') .at(2) From e2c8519b777991f1ec20d1df2f5b2727748b3349 Mon Sep 17 00:00:00 2001 From: "Keith J. Grant" Date: Mon, 3 May 2021 16:00:09 -0700 Subject: [PATCH 08/11] convert InventoryHostGroups list to table --- .../InventoryHostGroupItem.jsx | 87 ++++++++----------- .../InventoryHostGroupItem.test.jsx | 32 ++++--- .../InventoryHostGroupsList.jsx | 25 +++--- .../InventoryHostGroupsList.test.jsx | 47 ++++++---- 4 files changed, 104 insertions(+), 87 deletions(-) diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.jsx index b87ab23da0..58b24b7dac 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.jsx @@ -1,66 +1,55 @@ import React from 'react'; import { bool, func, number, oneOfType, string } from 'prop-types'; - import { t } from '@lingui/macro'; - -import { - Button, - DataListAction, - DataListCheck, - DataListItem, - DataListItemCells, - DataListItemRow, - Tooltip, -} from '@patternfly/react-core'; +import { Button } from '@patternfly/react-core'; +import { Tr, Td } from '@patternfly/react-table'; import { Link } from 'react-router-dom'; import { PencilAltIcon } from '@patternfly/react-icons'; -import DataListCell from '../../../components/DataListCell'; +import { ActionsTd, ActionItem } from '../../../components/PaginatedTable'; import { Group } from '../../../types'; -function InventoryHostGroupItem({ group, inventoryId, isSelected, onSelect }) { +function InventoryHostGroupItem({ + group, + inventoryId, + isSelected, + onSelect, + rowIndex, +}) { const labelId = `check-action-${group.id}`; const detailUrl = `/inventories/inventory/${inventoryId}/groups/${group.id}/details`; const editUrl = `/inventories/inventory/${inventoryId}/groups/${group.id}/edit`; return ( - - - - - - {group.name} - - , - ]} - /> - + + + + {group.name} + + + + - {group.summary_fields.user_capabilities.edit && ( - - - - )} - - - + + + + ); } diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.test.jsx index 6092de88c7..2da25d1cde 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupItem.test.jsx @@ -18,12 +18,16 @@ describe('', () => { beforeEach(() => { wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); }); @@ -40,12 +44,16 @@ describe('', () => { copyMockGroup.summary_fields.user_capabilities.edit = false; wrapper = mountWithContexts( - {}} - /> + + + {}} + /> + +
); expect(wrapper.find('PencilAltIcon').exists()).toBeFalsy(); }); diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx index 4454be3417..49868207c5 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.jsx @@ -12,9 +12,11 @@ import { HostsAPI, InventoriesAPI } from '../../../api'; import DataListToolbar from '../../../components/DataListToolbar'; import AlertModal from '../../../components/AlertModal'; import ErrorDetail from '../../../components/ErrorDetail'; -import PaginatedDataList, { - ToolbarAddButton, -} from '../../../components/PaginatedDataList'; +import PaginatedTable, { + HeaderRow, + HeaderCell, +} from '../../../components/PaginatedTable'; +import { ToolbarAddButton } from '../../../components/PaginatedDataList'; import AssociateModal from '../../../components/AssociateModal'; import DisassociateButton from '../../../components/DisassociateButton'; import AdHocCommands from '../../../components/AdHocCommands/AdHocCommands'; @@ -146,7 +148,7 @@ function InventoryHostGroupsList() { return ( <> - ( + headerRow={ + + {t`Name`} + {t`Actions`} + + } + renderRow={(item, index) => ( row.id === item.id)} onSelect={() => handleSelect(item)} + rowIndex={index} /> )} renderToolbar={props => ( diff --git a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.test.jsx b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.test.jsx index 493e9dc65a..043618c46a 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.test.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryHostGroups/InventoryHostGroupsList.test.jsx @@ -114,46 +114,63 @@ describe('', () => { test('should check and uncheck the row item', async () => { expect( - wrapper.find('DataListCheck[id="select-group-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); await act(async () => { - wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')( - true - ); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(true); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-group-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(true); await act(async () => { - wrapper.find('DataListCheck[id="select-group-1"]').invoke('onChange')( - false - ); + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .invoke('onChange')(false); }); wrapper.update(); expect( - wrapper.find('DataListCheck[id="select-group-1"]').props().checked + wrapper + .find('.pf-c-table__check') + .first() + .find('input') + .props().checked ).toBe(false); }); test('should check all row items when select all is checked', async () => { - wrapper.find('DataListCheck').forEach(el => { + expect.assertions(9); + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(false); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(true); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(false); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(false); }); }); @@ -230,15 +247,15 @@ describe('', () => { test('expected api calls are made for multi-disassociation', async () => { expect(HostsAPI.disassociateGroup).toHaveBeenCalledTimes(0); expect(HostsAPI.readAllGroups).toHaveBeenCalledTimes(1); - expect(wrapper.find('DataListCheck').length).toBe(3); - wrapper.find('DataListCheck').forEach(el => { + expect(wrapper.find('.pf-c-table__check').length).toBe(3); + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(false); }); await act(async () => { wrapper.find('Checkbox#select-all').invoke('onChange')(true); }); wrapper.update(); - wrapper.find('DataListCheck').forEach(el => { + wrapper.find('.pf-c-table__check input').forEach(el => { expect(el.props().checked).toBe(true); }); wrapper.find('button[aria-label="Disassociate"]').simulate('click'); From 510a546d8cc84604bceb9510353383d71db0cd41 Mon Sep 17 00:00:00 2001 From: Tiago Date: Tue, 4 May 2021 10:57:10 -0300 Subject: [PATCH 09/11] add testability --- awx/ui_next/src/screens/Host/HostList/HostListItem.jsx | 1 + .../src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx | 1 + 2 files changed, 2 insertions(+) diff --git a/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx b/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx index 01bd808761..071b88be45 100644 --- a/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx +++ b/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx @@ -17,6 +17,7 @@ function HostListItem({ host, isSelected, onSelect, detailUrl, rowIndex }) { return ( Date: Tue, 4 May 2021 14:00:24 -0300 Subject: [PATCH 10/11] add testability --- .../Inventory/InventorySources/InventorySourceListItem.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx index 3ca06649a7..070ef935ea 100644 --- a/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventorySources/InventorySourceListItem.jsx @@ -52,6 +52,7 @@ function InventorySourceListItem({ <> Date: Tue, 4 May 2021 14:26:55 -0300 Subject: [PATCH 11/11] change testability locator --- awx/ui_next/src/screens/Host/HostList/HostListItem.jsx | 2 +- .../screens/Inventory/InventoryGroups/InventoryGroupItem.jsx | 1 + .../Inventory/InventoryHostGroups/InventoryHostGroupItem.jsx | 1 + .../src/screens/Inventory/InventoryHosts/InventoryHostItem.jsx | 2 +- .../Inventory/InventorySources/InventorySourceListItem.jsx | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx b/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx index 071b88be45..34bbc518a6 100644 --- a/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx +++ b/awx/ui_next/src/screens/Host/HostList/HostListItem.jsx @@ -17,7 +17,7 @@ function HostListItem({ host, isSelected, onSelect, detailUrl, rowIndex }) { return (