@htlkg/components 0.0.1
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/dist/composables/index.js +388 -0
- package/dist/composables/index.js.map +1 -0
- package/package.json +41 -0
- package/src/composables/index.ts +6 -0
- package/src/composables/useForm.test.ts +229 -0
- package/src/composables/useForm.ts +130 -0
- package/src/composables/useFormValidation.test.ts +189 -0
- package/src/composables/useFormValidation.ts +83 -0
- package/src/composables/useModal.property.test.ts +164 -0
- package/src/composables/useModal.ts +43 -0
- package/src/composables/useNotifications.test.ts +166 -0
- package/src/composables/useNotifications.ts +81 -0
- package/src/composables/useTable.property.test.ts +198 -0
- package/src/composables/useTable.ts +134 -0
- package/src/composables/useTabs.property.test.ts +247 -0
- package/src/composables/useTabs.ts +101 -0
- package/src/data/Chart.demo.vue +340 -0
- package/src/data/Chart.md +525 -0
- package/src/data/Chart.vue +133 -0
- package/src/data/DataList.md +80 -0
- package/src/data/DataList.test.ts +69 -0
- package/src/data/DataList.vue +46 -0
- package/src/data/SearchableSelect.md +107 -0
- package/src/data/SearchableSelect.vue +124 -0
- package/src/data/Table.demo.vue +296 -0
- package/src/data/Table.md +588 -0
- package/src/data/Table.property.test.ts +548 -0
- package/src/data/Table.test.ts +562 -0
- package/src/data/Table.unit.test.ts +544 -0
- package/src/data/Table.vue +321 -0
- package/src/data/index.ts +5 -0
- package/src/domain/BrandCard.md +81 -0
- package/src/domain/BrandCard.vue +63 -0
- package/src/domain/BrandSelector.md +84 -0
- package/src/domain/BrandSelector.vue +65 -0
- package/src/domain/ProductBadge.md +60 -0
- package/src/domain/ProductBadge.vue +47 -0
- package/src/domain/UserAvatar.md +84 -0
- package/src/domain/UserAvatar.vue +60 -0
- package/src/domain/domain-components.property.test.ts +449 -0
- package/src/domain/index.ts +4 -0
- package/src/forms/DateRange.demo.vue +273 -0
- package/src/forms/DateRange.md +337 -0
- package/src/forms/DateRange.vue +110 -0
- package/src/forms/JsonSchemaForm.demo.vue +549 -0
- package/src/forms/JsonSchemaForm.md +112 -0
- package/src/forms/JsonSchemaForm.property.test.ts +817 -0
- package/src/forms/JsonSchemaForm.test.ts +601 -0
- package/src/forms/JsonSchemaForm.unit.test.ts +801 -0
- package/src/forms/JsonSchemaForm.vue +615 -0
- package/src/forms/index.ts +3 -0
- package/src/index.ts +17 -0
- package/src/navigation/Breadcrumbs.demo.vue +142 -0
- package/src/navigation/Breadcrumbs.md +102 -0
- package/src/navigation/Breadcrumbs.test.ts +69 -0
- package/src/navigation/Breadcrumbs.vue +58 -0
- package/src/navigation/Stepper.demo.vue +337 -0
- package/src/navigation/Stepper.md +174 -0
- package/src/navigation/Stepper.vue +146 -0
- package/src/navigation/Tabs.demo.vue +293 -0
- package/src/navigation/Tabs.md +163 -0
- package/src/navigation/Tabs.test.ts +176 -0
- package/src/navigation/Tabs.vue +104 -0
- package/src/navigation/index.ts +5 -0
- package/src/overlays/Alert.demo.vue +377 -0
- package/src/overlays/Alert.md +248 -0
- package/src/overlays/Alert.test.ts +166 -0
- package/src/overlays/Alert.vue +70 -0
- package/src/overlays/Drawer.md +140 -0
- package/src/overlays/Drawer.test.ts +92 -0
- package/src/overlays/Drawer.vue +76 -0
- package/src/overlays/Modal.demo.vue +149 -0
- package/src/overlays/Modal.md +385 -0
- package/src/overlays/Modal.test.ts +128 -0
- package/src/overlays/Modal.vue +86 -0
- package/src/overlays/Notification.md +150 -0
- package/src/overlays/Notification.test.ts +96 -0
- package/src/overlays/Notification.vue +58 -0
- package/src/overlays/index.ts +4 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import * as fc from 'fast-check';
|
|
3
|
+
import { mount } from '@vue/test-utils';
|
|
4
|
+
import Table from './Table.vue';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Property tests for Table component
|
|
8
|
+
* Tests with random data generation to find edge cases
|
|
9
|
+
*/
|
|
10
|
+
describe('Table property tests', () => {
|
|
11
|
+
const globalStubs = {
|
|
12
|
+
uiTable: true,
|
|
13
|
+
uiPagination: true,
|
|
14
|
+
uiDropdown: true,
|
|
15
|
+
uiSmartFilterMultipleV2: true,
|
|
16
|
+
uiNoResults: true
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
it('should handle any number of columns', () => {
|
|
20
|
+
fc.assert(
|
|
21
|
+
fc.property(
|
|
22
|
+
fc.array(
|
|
23
|
+
fc.record({
|
|
24
|
+
name: fc.string({ minLength: 1, maxLength: 20 }),
|
|
25
|
+
value: fc.string({ minLength: 1, maxLength: 20 })
|
|
26
|
+
}),
|
|
27
|
+
{ minLength: 1, maxLength: 10 }
|
|
28
|
+
),
|
|
29
|
+
(columns) => {
|
|
30
|
+
const items = [{ id: 1 }];
|
|
31
|
+
|
|
32
|
+
const wrapper = mount(Table, {
|
|
33
|
+
props: { items, columns },
|
|
34
|
+
global: { stubs: globalStubs }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const vm = wrapper.vm as any;
|
|
38
|
+
expect(vm.tableHeader).toHaveLength(columns.length);
|
|
39
|
+
expect(vm.tableItems[0].row).toHaveLength(columns.length);
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
{ numRuns: 100 }
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should handle any number of items', () => {
|
|
47
|
+
fc.assert(
|
|
48
|
+
fc.property(
|
|
49
|
+
fc.array(
|
|
50
|
+
fc.record({
|
|
51
|
+
id: fc.integer({ min: 1, max: 10000 }),
|
|
52
|
+
name: fc.string({ minLength: 1, maxLength: 50 }),
|
|
53
|
+
value: fc.integer()
|
|
54
|
+
}),
|
|
55
|
+
{ minLength: 0, maxLength: 100 }
|
|
56
|
+
),
|
|
57
|
+
(items) => {
|
|
58
|
+
const columns = [
|
|
59
|
+
{ name: 'ID', value: 'id' },
|
|
60
|
+
{ name: 'Name', value: 'name' },
|
|
61
|
+
{ name: 'Value', value: 'value' }
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const wrapper = mount(Table, {
|
|
65
|
+
props: { items, columns },
|
|
66
|
+
global: { stubs: globalStubs }
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const vm = wrapper.vm as any;
|
|
70
|
+
expect(vm.tableItems).toHaveLength(items.length);
|
|
71
|
+
|
|
72
|
+
if (items.length === 0) {
|
|
73
|
+
expect(vm.showInitialNoResults).toBe(true);
|
|
74
|
+
} else {
|
|
75
|
+
expect(vm.showTable).toBe(true);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
{ numRuns: 100 }
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should correctly map item properties to table rows', () => {
|
|
84
|
+
fc.assert(
|
|
85
|
+
fc.property(
|
|
86
|
+
fc.array(
|
|
87
|
+
fc.record({
|
|
88
|
+
id: fc.integer(),
|
|
89
|
+
field1: fc.string(),
|
|
90
|
+
field2: fc.integer(),
|
|
91
|
+
field3: fc.boolean()
|
|
92
|
+
}),
|
|
93
|
+
{ minLength: 1, maxLength: 10 }
|
|
94
|
+
),
|
|
95
|
+
(items) => {
|
|
96
|
+
const columns = [
|
|
97
|
+
{ name: 'Field 1', value: 'field1' },
|
|
98
|
+
{ name: 'Field 2', value: 'field2' },
|
|
99
|
+
{ name: 'Field 3', value: 'field3' }
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
const wrapper = mount(Table, {
|
|
103
|
+
props: { items, columns },
|
|
104
|
+
global: { stubs: globalStubs }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const vm = wrapper.vm as any;
|
|
108
|
+
|
|
109
|
+
items.forEach((item, index) => {
|
|
110
|
+
const tableItem = vm.tableItems[index];
|
|
111
|
+
expect(tableItem.id).toBe(item.id);
|
|
112
|
+
expect(tableItem.row[0]).toBe(String(item.field1));
|
|
113
|
+
expect(tableItem.row[1]).toBe(String(item.field2));
|
|
114
|
+
expect(tableItem.row[2]).toBe(String(item.field3));
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
{ numRuns: 100 }
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should handle page changes for any valid page number', () => {
|
|
123
|
+
fc.assert(
|
|
124
|
+
fc.property(
|
|
125
|
+
fc.integer({ min: 1, max: 100 }),
|
|
126
|
+
fc.integer({ min: 1, max: 100 }),
|
|
127
|
+
(currentPage, newPage) => {
|
|
128
|
+
const items = Array.from({ length: 50 }, (_, i) => ({ id: i + 1 }));
|
|
129
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
130
|
+
|
|
131
|
+
const wrapper = mount(Table, {
|
|
132
|
+
props: { items, columns, currentPage, totalPages: 100 },
|
|
133
|
+
global: { stubs: globalStubs }
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const vm = wrapper.vm as any;
|
|
137
|
+
vm.handleChangePage(newPage);
|
|
138
|
+
|
|
139
|
+
expect(wrapper.emitted('update:currentPage')).toBeDefined();
|
|
140
|
+
expect(wrapper.emitted('update:currentPage')?.[0]).toEqual([newPage]);
|
|
141
|
+
expect(wrapper.emitted('page-changed')?.[0]).toEqual([newPage]);
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
{ numRuns: 100 }
|
|
145
|
+
);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should handle page size changes for any valid size', () => {
|
|
149
|
+
fc.assert(
|
|
150
|
+
fc.property(
|
|
151
|
+
fc.constantFrom(10, 25, 50, 100),
|
|
152
|
+
(pageSize) => {
|
|
153
|
+
const items = Array.from({ length: 100 }, (_, i) => ({ id: i + 1 }));
|
|
154
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
155
|
+
|
|
156
|
+
const wrapper = mount(Table, {
|
|
157
|
+
props: { items, columns, itemsPerPage: 10 },
|
|
158
|
+
global: { stubs: globalStubs }
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const vm = wrapper.vm as any;
|
|
162
|
+
vm.handleChangePageSize({ value: String(pageSize) });
|
|
163
|
+
|
|
164
|
+
expect(wrapper.emitted('update:itemsPerPage')?.[0]).toEqual([pageSize]);
|
|
165
|
+
expect(wrapper.emitted('update:currentPage')?.[0]).toEqual([1]);
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
{ numRuns: 50 }
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should handle sorting by any column', () => {
|
|
173
|
+
fc.assert(
|
|
174
|
+
fc.property(
|
|
175
|
+
fc.array(
|
|
176
|
+
fc.record({
|
|
177
|
+
name: fc.string({ minLength: 1, maxLength: 20 }),
|
|
178
|
+
value: fc.string({ minLength: 1, maxLength: 20 })
|
|
179
|
+
}),
|
|
180
|
+
{ minLength: 1, maxLength: 5 }
|
|
181
|
+
),
|
|
182
|
+
fc.constantFrom('asc', 'desc'),
|
|
183
|
+
(columns, direction) => {
|
|
184
|
+
const items = [{ id: 1 }];
|
|
185
|
+
|
|
186
|
+
const wrapper = mount(Table, {
|
|
187
|
+
props: { items, columns },
|
|
188
|
+
global: { stubs: globalStubs }
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const vm = wrapper.vm as any;
|
|
192
|
+
const columnToSort = columns[0].value;
|
|
193
|
+
|
|
194
|
+
vm.handleOrderBy({ value: columnToSort, orderDirection: direction });
|
|
195
|
+
|
|
196
|
+
expect(wrapper.emitted('update:orderedBy')?.[0]).toEqual([columnToSort]);
|
|
197
|
+
expect(wrapper.emitted('update:orderDirection')?.[0]).toEqual([direction]);
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
{ numRuns: 100 }
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should handle column visibility for any column index', () => {
|
|
205
|
+
fc.assert(
|
|
206
|
+
fc.property(
|
|
207
|
+
fc.integer({ min: 3, max: 10 }),
|
|
208
|
+
fc.integer({ min: 0, max: 9 }),
|
|
209
|
+
(numColumns, columnIndex) => {
|
|
210
|
+
const columns = Array.from({ length: numColumns }, (_, i) => ({
|
|
211
|
+
name: `Column ${i}`,
|
|
212
|
+
value: `col${i}`
|
|
213
|
+
}));
|
|
214
|
+
const items = [{ id: 1 }];
|
|
215
|
+
|
|
216
|
+
if (columnIndex >= numColumns) return;
|
|
217
|
+
|
|
218
|
+
const wrapper = mount(Table, {
|
|
219
|
+
props: { items, columns },
|
|
220
|
+
global: { stubs: globalStubs }
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const vm = wrapper.vm as any;
|
|
224
|
+
|
|
225
|
+
// Hide column
|
|
226
|
+
vm.handleColumnsVisibilityChanged({ index: columnIndex, hidden: true });
|
|
227
|
+
expect(vm.hiddenColumns).toContain(columnIndex);
|
|
228
|
+
|
|
229
|
+
// Show column
|
|
230
|
+
vm.handleColumnsVisibilityChanged({ index: columnIndex, hidden: false });
|
|
231
|
+
expect(vm.hiddenColumns).not.toContain(columnIndex);
|
|
232
|
+
}
|
|
233
|
+
),
|
|
234
|
+
{ numRuns: 100 }
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should handle selection of any subset of items', () => {
|
|
239
|
+
fc.assert(
|
|
240
|
+
fc.property(
|
|
241
|
+
fc.array(
|
|
242
|
+
fc.record({
|
|
243
|
+
id: fc.integer({ min: 1, max: 1000 }),
|
|
244
|
+
name: fc.string()
|
|
245
|
+
}),
|
|
246
|
+
{ minLength: 1, maxLength: 20 }
|
|
247
|
+
),
|
|
248
|
+
fc.array(fc.integer({ min: 0, max: 19 }), { maxLength: 10 }),
|
|
249
|
+
(items, selectedIndices) => {
|
|
250
|
+
const columns = [
|
|
251
|
+
{ name: 'ID', value: 'id' },
|
|
252
|
+
{ name: 'Name', value: 'name' }
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
const wrapper = mount(Table, {
|
|
256
|
+
props: { items, columns },
|
|
257
|
+
global: { stubs: globalStubs }
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const vm = wrapper.vm as any;
|
|
261
|
+
|
|
262
|
+
// Select items by indices
|
|
263
|
+
const selectedIds = selectedIndices
|
|
264
|
+
.filter(i => i < items.length)
|
|
265
|
+
.map(i => items[i].id);
|
|
266
|
+
|
|
267
|
+
// Get unique IDs (Set removes duplicates)
|
|
268
|
+
const uniqueSelectedIds = [...new Set(selectedIds)];
|
|
269
|
+
|
|
270
|
+
vm.handleTableAction({ action: 'test', items: selectedIds });
|
|
271
|
+
|
|
272
|
+
expect(vm.selectedItemIds.size).toBe(uniqueSelectedIds.length);
|
|
273
|
+
uniqueSelectedIds.forEach(id => {
|
|
274
|
+
expect(vm.selectedItemIds.has(id)).toBe(true);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
),
|
|
278
|
+
{ numRuns: 100 }
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should emit correct events for any action', () => {
|
|
283
|
+
fc.assert(
|
|
284
|
+
fc.property(
|
|
285
|
+
fc.string({ minLength: 1, maxLength: 20 }),
|
|
286
|
+
fc.array(fc.integer({ min: 1, max: 100 }), { maxLength: 10 }),
|
|
287
|
+
(action, itemIds) => {
|
|
288
|
+
const items = itemIds.map(id => ({ id, name: `Item ${id}` }));
|
|
289
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
290
|
+
|
|
291
|
+
const wrapper = mount(Table, {
|
|
292
|
+
props: { items, columns },
|
|
293
|
+
global: { stubs: globalStubs }
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const vm = wrapper.vm as any;
|
|
297
|
+
vm.handleTableAction({ action, items: itemIds });
|
|
298
|
+
|
|
299
|
+
expect(wrapper.emitted('table-action')).toBeDefined();
|
|
300
|
+
expect(wrapper.emitted('table-action')?.[0]).toEqual([
|
|
301
|
+
{ action, items: itemIds }
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
),
|
|
305
|
+
{ numRuns: 100 }
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should handle items with missing properties gracefully', () => {
|
|
310
|
+
fc.assert(
|
|
311
|
+
fc.property(
|
|
312
|
+
fc.array(
|
|
313
|
+
fc.record({
|
|
314
|
+
id: fc.option(fc.integer(), { nil: undefined }),
|
|
315
|
+
name: fc.option(fc.string(), { nil: undefined }),
|
|
316
|
+
email: fc.option(fc.string(), { nil: undefined })
|
|
317
|
+
}),
|
|
318
|
+
{ minLength: 1, maxLength: 10 }
|
|
319
|
+
),
|
|
320
|
+
(items) => {
|
|
321
|
+
const columns = [
|
|
322
|
+
{ name: 'ID', value: 'id' },
|
|
323
|
+
{ name: 'Name', value: 'name' },
|
|
324
|
+
{ name: 'Email', value: 'email' }
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
const wrapper = mount(Table, {
|
|
328
|
+
props: { items, columns },
|
|
329
|
+
global: { stubs: globalStubs }
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const vm = wrapper.vm as any;
|
|
333
|
+
|
|
334
|
+
// Should not throw and should handle undefined values
|
|
335
|
+
expect(vm.tableItems).toHaveLength(items.length);
|
|
336
|
+
vm.tableItems.forEach((tableItem: any) => {
|
|
337
|
+
expect(tableItem.row).toHaveLength(3);
|
|
338
|
+
tableItem.row.forEach((cell: any) => {
|
|
339
|
+
expect(typeof cell).toBe('string');
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
),
|
|
344
|
+
{ numRuns: 100 }
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('should maintain selection state across operations', () => {
|
|
349
|
+
fc.assert(
|
|
350
|
+
fc.property(
|
|
351
|
+
fc.array(
|
|
352
|
+
fc.record({
|
|
353
|
+
id: fc.integer({ min: 1, max: 100 }),
|
|
354
|
+
name: fc.string()
|
|
355
|
+
}),
|
|
356
|
+
{ minLength: 5, maxLength: 20 }
|
|
357
|
+
),
|
|
358
|
+
(items) => {
|
|
359
|
+
const columns = [
|
|
360
|
+
{ name: 'ID', value: 'id' },
|
|
361
|
+
{ name: 'Name', value: 'name' }
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
const wrapper = mount(Table, {
|
|
365
|
+
props: { items, columns },
|
|
366
|
+
global: { stubs: globalStubs }
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const vm = wrapper.vm as any;
|
|
370
|
+
|
|
371
|
+
// Select all items
|
|
372
|
+
vm.handleSelectAllItems();
|
|
373
|
+
const initialSize = vm.selectedItemIds.size;
|
|
374
|
+
|
|
375
|
+
// Size should match unique IDs (Set removes duplicates)
|
|
376
|
+
const uniqueIds = new Set(items.map(item => item.id));
|
|
377
|
+
expect(initialSize).toBe(uniqueIds.size);
|
|
378
|
+
|
|
379
|
+
// Deselect all
|
|
380
|
+
vm.handleDeselectAllItems();
|
|
381
|
+
expect(vm.selectedItemIds.size).toBe(0);
|
|
382
|
+
|
|
383
|
+
// Select all again
|
|
384
|
+
vm.handleSelectAllItems();
|
|
385
|
+
expect(vm.selectedItemIds.size).toBe(initialSize);
|
|
386
|
+
}
|
|
387
|
+
),
|
|
388
|
+
{ numRuns: 100 }
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('should handle loading state correctly', () => {
|
|
393
|
+
fc.assert(
|
|
394
|
+
fc.property(
|
|
395
|
+
fc.boolean(),
|
|
396
|
+
fc.array(fc.record({ id: fc.integer() }), { maxLength: 10 }),
|
|
397
|
+
(loading, items) => {
|
|
398
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
399
|
+
|
|
400
|
+
const wrapper = mount(Table, {
|
|
401
|
+
props: { items, columns, loading },
|
|
402
|
+
global: { stubs: globalStubs }
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const vm = wrapper.vm as any;
|
|
406
|
+
|
|
407
|
+
if (loading) {
|
|
408
|
+
expect(vm.showTable).toBe(true);
|
|
409
|
+
expect(vm.showInitialNoResults).toBe(false);
|
|
410
|
+
} else if (items.length === 0) {
|
|
411
|
+
expect(vm.showInitialNoResults).toBe(true);
|
|
412
|
+
} else {
|
|
413
|
+
expect(vm.showTable).toBe(true);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
),
|
|
417
|
+
{ numRuns: 100 }
|
|
418
|
+
);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should reset selection when sorting or paginating', () => {
|
|
422
|
+
fc.assert(
|
|
423
|
+
fc.property(
|
|
424
|
+
fc.array(
|
|
425
|
+
fc.record({ id: fc.integer({ min: 1, max: 100 }) }),
|
|
426
|
+
{ minLength: 5, maxLength: 20 }
|
|
427
|
+
),
|
|
428
|
+
fc.constantFrom('name', 'id', 'email'),
|
|
429
|
+
fc.integer({ min: 1, max: 10 }),
|
|
430
|
+
(items, sortField, newPage) => {
|
|
431
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
432
|
+
|
|
433
|
+
const wrapper = mount(Table, {
|
|
434
|
+
props: { items, columns },
|
|
435
|
+
global: { stubs: globalStubs }
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const vm = wrapper.vm as any;
|
|
439
|
+
|
|
440
|
+
// Select items
|
|
441
|
+
vm.handleSelectAllItems();
|
|
442
|
+
const uniqueIds = new Set(items.map(item => item.id));
|
|
443
|
+
expect(vm.selectedItemIds.size).toBe(uniqueIds.size);
|
|
444
|
+
|
|
445
|
+
// Sort - should reset
|
|
446
|
+
vm.handleOrderBy({ value: sortField, orderDirection: 'asc' });
|
|
447
|
+
expect(vm.resetSelected).toBe(true);
|
|
448
|
+
|
|
449
|
+
// Reset flag
|
|
450
|
+
vm.resetSelected = false;
|
|
451
|
+
|
|
452
|
+
// Change page - should reset
|
|
453
|
+
vm.handleChangePage(newPage);
|
|
454
|
+
expect(vm.resetSelected).toBe(true);
|
|
455
|
+
}
|
|
456
|
+
),
|
|
457
|
+
{ numRuns: 100 }
|
|
458
|
+
);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('should handle filters with any structure', () => {
|
|
462
|
+
fc.assert(
|
|
463
|
+
fc.property(
|
|
464
|
+
fc.dictionary(
|
|
465
|
+
fc.string({ minLength: 1, maxLength: 20 }),
|
|
466
|
+
fc.oneof(
|
|
467
|
+
fc.string(),
|
|
468
|
+
fc.integer(),
|
|
469
|
+
fc.boolean()
|
|
470
|
+
)
|
|
471
|
+
),
|
|
472
|
+
(filters) => {
|
|
473
|
+
const items = [{ id: 1 }];
|
|
474
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
475
|
+
|
|
476
|
+
const wrapper = mount(Table, {
|
|
477
|
+
props: { items, columns },
|
|
478
|
+
global: { stubs: globalStubs }
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const vm = wrapper.vm as any;
|
|
482
|
+
vm.handleSmartFiltersSent(filters);
|
|
483
|
+
|
|
484
|
+
expect(wrapper.emitted('smart-filters-sent')?.[0]).toEqual([filters]);
|
|
485
|
+
expect(wrapper.emitted('multiple-filters-applied')?.[0]).toEqual([filters]);
|
|
486
|
+
expect(vm.hasUserSearched).toBe(true);
|
|
487
|
+
}
|
|
488
|
+
),
|
|
489
|
+
{ numRuns: 100 }
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('should handle pagination calculations correctly', () => {
|
|
494
|
+
fc.assert(
|
|
495
|
+
fc.property(
|
|
496
|
+
fc.integer({ min: 0, max: 1000 }),
|
|
497
|
+
fc.constantFrom(10, 25, 50, 100),
|
|
498
|
+
(totalItems, itemsPerPage) => {
|
|
499
|
+
const items = Array.from({ length: Math.min(totalItems, itemsPerPage) }, (_, i) => ({ id: i }));
|
|
500
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
501
|
+
const totalPages = Math.ceil(totalItems / itemsPerPage) || 1;
|
|
502
|
+
|
|
503
|
+
const wrapper = mount(Table, {
|
|
504
|
+
props: {
|
|
505
|
+
items,
|
|
506
|
+
columns,
|
|
507
|
+
totalItems,
|
|
508
|
+
itemsPerPage,
|
|
509
|
+
totalPages
|
|
510
|
+
},
|
|
511
|
+
global: { stubs: globalStubs }
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const vm = wrapper.vm as any;
|
|
515
|
+
expect(vm.totalItems).toBe(totalItems);
|
|
516
|
+
expect(vm.itemsPerPage).toBe(itemsPerPage);
|
|
517
|
+
expect(vm.totalPages).toBe(totalPages);
|
|
518
|
+
}
|
|
519
|
+
),
|
|
520
|
+
{ numRuns: 100 }
|
|
521
|
+
);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('should expose all required methods', () => {
|
|
525
|
+
fc.assert(
|
|
526
|
+
fc.property(
|
|
527
|
+
fc.constant(null),
|
|
528
|
+
() => {
|
|
529
|
+
const items = [{ id: 1 }];
|
|
530
|
+
const columns = [{ name: 'ID', value: 'id' }];
|
|
531
|
+
|
|
532
|
+
const wrapper = mount(Table, {
|
|
533
|
+
props: { items, columns },
|
|
534
|
+
global: { stubs: globalStubs }
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
const vm = wrapper.vm as any;
|
|
538
|
+
|
|
539
|
+
// Check all exposed methods exist
|
|
540
|
+
expect(typeof vm.clearSelection).toBe('function');
|
|
541
|
+
expect(typeof vm.getHiddenColumns).toBe('function');
|
|
542
|
+
expect(typeof vm.getSelectedItems).toBe('function');
|
|
543
|
+
}
|
|
544
|
+
),
|
|
545
|
+
{ numRuns: 50 }
|
|
546
|
+
);
|
|
547
|
+
});
|
|
548
|
+
});
|