@rickcedwhat/playwright-smart-table 6.20.1 ā 6.21.0-next.3ab7a18
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 +1 -0
- package/dist/engine/rowFinder.js +73 -75
- package/dist/engine/scanPages.d.ts +58 -0
- package/dist/engine/scanPages.js +46 -0
- package/dist/engine/tableIteration.d.ts +1 -0
- package/dist/engine/tableIteration.js +40 -22
- package/dist/filterEngine.d.ts +4 -0
- package/dist/filterEngine.js +15 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +51 -10
- package/dist/plugins/index.js +14 -6
- package/dist/presets/glide/index.d.ts +1 -1
- package/dist/presets/glide/index.js +2 -2
- package/dist/presets/mui.js +15 -29
- package/dist/presets/rdg.d.ts +1 -1
- package/dist/presets/rdg.js +4 -4
- package/dist/smartRow.d.ts +2 -2
- package/dist/smartRow.js +163 -51
- package/dist/strategies/columns.d.ts +5 -9
- package/dist/strategies/columns.js +0 -10
- package/dist/strategies/contentReady.d.ts +20 -0
- package/dist/strategies/contentReady.js +62 -0
- package/dist/strategies/filter.d.ts +0 -4
- package/dist/strategies/filter.js +0 -16
- package/dist/strategies/headers.d.ts +5 -0
- package/dist/strategies/headers.js +17 -9
- package/dist/strategies/index.d.ts +23 -15
- package/dist/strategies/index.js +9 -5
- package/dist/strategies/pagination.js +11 -2
- package/dist/strategies/viewport.d.ts +4 -1
- package/dist/strategies/viewport.js +62 -32
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +77 -17
- package/dist/types.d.ts +74 -17
- package/dist/useTable.js +125 -56
- package/dist/utils/elementTracker.js +6 -3
- package/dist/utils/loadingWait.d.ts +15 -0
- package/dist/utils/loadingWait.js +43 -0
- package/dist/utils/pageIndex.d.ts +12 -0
- package/dist/utils/pageIndex.js +19 -0
- package/dist/utils/resolveCellLocator.d.ts +35 -0
- package/dist/utils/resolveCellLocator.js +52 -0
- package/package.json +1 -1
package/dist/useTable.js
CHANGED
|
@@ -28,10 +28,12 @@ const tableMapper_1 = require("./engine/tableMapper");
|
|
|
28
28
|
const rowFinder_1 = require("./engine/rowFinder");
|
|
29
29
|
const tableIteration_1 = require("./engine/tableIteration");
|
|
30
30
|
const rowResolution_1 = require("./engine/rowResolution");
|
|
31
|
+
const scanPages_1 = require("./engine/scanPages");
|
|
31
32
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
32
33
|
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
33
34
|
const elementTracker_1 = require("./utils/elementTracker");
|
|
34
|
-
const
|
|
35
|
+
const loadingWait_1 = require("./utils/loadingWait");
|
|
36
|
+
const pageIndex_1 = require("./utils/pageIndex");
|
|
35
37
|
// Helper to safely serialize objects containing functions for logging
|
|
36
38
|
const safeStringify = (obj) => {
|
|
37
39
|
try {
|
|
@@ -145,11 +147,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
145
147
|
// Placeholder for the final table object
|
|
146
148
|
let finalTable = null;
|
|
147
149
|
// Helper factory
|
|
148
|
-
const _makeSmart = (rowLocator, map, rowIndex, tablePageIndex, barrier, rowSelector) => {
|
|
150
|
+
const _makeSmart = (rowLocator, map, rowIndex, tablePageIndex, barrier, rowSelector, renderWindowPosition = false) => {
|
|
149
151
|
const effectiveLocator = rowSelector
|
|
150
152
|
? rootLocator.locator(rowSelector)
|
|
151
153
|
: rowLocator;
|
|
152
|
-
const sr = (0, smartRow_1.default)(effectiveLocator, map, rowIndex, config, rootLocator, resolve, finalTable, tablePageIndex, barrier);
|
|
154
|
+
const sr = (0, smartRow_1.default)(effectiveLocator, map, rowIndex, config, rootLocator, resolve, finalTable, tablePageIndex, barrier, renderWindowPosition);
|
|
153
155
|
if (rowSelector)
|
|
154
156
|
sr._selfHealing = true;
|
|
155
157
|
return sr;
|
|
@@ -183,7 +185,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
183
185
|
const _handlePrompt = async (promptName, content) => {
|
|
184
186
|
let finalPrompt = content;
|
|
185
187
|
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
|
|
186
|
-
|
|
188
|
+
(0, debugUtils_1.logDebug)(config, 'info', `Throwing error to display [${promptName}] cleanly...`);
|
|
187
189
|
throw new Error(finalPrompt);
|
|
188
190
|
};
|
|
189
191
|
const _autoInit = async () => {
|
|
@@ -229,9 +231,12 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
229
231
|
};
|
|
230
232
|
const result = {
|
|
231
233
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
232
|
-
set currentPageIndex(v) {
|
|
234
|
+
set currentPageIndex(v) {
|
|
235
|
+
console.warn('[SmartTable] Manually assigning table.currentPageIndex can desync pagination path planning. Prefer reset() / library navigation; writable access may become read-only in v7.');
|
|
236
|
+
tableState.currentPageIndex = v;
|
|
237
|
+
},
|
|
233
238
|
init: async (options) => {
|
|
234
|
-
var _a;
|
|
239
|
+
var _a, _b, _c;
|
|
235
240
|
if (tableMapper.isInitialized() || tableState.empty)
|
|
236
241
|
return result;
|
|
237
242
|
if (config.strategies.sorting)
|
|
@@ -240,6 +245,10 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
240
245
|
(0, validation_1.validateFillStrategy)(config.strategies.fill);
|
|
241
246
|
(0, debugUtils_1.warnIfDebugInCI)(config);
|
|
242
247
|
(0, debugUtils_1.logDebug)(config, 'info', 'Initializing table');
|
|
248
|
+
if (config.maxPages === 1 &&
|
|
249
|
+
(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) || ((_b = config.strategies.pagination) === null || _b === void 0 ? void 0 : _b.goNextBulk))) {
|
|
250
|
+
console.warn('[SmartTable] maxPages is 1 but a pagination strategy is configured ā iteration/find will never leave page 1. Pass maxPages: N (or Infinity) to scan further pages.');
|
|
251
|
+
}
|
|
243
252
|
let map;
|
|
244
253
|
try {
|
|
245
254
|
map = await tableMapper.getMap(options === null || options === void 0 ? void 0 : options.timeout);
|
|
@@ -256,7 +265,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
256
265
|
throw headerError;
|
|
257
266
|
}
|
|
258
267
|
(0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${map.size} columns`, Array.from(map.keys()));
|
|
259
|
-
if ((
|
|
268
|
+
if ((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.detectCurrentPage) {
|
|
260
269
|
try {
|
|
261
270
|
const detected = await config.strategies.pagination.detectCurrentPage(rootLocator);
|
|
262
271
|
if (Number.isInteger(detected) && detected >= 0) {
|
|
@@ -277,12 +286,19 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
277
286
|
return result;
|
|
278
287
|
},
|
|
279
288
|
scrollToColumn: async (columnName) => {
|
|
289
|
+
var _a;
|
|
280
290
|
log(`scrollToColumn: column="${columnName}"`);
|
|
281
291
|
const map = await tableMapper.getMap();
|
|
282
292
|
const idx = map.get(columnName);
|
|
283
293
|
if (idx === undefined)
|
|
284
294
|
throw _createColumnError(columnName, map);
|
|
285
|
-
//
|
|
295
|
+
// Prefer viewport strategy when configured (#430). Raw scrollIntoViewIfNeeded on a
|
|
296
|
+
// header can shift Y and evict virtualized rows; viewport.scrollToColumn is X-aware.
|
|
297
|
+
const viewportScroll = (_a = config.strategies.viewport) === null || _a === void 0 ? void 0 : _a.scrollToColumn;
|
|
298
|
+
if (viewportScroll) {
|
|
299
|
+
await viewportScroll(createStrategyContext(), idx);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
286
302
|
const headerCell = resolve(config.headerSelector, rootLocator).nth(idx);
|
|
287
303
|
await headerCell.scrollIntoViewIfNeeded();
|
|
288
304
|
},
|
|
@@ -301,7 +317,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
301
317
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
302
318
|
},
|
|
303
319
|
countRows: async (filters, options) => {
|
|
304
|
-
var _a, _b, _c, _d;
|
|
320
|
+
var _a, _b, _c, _d, _e;
|
|
305
321
|
if (tableState.empty)
|
|
306
322
|
return 0;
|
|
307
323
|
await _autoInit();
|
|
@@ -359,6 +375,12 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
359
375
|
}
|
|
360
376
|
return count;
|
|
361
377
|
};
|
|
378
|
+
// Wait for table to finish loading before counting
|
|
379
|
+
const isTableLoading = (_e = config.strategies.loading) === null || _e === void 0 ? void 0 : _e.isTableLoading;
|
|
380
|
+
if (isTableLoading) {
|
|
381
|
+
const ctx = createStrategyContext();
|
|
382
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
383
|
+
}
|
|
362
384
|
if (!hasPagination) {
|
|
363
385
|
log(`countRows: counting rows in current viewport (no pagination)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
364
386
|
if (!hasPostFilters)
|
|
@@ -372,19 +394,31 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
372
394
|
let pagesScanned = 1;
|
|
373
395
|
let paginationError;
|
|
374
396
|
try {
|
|
375
|
-
|
|
397
|
+
const scanned = await (0, scanPages_1.scanPages)({
|
|
398
|
+
advancePage: _advancePage,
|
|
399
|
+
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
400
|
+
config,
|
|
401
|
+
waitForReady: async () => {
|
|
402
|
+
if (!isTableLoading)
|
|
403
|
+
return;
|
|
404
|
+
const ctx = createStrategyContext();
|
|
405
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
406
|
+
},
|
|
407
|
+
}, async ({ pagesScanned: pageNum }) => {
|
|
376
408
|
const rowLocators = resolveRows();
|
|
377
409
|
const newIndices = await tracker.getUnseenIndices(rowLocators);
|
|
378
410
|
const candidates = await rowLocators.all();
|
|
379
411
|
const matched = await countPostFilterMatches(candidates, newIndices);
|
|
380
412
|
total += matched;
|
|
381
|
-
log(`countRows: page ${
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
413
|
+
log(`countRows: page ${pageNum} ā ${matched} row(s) (running total: ${total})`);
|
|
414
|
+
return 'continue';
|
|
415
|
+
}, {
|
|
416
|
+
maxPages: effectiveMaxPages,
|
|
417
|
+
useBulk: false,
|
|
418
|
+
label: 'countRows',
|
|
419
|
+
finalScanOnEof: true,
|
|
420
|
+
});
|
|
421
|
+
pagesScanned = scanned.pagesScanned;
|
|
388
422
|
}
|
|
389
423
|
catch (e) {
|
|
390
424
|
paginationError = e;
|
|
@@ -428,6 +462,10 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
428
462
|
return (text || '').trim();
|
|
429
463
|
}, options);
|
|
430
464
|
},
|
|
465
|
+
/**
|
|
466
|
+
* @deprecated Use `mapColumn` (or `map`) instead. Will be removed in v7.0.0.
|
|
467
|
+
* Iterates over rows and extracts the value of a single column as strings.
|
|
468
|
+
*/
|
|
431
469
|
getColumnValues: async (columnName, options = {}) => {
|
|
432
470
|
log(`getColumnValues: column="${columnName}" options=${safeStringify(options)}`);
|
|
433
471
|
const values = await result.mapColumn(columnName, options);
|
|
@@ -463,6 +501,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
463
501
|
throw new Error(`getRow() cannot filter by synthetic column(s): ${syntheticKeys.join(', ')}. ` +
|
|
464
502
|
`Use findRow() instead ā synthetic columns require async evaluation.`);
|
|
465
503
|
}
|
|
504
|
+
const overrideKeys = Object.keys(filters).filter(k => { var _a, _b; return (_b = (_a = config.columnOverrides) === null || _a === void 0 ? void 0 : _a[k]) === null || _b === void 0 ? void 0 : _b.read; });
|
|
505
|
+
if (overrideKeys.length > 0) {
|
|
506
|
+
throw new Error(`getRow() cannot filter by columnOverrides.read column(s): ${overrideKeys.join(', ')}. ` +
|
|
507
|
+
`Use findRow() instead ā override columns require async evaluation.`);
|
|
508
|
+
}
|
|
466
509
|
const map = tableMapper.getMapSync();
|
|
467
510
|
if (!map)
|
|
468
511
|
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.');
|
|
@@ -476,7 +519,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
476
519
|
if (!map)
|
|
477
520
|
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.');
|
|
478
521
|
const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
|
|
479
|
-
return _makeSmart(rowLocator, map, index);
|
|
522
|
+
return _makeSmart(rowLocator, map, index, undefined, undefined, undefined, true);
|
|
480
523
|
},
|
|
481
524
|
findRowByIndex: async (index, options) => {
|
|
482
525
|
var _a, _b;
|
|
@@ -552,7 +595,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
552
595
|
},
|
|
553
596
|
sorting: {
|
|
554
597
|
apply: async (columnName, direction) => {
|
|
555
|
-
var _a, _b, _c, _d, _e;
|
|
598
|
+
var _a, _b, _c, _d, _e, _f;
|
|
556
599
|
await _autoInit();
|
|
557
600
|
if (!config.strategies.sorting)
|
|
558
601
|
throw new Error('No sorting strategy has been configured.');
|
|
@@ -567,15 +610,15 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
567
610
|
}
|
|
568
611
|
await config.strategies.sorting.doSort({ columnName, direction, context });
|
|
569
612
|
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
570
|
-
const timeout = (_b = config.strategies.loading.sortStabilizationTimeout) !== null && _b !== void 0 ? _b : 10000;
|
|
571
|
-
const poll = (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
613
|
+
const timeout = (_c = (_b = config.strategies.loading.sortStabilizationTimeout) !== null && _b !== void 0 ? _b : config.strategies.loading.loadingTimeout) !== null && _c !== void 0 ? _c : 10000;
|
|
614
|
+
const poll = (_d = config.strategies.loading.sortStabilizationPollInterval) !== null && _d !== void 0 ? _d : 100;
|
|
615
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, context, rootLocator.page(), 'sorting.apply', {
|
|
616
|
+
timeout,
|
|
617
|
+
pollInterval: poll,
|
|
618
|
+
});
|
|
576
619
|
}
|
|
577
620
|
else {
|
|
578
|
-
const fallback = (
|
|
621
|
+
const fallback = (_f = (_e = config.strategies.loading) === null || _e === void 0 ? void 0 : _e.sortStabilizationFallbackDelay) !== null && _f !== void 0 ? _f : 200;
|
|
579
622
|
await rootLocator.page().waitForTimeout(fallback);
|
|
580
623
|
}
|
|
581
624
|
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
@@ -597,43 +640,65 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
597
640
|
}
|
|
598
641
|
},
|
|
599
642
|
// āāā Shared async row iterator āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
643
|
+
/**
|
|
644
|
+
* Streams rows using the same page-walk + collection path as `map` / `forEach`
|
|
645
|
+
* (`scanPages` + runMap): overscan filtering, loading-before-dedupe, EOF final
|
|
646
|
+
* scan, and config dedupe. Concurrency is sequential so yields stay ordered. (#427)
|
|
647
|
+
*/
|
|
600
648
|
[Symbol.asyncIterator]() {
|
|
601
649
|
return __asyncGenerator(this, arguments, function* _a() {
|
|
602
|
-
var _b;
|
|
603
650
|
yield __await(_autoInit());
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
651
|
+
log(`iterator: starting via runMap (maxPages=${config.maxPages})`);
|
|
652
|
+
const queue = [];
|
|
653
|
+
let notify;
|
|
654
|
+
let release;
|
|
655
|
+
let finished = false;
|
|
656
|
+
let cancelled = false;
|
|
657
|
+
let runError;
|
|
658
|
+
const wake = () => {
|
|
659
|
+
notify === null || notify === void 0 ? void 0 : notify();
|
|
660
|
+
notify = undefined;
|
|
661
|
+
};
|
|
662
|
+
const runPromise = (0, tableIteration_1.runMap)({
|
|
663
|
+
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
664
|
+
getMap: () => tableMapper.getMapSync(),
|
|
665
|
+
advancePage: _advancePage,
|
|
666
|
+
makeSmartRow: (loc, map, idx, pageIdx, barrier, sel) => _makeSmart(loc, map, idx, pageIdx, barrier, sel),
|
|
667
|
+
createSmartRowArray: smartRowArray_1.createSmartRowArray,
|
|
668
|
+
config,
|
|
669
|
+
getPage: () => rootLocator.page(),
|
|
670
|
+
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
671
|
+
getContext: () => createStrategyContext(),
|
|
672
|
+
}, async (ctx) => {
|
|
673
|
+
queue.push({
|
|
674
|
+
row: ctx.row,
|
|
675
|
+
index: ctx.index,
|
|
676
|
+
rowIndex: ctx.rowIndex,
|
|
677
|
+
pageIndex: ctx.pageIndex,
|
|
678
|
+
});
|
|
679
|
+
wake();
|
|
680
|
+
await new Promise((resolve) => { release = resolve; });
|
|
681
|
+
if (cancelled)
|
|
682
|
+
ctx.stop();
|
|
683
|
+
}, { concurrency: 'sequential' }, 'iterator').then(() => { finished = true; wake(); }, (err) => { runError = err; finished = true; wake(); });
|
|
609
684
|
try {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
const rowLocators = resolve(config.rowSelector, rootLocator);
|
|
614
|
-
const newIndices = yield __await(tracker.getUnseenIndices(rowLocators));
|
|
615
|
-
const pageRows = yield __await(rowLocators.all());
|
|
616
|
-
const barrier = new navigationBarrier_1.NavigationBarrier(newIndices.length);
|
|
617
|
-
for (const idx of newIndices) {
|
|
618
|
-
// index = visit-order counter; rowIndex = logical/data index (B-hybrid, #362).
|
|
619
|
-
const resolved = yield __await((0, rowResolution_1.resolveLogicalRowIndex)(pageRows[idx], config, () => rowIndex));
|
|
620
|
-
const logical = (_b = resolved === null || resolved === void 0 ? void 0 : resolved.index) !== null && _b !== void 0 ? _b : rowIndex;
|
|
621
|
-
const sr = _makeSmart(pageRows[idx], map, logical, tableState.currentPageIndex, barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
|
|
622
|
-
// Iterator rows share one per-page DOM snapshot (positional locators); disable
|
|
623
|
-
// toJSON's scroll-back recovery so reading one row can't invalidate the others (#366).
|
|
624
|
-
sr._inBatch = true;
|
|
625
|
-
yield yield __await({ row: sr, index: rowIndex, rowIndex: logical, pageIndex: tableState.currentPageIndex });
|
|
626
|
-
rowIndex++;
|
|
685
|
+
while (!finished || queue.length > 0) {
|
|
686
|
+
if (queue.length === 0) {
|
|
687
|
+
yield __await(new Promise((resolve) => { notify = resolve; }));
|
|
627
688
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
689
|
+
while (queue.length > 0) {
|
|
690
|
+
yield yield __await(queue.shift());
|
|
691
|
+
release === null || release === void 0 ? void 0 : release();
|
|
692
|
+
release = undefined;
|
|
693
|
+
}
|
|
694
|
+
if (runError)
|
|
695
|
+
throw runError;
|
|
633
696
|
}
|
|
634
697
|
}
|
|
635
698
|
finally {
|
|
636
|
-
|
|
699
|
+
cancelled = true;
|
|
700
|
+
release === null || release === void 0 ? void 0 : release();
|
|
701
|
+
yield __await(runPromise.catch(() => { }));
|
|
637
702
|
}
|
|
638
703
|
});
|
|
639
704
|
},
|
|
@@ -705,10 +770,14 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
705
770
|
await _handlePrompt('Smart Table Config', content);
|
|
706
771
|
},
|
|
707
772
|
generateConfigPrompt: async () => {
|
|
708
|
-
|
|
773
|
+
(0, debugUtils_1.logDebug)(config, 'error', 'generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
|
|
709
774
|
return result.generateConfig();
|
|
710
775
|
},
|
|
711
776
|
};
|
|
777
|
+
Object.defineProperty(result, pageIndex_1.SET_CURRENT_PAGE_INDEX, {
|
|
778
|
+
value: (v) => { tableState.currentPageIndex = v; },
|
|
779
|
+
enumerable: false,
|
|
780
|
+
});
|
|
712
781
|
createStrategyContext = () => ({
|
|
713
782
|
root: rootLocator,
|
|
714
783
|
config,
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Page } from '@playwright/test';
|
|
2
|
+
import type { FinalTableConfig, TableContext } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Poll `loading.isTableLoading` until false or timeout.
|
|
5
|
+
* Shared by countRows, findRow(s), and sort stabilization (#434).
|
|
6
|
+
*
|
|
7
|
+
* Timeout resolution (first defined wins):
|
|
8
|
+
* 1. Explicit `options.timeout` ā e.g. the sort caller's stabilization timeout
|
|
9
|
+
* 2. `loadingTimeout` ā general gate for any isTableLoading poll
|
|
10
|
+
* 3. 10_000 ms default
|
|
11
|
+
*/
|
|
12
|
+
export declare function waitWhileTableLoading(config: FinalTableConfig, context: TableContext, page: Page, label: string, options?: {
|
|
13
|
+
timeout?: number;
|
|
14
|
+
pollInterval?: number;
|
|
15
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitWhileTableLoading = waitWhileTableLoading;
|
|
4
|
+
const debugUtils_1 = require("./debugUtils");
|
|
5
|
+
/**
|
|
6
|
+
* Poll `loading.isTableLoading` until false or timeout.
|
|
7
|
+
* Shared by countRows, findRow(s), and sort stabilization (#434).
|
|
8
|
+
*
|
|
9
|
+
* Timeout resolution (first defined wins):
|
|
10
|
+
* 1. Explicit `options.timeout` ā e.g. the sort caller's stabilization timeout
|
|
11
|
+
* 2. `loadingTimeout` ā general gate for any isTableLoading poll
|
|
12
|
+
* 3. 10_000 ms default
|
|
13
|
+
*/
|
|
14
|
+
async function waitWhileTableLoading(config, context, page, label, options) {
|
|
15
|
+
var _a, _b, _c, _d, _e;
|
|
16
|
+
const isTableLoading = (_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading;
|
|
17
|
+
if (!isTableLoading)
|
|
18
|
+
return;
|
|
19
|
+
const loading = config.strategies.loading;
|
|
20
|
+
const timeout = (_c = (_b = options === null || options === void 0 ? void 0 : options.timeout) !== null && _b !== void 0 ? _b : loading.loadingTimeout) !== null && _c !== void 0 ? _c : 10000;
|
|
21
|
+
const poll = (_e = (_d = options === null || options === void 0 ? void 0 : options.pollInterval) !== null && _d !== void 0 ? _d : loading.sortStabilizationPollInterval) !== null && _e !== void 0 ? _e : 200;
|
|
22
|
+
const deadline = Date.now() + timeout;
|
|
23
|
+
while (Date.now() < deadline) {
|
|
24
|
+
let timer;
|
|
25
|
+
try {
|
|
26
|
+
// A custom predicate may never settle. Stop waiting when the budget expires;
|
|
27
|
+
// Promise.race also handles any later rejection from that predicate.
|
|
28
|
+
const expired = new Promise(resolve => {
|
|
29
|
+
timer = setTimeout(() => resolve(false), Math.max(0, deadline - Date.now()));
|
|
30
|
+
});
|
|
31
|
+
if (!(await Promise.race([isTableLoading(context), expired])))
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
}
|
|
37
|
+
const remaining = deadline - Date.now();
|
|
38
|
+
if (remaining <= 0)
|
|
39
|
+
return;
|
|
40
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `${label}: table is loading... waiting`);
|
|
41
|
+
await page.waitForTimeout(Math.min(poll, remaining));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal setter for `table.currentPageIndex` used by library navigation
|
|
3
|
+
* (bringIntoView path planner, etc.) so the public setter can warn on
|
|
4
|
+
* external writes without spamming on every library update (#434).
|
|
5
|
+
*/
|
|
6
|
+
export declare const SET_CURRENT_PAGE_INDEX: unique symbol;
|
|
7
|
+
export type PageIndexWritable = {
|
|
8
|
+
currentPageIndex: number;
|
|
9
|
+
[SET_CURRENT_PAGE_INDEX]?: (n: number) => void;
|
|
10
|
+
};
|
|
11
|
+
/** Prefer the internal setter when present; fall back to the public property. */
|
|
12
|
+
export declare function setCurrentPageIndex(table: PageIndexWritable, n: number): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SET_CURRENT_PAGE_INDEX = void 0;
|
|
4
|
+
exports.setCurrentPageIndex = setCurrentPageIndex;
|
|
5
|
+
/**
|
|
6
|
+
* Internal setter for `table.currentPageIndex` used by library navigation
|
|
7
|
+
* (bringIntoView path planner, etc.) so the public setter can warn on
|
|
8
|
+
* external writes without spamming on every library update (#434).
|
|
9
|
+
*/
|
|
10
|
+
exports.SET_CURRENT_PAGE_INDEX = Symbol.for('playwright-smart-table.setCurrentPageIndex');
|
|
11
|
+
/** Prefer the internal setter when present; fall back to the public property. */
|
|
12
|
+
function setCurrentPageIndex(table, n) {
|
|
13
|
+
const internal = table[exports.SET_CURRENT_PAGE_INDEX];
|
|
14
|
+
if (typeof internal === 'function') {
|
|
15
|
+
internal(n);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
table.currentPageIndex = n;
|
|
19
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Locator, Page } from '@playwright/test';
|
|
2
|
+
import type { FinalTableConfig, Selector } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a cell locator for a concrete row ā prefers `strategies.getCellLocator`,
|
|
5
|
+
* otherwise `cellSelector` + `.nth(columnIndex)`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveCellLocator(args: {
|
|
8
|
+
config: FinalTableConfig;
|
|
9
|
+
resolve: (selector: Selector, parent: Locator | Page) => Locator;
|
|
10
|
+
row: Locator;
|
|
11
|
+
root: Locator;
|
|
12
|
+
columnName: string;
|
|
13
|
+
columnIndex: number;
|
|
14
|
+
rowIndex?: number;
|
|
15
|
+
page?: Page;
|
|
16
|
+
}): Locator;
|
|
17
|
+
/**
|
|
18
|
+
* Cell locator for Playwright `rows.filter({ has })`.
|
|
19
|
+
*
|
|
20
|
+
* When `getCellLocator` is set, pass `page.locator(':scope')` as the row so the
|
|
21
|
+
* strategy's `row.locator(...)` chain stays relative to each candidate row under
|
|
22
|
+
* `filter({ has })`. Using the rows collection or a document-rooted parent nests
|
|
23
|
+
* the row selector and matches nothing.
|
|
24
|
+
*
|
|
25
|
+
* Without `getCellLocator`, keeps the historical page-scoped `cellSelector.nth(i)`
|
|
26
|
+
* template that Playwright re-bases into each row.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCellLocatorForFilter(args: {
|
|
29
|
+
config: FinalTableConfig;
|
|
30
|
+
resolve: (selector: Selector, parent: Locator | Page) => Locator;
|
|
31
|
+
page: Page;
|
|
32
|
+
root?: Locator;
|
|
33
|
+
columnName: string;
|
|
34
|
+
columnIndex: number;
|
|
35
|
+
}): Locator;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveCellLocator = resolveCellLocator;
|
|
4
|
+
exports.resolveCellLocatorForFilter = resolveCellLocatorForFilter;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a cell locator for a concrete row ā prefers `strategies.getCellLocator`,
|
|
7
|
+
* otherwise `cellSelector` + `.nth(columnIndex)`.
|
|
8
|
+
*/
|
|
9
|
+
function resolveCellLocator(args) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const { config, resolve, row, root, columnName, columnIndex, rowIndex } = args;
|
|
12
|
+
const page = (_a = args.page) !== null && _a !== void 0 ? _a : root.page();
|
|
13
|
+
if ((_b = config.strategies) === null || _b === void 0 ? void 0 : _b.getCellLocator) {
|
|
14
|
+
return config.strategies.getCellLocator({
|
|
15
|
+
row,
|
|
16
|
+
root,
|
|
17
|
+
columnName,
|
|
18
|
+
columnIndex,
|
|
19
|
+
rowIndex,
|
|
20
|
+
page,
|
|
21
|
+
config,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return resolve(config.cellSelector, row).nth(columnIndex);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Cell locator for Playwright `rows.filter({ has })`.
|
|
28
|
+
*
|
|
29
|
+
* When `getCellLocator` is set, pass `page.locator(':scope')` as the row so the
|
|
30
|
+
* strategy's `row.locator(...)` chain stays relative to each candidate row under
|
|
31
|
+
* `filter({ has })`. Using the rows collection or a document-rooted parent nests
|
|
32
|
+
* the row selector and matches nothing.
|
|
33
|
+
*
|
|
34
|
+
* Without `getCellLocator`, keeps the historical page-scoped `cellSelector.nth(i)`
|
|
35
|
+
* template that Playwright re-bases into each row.
|
|
36
|
+
*/
|
|
37
|
+
function resolveCellLocatorForFilter(args) {
|
|
38
|
+
var _a;
|
|
39
|
+
const { config, resolve, page, root, columnName, columnIndex } = args;
|
|
40
|
+
if ((_a = config.strategies) === null || _a === void 0 ? void 0 : _a.getCellLocator) {
|
|
41
|
+
const scopeRow = page.locator(':scope');
|
|
42
|
+
return config.strategies.getCellLocator({
|
|
43
|
+
row: scopeRow,
|
|
44
|
+
root: root !== null && root !== void 0 ? root : scopeRow,
|
|
45
|
+
columnName,
|
|
46
|
+
columnIndex,
|
|
47
|
+
page,
|
|
48
|
+
config,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return resolve(config.cellSelector, page).nth(columnIndex);
|
|
52
|
+
}
|