@rickcedwhat/playwright-smart-table 6.24.0 → 6.25.0-next.49ce2de

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.
@@ -0,0 +1,27 @@
1
+ import type { Locator } from '@playwright/test';
2
+ import type { FilterValue, FinalTableConfig, TableContext } from '../types';
3
+ import type { FilterEngine } from '../filterEngine';
4
+ import type { TableMapper } from './tableMapper';
5
+ import type { RowFinder } from './rowFinder';
6
+ export interface CountRowsEnv<T = any> {
7
+ config: FinalTableConfig<T>;
8
+ rootLocator: Locator;
9
+ getRowLocators: () => Locator;
10
+ tableMapper: TableMapper;
11
+ filterEngine: FilterEngine;
12
+ rowFinder: RowFinder<T>;
13
+ advancePage: (useBulk: boolean) => Promise<boolean>;
14
+ getCurrentPageIndex: () => number;
15
+ getContext: () => TableContext;
16
+ /** Restores page 1 after a paginated count. */
17
+ reset: () => Promise<void>;
18
+ }
19
+ /**
20
+ * Counts matching rows on the current page, or across pages when pagination and
21
+ * `maxPages > 1` allow it. A paginated count always resets to page 1 afterwards (#348).
22
+ * Callers must have initialized the table.
23
+ */
24
+ export declare function countRows<T>(env: CountRowsEnv<T>, filters?: Record<string, FilterValue>, options?: {
25
+ exact?: boolean;
26
+ maxPages?: number;
27
+ }): Promise<number>;
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.countRows = countRows;
4
+ const scanPages_1 = require("./scanPages");
5
+ const elementTracker_1 = require("../utils/elementTracker");
6
+ const debugUtils_1 = require("../utils/debugUtils");
7
+ const loadingWait_1 = require("../utils/loadingWait");
8
+ const safeStringify_1 = require("../utils/safeStringify");
9
+ function log(config, msg) {
10
+ (0, debugUtils_1.logDebug)(config, 'verbose', msg);
11
+ }
12
+ /** Splits filters by how they're evaluated: DOM text, `columnOverrides.read`, or synthetic columns. */
13
+ function partitionFilters(config, filters) {
14
+ var _a, _b, _c;
15
+ const domFilters = {};
16
+ const overrideFilters = {};
17
+ const syntheticFilters = {};
18
+ for (const [key, value] of Object.entries(filters)) {
19
+ if ((_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[key]) {
20
+ syntheticFilters[key] = value;
21
+ }
22
+ else if ((_c = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[key]) === null || _c === void 0 ? void 0 : _c.read) {
23
+ overrideFilters[key] = value;
24
+ }
25
+ else {
26
+ domFilters[key] = value;
27
+ }
28
+ }
29
+ return { domFilters, overrideFilters, syntheticFilters };
30
+ }
31
+ function requireMap(tableMapper) {
32
+ const map = tableMapper.getMapSync();
33
+ if (!map)
34
+ throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
35
+ return map;
36
+ }
37
+ /**
38
+ * Counts matching rows on the current page, or across pages when pagination and
39
+ * `maxPages > 1` allow it. A paginated count always resets to page 1 afterwards (#348).
40
+ * Callers must have initialized the table.
41
+ */
42
+ async function countRows(env, filters, options) {
43
+ var _a;
44
+ var _b, _c;
45
+ const { config, rootLocator, tableMapper, rowFinder } = env;
46
+ const filtersRecord = filters !== null && filters !== void 0 ? filters : {};
47
+ const hasFilters = Object.keys(filtersRecord).length > 0;
48
+ const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
49
+ const pag = config.strategies.pagination;
50
+ const hasPagination = effectiveMaxPages > 1 && !!((pag === null || pag === void 0 ? void 0 : pag.goNext) || (pag === null || pag === void 0 ? void 0 : pag.goNextBulk));
51
+ const exact = (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false;
52
+ const { domFilters, overrideFilters, syntheticFilters } = partitionFilters(config, filtersRecord);
53
+ const hasOverrides = Object.keys(overrideFilters).length > 0;
54
+ const hasSynthetics = Object.keys(syntheticFilters).length > 0;
55
+ const hasPostFilters = hasOverrides || hasSynthetics;
56
+ const resolveRows = () => {
57
+ const rows = env.getRowLocators();
58
+ if (Object.keys(domFilters).length === 0)
59
+ return rows;
60
+ return env.filterEngine.applyFilters(rows, domFilters, requireMap(tableMapper), exact, rootLocator.page(), rootLocator);
61
+ };
62
+ const countPostFilterMatches = async (candidates, indices) => {
63
+ if (!hasPostFilters)
64
+ return indices.length;
65
+ const map = requireMap(tableMapper);
66
+ let count = 0;
67
+ for (const idx of indices) {
68
+ const row = candidates[idx];
69
+ if (hasOverrides && !await rowFinder.matchesOverrideFilters(row, overrideFilters, map, exact))
70
+ continue;
71
+ if (hasSynthetics && !await rowFinder.matchesSyntheticFilters(row, syntheticFilters, map, exact))
72
+ continue;
73
+ count++;
74
+ }
75
+ return count;
76
+ };
77
+ const isTableLoading = (_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading;
78
+ const waitForReady = async () => {
79
+ if (!isTableLoading)
80
+ return;
81
+ await (0, loadingWait_1.waitWhileTableLoading)(config, env.getContext(), rootLocator.page(), 'countRows');
82
+ };
83
+ await waitForReady();
84
+ const filterSuffix = hasFilters ? ` filters=${(0, safeStringify_1.safeStringify)(filtersRecord)}` : '';
85
+ if (!hasPagination) {
86
+ log(config, `countRows: counting rows in current viewport (no pagination)${filterSuffix}`);
87
+ if (!hasPostFilters)
88
+ return resolveRows().count();
89
+ const all = await resolveRows().all();
90
+ return countPostFilterMatches(all, all.map((_, i) => i));
91
+ }
92
+ log(config, `countRows: paginating up to ${effectiveMaxPages} page(s)${filterSuffix}`);
93
+ const tracker = new elementTracker_1.ElementTracker('countRows');
94
+ let total = 0;
95
+ let pagesScanned = 1;
96
+ let paginationError;
97
+ try {
98
+ const scanned = await (0, scanPages_1.scanPages)({
99
+ advancePage: env.advancePage,
100
+ getCurrentPageIndex: env.getCurrentPageIndex,
101
+ config,
102
+ waitForReady,
103
+ }, async ({ pagesScanned: pageNum }) => {
104
+ const rowLocators = resolveRows();
105
+ const newIndices = await tracker.getUnseenIndices(rowLocators);
106
+ const candidates = await rowLocators.all();
107
+ const matched = await countPostFilterMatches(candidates, newIndices);
108
+ total += matched;
109
+ log(config, `countRows: page ${pageNum} — ${matched} row(s) (running total: ${total})`);
110
+ return 'continue';
111
+ }, {
112
+ maxPages: effectiveMaxPages,
113
+ useBulk: false,
114
+ label: 'countRows',
115
+ finalScanOnEof: true,
116
+ });
117
+ pagesScanned = scanned.pagesScanned;
118
+ }
119
+ catch (e) {
120
+ paginationError = e;
121
+ throw e;
122
+ }
123
+ finally {
124
+ await tracker.cleanup(rootLocator.page());
125
+ // Always restore page 1, even if pagination threw mid-count (#348), so the table
126
+ // stays usable for callers that catch the error. If reset() itself fails, only let
127
+ // it surface when pagination succeeded — otherwise preserve the original error.
128
+ log(config, `countRows: ${total} total row(s) across ${pagesScanned} page(s) — resetting`);
129
+ try {
130
+ await env.reset();
131
+ }
132
+ catch (resetError) {
133
+ if (paginationError === undefined)
134
+ throw resetError;
135
+ (0, debugUtils_1.logDebug)(config, 'error', 'countRows: reset() failed after pagination error', resetError);
136
+ }
137
+ }
138
+ return total;
139
+ }
@@ -0,0 +1,19 @@
1
+ import type { Locator } from '@playwright/test';
2
+ import type { FinalTableConfig, SmartRow, TableContext } from '../types';
3
+ export interface RowByIndexEnv<T = any> {
4
+ config: FinalTableConfig<T>;
5
+ getRowLocators: () => Locator;
6
+ getMap: () => Map<string, number>;
7
+ advancePage: (useBulk: boolean) => Promise<boolean>;
8
+ getCurrentPageIndex: () => number;
9
+ getContext: () => TableContext;
10
+ makeSmartRow: (rowLocator: Locator, map: Map<string, number>, rowIndex: number, tablePageIndex: number, healSelector?: string) => SmartRow<T>;
11
+ }
12
+ /**
13
+ * Finds the row whose logical index (per `strategies.resolveRowIndex`) is `index`:
14
+ * already mounted, else via `viewport.scrollToRow`, else by advancing pages until it
15
+ * mounts (bounded by `maxPages`). Callers must have initialized the table.
16
+ */
17
+ export declare function findRowByIndex<T>(env: RowByIndexEnv<T>, index: number, options?: {
18
+ maxPages?: number;
19
+ }): Promise<SmartRow<T>>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findRowByIndex = findRowByIndex;
4
+ const rowResolution_1 = require("./rowResolution");
5
+ const scanPages_1 = require("./scanPages");
6
+ const debugUtils_1 = require("../utils/debugUtils");
7
+ const safeStringify_1 = require("../utils/safeStringify");
8
+ /**
9
+ * Finds the row whose logical index (per `strategies.resolveRowIndex`) is `index`:
10
+ * already mounted, else via `viewport.scrollToRow`, else by advancing pages until it
11
+ * mounts (bounded by `maxPages`). Callers must have initialized the table.
12
+ */
13
+ async function findRowByIndex(env, index, options) {
14
+ var _a;
15
+ const { config } = env;
16
+ (0, debugUtils_1.logDebug)(config, 'verbose', `findRowByIndex: index=${index} options=${(0, safeStringify_1.safeStringify)(options)}`);
17
+ const resolveRI = config.strategies.resolveRowIndex;
18
+ const viewport = config.strategies.viewport;
19
+ // Identifying a row by its logical index requires a resolveRowIndex strategy. Without one
20
+ // the library can only offer render-window-relative getRowByIndex, or filter-based findRow.
21
+ if (!resolveRI) {
22
+ throw new Error(`findRowByIndex(${index}) requires a strategies.resolveRowIndex to identify rows by their logical index. ` +
23
+ `Configure one (e.g. read the grid's row-index attribute), or use getRowByIndex() (current render window) / findRow(filters).`);
24
+ }
25
+ let foundSelector;
26
+ // The currently-mounted row whose logical index === index, if any.
27
+ const findMounted = async () => {
28
+ const rows = await env.getRowLocators().all();
29
+ for (const r of rows) {
30
+ const result = await resolveRI(r);
31
+ if (result === undefined)
32
+ continue;
33
+ const normalized = (0, rowResolution_1.normalizeRowIndexResult)(result);
34
+ if (normalized.index === index) {
35
+ foundSelector = normalized.selector;
36
+ return r;
37
+ }
38
+ }
39
+ return null;
40
+ };
41
+ // 1. Already mounted (no navigation needed)?
42
+ let loc = await findMounted();
43
+ // 2. Fast path: jump straight to it via the viewport's random-access scroll.
44
+ if (!loc && (viewport === null || viewport === void 0 ? void 0 : viewport.scrollToRow)) {
45
+ (0, debugUtils_1.logDebug)(config, 'verbose', `findRowByIndex: scrolling to row ${index}`);
46
+ await viewport.scrollToRow(env.getContext(), index);
47
+ loc = await findMounted();
48
+ }
49
+ // 3. Fallback: advance pages (on infinite-scroll tables, a "page" is a scroll step) until
50
+ // the row is mounted. Page 1 was already searched above.
51
+ const maxPages = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
52
+ const scanForMounted = async () => {
53
+ let hit = null;
54
+ await (0, scanPages_1.scanPages)({ advancePage: env.advancePage, getCurrentPageIndex: env.getCurrentPageIndex, config }, async ({ pagesScanned }) => {
55
+ if (pagesScanned > 1)
56
+ hit = await findMounted();
57
+ return hit ? 'stop' : 'continue';
58
+ }, { maxPages, useBulk: false, label: 'findRowByIndex', finalScanOnEof: false });
59
+ return hit;
60
+ };
61
+ if (!loc && config.strategies.pagination) {
62
+ loc = await scanForMounted();
63
+ }
64
+ if (!loc) {
65
+ throw new Error(`findRowByIndex(${index}): could not reach row ${index}. ` +
66
+ `Ensure a viewport.scrollToRow or pagination can bring it into view (searched up to maxPages=${maxPages}).`);
67
+ }
68
+ await (0, debugUtils_1.debugDelay)(config, 'findRow');
69
+ return env.makeSmartRow(loc, env.getMap(), index, env.getCurrentPageIndex(), foundSelector);
70
+ }
@@ -25,6 +25,8 @@ export declare class RowFinder<T = any> {
25
25
  maxPages?: number;
26
26
  }): Promise<SmartRow<T>>;
27
27
  private waitForTableReady;
28
+ /** Unfiltered rows keep the plain `SmartRow #n` name that `_makeSmart` applies. */
29
+ private nameFilteredRow;
28
30
  findRows(filters?: Record<string, FilterValue>, options?: {
29
31
  exact?: boolean;
30
32
  maxPages?: number;
@@ -9,6 +9,7 @@ const navigationBarrier_1 = require("../utils/navigationBarrier");
9
9
  const rowResolution_1 = require("./rowResolution");
10
10
  const resolveCellLocator_1 = require("../utils/resolveCellLocator");
11
11
  const scanPages_1 = require("./scanPages");
12
+ const locatorDescription_1 = require("../utils/locatorDescription");
12
13
  const loadingWait_1 = require("../utils/loadingWait");
13
14
  class RowFinder {
14
15
  constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }, advancePage = async () => false) {
@@ -111,12 +112,13 @@ class RowFinder {
111
112
  await (0, debugUtils_1.debugDelay)(this.config, 'findRow');
112
113
  const map = await this.tableMapper.getMap();
113
114
  const resolved = await this.resolveRowIndex(rowLocator);
114
- return this.makeSmartRow(rowLocator, map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, undefined, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
115
+ const describedRow = (0, locatorDescription_1.describeLocator)(rowLocator, (0, locatorDescription_1.describeRowByFilters)(filters), this.config);
116
+ return this.makeSmartRow(describedRow, map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, undefined, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
115
117
  }
116
118
  (0, debugUtils_1.logDebug)(this.config, 'error', 'Row not found', filters);
117
119
  await (0, debugUtils_1.debugDelay)(this.config, 'findRow');
118
- const sentinel = this.resolve(this.config.rowSelector, this.rootLocator)
119
- .filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
120
+ const sentinel = (0, locatorDescription_1.describeLocator)(this.resolve(this.config.rowSelector, this.rootLocator)
121
+ .filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() }), (0, locatorDescription_1.describeSentinelRow)(filters), this.config);
120
122
  const smartRow = this.makeSmartRow(sentinel, await this.tableMapper.getMap(), undefined);
121
123
  smartRow[sentinel_1.SENTINEL_ROW] = true;
122
124
  return smartRow;
@@ -134,6 +136,12 @@ class RowFinder {
134
136
  };
135
137
  await (0, loadingWait_1.waitWhileTableLoading)(this.config, context, this.rootLocator.page(), 'waitForTableReady');
136
138
  }
139
+ /** Unfiltered rows keep the plain `SmartRow #n` name that `_makeSmart` applies. */
140
+ nameFilteredRow(row, resolved, filters) {
141
+ if (!resolved || Object.keys(filters).length === 0)
142
+ return row;
143
+ return (0, locatorDescription_1.describeLocator)(row, (0, locatorDescription_1.describeRowByIndex)(resolved.index, filters), this.config);
144
+ }
137
145
  async findRows(filters = {}, options) {
138
146
  var _a;
139
147
  var _b, _c;
@@ -168,7 +176,8 @@ class RowFinder {
168
176
  const barrier = useBarrier ? new navigationBarrier_1.NavigationBarrier(newIndices.length) : undefined;
169
177
  for (const idx of newIndices) {
170
178
  const resolved = await (0, rowResolution_1.resolveLogicalRowIndex)(currentRows[idx], this.config, () => allRows.length);
171
- const smartRow = this.makeSmartRow(currentRows[idx], map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
179
+ const rowLocator = this.nameFilteredRow(currentRows[idx], resolved, filtersRecord);
180
+ const smartRow = this.makeSmartRow(rowLocator, map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
172
181
  const loadingOutcome = await (0, rowResolution_1.resolveRowLoading)(smartRow, this.config.strategies.loading, 'skip', (msg) => (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: ${msg}`));
173
182
  if (loadingOutcome !== 'process') {
174
183
  barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
@@ -21,10 +21,10 @@ import type { FinalTableConfig } from '../types';
21
21
  * | findRow | yes | options.useBulkPagination | yes | DOM/post filters only |
22
22
  * | countRows | yes | no (single-step) | yes | count-only / post filters |
23
23
  * | async iterator | yes | no | via runMap path | same as map |
24
- * | findRowByIndex | no* | no | n/a | scroll/search, not a full scan |
24
+ * | findRowByIndex | no | no | no | stops once the target index is mounted* |
25
25
  * | bringIntoView | n/a | path planner | n/a | not a page scan |
26
26
  *
27
- * \* findRowByIndex stops when the target index is mounted; it does not collect rows.
27
+ * \* findRowByIndex only falls back to scanPages after the mounted / viewport.scrollToRow paths miss.
28
28
  */
29
29
  export type ScanDecision = 'continue' | 'stop';
30
30
  export interface ScanPageContext {
@@ -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;
@@ -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.49ce2de";
@@ -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.49ce2de";
@@ -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).
package/dist/smartRow.js CHANGED
@@ -7,6 +7,7 @@ const debugUtils_1 = require("./utils/debugUtils");
7
7
  const paginationPath_1 = require("./utils/paginationPath");
8
8
  const sentinel_1 = require("./utils/sentinel");
9
9
  const pageIndex_1 = require("./utils/pageIndex");
10
+ const locatorDescription_1 = require("./utils/locatorDescription");
10
11
  /**
11
12
  * Internal helper to navigate to a cell with active cell optimization.
12
13
  * Uses `strategies.navigation` primitives (goUp/goDown/goLeft/goRight, optional snap/seek/goHome).
@@ -328,7 +329,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
328
329
  // Attach Methods
329
330
  smart.getCell = (colName) => {
330
331
  var _a;
331
- var _b;
332
+ var _b, _c;
332
333
  if ((_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[colName]) {
333
334
  throw new Error(`Column "${colName}" is synthetic (no DOM cell) — use getValue("${colName}") or toJSON() instead`);
334
335
  }
@@ -351,7 +352,8 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
351
352
  else {
352
353
  baseLocator = resolve(config.cellSelector, rowLocator).nth(idx);
353
354
  }
354
- const smartCell = baseLocator;
355
+ const cellDescription = `${(_c = (0, locatorDescription_1.getLocatorDescription)(rowLocator)) !== null && _c !== void 0 ? _c : 'SmartRow'} › "${colName}" cell`;
356
+ const smartCell = (0, locatorDescription_1.describeLocator)(baseLocator, cellDescription, config);
355
357
  smartCell.bringIntoView = async () => {
356
358
  const page = rootLocator.page();
357
359
  const navigatedCell = await _navigateToCell({
@@ -850,36 +852,44 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
850
852
  barrier: smart._barrier
851
853
  });
852
854
  const columnOverride = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[colName];
853
- if (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) {
854
- const cellLocator = smart.getCell(colName);
855
- let currentValue;
856
- if (columnOverride.read) {
857
- currentValue = await columnOverride.read(cellLocator, {
858
- row: smart, columnName: colName, columnIndex: colIdx,
859
- getCell: (name) => smart.getCell(name),
855
+ try {
856
+ if (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) {
857
+ const cellLocator = smart.getCell(colName);
858
+ let currentValue;
859
+ if (columnOverride.read) {
860
+ currentValue = await columnOverride.read(cellLocator, {
861
+ row: smart, columnName: colName, columnIndex: colIdx,
862
+ getCell: (name) => smart.getCell(name),
863
+ });
864
+ }
865
+ await columnOverride.write({
866
+ cell: cellLocator,
867
+ targetValue: value,
868
+ currentValue,
869
+ row: smart
870
+ });
871
+ }
872
+ else {
873
+ const strategy = config.strategies.fill || fill_1.FillStrategies.default;
874
+ (0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
875
+ await strategy({
876
+ row: smart,
877
+ columnName: colName,
878
+ value,
879
+ index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
880
+ page: rowLocator.page(),
881
+ rootLocator,
882
+ config,
883
+ table: table,
884
+ fillOptions
860
885
  });
861
886
  }
862
- await columnOverride.write({
863
- cell: cellLocator,
864
- targetValue: value,
865
- currentValue,
866
- row: smart
867
- });
868
887
  }
869
- else {
870
- const strategy = config.strategies.fill || fill_1.FillStrategies.default;
871
- (0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
872
- await strategy({
873
- row: smart,
874
- columnName: colName,
875
- value,
876
- index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
877
- page: rowLocator.page(),
878
- rootLocator,
879
- config,
880
- table: table,
881
- fillOptions
882
- });
888
+ catch (err) {
889
+ const source = (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) ? 'columnOverrides.write' : 'fill strategy';
890
+ const wrapped = new Error(`[SmartTable] smartFill: ${source} for "${colName}" failed — ${err instanceof Error ? err.message : String(err)}`);
891
+ wrapped.cause = err;
892
+ throw wrapped;
883
893
  }
884
894
  // Delay after filling
885
895
  await (0, debugUtils_1.debugDelay)(config, 'getCell');
@@ -1,5 +1,4 @@
1
1
  import type { ContentReadyStrategy } from '../types';
2
- export type { ContentReadyStrategy };
3
2
  export declare const ContentReadyStrategies: {
4
3
  /**
5
4
  * Polls the row's text content until two consecutive reads match.
@@ -3,7 +3,6 @@ export * from './sorting';
3
3
  export * from './columns';
4
4
  export * from './headers';
5
5
  export * from './fill';
6
- export type { ColumnResolutionStrategy } from './resolution';
7
6
  export * from './dedupe';
8
7
  export * from './loading';
9
8
  export * from './stabilization';
@@ -102,10 +101,10 @@ export declare const Strategies: {
102
101
  textStable: (options?: {
103
102
  timeout?: number;
104
103
  interval?: number;
105
- }) => import("./contentReady").ContentReadyStrategy;
104
+ }) => import("..").ContentReadyStrategy;
106
105
  mutationSettled: (options?: {
107
106
  timeout?: number;
108
107
  quietPeriod?: number;
109
- }) => import("./contentReady").ContentReadyStrategy;
108
+ }) => import("..").ContentReadyStrategy;
110
109
  };
111
110
  };
@@ -1,4 +1,5 @@
1
1
  import { StrategyContext } from '../types';
2
+ /** @deprecated Not used by the library. Will be removed in v7. */
2
3
  export interface ColumnResolutionStrategy {
3
4
  /**
4
5
  * Resolves a column name (string or Regex) to a column index.
@@ -17,6 +18,3 @@ export interface ColumnResolutionStrategy {
17
18
  headerMap: Map<string, number>;
18
19
  }): string;
19
20
  }
20
- export declare const ResolutionStrategies: {
21
- default: ColumnResolutionStrategy;
22
- };
@@ -1,30 +1,2 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ResolutionStrategies = void 0;
4
- exports.ResolutionStrategies = {
5
- default: {
6
- resolveIndex: ({ query, headerMap }) => {
7
- // 1. Exact / String Match
8
- if (typeof query === 'string') {
9
- if (headerMap.has(query))
10
- return headerMap.get(query);
11
- }
12
- // 2. Regex Match
13
- if (query instanceof RegExp) {
14
- for (const [colName, idx] of headerMap.entries()) {
15
- if (query.test(colName))
16
- return idx;
17
- }
18
- return undefined;
19
- }
20
- // 3. (Optional) Fuzzy String Match fallback could go here
21
- // But for strict default strategy, we might want to keep it simple first
22
- // The original code didn't do fuzzy *resolution* logic inside the get(), it just did strict get().
23
- // The fuzzy logic was only for *suggestions* on error.
24
- return undefined;
25
- },
26
- resolveName: ({ query }) => {
27
- return query.toString();
28
- }
29
- }
30
- };