@rickcedwhat/playwright-smart-table 3.2.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/useTable.js CHANGED
@@ -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.ColumnStrategies = exports.HeaderStrategies = exports.FillStrategies = exports.SortingStrategies = exports.TableStrategies = exports.PaginationStrategies = exports.useTable = void 0;
12
+ exports.Strategies = exports.ResolutionStrategies = exports.CellNavigationStrategies = exports.HeaderStrategies = exports.FillStrategies = exports.SortingStrategies = exports.PaginationStrategies = exports.useTable = void 0;
13
13
  const typeContext_1 = require("./typeContext");
14
14
  const sorting_1 = require("./strategies/sorting");
15
15
  const pagination_1 = require("./strategies/pagination");
@@ -18,14 +18,29 @@ Object.defineProperty(exports, "FillStrategies", { enumerable: true, get: functi
18
18
  const headers_1 = require("./strategies/headers");
19
19
  Object.defineProperty(exports, "HeaderStrategies", { enumerable: true, get: function () { return headers_1.HeaderStrategies; } });
20
20
  const columns_1 = require("./strategies/columns");
21
- Object.defineProperty(exports, "ColumnStrategies", { enumerable: true, get: function () { return columns_1.ColumnStrategies; } });
21
+ Object.defineProperty(exports, "CellNavigationStrategies", { enumerable: true, get: function () { return columns_1.CellNavigationStrategies; } });
22
+ const smartRow_1 = require("./smartRow");
23
+ const filterEngine_1 = require("./filterEngine");
24
+ const resolution_1 = require("./strategies/resolution");
25
+ Object.defineProperty(exports, "ResolutionStrategies", { enumerable: true, get: function () { return resolution_1.ResolutionStrategies; } });
26
+ const strategies_1 = require("./strategies");
27
+ Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
28
+ const validation_1 = require("./strategies/validation");
22
29
  /**
23
30
  * Main hook to interact with a table.
24
31
  */
25
32
  const useTable = (rootLocator, configOptions = {}) => {
33
+ var _a;
26
34
  // Store whether pagination was explicitly provided in config
27
- const hasPaginationInConfig = configOptions.pagination !== undefined;
28
- const config = Object.assign({ rowSelector: "tbody tr", headerSelector: "th", cellSelector: "td", pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }), maxPages: 1, headerTransformer: ({ text, index, locator }) => text, autoScroll: true, debug: false, fillStrategy: fill_1.FillStrategies.default, headerStrategy: headers_1.HeaderStrategies.visible, columnStrategy: columns_1.ColumnStrategies.default, onReset: () => __awaiter(void 0, void 0, void 0, function* () { console.warn("⚠️ .reset() called but no 'onReset' strategy defined in config."); }) }, configOptions);
35
+ const hasPaginationInConfig = ((_a = configOptions.strategies) === null || _a === void 0 ? void 0 : _a.pagination) !== undefined;
36
+ // Default strategies
37
+ const defaultStrategies = {
38
+ fill: fill_1.FillStrategies.default,
39
+ header: headers_1.HeaderStrategies.visible,
40
+ cellNavigation: columns_1.CellNavigationStrategies.default,
41
+ pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
42
+ };
43
+ const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text, index, locator }) => text, autoScroll: true, debug: false, onReset: () => __awaiter(void 0, void 0, void 0, function* () { }) }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
29
44
  const resolve = (item, parent) => {
30
45
  if (typeof item === 'string')
31
46
  return parent.locator(item);
@@ -37,31 +52,28 @@ const useTable = (rootLocator, configOptions = {}) => {
37
52
  let _headerMap = null;
38
53
  let _hasPaginated = false;
39
54
  let _isInitialized = false;
55
+ // Helpers
40
56
  const logDebug = (msg) => {
41
57
  if (config.debug)
42
58
  console.log(`🔎 [SmartTable Debug] ${msg}`);
43
59
  };
44
- const _suggestColumnName = (colName, availableColumns) => {
45
- // Simple fuzzy matching - find columns with similar names
60
+ const _createColumnError = (colName, map, context) => {
61
+ const availableColumns = Array.from(map.keys());
62
+ // Use Suggestion Logic from ResolutionStrategy (if we had a fuzzy one, for now manual suggest)
46
63
  const lowerCol = colName.toLowerCase();
47
64
  const suggestions = availableColumns.filter(col => col.toLowerCase().includes(lowerCol) ||
48
65
  lowerCol.includes(col.toLowerCase()) ||
49
66
  col.toLowerCase().replace(/\s+/g, '') === lowerCol.replace(/\s+/g, ''));
67
+ let suggestion = '.';
50
68
  if (suggestions.length > 0 && suggestions[0] !== colName) {
51
- return `. Did you mean "${suggestions[0]}"?`;
69
+ suggestion = `. Did you mean "${suggestions[0]}"?`;
52
70
  }
53
- // Show similar column names (first 3)
54
- if (availableColumns.length > 0 && availableColumns.length <= 10) {
55
- return `. Available columns: ${availableColumns.map(c => `"${c}"`).join(', ')}`;
71
+ else if (availableColumns.length > 0 && availableColumns.length <= 10) {
72
+ suggestion = `. Available columns: ${availableColumns.map(c => `"${c}"`).join(', ')}`;
56
73
  }
57
74
  else if (availableColumns.length > 0) {
58
- return `. Available columns (first 5): ${availableColumns.slice(0, 5).map(c => `"${c}"`).join(', ')}, ...`;
75
+ suggestion = `. Available columns (first 5): ${availableColumns.slice(0, 5).map(c => `"${c}"`).join(', ')}, ...`;
59
76
  }
60
- return '.';
61
- };
62
- const _createColumnError = (colName, map, context) => {
63
- const availableColumns = Array.from(map.keys());
64
- const suggestion = _suggestColumnName(colName, availableColumns);
65
77
  const contextMsg = context ? ` (${context})` : '';
66
78
  return new Error(`Column "${colName}" not found${contextMsg}${suggestion}`);
67
79
  };
@@ -81,12 +93,7 @@ const useTable = (rootLocator, configOptions = {}) => {
81
93
  yield headerLoc.first().waitFor({ state: 'visible', timeout: headerTimeout });
82
94
  }
83
95
  catch (e) { /* Ignore hydration */ }
84
- // 1. Fetch headers using strategy
85
- const strategy = config.headerStrategy || headers_1.HeaderStrategies.visible;
86
- // We need to construct context - but wait, HeaderStrategies definition I made imports StrategyContext
87
- // We need to import HeaderStrategies in useTable.ts first (it was imported as HeaderStrategies?).
88
- // Wait, useTable.ts imports `FillStrategies`.
89
- // I need to import `HeaderStrategies`.
96
+ const strategy = config.strategies.header || headers_1.HeaderStrategies.visible;
90
97
  const context = {
91
98
  root: rootLocator,
92
99
  config: config,
@@ -94,20 +101,13 @@ const useTable = (rootLocator, configOptions = {}) => {
94
101
  resolve: resolve
95
102
  };
96
103
  const rawHeaders = yield strategy(context);
97
- // 2. Map Headers (Async)
98
- // Note: We lose locator access here for transformer unless strategy provides it.
99
- // For now assuming transformer handles missing locator or we don't pass it if generic strategy.
100
104
  const entries = yield Promise.all(rawHeaders.map((t, i) => __awaiter(void 0, void 0, void 0, function* () {
101
105
  let text = t.trim() || `__col_${i}`;
102
106
  if (config.headerTransformer) {
103
107
  text = yield config.headerTransformer({
104
108
  text,
105
109
  index: i,
106
- locator: rootLocator.locator(config.headerSelector).nth(i) // Best effort lazy locator?
107
- // Danger: scanning strategy implies nth(i) might not map to visual i if scrolled.
108
- // But map index maps to logical index.
109
- // If scanning returns 100 headers, nth(99) might not exist in DOM.
110
- // Passing fallback locator or stub.
110
+ locator: rootLocator.locator(config.headerSelector).nth(i)
111
111
  });
112
112
  }
113
113
  return [text, i];
@@ -116,89 +116,13 @@ const useTable = (rootLocator, configOptions = {}) => {
116
116
  logDebug(`Mapped ${entries.length} columns: ${JSON.stringify(entries.map(e => e[0]))}`);
117
117
  return _headerMap;
118
118
  });
119
- // Placeholder for the final table object, to be captured by closure
119
+ // Placeholder for the final table object
120
120
  let finalTable = null;
121
+ const filterEngine = new filterEngine_1.FilterEngine(config, resolve);
122
+ // Helper factory
121
123
  const _makeSmart = (rowLocator, map, rowIndex) => {
122
- const smart = rowLocator;
123
- smart.getRequestIndex = () => rowIndex;
124
- smart.rowIndex = rowIndex;
125
- smart.getCell = (colName) => {
126
- const idx = map.get(colName);
127
- if (idx === undefined) {
128
- const availableColumns = Array.from(map.keys());
129
- const suggestion = _suggestColumnName(colName, availableColumns);
130
- throw new Error(`Column "${colName}" not found${suggestion}`);
131
- }
132
- if (config.cellResolver) {
133
- return config.cellResolver({ row: rowLocator, columnName: colName, columnIndex: idx, rowIndex });
134
- }
135
- return resolve(config.cellSelector, rowLocator).nth(idx);
136
- };
137
- smart.toJSON = () => __awaiter(void 0, void 0, void 0, function* () {
138
- const result = {};
139
- const cells = typeof config.cellSelector === 'string'
140
- ? rowLocator.locator(config.cellSelector)
141
- : resolve(config.cellSelector, rowLocator);
142
- const texts = yield cells.allInnerTexts();
143
- for (const [col, idx] of map.entries()) {
144
- result[col] = (texts[idx] || '').trim();
145
- }
146
- return result;
147
- });
148
- // @ts-ignore - Intentionally overriding Locator's fill method to accept object
149
- smart.fill = (data, fillOptions) => __awaiter(void 0, void 0, void 0, function* () {
150
- logDebug(`Filling row with data: ${JSON.stringify(data)}`);
151
- // Fill each column
152
- for (const [colName, value] of Object.entries(data)) {
153
- const colIdx = map.get(colName);
154
- if (colIdx === undefined) {
155
- throw _createColumnError(colName, map, 'in fill data');
156
- }
157
- // Execute Column Strategy BEFORE filling
158
- yield config.columnStrategy({
159
- config: config,
160
- root: rootLocator,
161
- page: rootLocator.page(),
162
- resolve,
163
- column: colName,
164
- index: colIdx,
165
- rowLocator: rowLocator,
166
- rowIndex: rowIndex
167
- });
168
- // Use configured strategy or default to internal DOM logic
169
- const strategy = config.fillStrategy || fill_1.FillStrategies.default;
170
- yield strategy({
171
- row: smart,
172
- columnName: colName,
173
- value,
174
- index: -1,
175
- page: rootLocator.page(),
176
- rootLocator,
177
- table: finalTable,
178
- fillOptions
179
- });
180
- }
181
- logDebug('Fill operation completed');
182
- });
183
- // Alias for explicit usage avoiding Locator.fill conflict
184
- smart.smartFill = smart.fill;
185
- return smart;
186
- };
187
- const _applyFilters = (baseRows, filters, map, exact) => {
188
- let filtered = baseRows;
189
- const page = rootLocator.page();
190
- for (const [colName, value] of Object.entries(filters)) {
191
- const colIndex = map.get(colName);
192
- if (colIndex === undefined) {
193
- throw _createColumnError(colName, map, 'in filter');
194
- }
195
- const filterVal = typeof value === 'number' ? String(value) : value;
196
- const cellTemplate = resolve(config.cellSelector, page);
197
- filtered = filtered.filter({
198
- has: cellTemplate.nth(colIndex).getByText(filterVal, { exact }),
199
- });
200
- }
201
- return filtered;
124
+ // Use the wrapped SmartRow logic
125
+ return (0, smartRow_1.createSmartRow)(rowLocator, map, rowIndex, config, rootLocator, resolve, finalTable);
202
126
  };
203
127
  const _findRowLocator = (filters_1, ...args_1) => __awaiter(void 0, [filters_1, ...args_1], void 0, function* (filters, options = {}) {
204
128
  var _a;
@@ -208,11 +132,12 @@ const useTable = (rootLocator, configOptions = {}) => {
208
132
  logDebug(`Looking for row: ${JSON.stringify(filters)} (MaxPages: ${effectiveMaxPages})`);
209
133
  while (true) {
210
134
  const allRows = resolve(config.rowSelector, rootLocator);
211
- const matchedRows = _applyFilters(allRows, filters, map, options.exact || false);
135
+ // Use FilterEngine
136
+ const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
212
137
  const count = yield matchedRows.count();
213
138
  logDebug(`Page ${currentPage}: Found ${count} matches.`);
214
139
  if (count > 1) {
215
- // Try to get sample row data to help user identify the issue
140
+ // Sample data logic (simplified for refactor, kept inline or moved to util if needed)
216
141
  const sampleData = [];
217
142
  try {
218
143
  const firstFewRows = yield matchedRows.all();
@@ -222,12 +147,8 @@ const useTable = (rootLocator, configOptions = {}) => {
222
147
  sampleData.push(JSON.stringify(rowData));
223
148
  }
224
149
  }
225
- catch (e) {
226
- // If we can't extract sample data, that's okay - continue without it
227
- }
228
- const sampleMsg = sampleData.length > 0
229
- ? `\nSample matching rows:\n${sampleData.map((d, i) => ` ${i + 1}. ${d}`).join('\n')}`
230
- : '';
150
+ catch (e) { }
151
+ const sampleMsg = sampleData.length > 0 ? `\nSample matching rows:\n${sampleData.map((d, i) => ` ${i + 1}. ${d}`).join('\n')}` : '';
231
152
  throw new Error(`Strict Mode Violation: Found ${count} rows matching ${JSON.stringify(filters)} on page ${currentPage}. ` +
232
153
  `Expected exactly one match. Try adding more filters to make your query unique.${sampleMsg}`);
233
154
  }
@@ -241,7 +162,8 @@ const useTable = (rootLocator, configOptions = {}) => {
241
162
  page: rootLocator.page(),
242
163
  resolve: resolve
243
164
  };
244
- const didLoadMore = yield config.pagination(context);
165
+ const paginationResult = yield config.strategies.pagination(context);
166
+ const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
245
167
  if (didLoadMore) {
246
168
  _hasPaginated = true;
247
169
  currentPage++;
@@ -258,33 +180,27 @@ const useTable = (rootLocator, configOptions = {}) => {
258
180
  }
259
181
  });
260
182
  const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
183
+ // ... same logic ...
261
184
  const { output = 'console', includeTypes = true } = options;
262
185
  let finalPrompt = content;
263
- if (includeTypes) {
186
+ if (includeTypes)
264
187
  finalPrompt += `\n\n👇 Useful TypeScript Definitions 👇\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
265
- }
266
188
  if (output === 'error') {
267
189
  console.log(`⚠️ Throwing error to display [${promptName}] cleanly...`);
268
190
  throw new Error(finalPrompt);
269
191
  }
270
192
  console.log(finalPrompt);
271
193
  });
272
- // Helper to extract clean HTML for prompts
273
194
  const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
274
195
  return loc.evaluate((el) => {
275
196
  const clone = el.cloneNode(true);
276
- // 1. Remove Heavy/Useless Elements
277
197
  const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
278
198
  clone.querySelectorAll(removeSelectors).forEach(n => n.remove());
279
- // 2. Clean Attributes
280
199
  const walker = document.createTreeWalker(clone, NodeFilter.SHOW_ELEMENT);
281
200
  let currentNode = walker.currentNode;
282
201
  while (currentNode) {
283
- currentNode.removeAttribute('style'); // Inline styles are noise
202
+ currentNode.removeAttribute('style');
284
203
  currentNode.removeAttribute('data-reactid');
285
- // 3. Condense Tailwind Classes (Heuristic)
286
- // If class string is very long (>50 chars), keep the first few tokens and truncate.
287
- // This preserves "MuiRow" but cuts "text-sm p-4 hover:bg-gray-50 ..."
288
204
  const cls = currentNode.getAttribute('class');
289
205
  if (cls && cls.length > 80) {
290
206
  const tokens = cls.split(' ');
@@ -297,7 +213,6 @@ const useTable = (rootLocator, configOptions = {}) => {
297
213
  return clone.outerHTML;
298
214
  });
299
215
  });
300
- // Helper to ensure initialization for async methods
301
216
  const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
302
217
  if (!_isInitialized) {
303
218
  yield _getMap();
@@ -306,9 +221,8 @@ const useTable = (rootLocator, configOptions = {}) => {
306
221
  });
307
222
  const result = {
308
223
  init: (options) => __awaiter(void 0, void 0, void 0, function* () {
309
- if (_isInitialized && _headerMap) {
224
+ if (_isInitialized && _headerMap)
310
225
  return result;
311
- }
312
226
  yield _getMap(options === null || options === void 0 ? void 0 : options.timeout);
313
227
  _isInitialized = true;
314
228
  return result;
@@ -316,10 +230,9 @@ const useTable = (rootLocator, configOptions = {}) => {
316
230
  scrollToColumn: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
317
231
  const map = yield _getMap();
318
232
  const idx = map.get(columnName);
319
- if (idx === undefined) {
233
+ if (idx === undefined)
320
234
  throw _createColumnError(columnName, map);
321
- }
322
- yield config.columnStrategy({
235
+ yield config.strategies.cellNavigation({
323
236
  config: config,
324
237
  root: rootLocator,
325
238
  page: rootLocator.page(),
@@ -329,15 +242,13 @@ const useTable = (rootLocator, configOptions = {}) => {
329
242
  });
330
243
  }),
331
244
  getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
332
- if (!_isInitialized || !_headerMap) {
333
- throw new Error('Table not initialized. Call await table.init() first.');
334
- }
245
+ if (!_isInitialized || !_headerMap)
246
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
335
247
  return Array.from(_headerMap.keys());
336
248
  }),
337
249
  getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
338
- if (!_isInitialized || !_headerMap) {
250
+ if (!_isInitialized || !_headerMap)
339
251
  throw new Error('Table not initialized. Call await table.init() first.');
340
- }
341
252
  const idx = _headerMap.get(columnName);
342
253
  if (idx === undefined)
343
254
  throw _createColumnError(columnName, _headerMap, 'header cell');
@@ -345,21 +256,21 @@ const useTable = (rootLocator, configOptions = {}) => {
345
256
  }),
346
257
  reset: () => __awaiter(void 0, void 0, void 0, function* () {
347
258
  logDebug("Resetting table...");
348
- const context = {
349
- root: rootLocator,
350
- config: config,
351
- page: rootLocator.page(),
352
- resolve: resolve
353
- };
259
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
354
260
  yield config.onReset(context);
355
261
  _hasPaginated = false;
356
262
  _headerMap = null;
357
263
  _isInitialized = false;
358
264
  logDebug("Table reset complete.");
359
265
  }),
266
+ revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
267
+ logDebug("Revalidating table structure...");
268
+ _headerMap = null; // Clear the map to force re-scanning
269
+ yield _getMap(); // Re-scan headers
270
+ logDebug("Table revalidated.");
271
+ }),
360
272
  getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
361
273
  var _a, _b;
362
- // Auto-init if needed (async methods can auto-init)
363
274
  yield _ensureInitialized();
364
275
  const colIdx = _headerMap.get(column);
365
276
  if (colIdx === undefined)
@@ -378,10 +289,8 @@ const useTable = (rootLocator, configOptions = {}) => {
378
289
  results.push(yield mapper(cell));
379
290
  }
380
291
  if (currentPage < effectiveMaxPages) {
381
- const context = {
382
- root: rootLocator, config, page: rootLocator.page(), resolve
383
- };
384
- if (yield config.pagination(context)) {
292
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
293
+ if (yield config.strategies.pagination(context)) {
385
294
  _hasPaginated = true;
386
295
  currentPage++;
387
296
  continue;
@@ -391,44 +300,34 @@ const useTable = (rootLocator, configOptions = {}) => {
391
300
  }
392
301
  return results;
393
302
  }),
394
- // @ts-ignore - implementing overload
395
- getByRow: (filtersOrIndex, options = { exact: false }) => {
396
- // Throw error if not initialized
397
- if (!_isInitialized || !_headerMap) {
398
- throw new Error('Table not initialized. Call await table.init() first.');
399
- }
400
- // Handle Index Overload (1-based index)
401
- if (typeof filtersOrIndex === 'number') {
402
- const rowIndex = filtersOrIndex - 1;
403
- const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
404
- return _makeSmart(rowLocator, _headerMap, rowIndex);
405
- }
406
- // Handle Filter Logic (Sync - lazy)
407
- const filters = filtersOrIndex;
303
+ getRow: (filters, options = { exact: false }) => {
304
+ if (!_isInitialized || !_headerMap)
305
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
408
306
  const allRows = resolve(config.rowSelector, rootLocator);
409
- const matchedRows = _applyFilters(allRows, filters, _headerMap, options.exact || false);
307
+ const matchedRows = filterEngine.applyFilters(allRows, filters, _headerMap, options.exact || false, rootLocator.page());
410
308
  const rowLocator = matchedRows.first();
411
- // We pass rowIndex=0 as a fallback.
412
- // NOTE: Filter-based sync lookup doesn't know the absolute index easily without scanning.
413
- // If accurate rowIndex is needed for filtered rows, use searchForRow or getAllCurrentRows.
414
- return _makeSmart(rowLocator, _headerMap, 0);
309
+ return _makeSmart(rowLocator, _headerMap, 0); // fallback index 0
310
+ },
311
+ getRowByIndex: (index, options = {}) => {
312
+ if (!_isInitialized || !_headerMap)
313
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
314
+ const rowIndex = index - 1; // Convert 1-based to 0-based
315
+ const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
316
+ return _makeSmart(rowLocator, _headerMap, rowIndex);
415
317
  },
416
- searchForRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
417
- // Auto-init if needed (async methods can auto-init)
318
+ findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
418
319
  yield _ensureInitialized();
419
- // Full pagination logic (existing _findRowLocator logic)
420
320
  let row = yield _findRowLocator(filters, options);
421
321
  if (!row) {
422
322
  row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
423
323
  }
424
324
  return _makeSmart(row, _headerMap, 0);
425
325
  }),
426
- getAllCurrentRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
427
- // Auto-init if needed (async methods can auto-init)
326
+ getRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
428
327
  yield _ensureInitialized();
429
328
  let rowLocators = resolve(config.rowSelector, rootLocator);
430
329
  if (options === null || options === void 0 ? void 0 : options.filter) {
431
- rowLocators = _applyFilters(rowLocators, options.filter, _headerMap, options.exact || false);
330
+ rowLocators = filterEngine.applyFilters(rowLocators, options.filter, _headerMap, options.exact || false, rootLocator.page());
432
331
  }
433
332
  const rows = yield rowLocators.all();
434
333
  const smartRows = rows.map((loc, i) => _makeSmart(loc, _headerMap, i));
@@ -437,103 +336,99 @@ const useTable = (rootLocator, configOptions = {}) => {
437
336
  }
438
337
  return smartRows;
439
338
  }),
440
- /**
441
- * @deprecated Use getAllCurrentRows instead. This method will be removed in a future major version.
442
- */
443
- getAllRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
444
- console.warn("⚠️ [SmartTable] getAllRows is deprecated. Use getAllCurrentRows instead.");
445
- return result.getAllCurrentRows(options);
446
- }),
447
- generateConfigPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
448
- const html = yield _getCleanHtml(rootLocator);
449
- const separator = "=".repeat(50);
450
- const content = `\n${separator}\n🤖 COPY INTO GEMINI/ChatGPT 🤖\n${separator}\nI am using 'playwright-smart-table'.\nTarget Table Locator: ${rootLocator.toString()}\nGenerate config for:\n\`\`\`html\n${html.substring(0, 10000)} ...\n\`\`\`\n${separator}\n`;
451
- yield _handlePrompt('Smart Table Config', content, options);
452
- }),
453
- generateStrategyPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
454
- const container = rootLocator.locator('xpath=..');
455
- const html = yield _getCleanHtml(container);
456
- const content = `\n==================================================\n🤖 COPY INTO GEMINI/ChatGPT TO WRITE A STRATEGY 🤖\n==================================================\nI need a custom Pagination Strategy for 'playwright-smart-table'.\nContainer HTML:\n\`\`\`html\n${html.substring(0, 10000)} ...\n\`\`\`\n`;
457
- yield _handlePrompt('Smart Table Strategy', content, options);
339
+ findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
340
+ var _a, _b, _c, _d;
341
+ yield _ensureInitialized();
342
+ const allRows = [];
343
+ const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
344
+ let pageCount = 0;
345
+ // Collect rows from current page
346
+ let rowLocators = resolve(config.rowSelector, rootLocator);
347
+ rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false, rootLocator.page());
348
+ let rows = yield rowLocators.all();
349
+ allRows.push(...rows.map((loc, i) => _makeSmart(loc, _headerMap, i)));
350
+ // Paginate and collect more rows
351
+ while (pageCount < effectiveMaxPages && config.strategies.pagination) {
352
+ const paginationResult = yield config.strategies.pagination({
353
+ root: rootLocator,
354
+ config,
355
+ resolve,
356
+ page: rootLocator.page()
357
+ });
358
+ const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
359
+ if (!didPaginate)
360
+ break;
361
+ pageCount++;
362
+ _hasPaginated = true;
363
+ // Collect rows from new page
364
+ rowLocators = resolve(config.rowSelector, rootLocator);
365
+ rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_d = options === null || options === void 0 ? void 0 : options.exact) !== null && _d !== void 0 ? _d : false, rootLocator.page());
366
+ rows = yield rowLocators.all();
367
+ allRows.push(...rows.map((loc, i) => _makeSmart(loc, _headerMap, i)));
368
+ }
369
+ if (options === null || options === void 0 ? void 0 : options.asJSON) {
370
+ return Promise.all(allRows.map(r => r.toJSON()));
371
+ }
372
+ return allRows;
458
373
  }),
374
+ isInitialized: () => {
375
+ return _isInitialized;
376
+ },
459
377
  sorting: {
460
378
  apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
461
- // Auto-init if needed (async methods can auto-init)
462
379
  yield _ensureInitialized();
463
- if (!config.sorting) {
464
- throw new Error('No sorting strategy has been configured. Please add a `sorting` strategy to your useTable config.');
465
- }
380
+ if (!config.strategies.sorting)
381
+ throw new Error('No sorting strategy has been configured.');
466
382
  logDebug(`Applying sort for column "${columnName}" (${direction})`);
467
- const context = {
468
- root: rootLocator,
469
- config: config,
470
- page: rootLocator.page(),
471
- resolve: resolve
472
- };
473
- yield config.sorting.doSort({ columnName, direction, context });
383
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
384
+ yield config.strategies.sorting.doSort({ columnName, direction, context });
474
385
  }),
475
386
  getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
476
- // Auto-init if needed (async methods can auto-init)
477
387
  yield _ensureInitialized();
478
- if (!config.sorting) {
479
- throw new Error('No sorting strategy has been configured. Please add a `sorting` strategy to your useTable config.');
480
- }
481
- logDebug(`Getting sort state for column "${columnName}"`);
482
- const context = {
483
- root: rootLocator,
484
- config: config,
485
- page: rootLocator.page(),
486
- resolve: resolve
487
- };
488
- return config.sorting.getSortState({ columnName, context });
388
+ if (!config.strategies.sorting)
389
+ throw new Error('No sorting strategy has been configured.');
390
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
391
+ return config.strategies.sorting.getSortState({ columnName, context });
489
392
  })
490
393
  },
491
394
  iterateThroughTable: (callback, options) => __awaiter(void 0, void 0, void 0, function* () {
492
395
  var _a, _b, _c, _d;
493
- // Auto-init if needed (async methods can auto-init)
494
396
  yield _ensureInitialized();
495
- // Determine pagination strategy
496
- const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.pagination;
497
- // Check if pagination was explicitly provided in options or config
397
+ const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.strategies.pagination;
498
398
  const hasPaginationInOptions = (options === null || options === void 0 ? void 0 : options.pagination) !== undefined;
499
- if (!hasPaginationInOptions && !hasPaginationInConfig) {
500
- throw new Error('No pagination strategy provided. Either set pagination in options or in table config.');
501
- }
502
- // Reset to initial page before starting
399
+ if (!hasPaginationInOptions && !hasPaginationInConfig)
400
+ throw new Error('No pagination strategy provided.');
503
401
  yield result.reset();
504
402
  yield result.init();
505
- // Create restricted table instance (excludes problematic methods)
506
403
  const restrictedTable = {
507
404
  init: result.init,
508
405
  getHeaders: result.getHeaders,
509
406
  getHeaderCell: result.getHeaderCell,
510
- getByRow: result.getByRow,
511
- getAllCurrentRows: result.getAllCurrentRows,
407
+ getRow: result.getRow,
408
+ getRowByIndex: result.getRowByIndex,
409
+ findRow: result.findRow,
410
+ getRows: result.getRows,
411
+ findRows: result.findRows,
512
412
  getColumnValues: result.getColumnValues,
513
- generateConfigPrompt: result.generateConfigPrompt,
514
- generateStrategyPrompt: result.generateStrategyPrompt,
413
+ isInitialized: result.isInitialized,
515
414
  sorting: result.sorting,
516
- scrollToColumn: result.scrollToColumn, // Add to restricted result as well
415
+ scrollToColumn: result.scrollToColumn,
416
+ revalidate: result.revalidate,
517
417
  };
518
- // Default functions
519
418
  const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
520
419
  const getIsLast = (_c = options === null || options === void 0 ? void 0 : options.getIsLast) !== null && _c !== void 0 ? _c : (() => false);
521
- // Create allData array (persists across iterations)
522
420
  const allData = [];
523
421
  const effectiveMaxIterations = (_d = options === null || options === void 0 ? void 0 : options.maxIterations) !== null && _d !== void 0 ? _d : config.maxPages;
524
422
  let index = 0;
525
- let paginationResult = true; // Will be set after first pagination attempt
526
- let seenKeys = null; // Track seen keys across iterations for deduplication
423
+ let paginationResult = true;
424
+ let seenKeys = null;
527
425
  logDebug(`Starting iterateThroughTable (maxIterations: ${effectiveMaxIterations})`);
528
426
  while (index < effectiveMaxIterations) {
529
- // Get current rows
530
427
  const rowLocators = yield resolve(config.rowSelector, rootLocator).all();
531
428
  let rows = rowLocators.map((loc, i) => _makeSmart(loc, _headerMap, i));
532
- // Deduplicate if dedupeStrategy provided (across all iterations)
533
429
  if ((options === null || options === void 0 ? void 0 : options.dedupeStrategy) && rows.length > 0) {
534
- if (!seenKeys) {
430
+ if (!seenKeys)
535
431
  seenKeys = new Set();
536
- }
537
432
  const deduplicated = [];
538
433
  for (const row of rows) {
539
434
  const key = yield options.dedupeStrategy(row);
@@ -545,43 +440,20 @@ const useTable = (rootLocator, configOptions = {}) => {
545
440
  rows = deduplicated;
546
441
  logDebug(`Deduplicated ${rowLocators.length} rows to ${rows.length} unique rows (total seen: ${seenKeys.size})`);
547
442
  }
548
- // Determine flags (isLast will be checked after pagination attempt)
549
443
  const isFirst = getIsFirst({ index });
550
444
  let isLast = getIsLast({ index, paginationResult });
551
- // Check if this is the last iteration due to maxIterations (before attempting pagination)
552
445
  const isLastDueToMax = index === effectiveMaxIterations - 1;
553
- // Call onFirst hook if applicable
554
- if (isFirst && (options === null || options === void 0 ? void 0 : options.onFirst)) {
446
+ if (isFirst && (options === null || options === void 0 ? void 0 : options.onFirst))
555
447
  yield options.onFirst({ index, rows, allData });
556
- }
557
- // Call main callback
558
- const returnValue = yield callback({
559
- index,
560
- isFirst,
561
- isLast,
562
- rows,
563
- allData,
564
- table: restrictedTable,
565
- });
566
- // Append return value to allData
448
+ const returnValue = yield callback({ index, isFirst, isLast, rows, allData, table: restrictedTable });
567
449
  allData.push(returnValue);
568
- // Attempt pagination (before checking if we should continue)
569
- const context = {
570
- root: rootLocator,
571
- config: config,
572
- page: rootLocator.page(),
573
- resolve: resolve
574
- };
450
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
575
451
  paginationResult = yield paginationStrategy(context);
576
- // Now check isLast with updated paginationResult
577
452
  isLast = getIsLast({ index, paginationResult }) || isLastDueToMax;
578
- // Call onLast hook if applicable (after we know pagination failed or we're at max iterations)
579
- if (isLast && (options === null || options === void 0 ? void 0 : options.onLast)) {
453
+ if (isLast && (options === null || options === void 0 ? void 0 : options.onLast))
580
454
  yield options.onLast({ index, rows, allData });
581
- }
582
- // Check if we should continue
583
455
  if (isLast || !paginationResult) {
584
- logDebug(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult}, isLastDueToMax: ${isLastDueToMax})`);
456
+ logDebug(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
585
457
  break;
586
458
  }
587
459
  index++;
@@ -596,5 +468,4 @@ const useTable = (rootLocator, configOptions = {}) => {
596
468
  };
597
469
  exports.useTable = useTable;
598
470
  exports.PaginationStrategies = pagination_1.PaginationStrategies;
599
- exports.TableStrategies = pagination_1.TableStrategies;
600
471
  exports.SortingStrategies = sorting_1.SortingStrategies;