From 37b22945df8005869885408d8783d24bd41ac0ba Mon Sep 17 00:00:00 2001
From: lsprr <16653744+lsprr@users.noreply.github.com>
Date: Thu, 29 Feb 2024 19:27:17 -0500
Subject: [PATCH 1/3] test: switch table tests to react testing library
---
.../__tests__/src/components/Table/index.js | 278 ----------------
.../react/src/components/Table/index.test.tsx | 314 ++++++++++++++++++
2 files changed, 314 insertions(+), 278 deletions(-)
delete mode 100644 packages/react/__tests__/src/components/Table/index.js
create mode 100644 packages/react/src/components/Table/index.test.tsx
diff --git a/packages/react/__tests__/src/components/Table/index.js b/packages/react/__tests__/src/components/Table/index.js
deleted file mode 100644
index 233cf0a85..000000000
--- a/packages/react/__tests__/src/components/Table/index.js
+++ /dev/null
@@ -1,278 +0,0 @@
-import React from 'react';
-import { shallow, mount } from 'enzyme';
-import { spy } from 'sinon';
-import Table, {
- TableBody,
- TableCell,
- TableHead,
- TableHeader,
- TableRow,
- TableFooter
-} from '../../../../src/components/Table';
-import axe from '../../../axe';
-
-const renderDefaultTable = () =>
- mount(
-
-
-
-
- A
-
-
- B
-
-
-
-
-
-
- 1
-
- 2
-
-
-
-
-
- foo
-
- bar
-
-
-
- );
-
-describe('Table components', () => {
- test('render children', () => {
- const table = renderDefaultTable();
- const tableHead = table.find('TableHead');
- const tableRow = table.find('TableRow').at(0);
- const tableHeader = table.find('TableHeader').at(0);
- const tableBody = table.find('TableBody');
- const tableCell = table.find('TableCell').at(0);
- const tableFooter = table.find('TableFooter').at(0);
-
- const tableItems = [
- table,
- tableHead,
- tableRow,
- tableHeader,
- tableBody,
- tableCell,
- tableFooter
- ];
-
- tableItems.forEach(wrapper => {
- expect(!!wrapper.children().length).toBe(true);
- });
- });
-
- test('passes classNames through', () => {
- const table = renderDefaultTable();
- const tableHead = table.find('TableHead');
- const tableRow = table.find('TableRow').at(0);
- const tableHeader = table.find('TableHeader').at(0);
- const tableBody = table.find('TableBody');
- const tableCell = table.find('TableCell').at(0);
- const tableFooter = table.find('TableFooter').at(0);
-
- expect(table.is('.my-table')).toBe(true);
- expect(tableHead.is('.my-table-head')).toBe(true);
- expect(tableRow.is('.my-table-row')).toBe(true);
- expect(tableHeader.is('.my-table-header')).toBe(true);
- expect(tableBody.is('.my-table-body')).toBe(true);
- expect(tableCell.is('.my-table-cell')).toBe(true);
- expect(tableFooter.is('.my-table-footer')).toBe(true);
- });
-
- test('passes arbitrary props through', () => {
- const table = renderDefaultTable();
- const tableHead = table.find('TableHead');
- const tableRow = table.find('TableRow').at(0);
- const tableHeader = table.find('TableHeader').at(0);
- const tableBody = table.find('TableBody');
- const tableCell = table.find('TableCell').at(0);
- const tableFooter = table.find('TableFooter').at(0);
-
- const tableItems = [
- table,
- tableHead,
- tableRow,
- tableHeader,
- tableBody,
- tableCell,
- tableFooter
- ];
-
- tableItems.forEach(wrapper => {
- expect(wrapper.is('[data-foo="true"]')).toBe(true);
- });
-
- expect(tableHeader.is('[scope="col"]')).toBe(true);
- });
-
- test('renders the expected semantic HTML elements', () => {
- const table = shallow();
- const body = shallow(a);
- const cell = shallow(a);
- const head = shallow(a);
- const header = shallow(a);
- const row = shallow(a);
- const footer = shallow(a);
-
- expect(table.is('table')).toBe(true);
- expect(body.is('tbody')).toBe(true);
- expect(cell.is('td')).toBe(true);
- expect(head.is('thead')).toBe(true);
- expect(header.is('th')).toBe(true);
- expect(footer.is('tfoot')).toBe(true);
- expect(row.is('tr')).toBe(true);
- });
-
- test('renders border variant', () => {
- const wrapper = mount(
-
-
-
- Header
-
-
-
-
- Cell
-
-
-
- );
-
- expect(wrapper.find('.Table--border').exists()).toBe(true);
- });
-
- describe('Sortable Table', () => {
- test('renders sort button and icons when passing in sortDirection and onSort in TableHeader', () => {
- const wrapper = mount(
-
-
-
- null}>
- Sortable Header
-
-
-
-
- );
-
- expect(wrapper.find('button').exists()).toBe(true);
- expect(wrapper.find('.Icon--sort-triangle').exists()).toBe(true);
- expect(wrapper.find('Offscreen').text()).toBe('');
- });
-
- test('render className TableHeader--sorting when a TableHeader is actively sorting', () => {
- const wrapper = mount(
-
-
-
- null}>
- Sortable Header
-
-
-
-
- );
-
- expect(wrapper.find('.TableHeader--sort-ascending').exists()).toBe(true);
- });
-
- test('renders triangle up Icon and ascending message when sortDirection is ascending', () => {
- const wrapper = mount(
-
-
-
- null}
- >
- Sortable Header
-
-
-
-
- );
-
- expect(wrapper.find('Offscreen').text()).toBe('up and away');
- expect(wrapper.find('.Icon--table-sort-ascending').exists()).toBe(true);
- });
-
- test('renders triangle down Icon and descending message when sortDirection is descending', () => {
- const wrapper = mount(
-
-
-
- null}
- >
- Sortable Header
-
-
-
-
- );
-
- expect(wrapper.find('Offscreen').text()).toBe('down below');
- expect(wrapper.find('.Icon--table-sort-descending').exists()).toBe(true);
- });
-
- test('calls onSort when sort button is clicked', () => {
- const onSortSpy = spy();
- const wrapper = mount(
-
-
-
-
- Sortable Header
-
-
-
-
- );
-
- wrapper.find('button').simulate('click');
-
- expect(onSortSpy.calledOnce).toBe(true);
- });
-
- test('focus stays on the sort button after it is clicked', () => {
- const wrapper = mount(
-
-
-
- null}
- >
- Sortable Header
-
-
-
-
- );
-
- wrapper.find('button').simulate('click');
- wrapper.update();
-
- expect(document.activeElement.id).toBe(
- wrapper.find('button').getDOMNode().id
- );
- });
- });
-});
-
-test('returns 0 axe violations', async () => {
- const table = renderDefaultTable();
- expect(await axe(table.html())).toHaveNoViolations();
-});
diff --git a/packages/react/src/components/Table/index.test.tsx b/packages/react/src/components/Table/index.test.tsx
new file mode 100644
index 000000000..64953dd59
--- /dev/null
+++ b/packages/react/src/components/Table/index.test.tsx
@@ -0,0 +1,314 @@
+import React from 'react';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import Table, {
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+ TableFooter
+} from './';
+import axe from '../../axe';
+
+const renderDefaultTable = () => {
+ return render(
+
+
+
+
+ A
+
+
+ B
+
+
+
+
+
+
+ 1
+
+ 2
+
+
+
+
+
+ foo
+
+ bar
+
+
+
+ );
+};
+
+test('should render table and its components correctly', () => {
+ renderDefaultTable();
+
+ const table = screen.getByRole('table');
+ const tableHead = screen.getByTestId('thead');
+ const tableBody = screen.getByTestId('tbody');
+ const tableRows = screen.getAllByRole('row');
+ const tableHeaders = screen.getAllByRole('columnheader');
+ const tableCells = screen.getAllByRole('cell');
+ const tableFooter = screen.getByTestId('tfoot');
+
+ // Accessing the first elements of each type for more detailed assertions
+ const firstTableRow = tableRows[0];
+ const firstTableHeader = tableHeaders[0];
+ const firstTableCell = tableCells[0];
+
+ // Assertions on the lengths of various table components
+ expect(tableRows).toHaveLength(3); // Assuming there are three rows
+ expect(tableHeaders).toHaveLength(2); // Assuming there are two column headers
+ expect(tableCells).toHaveLength(4); // Assuming there are four cells
+
+ const tableItems = [
+ table,
+ tableHead,
+ tableBody,
+ firstTableRow,
+ firstTableHeader,
+ firstTableCell,
+ tableFooter
+ ];
+
+ tableItems.forEach((element) => {
+ expect(element).toBeInTheDocument();
+ });
+});
+
+test('should pass classNames through', () => {
+ renderDefaultTable();
+
+ const table = screen.getByRole('table');
+ const tableHead = screen.getByTestId('thead');
+ const tableBody = screen.getByTestId('tbody');
+ const tableRows = screen.getAllByRole('row');
+ const tableHeaders = screen.getAllByRole('columnheader');
+ const tableCells = screen.getAllByRole('cell');
+ const tableFooter = screen.getByTestId('tfoot');
+
+ const firstTableRow = tableRows[0];
+ const firstTableHeader = tableHeaders[0];
+ const firstTableCell = tableCells[0];
+
+ expect(table).toHaveClass('my-table');
+ expect(tableHead).toHaveClass('my-table-head');
+ expect(tableBody).toHaveClass('my-table-body');
+ expect(firstTableRow).toHaveClass('my-table-row');
+ expect(firstTableHeader).toHaveClass('my-table-header');
+ expect(firstTableCell).toHaveClass('my-table-cell');
+ expect(tableFooter).toHaveClass('my-table-footer');
+});
+
+test('should pass arbitrary props through', () => {
+ renderDefaultTable();
+
+ const table = screen.getByRole('table');
+ const tableHead = screen.getByTestId('thead');
+ const tableBody = screen.getByTestId('tbody');
+ const tableRows = screen.getAllByRole('row');
+ const tableHeaders = screen.getAllByRole('columnheader');
+ const tableCells = screen.getAllByRole('cell');
+ const tableFooter = screen.getByTestId('tfoot');
+
+ const firstTableRow = tableRows[0];
+ const firstTableHeader = tableHeaders[0];
+ const firstTableCell = tableCells[0];
+
+ const tableItems = [
+ table,
+ tableHead,
+ tableBody,
+ firstTableRow,
+ firstTableHeader,
+ firstTableCell,
+ tableFooter
+ ];
+
+ tableItems.forEach((element) => {
+ expect(element).toHaveAttribute('data-foo', 'true');
+ });
+
+ expect(firstTableHeader).toHaveAttribute('scope', 'col');
+});
+
+test('should render the expected semantic HTML elements', () => {
+ renderDefaultTable();
+
+ const table = screen.getByRole('table');
+ const tableHead = screen.getByTestId('thead');
+ const tableBody = screen.getByTestId('tbody');
+ const tableRows = screen.getAllByRole('row');
+ const tableHeaders = screen.getAllByRole('columnheader');
+ const tableCells = screen.getAllByRole('cell');
+ const tableFooter = screen.getByTestId('tfoot');
+
+ const firstTableRow = tableRows[0];
+ const firstTableHeader = tableHeaders[0];
+ const firstTableCell = tableCells[0];
+
+ expect(table.tagName).toBe('TABLE');
+ expect(tableHead.tagName).toBe('THEAD');
+ expect(tableBody.tagName).toBe('TBODY');
+ expect(firstTableRow.tagName).toBe('TR');
+ expect(firstTableHeader.tagName).toBe('TH');
+ expect(firstTableCell.tagName).toBe('TD');
+ expect(tableFooter.tagName).toBe('TFOOT');
+});
+
+test('should render with border variant', () => {
+ render(
+
+
+
+ Header
+
+
+
+
+ Cell
+
+
+
+ );
+
+ expect(screen.getByRole('table')).toHaveClass('Table--border');
+});
+
+test('should render sort button and icons with sortDirection and onSort in Table', () => {
+ render(
+
+
+
+ null}>
+ Sortable Header
+
+
+
+
+ );
+
+ expect(screen.getByRole('button')).toBeInTheDocument();
+ expect(screen.getByRole('status').closest('.Icon--sort-triangle'));
+ expect(screen.getByRole('status')).toHaveTextContent('');
+});
+
+test('should render className "TableHeader--sorting" when actively sorting', () => {
+ render(
+
+
+
+ null}>
+ Sortable Header
+
+
+
+
+ );
+
+ expect(screen.getByRole('status').closest('.TableHeader--sort-ascending'));
+});
+
+test('should render triangle up Icon and ascending message when sortDirection is ascending', () => {
+ render(
+
+
+
+ null}
+ >
+ Sortable Header
+
+
+
+
+ );
+
+ expect(screen.getByText('up and away')).toBeInTheDocument();
+ expect(screen.getByRole('status').closest('.Icon--table-sort-ascending'));
+});
+
+test('should render triangle down Icon and descending message when sortDirection is descending', () => {
+ render(
+
+
+
+ null}
+ >
+ Sortable Header
+
+
+
+
+ );
+
+ expect(screen.getByText('down below')).toBeInTheDocument();
+ expect(screen.getByRole('status').closest('.Icon--table-sort-descending'));
+});
+
+test('should call onSort when sort button is clicked', () => {
+ const onSortMock = jest.fn();
+
+ render(
+
+
+
+
+ Sortable Header
+
+
+
+
+ );
+
+ const button = screen.getByRole('button');
+
+ userEvent.click(button);
+ waitFor(() => {
+ expect(onSortMock).toHaveBeenCalledTimes(1);
+ });
+});
+
+test('should maintain focus on the sort button after it is clicked', () => {
+ render(
+
+
+
+ null}
+ >
+ Sortable Header
+
+
+
+
+ );
+
+ const button = screen.getByRole('button');
+
+ userEvent.click(button);
+ waitFor(() => {
+ expect(button).toHaveFocus();
+ });
+});
+
+test('returns 0 axe violations', async () => {
+ const { container } = renderDefaultTable();
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+});
From f727cd58f6eefcae2f65b575860fca9aa79853dc Mon Sep 17 00:00:00 2001
From: lsprr <16653744+lsprr@users.noreply.github.com>
Date: Fri, 1 Mar 2024 23:34:04 -0500
Subject: [PATCH 2/3] Minor corrections
---
packages/react/src/components/Table/index.test.tsx | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/packages/react/src/components/Table/index.test.tsx b/packages/react/src/components/Table/index.test.tsx
index 64953dd59..c0171fc93 100644
--- a/packages/react/src/components/Table/index.test.tsx
+++ b/packages/react/src/components/Table/index.test.tsx
@@ -259,7 +259,7 @@ test('should render triangle down Icon and descending message when sortDirection
expect(screen.getByRole('status').closest('.Icon--table-sort-descending'));
});
-test('should call onSort when sort button is clicked', () => {
+test('should call onSort when sort button is clicked', async () => {
const onSortMock = jest.fn();
render(
@@ -276,13 +276,13 @@ test('should call onSort when sort button is clicked', () => {
const button = screen.getByRole('button');
- userEvent.click(button);
- waitFor(() => {
+ await userEvent.click(button);
+ await waitFor(() => {
expect(onSortMock).toHaveBeenCalledTimes(1);
});
});
-test('should maintain focus on the sort button after it is clicked', () => {
+test('should maintain focus on the sort button after it is clicked', async () => {
render(
@@ -301,8 +301,8 @@ test('should maintain focus on the sort button after it is clicked', () => {
const button = screen.getByRole('button');
- userEvent.click(button);
- waitFor(() => {
+ await userEvent.click(button);
+ await waitFor(() => {
expect(button).toHaveFocus();
});
});
From 0a725fee15cacd8e8cfe12d3c15cb10f126e6bba Mon Sep 17 00:00:00 2001
From: lsprr <16653744+lsprr@users.noreply.github.com>
Date: Mon, 18 Mar 2024 16:10:15 -0400
Subject: [PATCH 3/3] Refactor and fix issues based on feedback
---
.../react/src/components/Table/index.test.tsx | 77 ++++++++++++++++---
1 file changed, 66 insertions(+), 11 deletions(-)
diff --git a/packages/react/src/components/Table/index.test.tsx b/packages/react/src/components/Table/index.test.tsx
index c0171fc93..752a8c499 100644
--- a/packages/react/src/components/Table/index.test.tsx
+++ b/packages/react/src/components/Table/index.test.tsx
@@ -99,13 +99,13 @@ test('should pass classNames through', () => {
const firstTableHeader = tableHeaders[0];
const firstTableCell = tableCells[0];
- expect(table).toHaveClass('my-table');
- expect(tableHead).toHaveClass('my-table-head');
- expect(tableBody).toHaveClass('my-table-body');
- expect(firstTableRow).toHaveClass('my-table-row');
- expect(firstTableHeader).toHaveClass('my-table-header');
- expect(firstTableCell).toHaveClass('my-table-cell');
- expect(tableFooter).toHaveClass('my-table-footer');
+ expect(table).toHaveClass('Table', 'my-table');
+ expect(tableHead).toHaveClass('TableHead', 'my-table-head');
+ expect(tableBody).toHaveClass('TableBody', 'my-table-body');
+ expect(firstTableRow).toHaveClass('TableRow', 'my-table-row');
+ expect(firstTableHeader).toHaveClass('TableHeader', 'my-table-header');
+ expect(firstTableCell).toHaveClass('TableCell', 'my-table-cell');
+ expect(tableFooter).toHaveClass('TableFooter', 'my-table-footer');
});
test('should pass arbitrary props through', () => {
@@ -180,7 +180,7 @@ test('should render with border variant', () => {
);
- expect(screen.getByRole('table')).toHaveClass('Table--border');
+ expect(screen.getByRole('table')).toHaveClass('Table', 'Table--border');
});
test('should render sort button and icons with sortDirection and onSort in Table', () => {
@@ -214,7 +214,14 @@ test('should render className "TableHeader--sorting" when actively sorting', ()
);
- expect(screen.getByRole('status').closest('.TableHeader--sort-ascending'));
+ expect(screen.getByRole('columnheader')).toHaveAttribute(
+ 'aria-sort',
+ 'ascending'
+ );
+ expect(screen.getByRole('columnheader')).toHaveClass(
+ 'TableHeader',
+ 'TableHeader--sort-ascending'
+ );
});
test('should render triangle up Icon and ascending message when sortDirection is ascending', () => {
@@ -234,7 +241,7 @@ test('should render triangle up Icon and ascending message when sortDirection is
);
- expect(screen.getByText('up and away')).toBeInTheDocument();
+ expect(screen.getByRole('status')).toHaveTextContent('up and away');
expect(screen.getByRole('status').closest('.Icon--table-sort-ascending'));
});
@@ -255,7 +262,7 @@ test('should render triangle down Icon and descending message when sortDirection
);
- expect(screen.getByText('down below')).toBeInTheDocument();
+ expect(screen.getByRole('status')).toHaveTextContent('down below');
expect(screen.getByRole('status').closest('.Icon--table-sort-descending'));
});
@@ -312,3 +319,51 @@ test('returns 0 axe violations', async () => {
const results = await axe(container);
expect(results).toHaveNoViolations();
});
+
+test('returns 0 axe violations without any sorting', async () => {
+ const { container } = render(
+
+
+
+ null}>
+ Sortable Header
+
+
+
+
+ );
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+});
+
+test('returns 0 axe violations with ascending sorting', async () => {
+ const { container } = render(
+
+
+
+ null}>
+ Sortable Header
+
+
+
+
+ );
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+});
+
+test('returns 0 axe violations with descending sorting', async () => {
+ const { container } = render(
+
+
+
+ null}>
+ Sortable Header
+
+
+
+
+ );
+ const results = await axe(container);
+ expect(results).toHaveNoViolations();
+});