Skip to content

Commit 6f95550

Browse files
committed
feat: add unit and component tests for sorting
1 parent c6a8cc5 commit 6f95550

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/* eslint-disable no-nested-ternary */
2+
import React from 'react';
3+
import { useDataViewSort } from '@patternfly/react-data-view/dist/dynamic/Hooks';
4+
import { DataViewTable, DataViewTr, DataViewTh } from '@patternfly/react-data-view/dist/dynamic/DataViewTable';
5+
import { BrowserRouter, useSearchParams } from 'react-router-dom';
6+
import { ThProps } from '@patternfly/react-table';
7+
8+
interface Repository {
9+
name: string;
10+
branches: string;
11+
prs: string;
12+
workspaces: string;
13+
lastCommit: string;
14+
}
15+
16+
const COLUMNS = [
17+
{ label: 'Repository', key: 'name', index: 0 },
18+
{ label: 'Branch', key: 'branches', index: 1 },
19+
{ label: 'Pull request', key: 'prs', index: 2 },
20+
{ label: 'Workspace', key: 'workspaces', index: 3 },
21+
{ label: 'Last commit', key: 'lastCommit', index: 4 },
22+
];
23+
24+
const repositories: Repository[] = [
25+
{ name: 'Repository one', branches: 'Branch one', prs: 'Pull request one', workspaces: 'Workspace one', lastCommit: '2023-11-01' },
26+
{ name: 'Repository six', branches: 'Branch six', prs: 'Pull request six', workspaces: 'Workspace six', lastCommit: '2023-11-06' },
27+
{ name: 'Repository two', branches: 'Branch two', prs: 'Pull request two', workspaces: 'Workspace two', lastCommit: '2023-11-02' },
28+
{ name: 'Repository five', branches: 'Branch five', prs: 'Pull request five', workspaces: 'Workspace five', lastCommit: '2023-11-05' },
29+
{ name: 'Repository three', branches: 'Branch three', prs: 'Pull request three', workspaces: 'Workspace three', lastCommit: '2023-11-03' },
30+
{ name: 'Repository four', branches: 'Branch four', prs: 'Pull request four', workspaces: 'Workspace four', lastCommit: '2023-11-04' },
31+
];
32+
33+
const sortData = (data: Repository[], sortBy: keyof Repository | undefined, direction: 'asc' | 'desc' | undefined) =>
34+
sortBy && direction
35+
? [ ...data ].sort((a, b) =>
36+
direction === 'asc'
37+
? a[sortBy] < b[sortBy] ? -1 : a[sortBy] > b[sortBy] ? 1 : 0
38+
: a[sortBy] > b[sortBy] ? -1 : a[sortBy] < b[sortBy] ? 1 : 0
39+
)
40+
: data;
41+
42+
const TestableTable: React.FunctionComponent = () => {
43+
const [ searchParams, setSearchParams ] = useSearchParams();
44+
const { sortBy, direction, onSort } = useDataViewSort({ searchParams, setSearchParams });
45+
const sortByIndex = React.useMemo(() => COLUMNS.findIndex(item => item.key === sortBy), [ sortBy ]);
46+
47+
const getSortParams = (columnIndex: number): ThProps['sort'] => ({
48+
sortBy: {
49+
index: sortByIndex,
50+
direction,
51+
defaultDirection: 'asc',
52+
},
53+
onSort: (_event, index, direction) => onSort(_event, COLUMNS[index].key, direction),
54+
columnIndex,
55+
});
56+
57+
const columns: DataViewTh[] = COLUMNS.map((column, index) => ({
58+
cell: column.label,
59+
props: { sort: getSortParams(index) },
60+
}));
61+
62+
const rows: DataViewTr[] = React.useMemo(
63+
() =>
64+
sortData(repositories, sortBy ? sortBy as keyof Repository : undefined, direction).map(({ name, branches, prs, workspaces, lastCommit }) => [
65+
name,
66+
branches,
67+
prs,
68+
workspaces,
69+
lastCommit,
70+
]),
71+
[ sortBy, direction ]
72+
);
73+
74+
return <DataViewTable aria-label="Repositories table" ouiaId="test-table" columns={columns} rows={rows} />;
75+
};
76+
77+
describe('DataViewTable Sorting with Hook', () => {
78+
it('sorts by repository name in ascending and descending order', () => {
79+
cy.mount(
80+
<BrowserRouter>
81+
<TestableTable />
82+
</BrowserRouter>
83+
);
84+
85+
cy.get('[data-ouia-component-id="test-table-th-0"]').click();
86+
cy.get('[data-ouia-component-id="test-table-td-0-0"]').should('contain', 'Repository five');
87+
cy.get('[data-ouia-component-id="test-table-td-5-0"]').should('contain', 'Repository two');
88+
89+
cy.get('[data-ouia-component-id="test-table-th-0"]').click();
90+
cy.get('[data-ouia-component-id="test-table-td-0-0"]').should('contain', 'Repository two');
91+
cy.get('[data-ouia-component-id="test-table-td-5-0"]').should('contain', 'Repository five');
92+
});
93+
94+
it('sorts by last commit date in ascending and descending order', () => {
95+
cy.mount(
96+
<BrowserRouter>
97+
<TestableTable />
98+
</BrowserRouter>
99+
);
100+
101+
cy.get('[data-ouia-component-id="test-table-th-4"]').click();
102+
cy.get('[data-ouia-component-id="test-table-td-0-4"]').should('contain', '2023-11-01');
103+
cy.get('[data-ouia-component-id="test-table-td-5-4"]').should('contain', '2023-11-06');
104+
105+
cy.get('[data-ouia-component-id="test-table-th-4"]').click();
106+
cy.get('[data-ouia-component-id="test-table-td-0-4"]').should('contain', '2023-11-06');
107+
cy.get('[data-ouia-component-id="test-table-td-5-4"]').should('contain', '2023-11-01');
108+
});
109+
});
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import '@testing-library/jest-dom';
2+
import { renderHook, act } from '@testing-library/react';
3+
import { useDataViewSort, UseDataViewSortProps, DataViewSortConfig, DataViewSortParams } from './sort';
4+
5+
describe('useDataViewSort', () => {
6+
const initialSort: DataViewSortConfig = { sortBy: 'name', direction: 'asc' };
7+
8+
it('should initialize with provided initial sort config', () => {
9+
const { result } = renderHook(() => useDataViewSort({ initialSort }));
10+
expect(result.current).toEqual(expect.objectContaining(initialSort));
11+
});
12+
13+
it('should initialize with empty sort config if no initialSort is provided', () => {
14+
const { result } = renderHook(() => useDataViewSort());
15+
expect(result.current).toEqual(expect.objectContaining({ sortBy: undefined, direction: 'asc' }));
16+
});
17+
18+
it('should update sort state when onSort is called', () => {
19+
const { result } = renderHook(() => useDataViewSort({ initialSort }));
20+
act(() => {
21+
result.current.onSort(undefined, 'age', 'desc');
22+
});
23+
expect(result.current).toEqual(expect.objectContaining({ sortBy: 'age', direction: 'desc' }));
24+
});
25+
26+
it('should sync with URL search params if isUrlSyncEnabled', () => {
27+
const searchParams = new URLSearchParams();
28+
const setSearchParams = jest.fn();
29+
const props: UseDataViewSortProps = {
30+
initialSort,
31+
searchParams,
32+
setSearchParams,
33+
};
34+
35+
const { result } = renderHook(() => useDataViewSort(props));
36+
37+
expect(setSearchParams).toHaveBeenCalledTimes(1);
38+
expect(result.current).toEqual(expect.objectContaining(initialSort));
39+
});
40+
41+
it('should validate direction and fallback to default direction if invalid direction is provided', () => {
42+
const searchParams = new URLSearchParams();
43+
searchParams.set(DataViewSortParams.SORT_BY, 'name');
44+
searchParams.set(DataViewSortParams.DIRECTION, 'invalid-direction');
45+
const { result } = renderHook(() => useDataViewSort({ searchParams, defaultDirection: 'desc' }));
46+
47+
expect(result.current).toEqual(expect.objectContaining({ sortBy: 'name', direction: 'desc' }));
48+
});
49+
50+
it('should update search params when URL sync is enabled and sort changes', () => {
51+
const searchParams = new URLSearchParams();
52+
const setSearchParams = jest.fn();
53+
const props: UseDataViewSortProps = {
54+
initialSort,
55+
searchParams,
56+
setSearchParams,
57+
};
58+
59+
const { result } = renderHook(() => useDataViewSort(props));
60+
act(() => {
61+
expect(setSearchParams).toHaveBeenCalledTimes(1);
62+
result.current.onSort(undefined, 'priority', 'desc');
63+
});
64+
65+
expect(setSearchParams).toHaveBeenCalledTimes(2);
66+
expect(result.current).toEqual(expect.objectContaining({ sortBy: 'priority', direction: 'desc' }));
67+
});
68+
69+
it('should prioritize searchParams values', () => {
70+
const searchParams = new URLSearchParams();
71+
searchParams.set(DataViewSortParams.SORT_BY, 'category');
72+
searchParams.set(DataViewSortParams.DIRECTION, 'desc');
73+
74+
const { result } = renderHook(
75+
(props: UseDataViewSortProps) => useDataViewSort(props),
76+
{ initialProps: { initialSort, searchParams } }
77+
);
78+
79+
expect(result.current).toEqual(expect.objectContaining({
80+
sortBy: 'category',
81+
direction: 'desc',
82+
}));
83+
});
84+
});

0 commit comments

Comments
 (0)