@rickcedwhat/playwright-smart-table 6.7.0 → 6.7.3

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/README.md CHANGED
@@ -128,6 +128,32 @@ const expensive = await table.filter(async ({ row }) => {
128
128
  });
129
129
  ```
130
130
 
131
+ ### Advanced: `columnOverrides`
132
+
133
+ For complex DOM structures, custom data extraction, or specialized input widgets, use `columnOverrides` to intercept how Smart Table interacts with specific columns:
134
+
135
+ ```typescript
136
+ const table = useTable(page.locator('#table'), {
137
+ columnOverrides: {
138
+ // Override how data is read from the 'Status' column (e.g., for .toJSON())
139
+ Status: {
140
+ read: async (cell) => {
141
+ const isChecked = await cell.locator('input[type="checkbox"]').isChecked();
142
+ return isChecked ? 'Active' : 'Inactive';
143
+ }
144
+ },
145
+ // Override how data is written to the 'Tags' column (for .smartFill())
146
+ Tags: {
147
+ write: async (cell, value) => {
148
+ await cell.click();
149
+ await page.keyboard.type(value);
150
+ await page.keyboard.press('Enter');
151
+ }
152
+ }
153
+ }
154
+ });
155
+ ```
156
+
131
157
  ## Key Features
132
158
 
133
159
  - 🎯 **Smart Locators** - Find rows by content, not position
@@ -19,10 +19,7 @@ export declare class RowFinder<T = any> {
19
19
  exact?: boolean;
20
20
  maxPages?: number;
21
21
  }): Promise<SmartRow<T>>;
22
- findRows(filtersOrOptions?: (Partial<T> | Record<string, FilterValue>) & ({
23
- exact?: boolean;
24
- maxPages?: number;
25
- }), legacyOptions?: {
22
+ findRows(filters?: Partial<T> | Record<string, FilterValue>, options?: {
26
23
  exact?: boolean;
27
24
  maxPages?: number;
28
25
  }): Promise<SmartRowArray<T>>;
@@ -8,22 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __rest = (this && this.__rest) || function (s, e) {
12
- var t = {};
13
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
- t[p] = s[p];
15
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
- t[p[i]] = s[p[i]];
19
- }
20
- return t;
21
- };
22
11
  Object.defineProperty(exports, "__esModule", { value: true });
23
12
  exports.RowFinder = void 0;
24
13
  const debugUtils_1 = require("../utils/debugUtils");
25
14
  const smartRowArray_1 = require("../utils/smartRowArray");
26
15
  const validation_1 = require("../strategies/validation");
16
+ const elementTracker_1 = require("../utils/elementTracker");
27
17
  class RowFinder {
28
18
  constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }) {
29
19
  this.rootLocator = rootLocator;
@@ -51,87 +41,75 @@ class RowFinder {
51
41
  yield (0, debugUtils_1.debugDelay)(this.config, 'findRow');
52
42
  const sentinel = this.resolve(this.config.rowSelector, this.rootLocator)
53
43
  .filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
54
- return this.makeSmartRow(sentinel, yield this.tableMapper.getMap(), 0);
44
+ const smartRow = this.makeSmartRow(sentinel, yield this.tableMapper.getMap(), 0);
45
+ smartRow._isSentinel = true;
46
+ return smartRow;
55
47
  });
56
48
  }
57
- findRows(filtersOrOptions,
58
- // Deprecated: verify legacy usage pattern support
59
- legacyOptions) {
49
+ findRows(filters, options) {
60
50
  return __awaiter(this, void 0, void 0, function* () {
61
- // Detect argument pattern:
62
- // Pattern A: findRows({ Name: 'Alice' }, { maxPages: 5 })
63
- // Pattern B: findRows({ maxPages: 5 }) <-- No filters, just options
64
- // Pattern C: findRows({ Name: 'Alice' }) <-- Only filters
65
51
  var _a, _b;
66
- let filters = {};
67
- let options = {};
68
- if (legacyOptions) {
69
- // Pattern A
70
- filters = filtersOrOptions;
71
- options = legacyOptions;
72
- }
73
- else {
74
- // Pattern B or C
75
- // We need to separate unknown keys (filters) from known options (exact, maxPages)
76
- // However, filtersOrOptions can be null/undefined
77
- if (filtersOrOptions) {
78
- const _c = filtersOrOptions, { exact, maxPages } = _c, rest = __rest(_c, ["exact", "maxPages"]);
79
- options = { exact, maxPages };
80
- filters = rest;
81
- }
82
- }
52
+ const filtersRecord = filters || {};
83
53
  const map = yield this.tableMapper.getMap();
84
54
  const allRows = [];
85
- const effectiveMaxPages = (_b = (_a = options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
55
+ const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
86
56
  let pagesScanned = 1;
87
- const collectMatches = () => __awaiter(this, void 0, void 0, function* () {
88
- var _a, _b;
89
- // ... logic ...
90
- let rowLocators = this.resolve(this.config.rowSelector, this.rootLocator);
91
- // Only apply filters if we have them
92
- if (Object.keys(filters).length > 0) {
93
- rowLocators = this.filterEngine.applyFilters(rowLocators, filters, map, (_a = options.exact) !== null && _a !== void 0 ? _a : false, this.rootLocator.page());
94
- }
95
- const currentRows = yield rowLocators.all();
96
- const isRowLoading = (_b = this.config.strategies.loading) === null || _b === void 0 ? void 0 : _b.isRowLoading;
97
- for (let i = 0; i < currentRows.length; i++) {
98
- const smartRow = this.makeSmartRow(currentRows[i], map, allRows.length + i, this.tableState.currentPageIndex);
99
- if (isRowLoading && (yield isRowLoading(smartRow)))
100
- continue;
101
- allRows.push(smartRow);
102
- }
103
- });
104
- // Scan first page
105
- yield collectMatches();
106
- // Pagination Loop - Corrected logic
107
- // We always scan at least 1 page.
108
- // If maxPages > 1, and we have a pagination strategy, we try to go next.
109
- while (pagesScanned < effectiveMaxPages && this.config.strategies.pagination) {
110
- const context = {
111
- root: this.rootLocator,
112
- config: this.config,
113
- resolve: this.resolve,
114
- page: this.rootLocator.page()
115
- };
116
- // Check if we should stop? (e.g. if we found enough rows? No, findRows finds ALL)
117
- let paginationResult;
118
- if (typeof this.config.strategies.pagination === 'function') {
119
- paginationResult = yield this.config.strategies.pagination(context);
120
- }
121
- else {
122
- // It's a PaginationPrimitives object, use goNext by default for findRows
123
- if (!this.config.strategies.pagination.goNext) {
124
- break; // Cannot paginate forward
57
+ const tracker = new elementTracker_1.ElementTracker('findRows');
58
+ try {
59
+ const collectMatches = () => __awaiter(this, void 0, void 0, function* () {
60
+ var _a, _b;
61
+ let rowLocators = this.resolve(this.config.rowSelector, this.rootLocator);
62
+ // Only apply filters if we have them
63
+ if (Object.keys(filtersRecord).length > 0) {
64
+ rowLocators = this.filterEngine.applyFilters(rowLocators, filtersRecord, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, this.rootLocator.page());
125
65
  }
126
- paginationResult = yield this.config.strategies.pagination.goNext(context);
127
- }
128
- const didPaginate = yield (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
129
- if (!didPaginate)
130
- break;
131
- this.tableState.currentPageIndex++;
132
- pagesScanned++;
133
- // Wait for reload logic if needed? Usually pagination handles it.
66
+ // Get only newly seen matched rows
67
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
68
+ const currentRows = yield rowLocators.all();
69
+ const isRowLoading = (_b = this.config.strategies.loading) === null || _b === void 0 ? void 0 : _b.isRowLoading;
70
+ for (const idx of newIndices) {
71
+ const smartRow = this.makeSmartRow(currentRows[idx], map, allRows.length, this.tableState.currentPageIndex);
72
+ if (isRowLoading && (yield isRowLoading(smartRow)))
73
+ continue;
74
+ allRows.push(smartRow);
75
+ }
76
+ });
77
+ // Scan first page
134
78
  yield collectMatches();
79
+ // Pagination Loop
80
+ while (pagesScanned < effectiveMaxPages && this.config.strategies.pagination) {
81
+ const context = {
82
+ root: this.rootLocator,
83
+ config: this.config,
84
+ resolve: this.resolve,
85
+ page: this.rootLocator.page()
86
+ };
87
+ let paginationResult;
88
+ if (typeof this.config.strategies.pagination === 'function') {
89
+ paginationResult = yield this.config.strategies.pagination(context);
90
+ }
91
+ else {
92
+ if (this.config.strategies.pagination.goNextBulk) {
93
+ paginationResult = yield this.config.strategies.pagination.goNextBulk(context);
94
+ }
95
+ else if (this.config.strategies.pagination.goNext) {
96
+ paginationResult = yield this.config.strategies.pagination.goNext(context);
97
+ }
98
+ else {
99
+ break;
100
+ }
101
+ }
102
+ const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
103
+ if (!didPaginate)
104
+ break;
105
+ const pagesJumped = typeof paginationResult === 'number' ? paginationResult : 1;
106
+ this.tableState.currentPageIndex += pagesJumped;
107
+ pagesScanned += pagesJumped;
108
+ yield collectMatches();
109
+ }
110
+ }
111
+ finally {
112
+ yield tracker.cleanup(this.rootLocator.page());
135
113
  }
136
114
  return (0, smartRowArray_1.createSmartRowArray)(allRows);
137
115
  });
@@ -192,16 +170,22 @@ class RowFinder {
192
170
  paginationResult = yield this.config.strategies.pagination(context);
193
171
  }
194
172
  else {
195
- if (!this.config.strategies.pagination.goNext) {
196
- this.log(`Page ${this.tableState.currentPageIndex}: Pagination failed (no goNext primitive).`);
173
+ if (this.config.strategies.pagination.goNextBulk) {
174
+ paginationResult = yield this.config.strategies.pagination.goNextBulk(context);
175
+ }
176
+ else if (this.config.strategies.pagination.goNext) {
177
+ paginationResult = yield this.config.strategies.pagination.goNext(context);
178
+ }
179
+ else {
180
+ this.log(`Page ${this.tableState.currentPageIndex}: Pagination failed (no goNext or goNextBulk primitive).`);
197
181
  return null;
198
182
  }
199
- paginationResult = yield this.config.strategies.pagination.goNext(context);
200
183
  }
201
184
  const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
202
185
  if (didLoadMore) {
203
- this.tableState.currentPageIndex++;
204
- pagesScanned++;
186
+ const pagesJumped = typeof paginationResult === 'number' ? paginationResult : 1;
187
+ this.tableState.currentPageIndex += pagesJumped;
188
+ pagesScanned += pagesJumped;
205
189
  continue;
206
190
  }
207
191
  else {
@@ -105,7 +105,7 @@ class TableMapper {
105
105
  text = yield this.config.headerTransformer({
106
106
  text,
107
107
  index: i,
108
- locator: this.rootLocator.locator(this.config.headerSelector).nth(i),
108
+ locator: this.resolve(this.config.headerSelector, this.rootLocator).nth(i),
109
109
  seenHeaders
110
110
  });
111
111
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { useTable } from './useTable';
2
- export type { TableConfig, TableResult, SmartRow, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, } from './types';
2
+ export type { TableConfig, TableResult, SmartRow, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, } from './types';
3
3
  export { Strategies } from './strategies';
4
4
  export { Plugins } from './plugins';
package/dist/plugins.d.ts CHANGED
@@ -3,7 +3,6 @@ export declare const Plugins: {
3
3
  Strategies: {
4
4
  header: (context: import("./types").TableContext) => Promise<string[]>;
5
5
  getCellLocator: ({ row, columnIndex }: any) => any;
6
- cellNavigation: ({ root, page, index }: any) => Promise<void>;
7
6
  navigation: {
8
7
  goRight: ({ root, page }: any) => Promise<void>;
9
8
  goLeft: ({ root, page }: any) => Promise<void>;
@@ -31,6 +30,9 @@ export declare const Plugins: {
31
30
  goRight: (context: import("./types").StrategyContext) => Promise<void>;
32
31
  goHome: (context: import("./types").StrategyContext) => Promise<void>;
33
32
  };
33
+ loading: {
34
+ isHeaderLoading: () => Promise<boolean>;
35
+ };
34
36
  getCellLocator: ({ row, columnIndex }: any) => any;
35
37
  getActiveCell: ({ page }: any) => Promise<{
36
38
  rowIndex: number;
package/dist/smartRow.js CHANGED
@@ -16,7 +16,6 @@ const debugUtils_1 = require("./utils/debugUtils");
16
16
  /**
17
17
  * Internal helper to navigate to a cell with active cell optimization.
18
18
  * Uses navigation primitives (goUp, goDown, goLeft, goRight, goHome) for orchestration.
19
- * Falls back to cellNavigation for backward compatibility.
20
19
  * Returns the target cell locator after navigation.
21
20
  */
22
21
  const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function* () {
@@ -91,7 +90,6 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
91
90
  }
92
91
  return null;
93
92
  }
94
- ;
95
93
  return null;
96
94
  });
97
95
  /**
@@ -121,10 +119,23 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
121
119
  }
122
120
  return resolve(config.cellSelector, rowLocator).nth(idx);
123
121
  };
122
+ smart.wasFound = () => {
123
+ return !smart._isSentinel;
124
+ };
124
125
  smart.toJSON = (options) => __awaiter(void 0, void 0, void 0, function* () {
125
126
  var _a;
126
127
  const result = {};
127
128
  const page = rootLocator.page();
129
+ // Build a getHeaderCell helper for the beforeCellRead context.
130
+ // Uses the table reference if available, otherwise falls back to index-based lookup.
131
+ const getHeaderCell = (table === null || table === void 0 ? void 0 : table.getHeaderCell)
132
+ ? table.getHeaderCell.bind(table)
133
+ : (colName) => __awaiter(void 0, void 0, void 0, function* () {
134
+ const idx = map.get(colName);
135
+ if (idx === undefined)
136
+ throw new Error(`Column "${colName}" not found`);
137
+ return resolve(config.headerSelector, rootLocator).nth(idx);
138
+ });
128
139
  for (const [col, idx] of map.entries()) {
129
140
  if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(col)) {
130
141
  continue;
@@ -132,13 +143,6 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
132
143
  // Check if we have a column override for this column
133
144
  const columnOverride = (_a = config.columnOverrides) === null || _a === void 0 ? void 0 : _a[col];
134
145
  const mapper = columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.read;
135
- if (mapper) {
136
- // Use custom mapper
137
- // Ensure we have the cell first (same navigation logic)
138
- // ... wait, the navigation logic below assumes we need to navigate.
139
- // If we have a mapper, we still need the cell locator.
140
- // Let's reuse the navigation logic to get targetCell
141
- }
142
146
  // --- Navigation Logic Start ---
143
147
  const cell = config.strategies.getCellLocator
144
148
  ? config.strategies.getCellLocator({
@@ -168,6 +172,19 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
168
172
  }
169
173
  }
170
174
  // --- Navigation Logic End ---
175
+ // Call beforeCellRead hook if configured.
176
+ // Fires for BOTH columnOverrides.read and the default innerText path.
177
+ if (config.strategies.beforeCellRead) {
178
+ yield config.strategies.beforeCellRead({
179
+ cell: targetCell,
180
+ columnName: col,
181
+ columnIndex: idx,
182
+ row: rowLocator,
183
+ page,
184
+ root: rootLocator,
185
+ getHeaderCell,
186
+ });
187
+ }
171
188
  if (mapper) {
172
189
  // Apply mapper
173
190
  const mappedValue = yield mapper(targetCell);
@@ -182,6 +199,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
182
199
  return result;
183
200
  });
184
201
  smart.smartFill = (data, fillOptions) => __awaiter(void 0, void 0, void 0, function* () {
202
+ var _a;
185
203
  (0, debugUtils_1.logDebug)(config, 'info', 'Filling row', data);
186
204
  for (const [colName, value] of Object.entries(data)) {
187
205
  if (value === undefined)
@@ -200,19 +218,35 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
200
218
  rowLocator,
201
219
  rowIndex
202
220
  });
203
- const strategy = config.strategies.fill || fill_1.FillStrategies.default;
204
- (0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
205
- yield strategy({
206
- row: smart,
207
- columnName: colName,
208
- value,
209
- index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
210
- page: rowLocator.page(),
211
- rootLocator,
212
- config,
213
- table: table,
214
- fillOptions
215
- });
221
+ const columnOverride = (_a = config.columnOverrides) === null || _a === void 0 ? void 0 : _a[colName];
222
+ if (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) {
223
+ const cellLocator = smart.getCell(colName);
224
+ let currentValue;
225
+ if (columnOverride.read) {
226
+ currentValue = yield columnOverride.read(cellLocator);
227
+ }
228
+ yield columnOverride.write({
229
+ cell: cellLocator,
230
+ targetValue: value,
231
+ currentValue,
232
+ row: smart
233
+ });
234
+ }
235
+ else {
236
+ const strategy = config.strategies.fill || fill_1.FillStrategies.default;
237
+ (0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
238
+ yield strategy({
239
+ row: smart,
240
+ columnName: colName,
241
+ value,
242
+ index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
243
+ page: rowLocator.page(),
244
+ rootLocator,
245
+ config,
246
+ table: table,
247
+ fillOptions
248
+ });
249
+ }
216
250
  // Delay after filling
217
251
  yield (0, debugUtils_1.debugDelay)(config, 'getCell');
218
252
  }
@@ -33,6 +33,9 @@ export declare const GlideStrategies: {
33
33
  goRight: (context: import("../types").StrategyContext) => Promise<void>;
34
34
  goHome: (context: import("../types").StrategyContext) => Promise<void>;
35
35
  };
36
+ loading: {
37
+ isHeaderLoading: () => Promise<boolean>;
38
+ };
36
39
  getCellLocator: ({ row, columnIndex }: any) => any;
37
40
  getActiveCell: ({ page }: any) => Promise<{
38
41
  rowIndex: number;
@@ -115,6 +115,9 @@ exports.GlideStrategies = {
115
115
  goRight: columns_1.glideGoRight,
116
116
  goHome: columns_1.glideGoHome
117
117
  },
118
+ loading: {
119
+ isHeaderLoading: () => __awaiter(void 0, void 0, void 0, function* () { return false; }) // Glide renders headers on a canvas, there is no innerText delay
120
+ },
118
121
  getCellLocator: exports.glideGetCellLocator,
119
122
  getActiveCell: exports.glideGetActiveCell
120
123
  };
@@ -15,6 +15,8 @@ export declare const Strategies: {
15
15
  previousBulk?: import("..").Selector;
16
16
  first?: import("..").Selector;
17
17
  }, options?: {
18
+ nextBulkPages?: number;
19
+ previousBulkPages?: number;
18
20
  stabilization?: import("./stabilization").StabilizationStrategy;
19
21
  timeout?: number;
20
22
  }) => import("../types").PaginationStrategy;
@@ -33,7 +35,7 @@ export declare const Strategies: {
33
35
  default: () => Promise<void>;
34
36
  };
35
37
  Header: {
36
- visible: ({ config, resolve, root }: import("../types").StrategyContext) => Promise<string[]>;
38
+ visible: ({ config, resolve, root }: import("..").StrategyContext) => Promise<string[]>;
37
39
  };
38
40
  Fill: {
39
41
  default: ({ row, columnName, value, fillOptions, config, table }: Parameters<import("../types").FillStrategy>[0]) => Promise<void>;
@@ -46,8 +48,8 @@ export declare const Strategies: {
46
48
  };
47
49
  Loading: {
48
50
  Table: {
49
- hasSpinner: (selector?: string) => ({ root }: import("../types").TableContext) => Promise<boolean>;
50
- custom: (fn: (context: import("../types").TableContext) => Promise<boolean>) => (context: import("../types").TableContext) => Promise<boolean>;
51
+ hasSpinner: (selector?: string) => ({ root }: import("..").TableContext) => Promise<boolean>;
52
+ custom: (fn: (context: import("..").TableContext) => Promise<boolean>) => (context: import("..").TableContext) => Promise<boolean>;
51
53
  never: () => Promise<boolean>;
52
54
  };
53
55
  Row: {
@@ -57,7 +59,7 @@ export declare const Strategies: {
57
59
  never: () => Promise<boolean>;
58
60
  };
59
61
  Headers: {
60
- stable: (duration?: number) => (context: import("../types").TableContext) => Promise<boolean>;
62
+ stable: (duration?: number) => (context: import("..").TableContext) => Promise<boolean>;
61
63
  never: () => Promise<boolean>;
62
64
  };
63
65
  };
@@ -8,6 +8,8 @@ export declare const PaginationStrategies: {
8
8
  previousBulk?: Selector;
9
9
  first?: Selector;
10
10
  }, options?: {
11
+ nextBulkPages?: number;
12
+ previousBulkPages?: number;
11
13
  stabilization?: StabilizationStrategy;
12
14
  timeout?: number;
13
15
  }) => PaginationStrategy;
@@ -13,9 +13,9 @@ exports.PaginationStrategies = void 0;
13
13
  const stabilization_1 = require("./stabilization");
14
14
  exports.PaginationStrategies = {
15
15
  click: (selectors, options = {}) => {
16
- var _a;
16
+ var _a, _b, _c;
17
17
  const defaultStabilize = (_a = options.stabilization) !== null && _a !== void 0 ? _a : stabilization_1.StabilizationStrategies.contentChanged({ scope: 'first', timeout: options.timeout });
18
- const createClicker = (selector) => {
18
+ const createClicker = (selector, returnVal = true) => {
19
19
  if (!selector)
20
20
  return undefined;
21
21
  return (context) => __awaiter(void 0, void 0, void 0, function* () {
@@ -26,14 +26,14 @@ exports.PaginationStrategies = {
26
26
  }
27
27
  return yield defaultStabilize(context, () => __awaiter(void 0, void 0, void 0, function* () {
28
28
  yield btn.click({ timeout: 2000 }).catch(() => { });
29
- }));
29
+ })).then(stabilized => stabilized ? returnVal : false);
30
30
  });
31
31
  };
32
32
  return {
33
33
  goNext: createClicker(selectors.next),
34
34
  goPrevious: createClicker(selectors.previous),
35
- goNextBulk: createClicker(selectors.nextBulk),
36
- goPreviousBulk: createClicker(selectors.previousBulk),
35
+ goNextBulk: createClicker(selectors.nextBulk, (_b = options.nextBulkPages) !== null && _b !== void 0 ? _b : 10),
36
+ goPreviousBulk: createClicker(selectors.previousBulk, (_c = options.previousBulkPages) !== null && _c !== void 0 ? _c : 10),
37
37
  goToFirst: createClicker(selectors.first)
38
38
  };
39
39
  },
@@ -9,10 +9,6 @@ export declare const scrollRightHeaderRDG: (context: TableContext) => Promise<st
9
9
  * changing during pagination/scrolling.
10
10
  */
11
11
  export declare const rdgGetCellLocator: ({ row, columnIndex }: any) => any;
12
- /**
13
- * Scrolls virtualized columns into view before reading.
14
- */
15
- export declare const rdgCellNavigation: ({ root, page, index }: any) => Promise<void>;
16
12
  /**
17
13
  * Scrolls the grid vertically to load more virtualized rows.
18
14
  */
@@ -27,7 +23,6 @@ export declare const rdgNavigation: {
27
23
  export declare const RDGStrategies: {
28
24
  header: (context: TableContext) => Promise<string[]>;
29
25
  getCellLocator: ({ row, columnIndex }: any) => any;
30
- cellNavigation: ({ root, page, index }: any) => Promise<void>;
31
26
  navigation: {
32
27
  goRight: ({ root, page }: any) => Promise<void>;
33
28
  goLeft: ({ root, page }: any) => Promise<void>;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.RDGStrategies = exports.rdgNavigation = exports.rdgPaginationStrategy = exports.rdgCellNavigation = exports.rdgGetCellLocator = exports.scrollRightHeaderRDG = void 0;
12
+ exports.RDGStrategies = exports.rdgNavigation = exports.rdgPaginationStrategy = exports.rdgGetCellLocator = exports.scrollRightHeaderRDG = void 0;
13
13
  /**
14
14
  * Scrolls the grid horizontally to collect all column headers.
15
15
  * Handles empty headers by labeling them (e.g. "Checkbox").
@@ -64,22 +64,6 @@ const rdgGetCellLocator = ({ row, columnIndex }) => {
64
64
  return row.locator(`[role="gridcell"][aria-colindex="${ariaColIndex}"]`);
65
65
  };
66
66
  exports.rdgGetCellLocator = rdgGetCellLocator;
67
- /**
68
- * Scrolls virtualized columns into view before reading.
69
- */
70
- const rdgCellNavigation = (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page, index }) {
71
- // Check if the column header is visible and scroll horizontally if needed
72
- const headerCell = root.locator(`[role="columnheader"][aria-colindex="${index + 1}"]`);
73
- const isVisible = yield headerCell.isVisible().catch(() => false);
74
- if (!isVisible) {
75
- const estimatedScroll = index * 150;
76
- yield root.evaluate((el, scrollAmount) => {
77
- el.scrollLeft = scrollAmount;
78
- }, estimatedScroll);
79
- yield page.waitForTimeout(300);
80
- }
81
- });
82
- exports.rdgCellNavigation = rdgCellNavigation;
83
67
  /**
84
68
  * Scrolls the grid vertically to load more virtualized rows.
85
69
  */
@@ -136,7 +120,6 @@ exports.rdgNavigation = {
136
120
  exports.RDGStrategies = {
137
121
  header: exports.scrollRightHeaderRDG,
138
122
  getCellLocator: exports.rdgGetCellLocator,
139
- cellNavigation: exports.rdgCellNavigation,
140
123
  navigation: exports.rdgNavigation,
141
124
  pagination: exports.rdgPaginationStrategy
142
125
  };
@@ -23,21 +23,16 @@ exports.SortingStrategies = {
23
23
  return {
24
24
  doSort(_a) {
25
25
  return __awaiter(this, arguments, void 0, function* ({ columnName, direction, context }) {
26
- const { getHeaderCell } = context;
27
- if (!getHeaderCell)
28
- throw new Error('getHeaderCell is required in StrategyContext for sorting.');
26
+ // getHeaderCell is always present on TableContext after table is initialized
27
+ const targetHeader = yield context.getHeaderCell(columnName);
29
28
  // The table engine handles verify-and-retry. We only provide the trigger here.
30
- const targetHeader = yield getHeaderCell(columnName);
31
29
  yield targetHeader.click();
32
30
  });
33
31
  },
34
32
  getSortState(_a) {
35
33
  return __awaiter(this, arguments, void 0, function* ({ columnName, context }) {
36
- const { getHeaderCell } = context;
37
34
  try {
38
- if (!getHeaderCell)
39
- throw new Error('getHeaderCell is required');
40
- const targetHeader = yield getHeaderCell(columnName);
35
+ const targetHeader = yield context.getHeaderCell(columnName);
41
36
  const ariaSort = yield targetHeader.getAttribute('aria-sort');
42
37
  if (ariaSort === 'ascending')
43
38
  return 'asc';
@@ -46,7 +41,7 @@ exports.SortingStrategies = {
46
41
  return 'none';
47
42
  }
48
43
  catch (_b) {
49
- return 'none'; // Header not found, so it's not sorted
44
+ return 'none'; // Header not found, treat as unsorted
50
45
  }
51
46
  });
52
47
  },
@@ -10,10 +10,13 @@ exports.validateFillStrategy = validateFillStrategy;
10
10
  * @param strategyName - Name of the strategy for error messages
11
11
  */
12
12
  function validatePaginationResult(result, strategyName = 'Custom Pagination Strategy') {
13
- if (typeof result !== 'boolean') {
14
- throw new Error(`[${strategyName}] Pagination strategy must return a boolean (true if paginated, false if no more pages). ` +
13
+ if (typeof result !== 'boolean' && typeof result !== 'number') {
14
+ throw new Error(`[${strategyName}] Pagination strategy must return a boolean (true if paginated, false if no more pages) or a number (pages jumped). ` +
15
15
  `Received: ${typeof result} (${JSON.stringify(result)})`);
16
16
  }
17
+ if (typeof result === 'number') {
18
+ return result > 0;
19
+ }
17
20
  return result;
18
21
  }
19
22
  /**