@rickcedwhat/playwright-smart-table 6.24.0 → 6.25.0-next.1e71a54

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 (47) hide show
  1. package/dist/engine/cellNavigation.d.ts +24 -0
  2. package/dist/engine/cellNavigation.js +326 -0
  3. package/dist/engine/countRows.d.ts +28 -0
  4. package/dist/engine/countRows.js +149 -0
  5. package/dist/engine/rowByIndex.d.ts +20 -0
  6. package/dist/engine/rowByIndex.js +79 -0
  7. package/dist/engine/rowFinder.d.ts +5 -1
  8. package/dist/engine/rowFinder.js +17 -10
  9. package/dist/engine/rowResolution.d.ts +2 -2
  10. package/dist/engine/rowResolution.js +11 -7
  11. package/dist/engine/rowSnapshot.d.ts +28 -0
  12. package/dist/engine/rowSnapshot.js +422 -0
  13. package/dist/engine/scanPages.d.ts +7 -2
  14. package/dist/engine/scanPages.js +11 -0
  15. package/dist/engine/sorting.d.ts +11 -0
  16. package/dist/engine/sorting.js +51 -0
  17. package/dist/engine/tableIteration.js +3 -1
  18. package/dist/engine/tableMapper.js +16 -5
  19. package/dist/index.d.ts +1 -1
  20. package/dist/packageVersion.d.ts +1 -1
  21. package/dist/packageVersion.js +1 -1
  22. package/dist/presets/glide/index.js +0 -1
  23. package/dist/presets/rdg.js +0 -2
  24. package/dist/smartRow.js +51 -721
  25. package/dist/strategies/columns.d.ts +1 -1
  26. package/dist/strategies/contentReady.d.ts +19 -2
  27. package/dist/strategies/contentReady.js +81 -0
  28. package/dist/strategies/index.d.ts +7 -3
  29. package/dist/strategies/resolution.d.ts +1 -3
  30. package/dist/strategies/resolution.js +0 -28
  31. package/dist/types.d.ts +40 -7
  32. package/dist/useTable.js +51 -241
  33. package/dist/utils/deadline.d.ts +2 -0
  34. package/dist/utils/deadline.js +19 -0
  35. package/dist/utils/locatorDescription.d.ts +15 -0
  36. package/dist/utils/locatorDescription.js +56 -0
  37. package/dist/utils/navigationBarrier.d.ts +0 -2
  38. package/dist/utils/navigationBarrier.js +0 -4
  39. package/dist/utils/safeStringify.d.ts +2 -0
  40. package/dist/utils/safeStringify.js +18 -0
  41. package/dist/utils/smartRowArray.d.ts +1 -1
  42. package/dist/utils/smartRowArray.js +4 -1
  43. package/package.json +5 -6
  44. package/dist/typeContext.d.ts +0 -6
  45. package/dist/typeContext.js +0 -1133
  46. package/dist/utils.d.ts +0 -7
  47. package/dist/utils.js +0 -21
@@ -2,6 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.scanPages = scanPages;
4
4
  const debugUtils_1 = require("../utils/debugUtils");
