@powerhousedao/document-engineering 1.39.0 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/src/table/components/default-fns/default-cell-on-save.d.ts +11 -0
- package/dist/src/table/components/default-fns/default-cell-on-save.d.ts.map +1 -0
- package/dist/src/table/components/default-fns/default-cell-on-save.js +18 -0
- package/dist/src/table/components/default-fns/default-cell-on-save.js.map +1 -0
- package/dist/src/table/table/object-set-table.d.ts.map +1 -1
- package/dist/src/table/table/object-set-table.js +2 -11
- package/dist/src/table/table/object-set-table.js.map +1 -1
- package/dist/src/table/tests/basic-rendering.test.d.ts +2 -0
- package/dist/src/table/tests/basic-rendering.test.d.ts.map +1 -0
- package/dist/src/table/tests/basic-rendering.test.js +332 -0
- package/dist/src/table/tests/basic-rendering.test.js.map +1 -0
- package/dist/src/table/tests/cell-selection.test.d.ts +2 -0
- package/dist/src/table/tests/cell-selection.test.d.ts.map +1 -0
- package/dist/src/table/tests/cell-selection.test.js +346 -0
- package/dist/src/table/tests/cell-selection.test.js.map +1 -0
- package/dist/src/table/tests/helpers/test-utils.d.ts +28 -0
- package/dist/src/table/tests/helpers/test-utils.d.ts.map +1 -0
- package/dist/src/table/tests/helpers/test-utils.js +85 -0
- package/dist/src/table/tests/helpers/test-utils.js.map +1 -0
- package/dist/src/table/tests/page-objects/base-table.page.d.ts +83 -0
- package/dist/src/table/tests/page-objects/base-table.page.d.ts.map +1 -0
- package/dist/src/table/tests/page-objects/base-table.page.js +146 -0
- package/dist/src/table/tests/page-objects/base-table.page.js.map +1 -0
- package/dist/src/table/tests/page-objects/table-cell.page.d.ts +58 -0
- package/dist/src/table/tests/page-objects/table-cell.page.d.ts.map +1 -0
- package/dist/src/table/tests/page-objects/table-cell.page.js +163 -0
- package/dist/src/table/tests/page-objects/table-cell.page.js.map +1 -0
- package/dist/src/table/tests/page-objects/table-header.page.d.ts +66 -0
- package/dist/src/table/tests/page-objects/table-header.page.d.ts.map +1 -0
- package/dist/src/table/tests/page-objects/table-header.page.js +156 -0
- package/dist/src/table/tests/page-objects/table-header.page.js.map +1 -0
- package/dist/src/table/tests/page-objects/table-row.page.d.ts +98 -0
- package/dist/src/table/tests/page-objects/table-row.page.d.ts.map +1 -0
- package/dist/src/table/tests/page-objects/table-row.page.js +240 -0
- package/dist/src/table/tests/page-objects/table-row.page.js.map +1 -0
- package/dist/src/table/tests/page-objects/table.page.d.ts +36 -0
- package/dist/src/table/tests/page-objects/table.page.d.ts.map +1 -0
- package/dist/src/table/tests/page-objects/table.page.js +46 -0
- package/dist/src/table/tests/page-objects/table.page.js.map +1 -0
- package/dist/src/table/tests/row-actions.test.d.ts +2 -0
- package/dist/src/table/tests/row-actions.test.d.ts.map +1 -0
- package/dist/src/table/tests/row-actions.test.js +601 -0
- package/dist/src/table/tests/row-actions.test.js.map +1 -0
- package/dist/src/table/tests/row-selection.test.d.ts +2 -0
- package/dist/src/table/tests/row-selection.test.d.ts.map +1 -0
- package/dist/src/table/tests/row-selection.test.js +272 -0
- package/dist/src/table/tests/row-selection.test.js.map +1 -0
- package/dist/src/table/tests/sorting.test.d.ts +2 -0
- package/dist/src/table/tests/sorting.test.d.ts.map +1 -0
- package/dist/src/table/tests/sorting.test.js +793 -0
- package/dist/src/table/tests/sorting.test.js.map +1 -0
- package/dist/src/ui/components/data-entry/date-picker/date-picker.js +1 -1
- package/dist/src/ui/components/data-entry/date-picker/date-picker.js.map +1 -1
- package/dist/style.css +4 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { fireEvent } from '@testing-library/react';
|
|
4
|
+
import { createMockPerson, createTestTableConfig, renderTestTable } from './helpers/test-utils.js';
|
|
5
|
+
import { TablePage } from './page-objects/table.page.js';
|
|
6
|
+
describe('ObjectSetTable - Sorting', () => {
|
|
7
|
+
describe('Basic Sorting Functionality', () => {
|
|
8
|
+
it('should sort string column in ascending order', () => {
|
|
9
|
+
const sortableConfig = createTestTableConfig({
|
|
10
|
+
columns: [
|
|
11
|
+
{
|
|
12
|
+
field: 'firstName',
|
|
13
|
+
title: 'First Name',
|
|
14
|
+
type: 'string',
|
|
15
|
+
sortable: true,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
field: 'status',
|
|
19
|
+
title: 'Status',
|
|
20
|
+
type: 'string',
|
|
21
|
+
sortable: true,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
field: 'payment',
|
|
25
|
+
title: 'Payment',
|
|
26
|
+
type: 'number',
|
|
27
|
+
sortable: true,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
32
|
+
const table = new TablePage(renderResult);
|
|
33
|
+
// Verify initial state - no sorting
|
|
34
|
+
expect(table.header.isColumnSorted(0)).toBe(false);
|
|
35
|
+
expect(table.header.getColumnSortDirection(0)).toBe(null);
|
|
36
|
+
// Click header to sort ascending (column 1 is First Name, column 0 is row number)
|
|
37
|
+
const headerCell = table.getHeaderCellByIndex(1);
|
|
38
|
+
expect(headerCell).toBeTruthy();
|
|
39
|
+
fireEvent.click(headerCell);
|
|
40
|
+
// Verify sorting state
|
|
41
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
42
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
43
|
+
// Verify data is sorted alphabetically (column 1 is First Name)
|
|
44
|
+
expect(table.cell.getCellValue(0, 1)).toBe('Alex');
|
|
45
|
+
expect(table.cell.getCellValue(1, 1)).toBe('Alice');
|
|
46
|
+
expect(table.cell.getCellValue(2, 1)).toBe('Bob');
|
|
47
|
+
expect(table.cell.getCellValue(3, 1)).toBe('Diana');
|
|
48
|
+
expect(table.cell.getCellValue(4, 1)).toBe('Fiona');
|
|
49
|
+
});
|
|
50
|
+
it('should sort string column in descending order', () => {
|
|
51
|
+
const sortableConfig = createTestTableConfig({
|
|
52
|
+
columns: [
|
|
53
|
+
{
|
|
54
|
+
field: 'firstName',
|
|
55
|
+
title: 'First Name',
|
|
56
|
+
type: 'string',
|
|
57
|
+
sortable: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
field: 'status',
|
|
61
|
+
title: 'Status',
|
|
62
|
+
type: 'string',
|
|
63
|
+
sortable: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
field: 'payment',
|
|
67
|
+
title: 'Payment',
|
|
68
|
+
type: 'number',
|
|
69
|
+
sortable: true,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
74
|
+
const table = new TablePage(renderResult);
|
|
75
|
+
// Click header twice to get descending order (column 1 is First Name)
|
|
76
|
+
const headerCell = table.getHeaderCellByIndex(1);
|
|
77
|
+
expect(headerCell).toBeTruthy();
|
|
78
|
+
fireEvent.click(headerCell); // First click: asc
|
|
79
|
+
fireEvent.click(headerCell); // Second click: desc
|
|
80
|
+
// Verify sorting state
|
|
81
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
82
|
+
expect(table.header.getColumnSortDirection(1)).toBe('desc');
|
|
83
|
+
// Verify data is sorted in descending order (column 1 is First Name)
|
|
84
|
+
expect(table.cell.getCellValue(0, 1)).toBe('Fiona');
|
|
85
|
+
expect(table.cell.getCellValue(1, 1)).toBe('Diana');
|
|
86
|
+
expect(table.cell.getCellValue(2, 1)).toBe('Bob');
|
|
87
|
+
expect(table.cell.getCellValue(3, 1)).toBe('Alice');
|
|
88
|
+
expect(table.cell.getCellValue(4, 1)).toBe('Alex');
|
|
89
|
+
});
|
|
90
|
+
it('should clear sorting on third click', () => {
|
|
91
|
+
const sortableConfig = createTestTableConfig({
|
|
92
|
+
columns: [
|
|
93
|
+
{
|
|
94
|
+
field: 'firstName',
|
|
95
|
+
title: 'First Name',
|
|
96
|
+
type: 'string',
|
|
97
|
+
sortable: true,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
field: 'status',
|
|
101
|
+
title: 'Status',
|
|
102
|
+
type: 'string',
|
|
103
|
+
sortable: true,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
field: 'payment',
|
|
107
|
+
title: 'Payment',
|
|
108
|
+
type: 'number',
|
|
109
|
+
sortable: true,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
114
|
+
const table = new TablePage(renderResult);
|
|
115
|
+
// Get original order (column 1 is First Name)
|
|
116
|
+
const originalOrder = table.cell.getColumnCells(1);
|
|
117
|
+
// Click header three times to cycle through: asc -> desc -> none (column 1 is First Name)
|
|
118
|
+
const headerCell = table.getHeaderCellByIndex(1);
|
|
119
|
+
expect(headerCell).toBeTruthy();
|
|
120
|
+
fireEvent.click(headerCell); // First click: asc
|
|
121
|
+
fireEvent.click(headerCell); // Second click: desc
|
|
122
|
+
fireEvent.click(headerCell); // Third click: none
|
|
123
|
+
// Verify sorting is cleared
|
|
124
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
125
|
+
expect(table.header.getColumnSortDirection(1)).toBe(null);
|
|
126
|
+
// Verify data is back to original order (column 1 is First Name)
|
|
127
|
+
const currentOrder = table.cell.getColumnCells(1);
|
|
128
|
+
expect(currentOrder).toEqual(originalOrder);
|
|
129
|
+
});
|
|
130
|
+
it('should sort numeric column correctly', () => {
|
|
131
|
+
const sortableConfig = createTestTableConfig({
|
|
132
|
+
columns: [
|
|
133
|
+
{
|
|
134
|
+
field: 'firstName',
|
|
135
|
+
title: 'First Name',
|
|
136
|
+
type: 'string',
|
|
137
|
+
sortable: true,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
field: 'payment',
|
|
141
|
+
title: 'Payment',
|
|
142
|
+
type: 'number',
|
|
143
|
+
sortable: true,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
148
|
+
const table = new TablePage(renderResult);
|
|
149
|
+
// Click payment column header to sort ascending (column 2 is Payment)
|
|
150
|
+
const paymentHeaderCell = table.getHeaderCellByIndex(2);
|
|
151
|
+
expect(paymentHeaderCell).toBeTruthy();
|
|
152
|
+
fireEvent.click(paymentHeaderCell);
|
|
153
|
+
// Verify sorting state
|
|
154
|
+
expect(table.header.isColumnSorted(2)).toBe(true);
|
|
155
|
+
expect(table.header.getColumnSortDirection(2)).toBe('asc');
|
|
156
|
+
// Verify numeric data is sorted correctly (column 2 is Payment)
|
|
157
|
+
expect(table.cell.getCellValue(0, 2)).toBe('0');
|
|
158
|
+
expect(table.cell.getCellValue(1, 2)).toBe('15');
|
|
159
|
+
expect(table.cell.getCellValue(2, 2)).toBe('100');
|
|
160
|
+
expect(table.cell.getCellValue(3, 2)).toBe('14522');
|
|
161
|
+
expect(table.cell.getCellValue(4, 2)).toBe('1065460');
|
|
162
|
+
});
|
|
163
|
+
it('should sort numeric column in descending order', () => {
|
|
164
|
+
const sortableConfig = createTestTableConfig({
|
|
165
|
+
columns: [
|
|
166
|
+
{
|
|
167
|
+
field: 'firstName',
|
|
168
|
+
title: 'First Name',
|
|
169
|
+
type: 'string',
|
|
170
|
+
sortable: true,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
field: 'payment',
|
|
174
|
+
title: 'Payment',
|
|
175
|
+
type: 'number',
|
|
176
|
+
sortable: true,
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
});
|
|
180
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
181
|
+
const table = new TablePage(renderResult);
|
|
182
|
+
// Click payment column header twice to sort descending (column 2 is Payment)
|
|
183
|
+
const paymentHeaderCell = table.getHeaderCellByIndex(2);
|
|
184
|
+
expect(paymentHeaderCell).toBeTruthy();
|
|
185
|
+
fireEvent.click(paymentHeaderCell); // First click: asc
|
|
186
|
+
fireEvent.click(paymentHeaderCell); // Second click: desc
|
|
187
|
+
// Verify sorting state
|
|
188
|
+
expect(table.header.isColumnSorted(2)).toBe(true);
|
|
189
|
+
expect(table.header.getColumnSortDirection(2)).toBe('desc');
|
|
190
|
+
// Verify numeric data is sorted in descending order (column 2 is Payment)
|
|
191
|
+
expect(table.cell.getCellValue(0, 2)).toBe('10234234230');
|
|
192
|
+
expect(table.cell.getCellValue(1, 2)).toBe('11231200');
|
|
193
|
+
expect(table.cell.getCellValue(2, 2)).toBe('1065460');
|
|
194
|
+
expect(table.cell.getCellValue(3, 2)).toBe('14522');
|
|
195
|
+
expect(table.cell.getCellValue(4, 2)).toBe('100');
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
describe('Sorting State Management', () => {
|
|
199
|
+
it('should switch sorting to different column', () => {
|
|
200
|
+
const sortableConfig = createTestTableConfig({
|
|
201
|
+
columns: [
|
|
202
|
+
{
|
|
203
|
+
field: 'firstName',
|
|
204
|
+
title: 'First Name',
|
|
205
|
+
type: 'string',
|
|
206
|
+
sortable: true,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
field: 'status',
|
|
210
|
+
title: 'Status',
|
|
211
|
+
type: 'string',
|
|
212
|
+
sortable: true,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
field: 'payment',
|
|
216
|
+
title: 'Payment',
|
|
217
|
+
type: 'number',
|
|
218
|
+
sortable: true,
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
});
|
|
222
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
223
|
+
const table = new TablePage(renderResult);
|
|
224
|
+
// Sort by firstName column (column 1 is First Name)
|
|
225
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
226
|
+
expect(firstNameHeader).toBeTruthy();
|
|
227
|
+
fireEvent.click(firstNameHeader);
|
|
228
|
+
// Verify firstName is sorted
|
|
229
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
230
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
231
|
+
expect(table.header.isColumnSorted(2)).toBe(false);
|
|
232
|
+
// Sort by status column (column 2 is Status)
|
|
233
|
+
const statusHeader = table.getHeaderCellByIndex(2);
|
|
234
|
+
expect(statusHeader).toBeTruthy();
|
|
235
|
+
fireEvent.click(statusHeader);
|
|
236
|
+
// Verify status is now sorted and firstName is not
|
|
237
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
238
|
+
expect(table.header.isColumnSorted(2)).toBe(true);
|
|
239
|
+
expect(table.header.getColumnSortDirection(2)).toBe('asc');
|
|
240
|
+
});
|
|
241
|
+
it('should maintain sort direction when switching columns', () => {
|
|
242
|
+
const sortableConfig = createTestTableConfig({
|
|
243
|
+
columns: [
|
|
244
|
+
{
|
|
245
|
+
field: 'firstName',
|
|
246
|
+
title: 'First Name',
|
|
247
|
+
type: 'string',
|
|
248
|
+
sortable: true,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
field: 'status',
|
|
252
|
+
title: 'Status',
|
|
253
|
+
type: 'string',
|
|
254
|
+
sortable: true,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
field: 'payment',
|
|
258
|
+
title: 'Payment',
|
|
259
|
+
type: 'number',
|
|
260
|
+
sortable: true,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
});
|
|
264
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
265
|
+
const table = new TablePage(renderResult);
|
|
266
|
+
// Sort firstName column to descending (column 1 is First Name)
|
|
267
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
268
|
+
expect(firstNameHeader).toBeTruthy();
|
|
269
|
+
fireEvent.click(firstNameHeader); // asc
|
|
270
|
+
fireEvent.click(firstNameHeader); // desc
|
|
271
|
+
// Verify firstName is sorted descending
|
|
272
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
273
|
+
expect(table.header.getColumnSortDirection(1)).toBe('desc');
|
|
274
|
+
// Switch to status column (column 2 is Status)
|
|
275
|
+
const statusHeader = table.getHeaderCellByIndex(2);
|
|
276
|
+
expect(statusHeader).toBeTruthy();
|
|
277
|
+
fireEvent.click(statusHeader);
|
|
278
|
+
// Verify status is sorted ascending (new column starts with asc)
|
|
279
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
280
|
+
expect(table.header.isColumnSorted(2)).toBe(true);
|
|
281
|
+
expect(table.header.getColumnSortDirection(2)).toBe('asc');
|
|
282
|
+
});
|
|
283
|
+
it('should handle multiple column sorting interactions', () => {
|
|
284
|
+
const sortableConfig = createTestTableConfig({
|
|
285
|
+
columns: [
|
|
286
|
+
{
|
|
287
|
+
field: 'firstName',
|
|
288
|
+
title: 'First Name',
|
|
289
|
+
type: 'string',
|
|
290
|
+
sortable: true,
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
field: 'status',
|
|
294
|
+
title: 'Status',
|
|
295
|
+
type: 'string',
|
|
296
|
+
sortable: true,
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
field: 'payment',
|
|
300
|
+
title: 'Payment',
|
|
301
|
+
type: 'number',
|
|
302
|
+
sortable: true,
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
});
|
|
306
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
307
|
+
const table = new TablePage(renderResult);
|
|
308
|
+
// Sort by firstName (column 1 is First Name)
|
|
309
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
310
|
+
expect(firstNameHeader).toBeTruthy();
|
|
311
|
+
fireEvent.click(firstNameHeader);
|
|
312
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
313
|
+
// Sort by payment (column 3 is Payment)
|
|
314
|
+
const paymentHeader = table.getHeaderCellByIndex(3);
|
|
315
|
+
expect(paymentHeader).toBeTruthy();
|
|
316
|
+
fireEvent.click(paymentHeader);
|
|
317
|
+
expect(table.header.getColumnSortDirection(3)).toBe('asc');
|
|
318
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
319
|
+
// Sort by status (column 2 is Status)
|
|
320
|
+
const statusHeader = table.getHeaderCellByIndex(2);
|
|
321
|
+
expect(statusHeader).toBeTruthy();
|
|
322
|
+
fireEvent.click(statusHeader);
|
|
323
|
+
expect(table.header.getColumnSortDirection(2)).toBe('asc');
|
|
324
|
+
expect(table.header.isColumnSorted(3)).toBe(false);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
describe('Non-Sortable Columns', () => {
|
|
328
|
+
it('should not sort non-sortable columns', () => {
|
|
329
|
+
const mixedConfig = createTestTableConfig({
|
|
330
|
+
columns: [
|
|
331
|
+
{
|
|
332
|
+
field: 'firstName',
|
|
333
|
+
title: 'First Name',
|
|
334
|
+
type: 'string',
|
|
335
|
+
sortable: false,
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
field: 'status',
|
|
339
|
+
title: 'Status',
|
|
340
|
+
type: 'string',
|
|
341
|
+
sortable: true,
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
field: 'payment',
|
|
345
|
+
title: 'Payment',
|
|
346
|
+
type: 'number',
|
|
347
|
+
sortable: false,
|
|
348
|
+
},
|
|
349
|
+
],
|
|
350
|
+
});
|
|
351
|
+
const renderResult = renderTestTable(mixedConfig);
|
|
352
|
+
const table = new TablePage(renderResult);
|
|
353
|
+
// Verify firstName column is not sortable (column 1 is First Name)
|
|
354
|
+
expect(table.header.isColumnSortable(1)).toBe(false);
|
|
355
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
356
|
+
// Verify status column is sortable (column 2 is Status)
|
|
357
|
+
expect(table.header.isColumnSortable(2)).toBe(true);
|
|
358
|
+
// Try to click non-sortable column - should not change sorting
|
|
359
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
360
|
+
expect(firstNameHeader).toBeTruthy();
|
|
361
|
+
fireEvent.click(firstNameHeader);
|
|
362
|
+
// Verify no sorting occurred
|
|
363
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
364
|
+
expect(table.header.getColumnSortDirection(1)).toBe(null);
|
|
365
|
+
});
|
|
366
|
+
it('should only allow sorting on sortable columns', () => {
|
|
367
|
+
const sortableConfig = createTestTableConfig({
|
|
368
|
+
columns: [
|
|
369
|
+
{
|
|
370
|
+
field: 'firstName',
|
|
371
|
+
title: 'First Name',
|
|
372
|
+
type: 'string',
|
|
373
|
+
sortable: true,
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
field: 'status',
|
|
377
|
+
title: 'Status',
|
|
378
|
+
type: 'string',
|
|
379
|
+
sortable: false,
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
field: 'payment',
|
|
383
|
+
title: 'Payment',
|
|
384
|
+
type: 'number',
|
|
385
|
+
sortable: true,
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
});
|
|
389
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
390
|
+
const table = new TablePage(renderResult);
|
|
391
|
+
// Verify sortable columns
|
|
392
|
+
expect(table.header.isColumnSortable(1)).toBe(true); // firstName
|
|
393
|
+
expect(table.header.isColumnSortable(2)).toBe(false); // status
|
|
394
|
+
expect(table.header.isColumnSortable(3)).toBe(true); // payment
|
|
395
|
+
// Sort by sortable column
|
|
396
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
397
|
+
expect(firstNameHeader).toBeTruthy();
|
|
398
|
+
fireEvent.click(firstNameHeader);
|
|
399
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
400
|
+
// Try to sort non-sortable column
|
|
401
|
+
const statusHeader = table.getHeaderCellByIndex(2);
|
|
402
|
+
expect(statusHeader).toBeTruthy();
|
|
403
|
+
fireEvent.click(statusHeader);
|
|
404
|
+
// Verify firstName is still sorted
|
|
405
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
406
|
+
expect(table.header.isColumnSorted(2)).toBe(false);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
describe('Null and Empty Value Handling', () => {
|
|
410
|
+
it('should handle null values in sortable columns', () => {
|
|
411
|
+
const sortableConfig = createTestTableConfig({
|
|
412
|
+
columns: [
|
|
413
|
+
{
|
|
414
|
+
field: 'firstName',
|
|
415
|
+
title: 'First Name',
|
|
416
|
+
type: 'string',
|
|
417
|
+
sortable: true,
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
field: 'status',
|
|
421
|
+
title: 'Status',
|
|
422
|
+
type: 'string',
|
|
423
|
+
sortable: true,
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
});
|
|
427
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
428
|
+
const table = new TablePage(renderResult);
|
|
429
|
+
// Sort firstName column ascending (column 1 is First Name)
|
|
430
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
431
|
+
expect(firstNameHeader).toBeTruthy();
|
|
432
|
+
fireEvent.click(firstNameHeader);
|
|
433
|
+
// Verify null values are placed at the end
|
|
434
|
+
const firstNameValues = table.cell.getColumnCells(1);
|
|
435
|
+
const nullValueIndices = firstNameValues
|
|
436
|
+
.map((value, index) => (value === '' ? index : -1))
|
|
437
|
+
.filter((index) => index !== -1);
|
|
438
|
+
// Null values should be at the end
|
|
439
|
+
expect(nullValueIndices.length).toBeGreaterThan(0);
|
|
440
|
+
const maxNullIndex = Math.max(...nullValueIndices);
|
|
441
|
+
const minNonNullIndex = Math.min(...firstNameValues
|
|
442
|
+
.map((value, index) => (value !== '' ? index : Infinity))
|
|
443
|
+
.filter((index) => index !== Infinity));
|
|
444
|
+
expect(maxNullIndex).toBeGreaterThan(minNonNullIndex);
|
|
445
|
+
});
|
|
446
|
+
it('should handle empty strings in sortable columns', () => {
|
|
447
|
+
const customData = [
|
|
448
|
+
createMockPerson({ firstName: 'Alice' }),
|
|
449
|
+
createMockPerson({ firstName: '' }),
|
|
450
|
+
createMockPerson({ firstName: 'Bob' }),
|
|
451
|
+
createMockPerson({ firstName: null }),
|
|
452
|
+
createMockPerson({ firstName: 'Charlie' }),
|
|
453
|
+
];
|
|
454
|
+
const sortableConfig = createTestTableConfig({
|
|
455
|
+
columns: [
|
|
456
|
+
{
|
|
457
|
+
field: 'firstName',
|
|
458
|
+
title: 'First Name',
|
|
459
|
+
type: 'string',
|
|
460
|
+
sortable: true,
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
data: customData,
|
|
464
|
+
});
|
|
465
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
466
|
+
const table = new TablePage(renderResult);
|
|
467
|
+
// Sort firstName column ascending (column 1 is First Name)
|
|
468
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
469
|
+
expect(firstNameHeader).toBeTruthy();
|
|
470
|
+
fireEvent.click(firstNameHeader);
|
|
471
|
+
// Verify empty and null values are handled correctly
|
|
472
|
+
const firstNameValues = table.cell.getColumnCells(1);
|
|
473
|
+
// Empty strings are placed at the beginning when sorting ascending
|
|
474
|
+
expect(firstNameValues[0]).toBe('');
|
|
475
|
+
expect(firstNameValues[1]).toBe('Alice');
|
|
476
|
+
expect(firstNameValues[2]).toBe('Bob');
|
|
477
|
+
expect(firstNameValues[3]).toBe('Charlie');
|
|
478
|
+
expect(firstNameValues[4]).toBe('');
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
describe('Custom Sorting Configuration', () => {
|
|
482
|
+
it('should work with custom sortable configuration', () => {
|
|
483
|
+
const customSortableConfig = createTestTableConfig({
|
|
484
|
+
columns: [
|
|
485
|
+
{
|
|
486
|
+
field: 'firstName',
|
|
487
|
+
title: 'First Name',
|
|
488
|
+
type: 'string',
|
|
489
|
+
sortable: true,
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
field: 'status',
|
|
493
|
+
title: 'Status',
|
|
494
|
+
type: 'string',
|
|
495
|
+
sortable: true,
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
field: 'payment',
|
|
499
|
+
title: 'Payment',
|
|
500
|
+
type: 'number',
|
|
501
|
+
sortable: true,
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
});
|
|
505
|
+
const renderResult = renderTestTable(customSortableConfig);
|
|
506
|
+
const table = new TablePage(renderResult);
|
|
507
|
+
// Verify all columns are sortable
|
|
508
|
+
expect(table.header.isColumnSortable(1)).toBe(true); // firstName
|
|
509
|
+
expect(table.header.isColumnSortable(2)).toBe(true); // status
|
|
510
|
+
expect(table.header.isColumnSortable(3)).toBe(true); // payment
|
|
511
|
+
// Test sorting each column
|
|
512
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
513
|
+
expect(firstNameHeader).toBeTruthy();
|
|
514
|
+
fireEvent.click(firstNameHeader);
|
|
515
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
516
|
+
const statusHeader = table.getHeaderCellByIndex(2);
|
|
517
|
+
expect(statusHeader).toBeTruthy();
|
|
518
|
+
fireEvent.click(statusHeader);
|
|
519
|
+
expect(table.header.getColumnSortDirection(2)).toBe('asc');
|
|
520
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
521
|
+
const paymentHeader = table.getHeaderCellByIndex(3);
|
|
522
|
+
expect(paymentHeader).toBeTruthy();
|
|
523
|
+
fireEvent.click(paymentHeader);
|
|
524
|
+
expect(table.header.getColumnSortDirection(3)).toBe('asc');
|
|
525
|
+
expect(table.header.isColumnSorted(2)).toBe(false);
|
|
526
|
+
});
|
|
527
|
+
it('should handle mixed sortable and non-sortable columns', () => {
|
|
528
|
+
const mixedConfig = createTestTableConfig({
|
|
529
|
+
columns: [
|
|
530
|
+
{
|
|
531
|
+
field: 'firstName',
|
|
532
|
+
title: 'First Name',
|
|
533
|
+
type: 'string',
|
|
534
|
+
sortable: true,
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
field: 'status',
|
|
538
|
+
title: 'Status',
|
|
539
|
+
type: 'string',
|
|
540
|
+
sortable: false,
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
field: 'payment',
|
|
544
|
+
title: 'Payment',
|
|
545
|
+
type: 'number',
|
|
546
|
+
sortable: true,
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
field: 'email',
|
|
550
|
+
title: 'Email',
|
|
551
|
+
type: 'string',
|
|
552
|
+
sortable: false,
|
|
553
|
+
},
|
|
554
|
+
],
|
|
555
|
+
});
|
|
556
|
+
const renderResult = renderTestTable(mixedConfig);
|
|
557
|
+
const table = new TablePage(renderResult);
|
|
558
|
+
// Verify sortable columns
|
|
559
|
+
expect(table.header.isColumnSortable(1)).toBe(true); // firstName
|
|
560
|
+
expect(table.header.isColumnSortable(2)).toBe(false); // status
|
|
561
|
+
expect(table.header.isColumnSortable(3)).toBe(true); // payment
|
|
562
|
+
expect(table.header.isColumnSortable(4)).toBe(false); // email
|
|
563
|
+
// Sort by sortable columns only
|
|
564
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
565
|
+
expect(firstNameHeader).toBeTruthy();
|
|
566
|
+
fireEvent.click(firstNameHeader);
|
|
567
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
568
|
+
const paymentHeader = table.getHeaderCellByIndex(3);
|
|
569
|
+
expect(paymentHeader).toBeTruthy();
|
|
570
|
+
fireEvent.click(paymentHeader);
|
|
571
|
+
expect(table.header.isColumnSorted(3)).toBe(true);
|
|
572
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
describe('Edge Cases', () => {
|
|
576
|
+
it('should handle sorting with single row', () => {
|
|
577
|
+
const singleRowData = [createMockPerson({ firstName: 'Single User' })];
|
|
578
|
+
const sortableConfig = createTestTableConfig({
|
|
579
|
+
columns: [
|
|
580
|
+
{
|
|
581
|
+
field: 'firstName',
|
|
582
|
+
title: 'First Name',
|
|
583
|
+
type: 'string',
|
|
584
|
+
sortable: true,
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
field: 'payment',
|
|
588
|
+
title: 'Payment',
|
|
589
|
+
type: 'number',
|
|
590
|
+
sortable: true,
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
data: singleRowData,
|
|
594
|
+
});
|
|
595
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
596
|
+
const table = new TablePage(renderResult);
|
|
597
|
+
// Sort by firstName (column 1 is First Name)
|
|
598
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
599
|
+
expect(firstNameHeader).toBeTruthy();
|
|
600
|
+
fireEvent.click(firstNameHeader);
|
|
601
|
+
// Verify sorting works with single row
|
|
602
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
603
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
604
|
+
expect(table.cell.getCellValue(0, 1)).toBe('Single User');
|
|
605
|
+
});
|
|
606
|
+
it('should handle sorting with empty data', () => {
|
|
607
|
+
const sortableConfig = createTestTableConfig({
|
|
608
|
+
columns: [
|
|
609
|
+
{
|
|
610
|
+
field: 'firstName',
|
|
611
|
+
title: 'First Name',
|
|
612
|
+
type: 'string',
|
|
613
|
+
sortable: true,
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
field: 'payment',
|
|
617
|
+
title: 'Payment',
|
|
618
|
+
type: 'number',
|
|
619
|
+
sortable: true,
|
|
620
|
+
},
|
|
621
|
+
],
|
|
622
|
+
data: [],
|
|
623
|
+
});
|
|
624
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
625
|
+
const table = new TablePage(renderResult);
|
|
626
|
+
// Try to sort with no data (column 1 is First Name)
|
|
627
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
628
|
+
expect(firstNameHeader).toBeTruthy();
|
|
629
|
+
fireEvent.click(firstNameHeader);
|
|
630
|
+
// Verify sorting state is set even with no data
|
|
631
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
632
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
633
|
+
expect(table.getRowCount()).toBe(0);
|
|
634
|
+
});
|
|
635
|
+
it('should handle rapid sorting changes', () => {
|
|
636
|
+
const sortableConfig = createTestTableConfig({
|
|
637
|
+
columns: [
|
|
638
|
+
{
|
|
639
|
+
field: 'firstName',
|
|
640
|
+
title: 'First Name',
|
|
641
|
+
type: 'string',
|
|
642
|
+
sortable: true,
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
field: 'status',
|
|
646
|
+
title: 'Status',
|
|
647
|
+
type: 'string',
|
|
648
|
+
sortable: true,
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
field: 'payment',
|
|
652
|
+
title: 'Payment',
|
|
653
|
+
type: 'number',
|
|
654
|
+
sortable: true,
|
|
655
|
+
},
|
|
656
|
+
],
|
|
657
|
+
});
|
|
658
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
659
|
+
const table = new TablePage(renderResult);
|
|
660
|
+
// Rapidly click different headers
|
|
661
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
662
|
+
const statusHeader = table.getHeaderCellByIndex(2);
|
|
663
|
+
const paymentHeader = table.getHeaderCellByIndex(3);
|
|
664
|
+
expect(firstNameHeader).toBeTruthy();
|
|
665
|
+
expect(statusHeader).toBeTruthy();
|
|
666
|
+
expect(paymentHeader).toBeTruthy();
|
|
667
|
+
fireEvent.click(firstNameHeader);
|
|
668
|
+
fireEvent.click(statusHeader);
|
|
669
|
+
fireEvent.click(paymentHeader);
|
|
670
|
+
// Verify only the last clicked column is sorted
|
|
671
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
672
|
+
expect(table.header.isColumnSorted(2)).toBe(false);
|
|
673
|
+
expect(table.header.isColumnSorted(3)).toBe(true);
|
|
674
|
+
expect(table.header.getColumnSortDirection(3)).toBe('asc');
|
|
675
|
+
});
|
|
676
|
+
it('should handle sorting with duplicate values', () => {
|
|
677
|
+
const duplicateData = [
|
|
678
|
+
createMockPerson({ firstName: 'Alice', payment: 100 }),
|
|
679
|
+
createMockPerson({ firstName: 'Bob', payment: 100 }),
|
|
680
|
+
createMockPerson({ firstName: 'Charlie', payment: 200 }),
|
|
681
|
+
createMockPerson({ firstName: 'Alice', payment: 150 }),
|
|
682
|
+
];
|
|
683
|
+
const sortableConfig = createTestTableConfig({
|
|
684
|
+
columns: [
|
|
685
|
+
{
|
|
686
|
+
field: 'firstName',
|
|
687
|
+
title: 'First Name',
|
|
688
|
+
type: 'string',
|
|
689
|
+
sortable: true,
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
field: 'payment',
|
|
693
|
+
title: 'Payment',
|
|
694
|
+
type: 'number',
|
|
695
|
+
sortable: true,
|
|
696
|
+
},
|
|
697
|
+
],
|
|
698
|
+
data: duplicateData,
|
|
699
|
+
});
|
|
700
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
701
|
+
const table = new TablePage(renderResult);
|
|
702
|
+
// Sort by firstName with duplicates (column 1 is First Name)
|
|
703
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
704
|
+
expect(firstNameHeader).toBeTruthy();
|
|
705
|
+
fireEvent.click(firstNameHeader);
|
|
706
|
+
// Verify sorting handles duplicates correctly
|
|
707
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
708
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
709
|
+
const firstNameValues = table.cell.getColumnCells(1);
|
|
710
|
+
expect(firstNameValues[0]).toBe('Alice');
|
|
711
|
+
expect(firstNameValues[1]).toBe('Alice');
|
|
712
|
+
expect(firstNameValues[2]).toBe('Bob');
|
|
713
|
+
expect(firstNameValues[3]).toBe('Charlie');
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
describe('Visual Indicators', () => {
|
|
717
|
+
it('should show sort indicators on sortable columns', () => {
|
|
718
|
+
const sortableConfig = createTestTableConfig({
|
|
719
|
+
columns: [
|
|
720
|
+
{
|
|
721
|
+
field: 'firstName',
|
|
722
|
+
title: 'First Name',
|
|
723
|
+
type: 'string',
|
|
724
|
+
sortable: true,
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
field: 'status',
|
|
728
|
+
title: 'Status',
|
|
729
|
+
type: 'string',
|
|
730
|
+
sortable: false,
|
|
731
|
+
},
|
|
732
|
+
{
|
|
733
|
+
field: 'payment',
|
|
734
|
+
title: 'Payment',
|
|
735
|
+
type: 'number',
|
|
736
|
+
sortable: true,
|
|
737
|
+
},
|
|
738
|
+
],
|
|
739
|
+
});
|
|
740
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
741
|
+
const table = new TablePage(renderResult);
|
|
742
|
+
// Verify sortable columns have sort indicators
|
|
743
|
+
expect(table.header.isColumnSortable(1)).toBe(true); // firstName
|
|
744
|
+
expect(table.header.isColumnSortable(2)).toBe(false); // status
|
|
745
|
+
expect(table.header.isColumnSortable(3)).toBe(true); // payment
|
|
746
|
+
// Sort a column and verify visual state
|
|
747
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
748
|
+
expect(firstNameHeader).toBeTruthy();
|
|
749
|
+
fireEvent.click(firstNameHeader);
|
|
750
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
751
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
752
|
+
});
|
|
753
|
+
it('should update sort indicators when sorting changes', () => {
|
|
754
|
+
const sortableConfig = createTestTableConfig({
|
|
755
|
+
columns: [
|
|
756
|
+
{
|
|
757
|
+
field: 'firstName',
|
|
758
|
+
title: 'First Name',
|
|
759
|
+
type: 'string',
|
|
760
|
+
sortable: true,
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
field: 'status',
|
|
764
|
+
title: 'Status',
|
|
765
|
+
type: 'string',
|
|
766
|
+
sortable: true,
|
|
767
|
+
},
|
|
768
|
+
],
|
|
769
|
+
});
|
|
770
|
+
const renderResult = renderTestTable(sortableConfig);
|
|
771
|
+
const table = new TablePage(renderResult);
|
|
772
|
+
// Initial state - no sorting
|
|
773
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
774
|
+
expect(table.header.isColumnSorted(2)).toBe(false);
|
|
775
|
+
// Sort firstName ascending (column 1 is First Name)
|
|
776
|
+
const firstNameHeader = table.getHeaderCellByIndex(1);
|
|
777
|
+
expect(firstNameHeader).toBeTruthy();
|
|
778
|
+
fireEvent.click(firstNameHeader);
|
|
779
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
780
|
+
expect(table.header.getColumnSortDirection(1)).toBe('asc');
|
|
781
|
+
expect(table.header.isColumnSorted(2)).toBe(false);
|
|
782
|
+
// Sort firstName descending
|
|
783
|
+
fireEvent.click(firstNameHeader);
|
|
784
|
+
expect(table.header.isColumnSorted(1)).toBe(true);
|
|
785
|
+
expect(table.header.getColumnSortDirection(1)).toBe('desc');
|
|
786
|
+
// Clear sorting
|
|
787
|
+
fireEvent.click(firstNameHeader);
|
|
788
|
+
expect(table.header.isColumnSorted(1)).toBe(false);
|
|
789
|
+
expect(table.header.getColumnSortDirection(1)).toBe(null);
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
//# sourceMappingURL=sorting.test.js.map
|