@rickcedwhat/playwright-smart-table 2.1.1 → 2.1.2

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
@@ -8,12 +8,11 @@ This library abstracts away the complexity of testing dynamic web tables. It han
8
8
 
9
9
  npm install @rickcedwhat/playwright-smart-table
10
10
 
11
-
12
11
  Requires @playwright/test as a peer dependency.
13
12
 
14
13
  ⚔ Quick Start
15
14
 
16
- 1. The Standard HTML Table
15
+ The Standard HTML Table
17
16
 
18
17
  For standard tables (<table>, <tr>, <td>), no configuration is needed (defaults work for most standard HTML tables).
19
18
 
@@ -31,7 +30,7 @@ await expect(row.getCell('Position')).toHaveText('Accountant');
31
30
  ```
32
31
  <!-- /embed: quick-start -->
33
32
 
34
- 2. Complex Grids (Material UI / AG-Grid / Divs)
33
+ Complex Grids (Material UI / AG-Grid / Divs)
35
34
 
36
35
  For modern React grids, simply override the selectors and define a pagination strategy.
37
36
 
@@ -78,6 +77,50 @@ console.log(data);
78
77
  ```
79
78
  <!-- /embed: smart-row -->
80
79
 
80
+ šŸš€ Advanced Usage
81
+
82
+ šŸ”Ž Debug Mode
83
+
84
+ Having trouble finding rows? Enable debug mode to see exactly what the library sees (headers mapped, rows scanned, pagination triggers).
85
+
86
+ <!-- embed: advanced-debug -->
87
+ ```typescript
88
+ const table = useTable(page.locator('#example'), {
89
+ headerSelector: 'thead th',
90
+ debug: true
91
+ });
92
+ ```
93
+ <!-- /embed: advanced-debug -->
94
+
95
+ šŸ”„ Resetting State
96
+
97
+ If your tests navigate deep into a table (e.g., Page 5), subsequent searches might fail. Use .reset() to return to the start.
98
+
99
+ <!-- embed: advanced-reset -->
100
+ ```typescript
101
+ // Navigate deep into the table (simulated by finding a row on page 2)
102
+ // For the test to pass, we need a valid row. 'Angelica Ramos' is usually on page 1 or 2 depending on sorting.
103
+ try {
104
+ await table.getByRow({ Name: 'Angelica Ramos' });
105
+ } catch (e) {}
106
+
107
+ // Reset internal state (and potentially UI) to Page 1
108
+ await table.reset();
109
+ ```
110
+ <!-- /embed: advanced-reset -->
111
+
112
+ šŸ“Š Column Scanning
113
+
114
+ Need to verify a specific column is sorted or contains specific data? Use getColumnValues for a high-performance scan.
115
+
116
+ <!-- embed: advanced-column-scan -->
117
+ ```typescript
118
+ // Quickly grab all text values from the "Office" column
119
+ const offices = await table.getColumnValues('Office');
120
+ expect(offices).toContain('Tokyo');
121
+ ```
122
+ <!-- /embed: advanced-column-scan -->
123
+
81
124
  šŸ“– API Reference
82
125
 
83
126
  getByRow(filters, options?)
@@ -134,16 +177,14 @@ Built-in Strategies
134
177
 
135
178
  clickNext(selector) Best for standard tables (Datatables, lists). Clicks a button and waits for data to change.
136
179
 
