@rickcedwhat/playwright-smart-table 6.19.0 → 6.20.1-next.19d3cd3
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/engine/rowFinder.d.ts +7 -1
- package/dist/engine/rowFinder.js +154 -45
- package/dist/engine/rowResolution.d.ts +11 -4
- package/dist/engine/rowResolution.js +10 -5
- package/dist/engine/tableIteration.d.ts +1 -1
- package/dist/engine/tableIteration.js +38 -15
- package/dist/engine/tableMapper.js +10 -3
- package/dist/index.d.ts +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +6 -3
- package/dist/presets/glide/headers.js +6 -3
- package/dist/presets/mui.js +3 -1
- package/dist/smartRow.js +167 -62
- package/dist/strategies/contentReady.d.ts +13 -0
- package/dist/strategies/contentReady.js +29 -0
- package/dist/strategies/fill.js +0 -1
- package/dist/strategies/headers.js +6 -3
- package/dist/strategies/index.d.ts +7 -0
- package/dist/strategies/index.js +3 -0
- package/dist/strategies/pagination.js +11 -2
- package/dist/strategies/viewport.js +26 -19
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +68 -8
- package/dist/types.d.ts +67 -8
- package/dist/useTable.js +115 -24
- package/dist/utils/elementTracker.js +6 -3
- package/dist/utils/mergeTableConfig.js +4 -0
- package/package.json +6 -1
package/dist/useTable.js
CHANGED
|
@@ -145,8 +145,14 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
145
145
|
// Placeholder for the final table object
|
|
146
146
|
let finalTable = null;
|
|
147
147
|
// Helper factory
|
|
148
|
-
const _makeSmart = (rowLocator, map, rowIndex, tablePageIndex, barrier) => {
|
|
149
|
-
|
|
148
|
+
const _makeSmart = (rowLocator, map, rowIndex, tablePageIndex, barrier, rowSelector) => {
|
|
149
|
+
const effectiveLocator = rowSelector
|
|
150
|
+
? rootLocator.locator(rowSelector)
|
|
151
|
+
: rowLocator;
|
|
152
|
+
const sr = (0, smartRow_1.default)(effectiveLocator, map, rowIndex, config, rootLocator, resolve, finalTable, tablePageIndex, barrier);
|
|
153
|
+
if (rowSelector)
|
|
154
|
+
sr._selfHealing = true;
|
|
155
|
+
return sr;
|
|
150
156
|
};
|
|
151
157
|
const tableState = { currentPageIndex: 0, empty: false };
|
|
152
158
|
const rowFinder = new rowFinder_1.RowFinder(rootLocator, config, resolve, filterEngine, tableMapper, _makeSmart, tableState, (useBulk) => _advancePage(useBulk));
|
|
@@ -281,9 +287,10 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
281
287
|
await headerCell.scrollIntoViewIfNeeded();
|
|
282
288
|
},
|
|
283
289
|
getHeaders: async () => {
|
|
290
|
+
var _a;
|
|
284
291
|
log("getHeaders: fetching available columns");
|
|
285
292
|
const map = await tableMapper.getMap();
|
|
286
|
-
return
|
|
293
|
+
return [...map.keys(), ...Object.keys((_a = config.syntheticColumns) !== null && _a !== void 0 ? _a : {})];
|
|
287
294
|
},
|
|
288
295
|
getHeaderCell: async (columnName) => {
|
|
289
296
|
log(`getHeaderCell: column="${columnName}"`);
|
|
@@ -293,27 +300,95 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
293
300
|
throw _createColumnError(columnName, map, 'header cell');
|
|
294
301
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
295
302
|
},
|
|
296
|
-
countRows: async () => {
|
|
303
|
+
countRows: async (filters, options) => {
|
|
304
|
+
var _a, _b, _c, _d, _e;
|
|
297
305
|
if (tableState.empty)
|
|
298
306
|
return 0;
|
|
299
307
|
await _autoInit();
|
|
308
|
+
const filtersRecord = (filters !== null && filters !== void 0 ? filters : {});
|
|
309
|
+
const hasFilters = Object.keys(filtersRecord).length > 0;
|
|
310
|
+
const effectiveMaxPages = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
300
311
|
const pag = config.strategies.pagination;
|
|
301
|
-
const hasPagination =
|
|
312
|
+
const hasPagination = effectiveMaxPages > 1 && !!((pag === null || pag === void 0 ? void 0 : pag.goNext) || (pag === null || pag === void 0 ? void 0 : pag.goNextBulk));
|
|
313
|
+
const domFilters = {};
|
|
314
|
+
const overrideFilters = {};
|
|
315
|
+
const syntheticFilters = {};
|
|
316
|
+
if (hasFilters) {
|
|
317
|
+
for (const [key, value] of Object.entries(filtersRecord)) {
|
|
318
|
+
if ((_b = config.syntheticColumns) === null || _b === void 0 ? void 0 : _b[key]) {
|
|
319
|
+
syntheticFilters[key] = value;
|
|
320
|
+
}
|
|
321
|
+
else if ((_d = (_c = config.columnOverrides) === null || _c === void 0 ? void 0 : _c[key]) === null || _d === void 0 ? void 0 : _d.read) {
|
|
322
|
+
overrideFilters[key] = value;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
domFilters[key] = value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const hasPostFilters = Object.keys(overrideFilters).length > 0 || Object.keys(syntheticFilters).length > 0;
|
|
330
|
+
const resolveRows = () => {
|
|
331
|
+
var _a;
|
|
332
|
+
let rows = resolve(config.rowSelector, rootLocator);
|
|
333
|
+
if (Object.keys(domFilters).length > 0) {
|
|
334
|
+
const map = tableMapper.getMapSync();
|
|
335
|
+
if (!map)
|
|
336
|
+
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
337
|
+
rows = filterEngine.applyFilters(rows, domFilters, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, rootLocator.page(), rootLocator);
|
|
338
|
+
}
|
|
339
|
+
return rows;
|
|
340
|
+
};
|
|
341
|
+
const countPostFilterMatches = async (candidates, indices) => {
|
|
342
|
+
var _a;
|
|
343
|
+
if (!hasPostFilters)
|
|
344
|
+
return indices.length;
|
|
345
|
+
const map = tableMapper.getMapSync();
|
|
346
|
+
if (!map)
|
|
347
|
+
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
348
|
+
const exact = (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false;
|
|
349
|
+
const hasOverrides = Object.keys(overrideFilters).length > 0;
|
|
350
|
+
const hasSynthetics = Object.keys(syntheticFilters).length > 0;
|
|
351
|
+
let count = 0;
|
|
352
|
+
for (const idx of indices) {
|
|
353
|
+
const row = candidates[idx];
|
|
354
|
+
if (hasOverrides && !await rowFinder.matchesOverrideFilters(row, overrideFilters, map, exact))
|
|
355
|
+
continue;
|
|
356
|
+
if (hasSynthetics && !await rowFinder.matchesSyntheticFilters(row, syntheticFilters, map, exact))
|
|
357
|
+
continue;
|
|
358
|
+
count++;
|
|
359
|
+
}
|
|
360
|
+
return count;
|
|
361
|
+
};
|
|
362
|
+
// Wait for table to finish loading before counting
|
|
363
|
+
const isTableLoading = (_e = config.strategies.loading) === null || _e === void 0 ? void 0 : _e.isTableLoading;
|
|
364
|
+
if (isTableLoading) {
|
|
365
|
+
const ctx = createStrategyContext();
|
|
366
|
+
while (await isTableLoading(ctx)) {
|
|
367
|
+
log('countRows: table is loading... waiting');
|
|
368
|
+
await rootLocator.page().waitForTimeout(200);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
302
371
|
if (!hasPagination) {
|
|
303
|
-
log(
|
|
304
|
-
|
|
372
|
+
log(`countRows: counting rows in current viewport (no pagination)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
373
|
+
if (!hasPostFilters)
|
|
374
|
+
return resolveRows().count();
|
|
375
|
+
const all = await resolveRows().all();
|
|
376
|
+
return countPostFilterMatches(all, all.map((_, i) => i));
|
|
305
377
|
}
|
|
306
|
-
log(`countRows: paginating up to ${
|
|
378
|
+
log(`countRows: paginating up to ${effectiveMaxPages} page(s)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
307
379
|
const tracker = new elementTracker_1.ElementTracker('countRows');
|
|
308
380
|
let total = 0;
|
|
309
381
|
let pagesScanned = 1;
|
|
310
382
|
let paginationError;
|
|
311
383
|
try {
|
|
312
384
|
while (true) {
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
385
|
+
const rowLocators = resolveRows();
|
|
386
|
+
const newIndices = await tracker.getUnseenIndices(rowLocators);
|
|
387
|
+
const candidates = await rowLocators.all();
|
|
388
|
+
const matched = await countPostFilterMatches(candidates, newIndices);
|
|
389
|
+
total += matched;
|
|
390
|
+
log(`countRows: page ${pagesScanned} — ${matched} row(s) (running total: ${total})`);
|
|
391
|
+
if (pagesScanned >= effectiveMaxPages)
|
|
317
392
|
break;
|
|
318
393
|
if (!await _advancePage(false))
|
|
319
394
|
break;
|
|
@@ -370,7 +445,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
370
445
|
reset: async () => {
|
|
371
446
|
var _a;
|
|
372
447
|
log("Resetting table...");
|
|
373
|
-
await config.onReset(createStrategyContext());
|
|
374
448
|
if ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst) {
|
|
375
449
|
log("Auto-navigating to first page...");
|
|
376
450
|
await config.strategies.pagination.goToFirst(createStrategyContext());
|
|
@@ -383,6 +457,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
383
457
|
tableMapper.clear();
|
|
384
458
|
log("Table reset complete. Calling autoInit to restore state.");
|
|
385
459
|
await _autoInit();
|
|
460
|
+
await config.onReset(createStrategyContext());
|
|
386
461
|
},
|
|
387
462
|
revalidate: async () => {
|
|
388
463
|
log("Revalidating table structure...");
|
|
@@ -392,6 +467,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
392
467
|
},
|
|
393
468
|
getRow: (filters, options = { exact: false }) => {
|
|
394
469
|
log(`getRow: filters=${safeStringify(filters)} exact=${options.exact}`);
|
|
470
|
+
const syntheticKeys = Object.keys(filters).filter(k => { var _a; return (_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[k]; });
|
|
471
|
+
if (syntheticKeys.length > 0) {
|
|
472
|
+
throw new Error(`getRow() cannot filter by synthetic column(s): ${syntheticKeys.join(', ')}. ` +
|
|
473
|
+
`Use findRow() instead — synthetic columns require async evaluation.`);
|
|
474
|
+
}
|
|
395
475
|
const map = tableMapper.getMapSync();
|
|
396
476
|
if (!map)
|
|
397
477
|
throw new Error('Initialization Error: You attempted to access a row before the table structure was mapped. Please call "await table.init()" once before using synchronous row access.');
|
|
@@ -420,12 +500,19 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
420
500
|
throw new Error(`findRowByIndex(${index}) requires a strategies.resolveRowIndex to identify rows by their logical index. ` +
|
|
421
501
|
`Configure one (e.g. read the grid's row-index attribute), or use getRowByIndex() (current render window) / findRow(filters).`);
|
|
422
502
|
}
|
|
503
|
+
let foundSelector;
|
|
423
504
|
// The currently-mounted row whose logical index === index, if any.
|
|
424
505
|
const findMounted = async () => {
|
|
425
506
|
const rows = await resolve(config.rowSelector, rootLocator).all();
|
|
426
507
|
for (const r of rows) {
|
|
427
|
-
|
|
508
|
+
const result = await resolveRI(r);
|
|
509
|
+
if (result === undefined)
|
|
510
|
+
continue;
|
|
511
|
+
const normalized = (0, rowResolution_1.normalizeRowIndexResult)(result);
|
|
512
|
+
if (normalized.index === index) {
|
|
513
|
+
foundSelector = normalized.selector;
|
|
428
514
|
return r;
|
|
515
|
+
}
|
|
429
516
|
}
|
|
430
517
|
return null;
|
|
431
518
|
};
|
|
@@ -454,7 +541,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
454
541
|
`Ensure a viewport.scrollToRow or pagination can bring it into view (searched up to maxPages=${(_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages}).`);
|
|
455
542
|
}
|
|
456
543
|
await (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
457
|
-
return _makeSmart(loc, map, index, tableState.currentPageIndex);
|
|
544
|
+
return _makeSmart(loc, map, index, tableState.currentPageIndex, undefined, foundSelector);
|
|
458
545
|
},
|
|
459
546
|
findRow: async (filters, options) => {
|
|
460
547
|
log(`findRow: filters=${safeStringify(filters)} options=${safeStringify(options)}`);
|
|
@@ -474,7 +561,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
474
561
|
},
|
|
475
562
|
sorting: {
|
|
476
563
|
apply: async (columnName, direction) => {
|
|
477
|
-
var _a;
|
|
564
|
+
var _a, _b, _c, _d, _e;
|
|
478
565
|
await _autoInit();
|
|
479
566
|
if (!config.strategies.sorting)
|
|
480
567
|
throw new Error('No sorting strategy has been configured.');
|
|
@@ -489,13 +576,16 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
489
576
|
}
|
|
490
577
|
await config.strategies.sorting.doSort({ columnName, direction, context });
|
|
491
578
|
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
492
|
-
const
|
|
579
|
+
const timeout = (_b = config.strategies.loading.sortStabilizationTimeout) !== null && _b !== void 0 ? _b : 10000;
|
|
580
|
+
const poll = (_c = config.strategies.loading.sortStabilizationPollInterval) !== null && _c !== void 0 ? _c : 100;
|
|
581
|
+
const deadline = Date.now() + timeout;
|
|
493
582
|
while (Date.now() < deadline && await config.strategies.loading.isTableLoading(context)) {
|
|
494
|
-
await rootLocator.page().waitForTimeout(
|
|
583
|
+
await rootLocator.page().waitForTimeout(poll);
|
|
495
584
|
}
|
|
496
585
|
}
|
|
497
586
|
else {
|
|
498
|
-
|
|
587
|
+
const fallback = (_e = (_d = config.strategies.loading) === null || _d === void 0 ? void 0 : _d.sortStabilizationFallbackDelay) !== null && _e !== void 0 ? _e : 200;
|
|
588
|
+
await rootLocator.page().waitForTimeout(fallback);
|
|
499
589
|
}
|
|
500
590
|
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
501
591
|
const newState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
@@ -535,8 +625,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
535
625
|
const barrier = new navigationBarrier_1.NavigationBarrier(newIndices.length);
|
|
536
626
|
for (const idx of newIndices) {
|
|
537
627
|
// index = visit-order counter; rowIndex = logical/data index (B-hybrid, #362).
|
|
538
|
-
const
|
|
539
|
-
const
|
|
628
|
+
const resolved = yield __await((0, rowResolution_1.resolveLogicalRowIndex)(pageRows[idx], config, () => rowIndex));
|
|
629
|
+
const logical = (_b = resolved === null || resolved === void 0 ? void 0 : resolved.index) !== null && _b !== void 0 ? _b : rowIndex;
|
|
630
|
+
const sr = _makeSmart(pageRows[idx], map, logical, tableState.currentPageIndex, barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
|
|
540
631
|
// Iterator rows share one per-page DOM snapshot (positional locators); disable
|
|
541
632
|
// toJSON's scroll-back recovery so reading one row can't invalidate the others (#366).
|
|
542
633
|
sr._inBatch = true;
|
|
@@ -563,7 +654,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
563
654
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
564
655
|
getMap: () => tableMapper.getMapSync(),
|
|
565
656
|
advancePage: _advancePage,
|
|
566
|
-
makeSmartRow: (loc, map, idx, pageIdx, barrier) => _makeSmart(loc, map, idx, pageIdx, barrier),
|
|
657
|
+
makeSmartRow: (loc, map, idx, pageIdx, barrier, sel) => _makeSmart(loc, map, idx, pageIdx, barrier, sel),
|
|
567
658
|
createSmartRowArray: smartRowArray_1.createSmartRowArray,
|
|
568
659
|
config,
|
|
569
660
|
getPage: () => rootLocator.page(),
|
|
@@ -578,7 +669,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
578
669
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
579
670
|
getMap: () => tableMapper.getMapSync(),
|
|
580
671
|
advancePage: _advancePage,
|
|
581
|
-
makeSmartRow: (loc, map, idx, pageIdx, barrier) => _makeSmart(loc, map, idx, pageIdx, barrier),
|
|
672
|
+
makeSmartRow: (loc, map, idx, pageIdx, barrier, sel) => _makeSmart(loc, map, idx, pageIdx, barrier, sel),
|
|
582
673
|
createSmartRowArray: smartRowArray_1.createSmartRowArray,
|
|
583
674
|
config,
|
|
584
675
|
getPage: () => rootLocator.page(),
|
|
@@ -603,7 +694,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
603
694
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
604
695
|
getMap: () => tableMapper.getMapSync(),
|
|
605
696
|
advancePage: _advancePage,
|
|
606
|
-
makeSmartRow: (loc, map, idx, pageIdx, barrier) => _makeSmart(loc, map, idx, pageIdx, barrier),
|
|
697
|
+
makeSmartRow: (loc, map, idx, pageIdx, barrier, sel) => _makeSmart(loc, map, idx, pageIdx, barrier, sel),
|
|
607
698
|
createSmartRowArray: smartRowArray_1.createSmartRowArray,
|
|
608
699
|
config,
|
|
609
700
|
getPage: () => rootLocator.page(),
|
|
@@ -18,7 +18,8 @@ class ElementTracker {
|
|
|
18
18
|
const seenMap = win[trackerId];
|
|
19
19
|
const newIndices = [];
|
|
20
20
|
elements.forEach((el, index) => {
|
|
21
|
-
const
|
|
21
|
+
const htmlEl = el;
|
|
22
|
+
const signature = htmlEl.offsetTop + '|' + (el.textContent || '');
|
|
22
23
|
if (seenMap.get(el) !== signature) {
|
|
23
24
|
newIndices.push(index);
|
|
24
25
|
}
|
|
@@ -39,8 +40,10 @@ class ElementTracker {
|
|
|
39
40
|
const seenMap = win[trackerId];
|
|
40
41
|
for (const index of indicesToCommit) {
|
|
41
42
|
const el = elements[index];
|
|
42
|
-
if (el)
|
|
43
|
-
|
|
43
|
+
if (el) {
|
|
44
|
+
const htmlEl = el;
|
|
45
|
+
seenMap.set(el, htmlEl.offsetTop + '|' + (el.textContent || ''));
|
|
46
|
+
}
|
|
44
47
|
}
|
|
45
48
|
}, [this.id, indices]);
|
|
46
49
|
}
|
|
@@ -56,5 +56,9 @@ function mergeTableConfig(base, overrides) {
|
|
|
56
56
|
if (base.columnOverrides || overrides.columnOverrides) {
|
|
57
57
|
result.columnOverrides = Object.assign(Object.assign({}, base.columnOverrides), overrides.columnOverrides);
|
|
58
58
|
}
|
|
59
|
+
// Deep-merge syntheticColumns (override keys replace, preset keys retained)
|
|
60
|
+
if (base.syntheticColumns || overrides.syntheticColumns) {
|
|
61
|
+
result.syntheticColumns = Object.assign(Object.assign({}, base.syntheticColumns), overrides.syntheticColumns);
|
|
62
|
+
}
|
|
59
63
|
return result;
|
|
60
64
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.20.1-next.19d3cd3",
|
|
4
4
|
"description": "Smart, column-aware table interactions for Playwright",
|
|
5
5
|
"author": "Cedrick Catalan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -110,6 +110,11 @@
|
|
|
110
110
|
"peerDependencies": {
|
|
111
111
|
"@playwright/test": "^1.40.0"
|
|
112
112
|
},
|
|
113
|
+
"peerDependenciesMeta": {
|
|
114
|
+
"@playwright/test": {
|
|
115
|
+
"optional": true
|
|
116
|
+
}
|
|
117
|
+
},
|
|
113
118
|
"devDependencies": {
|
|
114
119
|
"@commitlint/cli": "^21.0.0",
|
|
115
120
|
"@commitlint/config-conventional": "^21.0.0",
|