@rickcedwhat/playwright-smart-table 3.2.0 → 4.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.ColumnStrategies = exports.CellNavigationStrategies = exports.HeaderStrategies = exports.FillStrategies = exports.SortingStrategies = exports.DeprecatedTableStrategies = 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, "CellNavigationStrategies", { enumerable: true, get: function () { return columns_1.CellNavigationStrategies; } });
21
22
  Object.defineProperty(exports, "ColumnStrategies", { enumerable: true, get: function () { return columns_1.ColumnStrategies; } });
23
+ const smartRow_1 = require("./smartRow");
24
+ const filterEngine_1 = require("./filterEngine");
25
+ const resolution_1 = require("./strategies/resolution");
26
+ Object.defineProperty(exports, "ResolutionStrategies", { enumerable: true, get: function () { return resolution_1.ResolutionStrategies; } });
27
+ const strategies_1 = require("./strategies");
28
+ Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
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,7 @@ const useTable = (rootLocator, configOptions = {}) => {
241
162
  page: rootLocator.page(),
242
163
  resolve: resolve
243
164
  };
244
- const didLoadMore = yield config.pagination(context);
165
+ const didLoadMore = yield config.strategies.pagination(context);
245
166
  if (didLoadMore) {
246
167
  _hasPaginated = true;
247
168
  currentPage++;
@@ -258,33 +179,27 @@ const useTable = (rootLocator, configOptions = {}) => {
258
179
  }
259
180
  });
260
181
  const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
182
+ // ... same logic ...
261
183
  const { output = 'console', includeTypes = true } = options;
262
184
  let finalPrompt = content;
