@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.
Files changed (46) hide show
  1. package/dist/engine/rowFinder.d.ts +1 -0
  2. package/dist/engine/rowFinder.js +73 -75
  3. package/dist/engine/scanPages.d.ts +58 -0
  4. package/dist/engine/scanPages.js +46 -0
  5. package/dist/engine/tableIteration.d.ts +1 -0
  6. package/dist/engine/tableIteration.js +40 -22
  7. package/dist/filterEngine.d.ts +4 -0
  8. package/dist/filterEngine.js +15 -4
  9. package/dist/index.d.ts +6 -2
  10. package/dist/index.js +5 -1
  11. package/dist/packageVersion.d.ts +1 -1
  12. package/dist/packageVersion.js +1 -1
  13. package/dist/plugins/index.d.ts +51 -10
  14. package/dist/plugins/index.js +14 -6
  15. package/dist/presets/glide/index.d.ts +1 -1
  16. package/dist/presets/glide/index.js +2 -2
  17. package/dist/presets/mui.js +15 -29
  18. package/dist/presets/rdg.d.ts +1 -1
  19. package/dist/presets/rdg.js +4 -4
  20. package/dist/smartRow.d.ts +2 -2
  21. package/dist/smartRow.js +163 -51
  22. package/dist/strategies/columns.d.ts +5 -9
  23. package/dist/strategies/columns.js +0 -10
  24. package/dist/strategies/contentReady.d.ts +20 -0
  25. package/dist/strategies/contentReady.js +62 -0
  26. package/dist/strategies/filter.d.ts +0 -4
  27. package/dist/strategies/filter.js +0 -16
  28. package/dist/strategies/headers.d.ts +5 -0
  29. package/dist/strategies/headers.js +17 -9
  30. package/dist/strategies/index.d.ts +23 -15
  31. package/dist/strategies/index.js +9 -5
  32. package/dist/strategies/pagination.js +11 -2
  33. package/dist/strategies/viewport.d.ts +4 -1
  34. package/dist/strategies/viewport.js +62 -32
  35. package/dist/typeContext.d.ts +1 -1
  36. package/dist/typeContext.js +77 -17
  37. package/dist/types.d.ts +74 -17
  38. package/dist/useTable.js +125 -56
  39. package/dist/utils/elementTracker.js +6 -3
  40. package/dist/utils/loadingWait.d.ts +15 -0
  41. package/dist/utils/loadingWait.js +43 -0
  42. package/dist/utils/pageIndex.d.ts +12 -0
  43. package/dist/utils/pageIndex.js +19 -0
  44. package/dist/utils/resolveCellLocator.d.ts +35 -0
  45. package/dist/utils/resolveCellLocator.js +52 -0
  46. 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 navigationBarrier_1 = require("./utils/navigationBarrier");
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
- console.log(`āš ļø Throwing error to display [${promptName}] cleanly...`);
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) { tableState.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 ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.detectCurrentPage) {
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
- // Use header cell for scrolling
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
- while (true) {
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 ${pagesScanned} — ${matched} row(s) (running total: ${total})`);
382
- if (pagesScanned >= effectiveMaxPages)
383
- break;
384
- if (!await _advancePage(false))
385
- break;
386
- pagesScanned++;
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 = (_c = config.strategies.loading.sortStabilizationPollInterval) !== null && _c !== void 0 ? _c : 100;
572
- const deadline = Date.now() + timeout;
573
- while (Date.now() < deadline && await config.strategies.loading.isTableLoading(context)) {
574
- await rootLocator.page().waitForTimeout(poll);
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 = (_e = (_d = config.strategies.loading) === null || _d === void 0 ? void 0 : _d.sortStabilizationFallbackDelay) !== null && _e !== void 0 ? _e : 200;
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
- const map = tableMapper.getMapSync();
605
- const effectiveMaxPages = config.maxPages;
606
- const tracker = new elementTracker_1.ElementTracker('iterator');
607
- const useBulk = false; // iterator has no options; default goNext
608
- log(`iterator: starting (maxPages=${effectiveMaxPages})`);
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
- let rowIndex = 0;
611
- let pagesScanned = 1;
612
- while (true) {
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
- if (pagesScanned >= effectiveMaxPages)
629
- break;
630
- if (!(yield __await(_advancePage(useBulk))))
631
- break;
632
- pagesScanned++;
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
- yield __await(tracker.cleanup(rootLocator.page()));
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
- console.warn('āš ļø [playwright-smart-table] generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
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 signature = el.textContent || '';
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
- seenMap.set(el, el.textContent || '');
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "6.20.1",
3
+ "version": "6.21.0-next.3ab7a18",
4
4
  "description": "Smart, column-aware table interactions for Playwright",
5
5
  "author": "Cedrick Catalan",
6
6
  "license": "MIT",