import React from 'react'; import { render, WithRoute } from 'lib/testHelpers'; import Table, { TableProps, TimestampCell, SizeCell, LinkCell, TagCell, } from 'components/common/NewTable'; import { screen, waitFor } from '@testing-library/dom'; import { ColumnDef, Row } from '@tanstack/react-table'; import userEvent from '@testing-library/user-event'; import { formatTimestamp } from 'lib/dateTimeHelpers'; import { act } from '@testing-library/react'; import { ConnectorState, ConsumerGroupState } from 'generated-sources'; const mockedUsedNavigate = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: () => mockedUsedNavigate, })); type Datum = typeof data[0]; const data = [ { timestamp: 1660034383725, text: 'lorem', selectable: false, size: 1234, tag: ConnectorState.RUNNING, }, { timestamp: 1660034399999, text: 'ipsum', selectable: true, size: 3, tag: ConnectorState.FAILED, }, { timestamp: 1660034399922, text: 'dolor', selectable: true, size: 50000, tag: ConsumerGroupState.EMPTY, }, { timestamp: 1660034199922, text: 'sit', selectable: false, size: 1_312_323, tag: 'some_string', }, ]; const columns: ColumnDef[] = [ { header: 'DateTime', accessorKey: 'timestamp', cell: TimestampCell, }, { header: 'Text', accessorKey: 'text', cell: LinkCell, }, { header: 'Size', accessorKey: 'size', cell: SizeCell, }, { header: 'Tag', accessorKey: 'tag', cell: TagCell, }, ]; const ExpandedRow: React.FC = () =>
I am expanded row
; interface Props extends TableProps { path?: string; } const renderComponent = (props: Partial = {}) => { render( , { initialEntries: [props.path || ''] } ); }; describe('Table', () => { it('renders table', () => { renderComponent(); expect(screen.getByRole('table')).toBeInTheDocument(); expect(screen.getAllByRole('row').length).toEqual(data.length + 1); }); it('renders empty table', () => { renderComponent({ data: [] }); expect(screen.getByRole('table')).toBeInTheDocument(); expect(screen.getAllByRole('row').length).toEqual(2); expect(screen.getByText('No rows found')).toBeInTheDocument(); }); it('renders empty table with custom message', () => { const emptyMessage = 'Super custom message'; renderComponent({ data: [], emptyMessage }); expect(screen.getByRole('table')).toBeInTheDocument(); expect(screen.getAllByRole('row').length).toEqual(2); expect(screen.getByText(emptyMessage)).toBeInTheDocument(); }); it('renders SizeCell', () => { renderComponent(); expect(screen.getByText('1KB')).toBeInTheDocument(); expect(screen.getByText('3Bytes')).toBeInTheDocument(); expect(screen.getByText('49KB')).toBeInTheDocument(); expect(screen.getByText('1MB')).toBeInTheDocument(); }); it('renders TimestampCell', () => { renderComponent(); expect( screen.getByText(formatTimestamp(data[0].timestamp)) ).toBeInTheDocument(); }); describe('LinkCell', () => { it('renders link', () => { renderComponent(); expect(screen.getByRole('link', { name: 'lorem' })).toBeInTheDocument(); }); it('link click stops propagation', () => { const onRowClick = jest.fn(); renderComponent({ onRowClick }); const link = screen.getByRole('link', { name: 'lorem' }); userEvent.click(link); expect(onRowClick).not.toHaveBeenCalled(); }); }); describe('ExpanderCell', () => { it('renders button', () => { renderComponent({ getRowCanExpand: () => true }); const btns = screen.getAllByRole('button', { name: 'Expand row' }); expect(btns.length).toEqual(data.length); expect(screen.queryByText('I am expanded row')).not.toBeInTheDocument(); userEvent.click(btns[2]); expect(screen.getByText('I am expanded row')).toBeInTheDocument(); userEvent.click(btns[0]); expect(screen.getAllByText('I am expanded row').length).toEqual(2); }); it('does not render button', () => { renderComponent({ getRowCanExpand: () => false }); expect( screen.queryByRole('button', { name: 'Expand row' }) ).not.toBeInTheDocument(); expect(screen.queryByText('I am expanded row')).not.toBeInTheDocument(); }); }); it('renders TagCell', () => { renderComponent(); expect(screen.getByText(data[0].tag)).toBeInTheDocument(); expect(screen.getByText(data[1].tag)).toBeInTheDocument(); expect(screen.getByText(data[2].tag)).toBeInTheDocument(); expect(screen.getByText(data[3].tag)).toBeInTheDocument(); }); describe('Pagination', () => { it('does not render page buttons', () => { renderComponent(); expect( screen.queryByRole('button', { name: 'Next' }) ).not.toBeInTheDocument(); }); it('renders page buttons', async () => { renderComponent({ path: '?perPage=1' }); // Check it renders header row and only one data row expect(screen.getAllByRole('row').length).toEqual(2); expect(screen.getByText('lorem')).toBeInTheDocument(); // Check it renders page buttons const firstBtn = screen.getByRole('button', { name: '⇤' }); const prevBtn = screen.getByRole('button', { name: '← Previous' }); const nextBtn = screen.getByRole('button', { name: 'Next →' }); const lastBtn = screen.getByRole('button', { name: '⇥' }); expect(firstBtn).toBeInTheDocument(); expect(firstBtn).toBeDisabled(); expect(prevBtn).toBeInTheDocument(); expect(prevBtn).toBeDisabled(); expect(nextBtn).toBeInTheDocument(); expect(nextBtn).toBeEnabled(); expect(lastBtn).toBeInTheDocument(); expect(lastBtn).toBeEnabled(); userEvent.click(nextBtn); expect(screen.getByText('ipsum')).toBeInTheDocument(); expect(prevBtn).toBeEnabled(); expect(firstBtn).toBeEnabled(); userEvent.click(lastBtn); expect(screen.getByText('sit')).toBeInTheDocument(); expect(lastBtn).toBeDisabled(); expect(nextBtn).toBeDisabled(); userEvent.click(prevBtn); expect(screen.getByText('dolor')).toBeInTheDocument(); userEvent.click(firstBtn); expect(screen.getByText('lorem')).toBeInTheDocument(); }); it('renders go to page input', async () => { renderComponent({ path: '?perPage=1' }); // Check it renders header row and only one data row expect(screen.getAllByRole('row').length).toEqual(2); expect(screen.getByText('lorem')).toBeInTheDocument(); const input = screen.getByRole('spinbutton', { name: 'Go to page:' }); expect(input).toBeInTheDocument(); userEvent.clear(input); userEvent.type(input, '2'); expect(screen.getByText('ipsum')).toBeInTheDocument(); }); }); describe('Sorting', () => { it('sort rows', async () => { await act(() => renderComponent({ path: '/?sortBy=text&&sortDirection=desc', enableSorting: true, }) ); expect(screen.getAllByRole('row').length).toEqual(data.length + 1); const th = screen.getByRole('columnheader', { name: 'Text' }); expect(th).toBeInTheDocument(); let rows = []; // Check initial sort order by text column is descending rows = screen.getAllByRole('row'); expect(rows[4].textContent?.indexOf('dolor')).toBeGreaterThan(-1); expect(rows[3].textContent?.indexOf('ipsum')).toBeGreaterThan(-1); expect(rows[2].textContent?.indexOf('lorem')).toBeGreaterThan(-1); expect(rows[1].textContent?.indexOf('sit')).toBeGreaterThan(-1); // Disable sorting by text column await waitFor(() => userEvent.click(th)); rows = screen.getAllByRole('row'); expect(rows[1].textContent?.indexOf('lorem')).toBeGreaterThan(-1); expect(rows[2].textContent?.indexOf('ipsum')).toBeGreaterThan(-1); expect(rows[3].textContent?.indexOf('dolor')).toBeGreaterThan(-1); expect(rows[4].textContent?.indexOf('sit')).toBeGreaterThan(-1); // Sort by text column ascending await waitFor(() => userEvent.click(th)); rows = screen.getAllByRole('row'); expect(rows[1].textContent?.indexOf('dolor')).toBeGreaterThan(-1); expect(rows[2].textContent?.indexOf('ipsum')).toBeGreaterThan(-1); expect(rows[3].textContent?.indexOf('lorem')).toBeGreaterThan(-1); expect(rows[4].textContent?.indexOf('sit')).toBeGreaterThan(-1); }); }); describe('Row Selecting', () => { beforeEach(() => { renderComponent({ enableRowSelection: (row: Row) => row.original.selectable, batchActionsBar: () =>
I am Action Bar
, }); }); it('renders selectable rows', () => { expect(screen.getAllByRole('row').length).toEqual(data.length + 1); const checkboxes = screen.getAllByRole('checkbox'); expect(checkboxes.length).toEqual(data.length + 1); expect(checkboxes[1]).toBeDisabled(); expect(checkboxes[2]).toBeEnabled(); expect(checkboxes[3]).toBeEnabled(); expect(checkboxes[4]).toBeDisabled(); }); it('renders action bar', () => { expect(screen.getAllByRole('row').length).toEqual(data.length + 1); expect(screen.queryByText('I am Action Bar')).not.toBeInTheDocument(); const checkboxes = screen.getAllByRole('checkbox'); expect(checkboxes.length).toEqual(data.length + 1); userEvent.click(checkboxes[2]); expect(screen.getByText('I am Action Bar')).toBeInTheDocument(); }); }); describe('Clickable Row', () => { const onRowClick = jest.fn(); it('handles onRowClick', () => { renderComponent({ onRowClick }); const rows = screen.getAllByRole('row'); expect(rows.length).toEqual(data.length + 1); userEvent.click(rows[1]); expect(onRowClick).toHaveBeenCalledTimes(1); }); it('does nothing unless onRowClick is provided', () => { renderComponent(); const rows = screen.getAllByRole('row'); expect(rows.length).toEqual(data.length + 1); userEvent.click(rows[1]); }); it('does not handle onRowClick if enableRowSelection', () => { renderComponent({ onRowClick, enableRowSelection: true }); const rows = screen.getAllByRole('row'); expect(rows.length).toEqual(data.length + 1); userEvent.click(rows[1]); expect(onRowClick).not.toHaveBeenCalled(); }); it('does not handle onRowClick if expandable rows', () => { renderComponent({ onRowClick, getRowCanExpand: () => true }); const rows = screen.getAllByRole('row'); expect(rows.length).toEqual(data.length + 1); userEvent.click(rows[1]); expect(onRowClick).not.toHaveBeenCalled(); }); }); });