5
+ function throwIfAborted(env, label, signal) {
6
+ if (!(signal === null || signal === void 0 ? void 0 : signal.aborted))
7
+ return;
8
+ (0, debugUtils_1.logDebug)(env.config, 'info', `${label}: aborted via AbortSignal at page ${env.getCurrentPageIndex()}`);
9
+ throw signal.reason;
10
+ }
5
11
  async function scanPages(env, onPage, options) {
6
12
  var _a, _b;
7
13
  const maxPages = options.maxPages;
@@ -12,14 +18,17 @@ async function scanPages(env, onPage, options) {
12
18
  let reachedEnd = false;
13
19
  (0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: starting (maxPages=${maxPages}, useBulk=${useBulk}, finalScanOnEof=${finalScanOnEof})`);
14
20
  while (true) {
21
+ throwIfAborted(env, label, options.signal);
15
22
  if (env.waitForReady) {
16
23
  await env.waitForReady();
24
+ throwIfAborted(env, label, options.signal);
17
25
  }
18
26
  const decision = await onPage({
19
27
  pagesScanned,
20
28
  pageIndex: env.getCurrentPageIndex(),
21
29
  isFinalScan: reachedEnd,
22
30
  });
31
+ throwIfAborted(env, label, options.signal);
23
32
  if (decision === 'stop') {
24
33
  (0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: onPage returned stop at page ${env.getCurrentPageIndex()}`);
25
34
  break;
@@ -27,8 +36,10 @@ async function scanPages(env, onPage, options) {
27
36
  if (pagesScanned >= maxPages || reachedEnd) {
28
37
  break;
29
38
  }
39
+ throwIfAborted(env, label, options.signal);
30
40
  const prevPage = env.getCurrentPageIndex();
31
41
  const didAdvance = await env.advancePage(useBulk);
42
+ throwIfAborted(env, label, options.signal);
32
43
  if (!didAdvance) {
33
44
  if (finalScanOnEof) {
34
45
  (0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: pagination returned false — final scan`);
@@ -0,0 +1,11 @@
1
+ import type { Page } from '@playwright/test';
2
+ import type { FinalTableConfig, StrategyContext } from '../types';
3
+ export interface SortingEnv<T = any> {
4
+ config: FinalTableConfig<T>;
5
+ getPage: () => Page;
6
+ /** Strategy context including `getHeaderCell`. */
7
+ getContext: () => StrategyContext;
8
+ }
9
+ /** Drives the sorting strategy until `columnName` reports `direction`, retrying up to 3 times. */
10
+ export declare function applySort<T>(env: SortingEnv<T>, columnName: string, direction: 'asc' | 'desc'): Promise<void>;
11
+ export declare function getSortState<T>(env: SortingEnv<T>, columnName: string): Promise<'asc' | 'desc' | 'none'>;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applySort = applySort;
4
+ exports.getSortState = getSortState;
5
+ const debugUtils_1 = require("../utils/debugUtils");
6
+ const loadingWait_1 = require("../utils/loadingWait");
7
+ const MAX_SORT_ATTEMPTS = 3;
8
+ function requireSorting(config) {
9
+ if (!config.strategies.sorting)
10
+ throw new Error('No sorting strategy has been configured.');
11
+ return config.strategies.sorting;
12
+ }
13
+ /** Waits for the table to settle after `doSort`: `isTableLoading` when configured, otherwise a fixed delay. */
14
+ async function waitForSortToSettle(env, context) {
15
+ var _a, _b, _c, _d;
16
+ const loading = env.config.strategies.loading;
17
+ if (loading === null || loading === void 0 ? void 0 : loading.isTableLoading) {
18
+ const timeout = (_b = (_a = loading.sortStabilizationTimeout) !== null && _a !== void 0 ? _a : loading.loadingTimeout) !== null && _b !== void 0 ? _b : 10000;
19
+ const pollInterval = (_c = loading.sortStabilizationPollInterval) !== null && _c !== void 0 ? _c : 100;
20
+ await (0, loadingWait_1.waitWhileTableLoading)(env.config, context, env.getPage(), 'sorting.apply', { timeout, pollInterval });
21
+ }
22
+ else {
23
+ await env.getPage().waitForTimeout((_d = loading === null || loading === void 0 ? void 0 : loading.sortStabilizationFallbackDelay) !== null && _d !== void 0 ? _d : 200);
24
+ }
25
+ }
26
+ /** Drives the sorting strategy until `columnName` reports `direction`, retrying up to 3 times. */
27
+ async function applySort(env, columnName, direction) {
28
+ const sorting = requireSorting(env.config);
29
+ (0, debugUtils_1.logDebug)(env.config, 'verbose', `Applying sort for column "${columnName}" (${direction})`);
30
+ const context = env.getContext();
31
+ for (let i = 0; i < MAX_SORT_ATTEMPTS; i++) {
32
+ const currentState = await sorting.getSortState({ columnName, context });
33
+ if (currentState === direction) {
34
+ (0, debugUtils_1.logDebug)(env.config, 'verbose', `Sort for "${columnName}" is already "${direction}".`);
35
+ return;
36
+ }
37
+ await sorting.doSort({ columnName, direction, context });
38
+ await waitForSortToSettle(env, context);
39
+ await (0, debugUtils_1.debugDelay)(env.config, 'default');
40
+ const newState = await sorting.getSortState({ columnName, context });
41
+ if (newState === direction) {
42
+ (0, debugUtils_1.logDebug)(env.config, 'verbose', `Successfully sorted "${columnName}" to "${direction}".`);
43
+ return;
44
+ }
45
+ }
46
+ throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${MAX_SORT_ATTEMPTS} attempts.`);
47
+ }
48
+ async function getSortState(env, columnName) {
49
+ const sorting = requireSorting(env.config);
50
+ return sorting.getSortState({ columnName, context: env.getContext() });
51
+ }
@@ -45,8 +45,9 @@ async function runMap(env, callback, options = {}, label = 'map') {
45
45
  let rowIndex = 0;
46
46
  let stopped = false;
47
47
  let stoppedIndex = Infinity;
48
+ // In parallel/synchronized mode rows can call stop() out of order; the earliest row wins.
48
49
  const stop = (idx) => {
49
- if (!stopped) {
50
+ if (idx < stoppedIndex) {
50
51
  log(env.config, `${label}: stop() called at row ${idx} — halting`);
51
52
  stopped = true;
52
53
  stoppedIndex = idx;
@@ -195,6 +196,7 @@ async function runMap(env, callback, options = {}, label = 'map') {
195
196
  useBulk,
196
197
  label,
197
198
  finalScanOnEof: true,
199
+ signal: options.signal,
198
200
  });
199
201
  log(env.config, `${label}: complete — ${results.length} result(s) across ${pagesScanned} page(s)`);
200
202
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TableMapper = void 0;
4
4
  const headers_1 = require("../strategies/headers");
5
5
  const debugUtils_1 = require("../utils/debugUtils");
6
+ const deadline_1 = require("../utils/deadline");
6
7
  class TableMapper {
7
8
  constructor(rootLocator, config, resolve) {
8
9
  this._headerMap = null;
@@ -21,9 +22,11 @@ class TableMapper {
21
22
  this.log('Mapping headers...');
22
23
  const headerTimeout = timeout !== null && timeout !== void 0 ? timeout : 3000;
23
24
  const startTime = Date.now();
25
+ const deadline = startTime + headerTimeout;
26
+ const remaining = () => Math.max(0, deadline - Date.now());
24
27
  if (this.config.autoScroll) {
25
28
  try {
26
- await this.rootLocator.scrollIntoViewIfNeeded({ timeout: 1000 });
29
+ await (0, deadline_1.withinDeadline)(() => this.rootLocator.scrollIntoViewIfNeeded({ timeout: Math.max(1, Math.min(1000, remaining())) }), deadline, undefined);
27
30
  }
28
31
  catch (e) { }
29
32
  }
@@ -39,22 +42,29 @@ class TableMapper {
39
42
  while (Date.now() - startTime < headerTimeout) {
40
43
  // 1. Wait for visibility
41
44
  try {
42
- await headerLoc.first().waitFor({ state: 'visible', timeout: 200 });
45
+ await (0, deadline_1.withinDeadline)(() => headerLoc.first().waitFor({ state: 'visible', timeout: Math.max(1, Math.min(200, remaining())) }), deadline, undefined);
43
46
  }
44
47
  catch (e) {
45
48
  // Continue to check existing/loading state even if not strictly "visible" yet
46
49
  }
50
+ if (remaining() <= 0)
51
+ break;
47
52
  // 2. Check Smart Loading State
48
53
  if ((_a = this.config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isHeaderLoading) {
49
- const isStable = !(await this.config.strategies.loading.isHeaderLoading(context));
54
+ const isStable = !(await (0, deadline_1.withinDeadline)(() => this.config.strategies.loading.isHeaderLoading(context), deadline, true));
50
55
  if (!isStable) {
51
56
  this.log('Headers are loading/unstable... waiting');
52
- await new Promise(r => setTimeout(r, 100));
57
+ if (remaining() > 0)
58
+ await new Promise(r => setTimeout(r, Math.min(100, remaining())));
53
59
  continue;
54
60
  }
55
61
  }
56
62
  // 3. Attempt Scan
63
+ if (remaining() <= 0)
64
+ break;
57
65
  try {
66
+ // Preserve long-running discovery strategies (e.g. horizontal header scans).
67
+ // The deadline bounds readiness and retries, not a strategy's own operation.
58
68
  const rawHeaders = await strategy(context);
59
69
  const entries = await this.processHeaders(rawHeaders);
60
70
  // Success — validate before caching so a collision doesn't leave stale state
@@ -72,7 +82,8 @@ class TableMapper {
72
82
  catch (e) {
73
83
  lastError = e;
74
84
  this.log(`Header mapping failed. Attempt ${Math.floor((Date.now() - startTime) / 100)}: ${e.message}`);
75
- await new Promise(r => setTimeout(r, 100));
85
+ if (remaining() > 0)
86
+ await new Promise(r => setTimeout(r, Math.min(100, remaining())));
76
87
  }
77
88
  }
78
89
  throw lastError || new Error(`Timed out waiting for headers after ${headerTimeout}ms`);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { PLAYWRIGHT_SMART_TABLE_VERSION } from './packageVersion';
2
2
  export { useTable } from './useTable';
3
- export type { TableConfig, TableResult, SmartRow, SmartCell, Selector, TableSelector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, SyntheticColumnDef, ColumnOverride, ColumnOverrideReadContext, TableStrategies, LoadingStrategy, ViewportStrategy, FilterStrategy, NavigationPrimitives, DedupeStrategy, ContentReadyStrategy, FillStrategy, PaginationStrategy, HeaderStrategy, } from './types';
3
+ export type { TableConfig, TableResult, SmartRow, SmartCell, Selector, TableSelector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, SyntheticColumnDef, ColumnOverride, ColumnOverrideReadContext, TableStrategies, LoadingStrategy, ViewportStrategy, FilterStrategy, NavigationPrimitives, DedupeStrategy, ContentReadyStrategy, RowReadyPredicate, FillStrategy, PaginationStrategy, HeaderStrategy, } from './types';
4
4
  export { Strategies } from './strategies';
5
5
  export * as presets from './presets';
6
6
  /**
@@ -3,4 +3,4 @@
3
3
  * Generated by scripts/embed-version.mjs from package.json
4
4
  */
5
5
  /** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
6
- export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.24.0";
6
+ export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.25.0-next.1e71a54";
@@ -6,4 +6,4 @@ exports.PLAYWRIGHT_SMART_TABLE_VERSION = void 0;
6
6
  * Generated by scripts/embed-version.mjs from package.json
7
7
  */
8
8
  /** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
9
- exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.24.0";
9
+ exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.25.0-next.1e71a54";
@@ -102,7 +102,6 @@ const GlideDefaultStrategies = {
102
102
  getActiveCell: glideGetActiveCell
103
103
  };
104
104
  /** Strategies only for Glide Data Grid. Includes fillSimple; use when you want to supply your own selectors or override fill. */
105
- // fallow-ignore-next-line unused-export
106
105
  exports.GlideStrategies = Object.assign(Object.assign({}, GlideDefaultStrategies), { fillSimple: glideFillSimple });
107
106
  /**
108
107
  * Factory that returns a configured Glide preset.
@@ -99,7 +99,6 @@ const RDGDefaultStrategies = {
99
99
  pagination: rdgPaginationStrategy
100
100
  };
101
101
  /** Full strategies for React Data Grid. Use when you want to supply your own selectors: `strategies: presets.rdg.Strategies` */
102
- // fallow-ignore-next-line unused-export
103
102
  exports.RDGStrategies = RDGDefaultStrategies;
104
103
  /**
105
104
  * Full preset for React Data Grid (selectors + default strategies).
@@ -174,7 +173,6 @@ const rdg2DViewportStrategy = {
174
173
  };
175
174
  /** Default strategies for the RDG2D preset (extends RDG with viewport recovery). */
176
175
  const RDG2DDefaultStrategies = Object.assign(Object.assign({}, RDGDefaultStrategies), { viewport: rdg2DViewportStrategy });
177
- // fallow-ignore-next-line unused-export
178
176
  exports.RDG2DStrategies = RDG2DDefaultStrategies;
179
177
  /**
180
178
  * Full preset for React Data Grid with 2D virtualization (rows + columns both virtualized).