263
- if (includeTypes) {
185
+ if (includeTypes)
264
186
  finalPrompt += `\n\n👇 Useful TypeScript Definitions 👇\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
265
- }
266
187
  if (output === 'error') {
267
188
  console.log(`⚠️ Throwing error to display [${promptName}] cleanly...`);
268
189
  throw new Error(finalPrompt);
269
190
  }
270
191
  console.log(finalPrompt);
271
192
  });
272
- // Helper to extract clean HTML for prompts
273
193
  const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
274
194
  return loc.evaluate((el) => {
275
195
  const clone = el.cloneNode(true);
276
- // 1. Remove Heavy/Useless Elements
277
196
  const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
278
197
  clone.querySelectorAll(removeSelectors).forEach(n => n.remove());
279
- // 2. Clean Attributes
280
198
  const walker = document.createTreeWalker(clone, NodeFilter.SHOW_ELEMENT);
281
199
  let currentNode = walker.currentNode;
282
200
  while (currentNode) {
283
- currentNode.removeAttribute('style'); // Inline styles are noise
201
+ currentNode.removeAttribute('style');
284
202
  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
203
  const cls = currentNode.getAttribute('class');
289
204
  if (cls && cls.length > 80) {
290
205
  const tokens = cls.split(' ');
@@ -297,7 +212,6 @@ const useTable = (rootLocator, configOptions = {}) => {
297
212
  return clone.outerHTML;
298
213
  });
299
214
  });
300
- // Helper to ensure initialization for async methods
301
215
  const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
302
216
  if (!_isInitialized) {
303
217
  yield _getMap();
@@ -306,9 +220,8 @@ const useTable = (rootLocator, configOptions = {}) => {
306
220
  });
307
221
  const result = {
308
222
  init: (options) => __awaiter(void 0, void 0, void 0, function* () {
309
- if (_isInitialized && _headerMap) {
223
+ if (_isInitialized && _headerMap)
310
224
  return result;
311
- }
312
225
  yield _getMap(options === null || options === void 0 ? void 0 : options.timeout);
313
226
  _isInitialized = true;
314
227
  return result;
@@ -316,10 +229,9 @@ const useTable = (rootLocator, configOptions = {}) => {
316
229
  scrollToColumn: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
317
230
  const map = yield _getMap();
318
231
  const idx = map.get(columnName);
319
- if (idx === undefined) {
232
+ if (idx === undefined)
320
233
  throw _createColumnError(columnName, map);
321
- }
322
- yield config.columnStrategy({
234
+ yield config.strategies.cellNavigation({
323
235
  config: config,
324
236
  root: rootLocator,
325
237
  page: rootLocator.page(),
@@ -329,15 +241,13 @@ const useTable = (rootLocator, configOptions = {}) => {
329
241
  });
330
242
  }),
331
243
  getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
332
- if (!_isInitialized || !_headerMap) {
244
+ if (!_isInitialized || !_headerMap)
333
245
  throw new Error('Table not initialized. Call await table.init() first.');
334
- }
335
246
  return Array.from(_headerMap.keys());
336
247
  }),
337
248
  getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
338
- if (!_isInitialized || !_headerMap) {
249
+ if (!_isInitialized || !_headerMap)
339
250
  throw new Error('Table not initialized. Call await table.init() first.');
340
- }
341
251
  const idx = _headerMap.get(columnName);
342
252
  if (idx === undefined)
343
253
  throw _createColumnError(columnName, _headerMap, 'header cell');
@@ -345,21 +255,21 @@ const useTable = (rootLocator, configOptions = {}) => {
345
255
  }),
346
256
  reset: () => __awaiter(void 0, void 0, void 0, function* () {
347
257
  logDebug("Resetting table...");
348
- const context = {
349
- root: rootLocator,
350
- config: config,
351
- page: rootLocator.page(),
352
- resolve: resolve
353
- };
258
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
354
259
  yield config.onReset(context);
355
260
  _hasPaginated = false;
356
261
  _headerMap = null;
357
262
  _isInitialized = false;
358
263
  logDebug("Table reset complete.");
359
264
  }),
265
+ revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
266
+ logDebug("Revalidating table structure...");
267
+ _headerMap = null; // Clear the map to force re-scanning
268
+ yield _getMap(); // Re-scan headers
269
+ logDebug("Table revalidated.");
270
+ }),
360
271
  getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
361
272
  var _a, _b;
362
- // Auto-init if needed (async methods can auto-init)
363
273
  yield _ensureInitialized();
364
274
  const colIdx = _headerMap.get(column);
365
275
  if (colIdx === undefined)
@@ -378,10 +288,8 @@ const useTable = (rootLocator, configOptions = {}) => {
378
288
  results.push(yield mapper(cell));
379
289
  }
380
290
  if (currentPage < effectiveMaxPages) {
381
- const context = {
382
- root: rootLocator, config, page: rootLocator.page(), resolve
383
- };
384
- if (yield config.pagination(context)) {
291
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
292
+ if (yield config.strategies.pagination(context)) {
385
293
  _hasPaginated = true;
386
294
  currentPage++;
387
295
  continue;
@@ -391,32 +299,23 @@ const useTable = (rootLocator, configOptions = {}) => {
391
299
  }
392
300
  return results;
393
301
  }),
394
- // @ts-ignore - implementing overload
395
- getByRow: (filtersOrIndex, options = { exact: false }) => {
396
- // Throw error if not initialized
397
- if (!_isInitialized || !_headerMap) {
302
+ getByRow: (filters, options = { exact: false }) => {
303
+ if (!_isInitialized || !_headerMap)
398
304
  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;
408
305
  const allRows = resolve(config.rowSelector, rootLocator);
409
- const matchedRows = _applyFilters(allRows, filters, _headerMap, options.exact || false);
306
+ const matchedRows = filterEngine.applyFilters(allRows, filters, _headerMap, options.exact || false, rootLocator.page());
410
307
  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);
308
+ return _makeSmart(rowLocator, _headerMap, 0); // fallback index 0
309
+ },
310
+ getByRowIndex: (index, options = {}) => {
311
+ if (!_isInitialized || !_headerMap)
312
+ throw new Error('Table not initialized. Call await table.init() first.');
313
+ const rowIndex = index - 1; // Convert 1-based to 0-based
314
+ const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
315
+ return _makeSmart(rowLocator, _headerMap, rowIndex);
415
316
  },
416
317
  searchForRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
417
- // Auto-init if needed (async methods can auto-init)
418
318
  yield _ensureInitialized();
419
- // Full pagination logic (existing _findRowLocator logic)
420
319
  let row = yield _findRowLocator(filters, options);
421
320
  if (!row) {
422
321
  row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
@@ -424,11 +323,10 @@ const useTable = (rootLocator, configOptions = {}) => {
424
323
  return _makeSmart(row, _headerMap, 0);
425
324
  }),
426
325
  getAllCurrentRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
427
- // Auto-init if needed (async methods can auto-init)
428
326
  yield _ensureInitialized();
429
327
  let rowLocators = resolve(config.rowSelector, rootLocator);
430
328
  if (options === null || options === void 0 ? void 0 : options.filter) {
431
- rowLocators = _applyFilters(rowLocators, options.filter, _headerMap, options.exact || false);
329
+ rowLocators = filterEngine.applyFilters(rowLocators, options.filter, _headerMap, options.exact || false, rootLocator.page());
432
330
  }
433
331
  const rows = yield rowLocators.all();
434
332
  const smartRows = rows.map((loc, i) => _makeSmart(loc, _headerMap, i));
@@ -437,9 +335,6 @@ const useTable = (rootLocator, configOptions = {}) => {
437
335
  }
438
336
  return smartRows;
439
337
  }),
440
- /**
441
- * @deprecated Use getAllCurrentRows instead. This method will be removed in a future major version.
442
- */
443
338
  getAllRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
444
339
  console.warn("⚠️ [SmartTable] getAllRows is deprecated. Use getAllCurrentRows instead.");
445
340
  return result.getAllCurrentRows(options);
@@ -458,82 +353,58 @@ const useTable = (rootLocator, configOptions = {}) => {
458
353
  }),
459
354
  sorting: {
460
355
  apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
461
- // Auto-init if needed (async methods can auto-init)
462
356
  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
- }
357
+ if (!config.strategies.sorting)
358
+ throw new Error('No sorting strategy has been configured.');
466
359
  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 });
360
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
361
+ yield config.strategies.sorting.doSort({ columnName, direction, context });
474
362
  }),
475
363
  getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
476
- // Auto-init if needed (async methods can auto-init)
477
364
  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 });
365
+ if (!config.strategies.sorting)
366
+ throw new Error('No sorting strategy has been configured.');
367
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
368
+ return config.strategies.sorting.getSortState({ columnName, context });
489
369
  })
490
370
  },
491
371
  iterateThroughTable: (callback, options) => __awaiter(void 0, void 0, void 0, function* () {
492
372
  var _a, _b, _c, _d;
493
- // Auto-init if needed (async methods can auto-init)
494
373
  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
374
+ const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.strategies.pagination;
498
375
  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
376
+ if (!hasPaginationInOptions && !hasPaginationInConfig)
377
+ throw new Error('No pagination strategy provided.');
503
378
  yield result.reset();
504
379
  yield result.init();
505
- // Create restricted table instance (excludes problematic methods)
506
380
  const restrictedTable = {
507
381
  init: result.init,
508
382
  getHeaders: result.getHeaders,
509
383
  getHeaderCell: result.getHeaderCell,
510
384
  getByRow: result.getByRow,
385
+ getByRowIndex: result.getByRowIndex,
511
386
  getAllCurrentRows: result.getAllCurrentRows,
512
387
  getColumnValues: result.getColumnValues,
513
388
  generateConfigPrompt: result.generateConfigPrompt,
514
389
  generateStrategyPrompt: result.generateStrategyPrompt,
515
390
  sorting: result.sorting,
516
- scrollToColumn: result.scrollToColumn, // Add to restricted result as well
391
+ scrollToColumn: result.scrollToColumn,
392
+ revalidate: result.revalidate,
517
393
  };
518
- // Default functions
519
394
  const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
520
395
  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
396
  const allData = [];
523
397
  const effectiveMaxIterations = (_d = options === null || options === void 0 ? void 0 : options.maxIterations) !== null && _d !== void 0 ? _d : config.maxPages;
524
398
  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
399
+ let paginationResult = true;
400
+ let seenKeys = null;
527
401
  logDebug(`Starting iterateThroughTable (maxIterations: ${effectiveMaxIterations})`);
528
402
  while (index < effectiveMaxIterations) {
529
- // Get current rows
530
403
  const rowLocators = yield resolve(config.rowSelector, rootLocator).all();
531
404
  let rows = rowLocators.map((loc, i) => _makeSmart(loc, _headerMap, i));
532
- // Deduplicate if dedupeStrategy provided (across all iterations)
533
405
  if ((options === null || options === void 0 ? void 0 : options.dedupeStrategy) && rows.length > 0) {
534
- if (!seenKeys) {
406
+ if (!seenKeys)
535
407
  seenKeys = new Set();
536
- }
537
408
  const deduplicated = [];
538
409
  for (const row of rows) {
539
410
  const key = yield options.dedupeStrategy(row);
@@ -545,43 +416,20 @@ const useTable = (rootLocator, configOptions = {}) => {
545
416
  rows = deduplicated;
546
417
  logDebug(`Deduplicated ${rowLocators.length} rows to ${rows.length} unique rows (total seen: ${seenKeys.size})`);
547
418
  }
548
- // Determine flags (isLast will be checked after pagination attempt)
549
419
  const isFirst = getIsFirst({ index });
550
420
  let isLast = getIsLast({ index, paginationResult });
551
- // Check if this is the last iteration due to maxIterations (before attempting pagination)
552
421
  const isLastDueToMax = index === effectiveMaxIterations - 1;
553
- // Call onFirst hook if applicable
554
- if (isFirst && (options === null || options === void 0 ? void 0 : options.onFirst)) {
422
+ if (isFirst && (options === null || options === void 0 ? void 0 : options.onFirst))
555
423
  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
424
+ const returnValue = yield callback({ index, isFirst, isLast, rows, allData, table: restrictedTable });
567
425
  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
- };
426
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
575
427
  paginationResult = yield paginationStrategy(context);
576
- // Now check isLast with updated paginationResult
577
428
  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)) {
429
+ if (isLast && (options === null || options === void 0 ? void 0 : options.onLast))
580
430
  yield options.onLast({ index, rows, allData });
581
- }
582
- // Check if we should continue
583
431
  if (isLast || !paginationResult) {
584
- logDebug(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult}, isLastDueToMax: ${isLastDueToMax})`);
432
+ logDebug(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
585
433
  break;
586
434
  }
587
435
  index++;
@@ -596,5 +444,6 @@ const useTable = (rootLocator, configOptions = {}) => {
596
444
  };
597
445
  exports.useTable = useTable;
598
446
  exports.PaginationStrategies = pagination_1.PaginationStrategies;
599
- exports.TableStrategies = pagination_1.TableStrategies;
447
+ /** @deprecated Use Strategies.Pagination instead */
448
+ exports.DeprecatedTableStrategies = pagination_1.DeprecatedPaginationStrategies;
600
449
  exports.SortingStrategies = sorting_1.SortingStrategies;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "A smart table utility for Playwright with built-in pagination strategies that are fully extensible.",
5
5
  "repository": {
6
6
  "type": "git",