137
- pagination: TableStrategies.clickNext((root) =>
138
- root.page().getByRole('button', { name: 'Next' })
180
+ pagination: TableStrategies.clickNext((root) =>
181
+ root.page().getByRole('button', { name: 'Next' })
139
182
  )
140
183
 
141
-
142
184
  infiniteScroll() Best for Virtualized Grids (AG-Grid, HTMX). Aggressively scrolls to trigger data loading.
143
185
 
144
186
  pagination: TableStrategies.infiniteScroll()
145
187
 
146
-
147
188
  clickLoadMore(selector) Best for "Load More" buttons. Clicks and waits for row count to increase.
148
189
 
149
190
  šŸ› ļø Developer Tools
@@ -157,9 +198,8 @@ Prints a prompt you can paste into ChatGPT/Gemini to generate the TableConfig fo
157
198
  // Options: 'console' (default), 'error' (Throw error to see prompt in trace/cloud)
158
199
  await table.generateConfigPrompt({ output: 'console' });
159
200
 
160
-
161
201
  generateStrategyPrompt(options?)
162
202
 
163
203
  Prints a prompt to help you write a custom Pagination Strategy.
164
204
 
165
- await table.generateStrategyPrompt({ output: 'console' });
205
+ await table.generateStrategyPrompt({ output: 'console' });
@@ -3,4 +3,4 @@
3
3
  * This file is generated by scripts/embed-types.js
4
4
  * It contains the raw text of types.ts to provide context for LLM prompts.
5
5
  */
6
- export declare const TYPE_CONTEXT = "\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\nexport type SmartRow = Locator & {\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n};\n\nexport interface TableContext {\n root: Locator;\n config: Required<TableConfig>;\n page: Page;\n resolve: (selector: Selector, parent: Locator | Page) => Locator;\n}\n\nexport type PaginationStrategy = (context: TableContext) => Promise<boolean>;\n\nexport interface PromptOptions {\n /**\n * Output Strategy:\n * - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).\n * - 'console': Standard console logs (Default).\n */\n output?: 'console' | 'error';\n includeTypes?: boolean;\n}\n\nexport interface TableConfig {\n rowSelector?: Selector;\n headerSelector?: Selector;\n cellSelector?: Selector;\n pagination?: PaginationStrategy;\n maxPages?: number;\n /**\n * Hook to rename columns dynamically.\n * * @param args.text - The default innerText of the header.\n * @param args.index - The column index.\n * @param args.locator - The specific header cell locator.\n */\n headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n autoScroll?: boolean;\n}\n\nexport interface TableResult {\n getHeaders: () => Promise<string[]>;\n getHeaderCell: (columnName: string) => Promise<Locator>;\n\n getByRow: <T extends { asJSON?: boolean }>(\n filters: Record<string, string | RegExp | number>, \n options?: { exact?: boolean, maxPages?: number } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;\n\n getAllRows: <T extends { asJSON?: boolean }>(\n options?: { filter?: Record<string, any>, exact?: boolean } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n generateConfigPrompt: (options?: PromptOptions) => Promise<void>;\n generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;\n}\n";
6
+ export declare const TYPE_CONTEXT = "\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\nexport type SmartRow = Locator & {\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n};\n\nexport interface TableContext {\n root: Locator;\n config: Required<TableConfig>;\n page: Page;\n resolve: (selector: Selector, parent: Locator | Page) => Locator;\n}\n\nexport type PaginationStrategy = (context: TableContext) => Promise<boolean>;\n\nexport interface PromptOptions {\n /**\n * Output Strategy:\n * - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).\n * - 'console': Standard console logs (Default).\n */\n output?: 'console' | 'error';\n includeTypes?: boolean;\n}\n\nexport interface TableConfig {\n rowSelector?: Selector;\n headerSelector?: Selector;\n cellSelector?: Selector;\n pagination?: PaginationStrategy;\n maxPages?: number;\n /**\n * Hook to rename columns dynamically.\n * * @param args.text - The default innerText of the header.\n * @param args.index - The column index.\n * @param args.locator - The specific header cell locator.\n */\n headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n autoScroll?: boolean;\n /**\n * Enable debug mode to log internal state to console.\n */\n debug?: boolean;\n /**\n * Strategy to reset the table to the first page.\n * Called when table.reset() is invoked.\n */\n onReset?: (context: TableContext) => Promise<void>;\n}\n\nexport interface TableResult {\n getHeaders: () => Promise<string[]>;\n getHeaderCell: (columnName: string) => Promise<Locator>;\n\n getByRow: <T extends { asJSON?: boolean }>(\n filters: Record<string, string | RegExp | number>, \n options?: { exact?: boolean, maxPages?: number } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;\n\n getAllRows: <T extends { asJSON?: boolean }>(\n options?: { filter?: Record<string, any>, exact?: boolean } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n generateConfigPrompt: (options?: PromptOptions) => Promise<void>;\n generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;\n\n /**\n * Resets the table state (clears cache, flags) and invokes the onReset strategy.\n */\n reset: () => Promise<void>;\n\n /**\n * Scans a specific column across all pages and returns the values.\n */\n getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;\n}\n";
@@ -47,6 +47,15 @@ export interface TableConfig {
47
47
  */
48
48
  headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;
49
49
  autoScroll?: boolean;
50
+ /**
51
+ * Enable debug mode to log internal state to console.
52
+ */
53
+ debug?: boolean;
54
+ /**
55
+ * Strategy to reset the table to the first page.
56
+ * Called when table.reset() is invoked.
57
+ */
58
+ onReset?: (context: TableContext) => Promise<void>;
50
59
  }
51
60
 
52
61
  export interface TableResult {
@@ -64,5 +73,15 @@ export interface TableResult {
64
73
 
65
74
  generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
66
75
  generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
76
+
77
+ /**
78
+ * Resets the table state (clears cache, flags) and invokes the onReset strategy.
79
+ */
80
+ reset: () => Promise<void>;
81
+
82
+ /**
83
+ * Scans a specific column across all pages and returns the values.
84
+ */
85
+ getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;
67
86
  }
68
87
  `;
package/dist/types.d.ts CHANGED
@@ -38,6 +38,15 @@ export interface TableConfig {
38
38
  locator: Locator;
39
39
  }) => string | Promise<string>;
40
40
  autoScroll?: boolean;
41
+ /**
42
+ * Enable debug mode to log internal state to console.
43
+ */
44
+ debug?: boolean;
45
+ /**
46
+ * Strategy to reset the table to the first page.
47
+ * Called when table.reset() is invoked.
48
+ */
49
+ onReset?: (context: TableContext) => Promise<void>;
41
50
  }
42
51
  export interface TableResult {
43
52
  getHeaders: () => Promise<string[]>;
@@ -56,4 +65,15 @@ export interface TableResult {
56
65
  } & T) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
57
66
  generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
58
67
  generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
68
+ /**
69
+ * Resets the table state (clears cache, flags) and invokes the onReset strategy.
70
+ */
71
+ reset: () => Promise<void>;
72
+ /**
73
+ * Scans a specific column across all pages and returns the values.
74
+ */
75
+ getColumnValues: <V = string>(column: string, options?: {
76
+ mapper?: (cell: Locator) => Promise<V> | V;
77
+ maxPages?: number;
78
+ }) => Promise<V[]>;
59
79
  }
package/dist/useTable.js CHANGED
@@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.useTable = void 0;
13
13
  const typeContext_1 = require("./typeContext");
14
14
  const useTable = (rootLocator, configOptions = {}) => {
15
- 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 }, configOptions);
15
+ 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, onReset: () => __awaiter(void 0, void 0, void 0, function* () { console.warn("āš ļø .reset() called but no 'onReset' strategy defined in config."); }) }, configOptions);
16
16
  const resolve = (item, parent) => {
17
17
  if (typeof item === 'string')
18
18
  return parent.locator(item);
@@ -20,10 +20,17 @@ const useTable = (rootLocator, configOptions = {}) => {
20
20
  return item(parent);
21
21
  return item;
22
22
  };
23
+ // Internal State
23
24
  let _headerMap = null;
25
+ let _hasPaginated = false;
26
+ const logDebug = (msg) => {
27
+ if (config.debug)
28
+ console.log(`šŸ”Ž [SmartTable Debug] ${msg}`);
29
+ };
24
30
  const _getMap = () => __awaiter(void 0, void 0, void 0, function* () {
25
31
  if (_headerMap)
26
32
  return _headerMap;
33
+ logDebug('Mapping headers...');
27
34
  if (config.autoScroll) {
28
35
  try {
29
36
  yield rootLocator.scrollIntoViewIfNeeded({ timeout: 1000 });
@@ -37,7 +44,7 @@ const useTable = (rootLocator, configOptions = {}) => {
37
44
  catch (e) { /* Ignore hydration */ }
38
45
  // 1. Fetch data efficiently
39
46
  const texts = yield headerLoc.allInnerTexts();
40
- const locators = yield headerLoc.all(); // Need specific locators for the transformer
47
+ const locators = yield headerLoc.all();
41
48
  // 2. Map Headers (Async)
42
49
  const entries = yield Promise.all(texts.map((t, i) => __awaiter(void 0, void 0, void 0, function* () {
43
50
  let text = t.trim() || `__col_${i}`;
@@ -51,6 +58,7 @@ const useTable = (rootLocator, configOptions = {}) => {
51
58
  return [text, i];
52
59
  })));
53
60
  _headerMap = new Map(entries);
61
+ logDebug(`Mapped ${entries.length} columns: ${JSON.stringify(entries.map(e => e[0]))}`);
54
62
  return _headerMap;
55
63
  });
56
64
  const _makeSmart = (rowLocator, map) => {
@@ -99,15 +107,18 @@ const useTable = (rootLocator, configOptions = {}) => {
99
107
  const map = yield _getMap();
100
108
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
101
109
  let currentPage = 1;
110
+ logDebug(`Looking for row: ${JSON.stringify(filters)} (MaxPages: ${effectiveMaxPages})`);
102
111
  while (true) {
103
112
  const allRows = resolve(config.rowSelector, rootLocator);
104
113
  const matchedRows = _applyFilters(allRows, filters, map, options.exact || false);
105
114
  const count = yield matchedRows.count();
115
+ logDebug(`Page ${currentPage}: Found ${count} matches.`);
106
116
  if (count > 1)
107
117
  throw new Error(`Strict Mode Violation: Found ${count} rows matching ${JSON.stringify(filters)}.`);
108
118
  if (count === 1)
109
119
  return matchedRows.first();
110
120
  if (currentPage < effectiveMaxPages) {
121
+ logDebug(`Page ${currentPage}: Not found. Attempting pagination...`);
111
122
  const context = {
112
123
  root: rootLocator,
113
124
  config: config,
@@ -116,9 +127,16 @@ const useTable = (rootLocator, configOptions = {}) => {
116
127
  };
117
128
  const didLoadMore = yield config.pagination(context);
118
129
  if (didLoadMore) {
130
+ _hasPaginated = true;
119
131
  currentPage++;
120
132
  continue;
121
133
  }
134
+ else {
135
+ logDebug(`Page ${currentPage}: Pagination failed (end of data).`);
136
+ }
137
+ }
138
+ if (_hasPaginated) {
139
+ console.warn(`āš ļø [SmartTable] Row not found. The table has been paginated (Current Page: ${currentPage}). You may need to call 'await table.reset()' if the target row is on a previous page.`);
122
140
  }
123
141
  return null;
124
142
  }
@@ -135,6 +153,34 @@ const useTable = (rootLocator, configOptions = {}) => {
135
153
  }
136
154
  console.log(finalPrompt);
137
155
  });
156
+ // Helper to extract clean HTML for prompts
157
+ const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
158
+ return loc.evaluate((el) => {
159
+ const clone = el.cloneNode(true);
160
+ // 1. Remove Heavy/Useless Elements
161
+ const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
162
+ clone.querySelectorAll(removeSelectors).forEach(n => n.remove());
163
+ // 2. Clean Attributes
164
+ const walker = document.createTreeWalker(clone, NodeFilter.SHOW_ELEMENT);
165
+ let currentNode = walker.currentNode;
166
+ while (currentNode) {
167
+ currentNode.removeAttribute('style'); // Inline styles are noise
168
+ currentNode.removeAttribute('data-reactid');
169
+ // 3. Condense Tailwind Classes (Heuristic)
170
+ // If class string is very long (>50 chars), keep the first few tokens and truncate.
171
+ // This preserves "MuiRow" but cuts "text-sm p-4 hover:bg-gray-50 ..."
172
+ const cls = currentNode.getAttribute('class');
173
+ if (cls && cls.length > 80) {
174
+ const tokens = cls.split(' ');
175
+ if (tokens.length > 5) {
176
+ currentNode.setAttribute('class', tokens.slice(0, 4).join(' ') + ' ...');
177
+ }
178
+ }
179
+ currentNode = walker.nextNode();
180
+ }
181
+ return clone.outerHTML;
182
+ });
183
+ });
138
184
  return {
139
185
  getHeaders: () => __awaiter(void 0, void 0, void 0, function* () { return Array.from((yield _getMap()).keys()); }),
140
186
  getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
@@ -144,6 +190,52 @@ const useTable = (rootLocator, configOptions = {}) => {
144
190
  throw new Error(`Column '${columnName}' not found.`);
145
191
  return resolve(config.headerSelector, rootLocator).nth(idx);
146
192
  }),
193
+ reset: () => __awaiter(void 0, void 0, void 0, function* () {
194
+ logDebug("Resetting table...");
195
+ const context = {
196
+ root: rootLocator,
197
+ config: config,
198
+ page: rootLocator.page(),
199
+ resolve: resolve
200
+ };
201
+ yield config.onReset(context);
202
+ _hasPaginated = false;
203
+ _headerMap = null;
204
+ logDebug("Table reset complete.");
205
+ }),
206
+ getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
207
+ var _a, _b;
208
+ const map = yield _getMap();
209
+ const colIdx = map.get(column);
210
+ if (colIdx === undefined)
211
+ throw new Error(`Column '${column}' not found.`);
212
+ const mapper = (_a = options === null || options === void 0 ? void 0 : options.mapper) !== null && _a !== void 0 ? _a : ((c) => c.innerText());
213
+ const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
214
+ let currentPage = 1;
215
+ const results = [];
216
+ logDebug(`Getting column values for '${column}' (Pages: ${effectiveMaxPages})`);
217
+ while (true) {
218
+ const rows = yield resolve(config.rowSelector, rootLocator).all();
219
+ for (const row of rows) {
220
+ const cell = typeof config.cellSelector === 'string'
221
+ ? row.locator(config.cellSelector).nth(colIdx)
222
+ : resolve(config.cellSelector, row).nth(colIdx);
223
+ results.push(yield mapper(cell));
224
+ }
225
+ if (currentPage < effectiveMaxPages) {
226
+ const context = {
227
+ root: rootLocator, config, page: rootLocator.page(), resolve
228
+ };
229
+ if (yield config.pagination(context)) {
230
+ _hasPaginated = true;
231
+ currentPage++;
232
+ continue;
233
+ }
234
+ }
235
+ break;
236
+ }
237
+ return results;
238
+ }),
147
239
  getByRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
148
240
  let row = yield _findRowLocator(filters, options);
149
241
  if (!row) {
@@ -169,17 +261,51 @@ const useTable = (rootLocator, configOptions = {}) => {
169
261
  return smartRows;
170
262
  }),
171
263
  generateConfigPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
172
- const html = yield rootLocator.evaluate((el) => el.outerHTML);
264
+ const html = yield _getCleanHtml(rootLocator);
173
265
  const separator = "=".repeat(50);
174
- const content = `\n${separator}\nšŸ¤– COPY INTO GEMINI/ChatGPT šŸ¤–\n${separator}\nI am using 'playwright-smart-table'. Generate config for:\n\`\`\`html\n${html.substring(0, 5000)} ...\n\`\`\`\n${separator}\n`;
266
+ const content = `\n${separator}\nšŸ¤– COPY INTO GEMINI/ChatGPT šŸ¤–\n${separator}\nI am using 'playwright-smart-table'. Generate config for:\n\`\`\`html\n${html.substring(0, 10000)} ...\n\`\`\`\n${separator}\n`;
175
267
  yield _handlePrompt('Smart Table Config', content, options);
176
268
  }),
177
269
  generateStrategyPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
178
270
  const container = rootLocator.locator('xpath=..');
179
- const html = yield container.evaluate((el) => el.outerHTML);
180
- 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, 5000)} ...\n\`\`\`\n`;
271
+ const html = yield _getCleanHtml(container);
272
+ 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`;
181
273
  yield _handlePrompt('Smart Table Strategy', content, options);
182
- })
274
+ }),
275
+ /* * 🚧 ROADMAP (v2.2) 🚧
276
+ * The following features are planned. Implementations are tentative.
277
+ * DO NOT DELETE THIS SECTION UNTIL IMPLEMENTED OR REMOVED.
278
+ * THIS IS BEING USED TO TRACK FUTURE DEVELOPMENT.
279
+ */
280
+ // __roadmap__fill: async (data: Record<string, any>) => {
281
+ // /* // * Goal: Fill a row with data intelligently.
282
+ // * Priority: Medium
283
+ // * Challenge: Handling different input types (select, checkbox, custom divs) blindly.
284
+ // */
285
+ // // const row = ... get row context ...
286
+ // // for (const [col, val] of Object.entries(data)) {
287
+ // // const cell = row.getCell(col);
288
+ // // const input = cell.locator('input, select, [role="checkbox"]');
289
+ // // if (await input.count() > 1) console.warn("Ambiguous input");
290
+ // // // Heuristics go here...
291
+ // // }
292
+ // // Note: Maybe we could pass the locator in the options for more control.
293
+ // },
294
+ // __roadmap__auditPages: async (options: { maxPages: number, audit: (rows: SmartRow[], page: number) => Promise<void> }) => {
295
+ // /*
296
+ // * Goal: Walk through pages and run a verification function on every page.
297
+ // * Priority: Low (Specific use case)
298
+ // * Logic:
299
+ // * let page = 1;
300
+ // * while (page <= options.maxPages) {
301
+ // * const rows = await getAllRows();
302
+ // * await options.audit(rows, page);
303
+ // * if (!await pagination(ctx)) break;
304
+ // * page++;
305
+ // * }
306
+ // */
307
+ // // Note: Maybe make is possible to skip several pages at once if the pagination strategy supports it.
308
+ // }
183
309
  };
184
310
  };
185
311
  exports.useTable = useTable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
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",