@rickcedwhat/playwright-smart-table 2.1.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,22 @@
1
- Playwright Smart Table 🧠
1
+ # Playwright Smart Table 🧠
2
2
 
3
- A production-ready, type-safe table wrapper for Playwright.
3
+ A production-ready, type-safe table wrapper for Playwright that abstracts away the complexity of testing dynamic web tables. Handles pagination, infinite scroll, virtualization, and data grids (MUI, AG-Grid) so your tests remain clean and readable.
4
4
 
5
- This library abstracts away the complexity of testing dynamic web tables. It handles Pagination, Infinite Scroll, Virtualization, and Data Grids (MUI, AG-Grid) so your tests remain clean and readable.
6
-
7
- šŸ“¦ Installation
5
+ ## šŸ“¦ Installation
8
6
 
7
+ ```bash
9
8
  npm install @rickcedwhat/playwright-smart-table
9
+ ```
10
+
11
+ > **Note:** Requires `@playwright/test` as a peer dependency.
10
12
 
11
- Requires @playwright/test as a peer dependency.
13
+ ---
12
14
 
13
- ⚔ Quick Start
15
+ ## šŸŽÆ Getting Started
14
16
 
15
- The Standard HTML Table
17
+ ### Step 1: Basic Table Interaction
16
18
 
17
- For standard tables (<table>, <tr>, <td>), no configuration is needed (defaults work for most standard HTML tables).
19
+ For standard HTML tables (`<table>`, `<tr>`, `<td>`), the library works out of the box with sensible defaults:
18
20
 
19
21
  <!-- embed: quick-start -->
20
22
  ```typescript
@@ -30,9 +32,43 @@ await expect(row.getCell('Position')).toHaveText('Accountant');
30
32
  ```
31
33
  <!-- /embed: quick-start -->
32
34
 
33
- Complex Grids (Material UI / AG-Grid / Divs)
35
+ **What's happening here?**
36
+ - `useTable()` creates a smart table wrapper around your table locator
37
+ - `getByRow()` finds a specific row by column values
38
+ - The returned `SmartRow` knows its column structure, so `.getCell('Position')` works directly
39
+
40
+ ### Step 2: Understanding SmartRow
41
+
42
+ The `SmartRow` is the core power of this library. Unlike a standard Playwright `Locator`, it understands your table's column structure.
43
+
44
+ <!-- embed: smart-row -->
45
+ ```typescript
46
+ // 1. Get SmartRow via getByRow
47
+ const row = await table.getByRow({ Name: 'Airi Satou' });
48
+
49
+ // 2. Interact with cell
50
+ // āœ… Good: Resilient to column reordering
51
+ await row.getCell('Position').click();
52
+
53
+ // 3. Dump data from row
54
+ const data = await row.toJSON();
55
+ console.log(data);
56
+ // { Name: "Airi Satou", Position: "Accountant", ... }
57
+ ```
58
+ <!-- /embed: smart-row -->
59
+
60
+ **Key Benefits:**
61
+ - āœ… Column names instead of indices (survives column reordering)
62
+ - āœ… Extends Playwright's `Locator` API (all `.click()`, `.isVisible()`, etc. work)
63
+ - āœ… `.toJSON()` for quick data extraction
64
+
65
+ ---
66
+
67
+ ## šŸ”§ Configuration & Advanced Scenarios
34
68
 
35
- For modern React grids, simply override the selectors and define a pagination strategy.
69
+ ### Working with Paginated Tables
70
+
71
+ For tables that span multiple pages, configure a pagination strategy:
36
72
 
37
73
  <!-- embed: pagination -->
38
74
  ```typescript
@@ -55,83 +91,131 @@ await expect(await table.getByRow({ Name: "Colleen Hurst" })).toBeVisible();
55
91
  ```
56
92
  <!-- /embed: pagination -->
57
93
 
58
- 🧠 SmartRow Pattern
59
-
60
- The core power of this library is the SmartRow.
61
-
62
- Unlike a standard Playwright Locator, a SmartRow is aware of its context within the table's schema. It extends the standard Locator API, so you can chain standard Playwright methods (.click(), .isVisible()) directly off it.
63
-
64
- <!-- embed: smart-row -->
65
- ```typescript
66
- // 1. Get SmartRow via getByRow
67
- const row = await table.getByRow({ Name: 'Airi Satou' });
68
-
69
- // 2. Interact with cell (No more getByCell needed!)
70
- // āœ… Good: Resilient to column reordering
71
- await row.getCell('Position').click();
94
+ ### Debug Mode
72
95
 
73
- // 3. Dump data from row
74
- const data = await row.toJSON();
75
- console.log(data);
76
- // { Name: "Airi Satou", Position: "Accountant", ... }
77
- ```
78
- <!-- /embed: smart-row -->
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).
96
+ Enable debug logging to see exactly what the library is doing:
85
97
 
86
98
  <!-- embed: advanced-debug -->
87
99
  ```typescript
88
100
  const table = useTable(page.locator('#example'), {
89
101
  headerSelector: 'thead th',
90
- debug: true
102
+ debug: true // Enables verbose logging of internal operations
91
103
  });
104
+
105
+ const row = await table.getByRow({ Name: 'Airi Satou' });
106
+ await expect(row).toBeVisible();
92
107
  ```
93
108
  <!-- /embed: advanced-debug -->
94
109
 
95
- šŸ”„ Resetting State
110
+ This will log header mappings, row scans, and pagination triggers to help troubleshoot issues.
111
+
112
+ ### Resetting Table State
96
113
 
97
- If your tests navigate deep into a table (e.g., Page 5), subsequent searches might fail. Use .reset() to return to the start.
114
+ If your tests navigate deep into a paginated table, use `.reset()` to return to the first page:
98
115
 
99
116
  <!-- embed: advanced-reset -->
100
117
  ```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.
118
+ // Navigate deep into the table by searching for a row on a later page
103
119
  try {
104
120
  await table.getByRow({ Name: 'Angelica Ramos' });
105
121
  } catch (e) {}
106
122
 
107
123
  // Reset internal state (and potentially UI) to Page 1
108
124
  await table.reset();
125
+
126
+ // Now subsequent searches start from the beginning
127
+ const firstPageRow = await table.getByRow({ Name: 'Airi Satou' });
128
+ await expect(firstPageRow).toBeVisible();
109
129
  ```
110
130
  <!-- /embed: advanced-reset -->
111
131
 
112
- šŸ“Š Column Scanning
132
+ ### Column Scanning
113
133
 
114
- Need to verify a specific column is sorted or contains specific data? Use getColumnValues for a high-performance scan.
134
+ Efficiently extract all values from a specific column:
115
135
 
116
136
  <!-- embed: advanced-column-scan -->
117
137
  ```typescript
118
138
  // Quickly grab all text values from the "Office" column
119
139
  const offices = await table.getColumnValues('Office');
120
140
  expect(offices).toContain('Tokyo');
141
+ expect(offices.length).toBeGreaterThan(0);
121
142
  ```
122
143
  <!-- /embed: advanced-column-scan -->
123
144
 
124
- šŸ“– API Reference
145
+ ### Transforming Column Headers
146
+
147
+ Use `headerTransformer` to normalize or rename column headers. This is especially useful for tables with empty headers, inconsistent formatting, or when you want to use cleaner names in your tests.
148
+
149
+ **Example 1: Renaming Empty Columns**
125
150
 
126
- getByRow(filters, options?)
151
+ Tables with empty header cells (like Material UI DataGrids) get auto-assigned names like `__col_0`, `__col_1`. Transform them to meaningful names:
127
152
 
128
- Strict Retrieval. Finds a single specific row.
153
+ <!-- embed: header-transformer -->
154
+ ```typescript
155
+ const table = useTable(page.locator('.MuiDataGrid-root').first(), {
156
+ rowSelector: '.MuiDataGrid-row',
157
+ headerSelector: '.MuiDataGrid-columnHeader',
158
+ cellSelector: '.MuiDataGrid-cell',
159
+ pagination: TableStrategies.clickNext(
160
+ (root) => root.getByRole("button", { name: "Go to next page" })
161
+ ),
162
+ maxPages: 5,
163
+ // Transform empty columns (detected as __col_0, __col_1, etc.) to meaningful names
164
+ headerTransformer: ({ text }) => {
165
+ // We know there is only one empty column which we will rename to "Actions" for easier reference
166
+ if (text.includes('__col_') || text.trim() === '') {
167
+ return 'Actions';
168
+ }
169
+ return text;
170
+ }
171
+ });
172
+
173
+ const headers = await table.getHeaders();
174
+ // Now we can reference the "Actions" column even if it has no header text
175
+ expect(headers).toContain('Actions');
176
+
177
+ // Use the renamed column
178
+ const row = await table.getByRow({ "Last name": "Melisandre" });
179
+ await row.getCell('Actions').getByLabel("Select row").click();
180
+ ```
181
+ <!-- /embed: header-transformer -->
129
182
 
130
- Throws Error if >1 rows match (ambiguous query).
183
+ **Example 2: Normalizing Column Names**
131
184
 
132
- Returns Sentinel if 0 rows match (allows not.toBeVisible() assertions).
185
+ Clean up inconsistent column names (extra spaces, inconsistent casing):
133
186
 
134
- Auto-Paginates if the row isn't found on the current page.
187
+ <!-- embed: header-transformer-normalize -->
188
+ ```typescript
189
+ const table = useTable(page.locator('#table1'), {
190
+ // Normalize column names: remove extra spaces, handle inconsistent casing
191
+ headerTransformer: ({ text }) => {
192
+ return text.trim()
193
+ .replace(/\s+/g, ' ') // Normalize whitespace
194
+ .replace(/^\s*|\s*$/g, ''); // Remove leading/trailing spaces
195
+ }
196
+ });
197
+
198
+ // Now column names are consistent
199
+ const row = await table.getByRow({ "Last Name": "Doe" });
200
+ await expect(row.getCell("Email")).toHaveText("jdoe@hotmail.com");
201
+ ```
202
+ <!-- /embed: header-transformer-normalize -->
203
+
204
+ ---
205
+
206
+ ## šŸ“– API Reference
207
+
208
+ ### Table Methods
209
+
210
+ #### <a name="getbyrow"></a>`getByRow(filters, options?)`
211
+
212
+ **Purpose:** Strict retrieval - finds exactly one row matching the filters.
213
+
214
+ **Behavior:**
215
+ - āœ… Returns `SmartRow` if exactly one match
216
+ - āŒ Throws error if multiple matches (ambiguous query)
217
+ - šŸ‘» Returns sentinel locator if no match (allows `.not.toBeVisible()` assertions)
218
+ - šŸ”„ Auto-paginates if row isn't on current page
135
219
 
136
220
  <!-- embed: get-by-row -->
137
221
  ```typescript
@@ -144,18 +228,49 @@ await expect(await table.getByRow({ Name: "Ghost User" })).not.toBeVisible();
144
228
  ```
145
229
  <!-- /embed: get-by-row -->
146
230
 
147
- getAllRows(options?)
231
+ **Type Signature:**
232
+ <!-- embed-type: TableResult -->
233
+ ```typescript
234
+ export interface TableResult {
235
+ getHeaders: () => Promise<string[]>;
236
+ getHeaderCell: (columnName: string) => Promise<Locator>;
237
+
238
+ getByRow: <T extends { asJSON?: boolean }>(
239
+ filters: Record<string, string | RegExp | number>,
240
+ options?: { exact?: boolean, maxPages?: number } & T
241
+ ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
242
+
243
+ getAllRows: <T extends { asJSON?: boolean }>(
244
+ options?: { filter?: Record<string, any>, exact?: boolean } & T
245
+ ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
246
+
247
+ generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
248
+ generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
249
+
250
+ /**
251
+ * Resets the table state (clears cache, flags) and invokes the onReset strategy.
252
+ */
253
+ reset: () => Promise<void>;
254
+
255
+ /**
256
+ * Scans a specific column across all pages and returns the values.
257
+ */
258
+ getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;
259
+ }
260
+ ```
261
+ <!-- /embed-type: TableResult -->
148
262
 
149
- Inclusive Retrieval. Gets a collection of rows.
263
+ #### <a name="getallrows"></a>`getAllRows(options?)`
150
264
 
151
- Returns: Array of SmartRow objects.
265
+ **Purpose:** Inclusive retrieval - gets all rows matching optional filters.
152
266
 
153
- Best for: Checking existence ("at least one") or validating sort order.
267
+ **Best for:** Checking existence, validating sort order, bulk data extraction.
154
268
 
155
269
  <!-- embed: get-all-rows -->
156
270
  ```typescript
157
271
  // 1. Get ALL rows on the current page
158
272
  const allRows = await table.getAllRows();
273
+ expect(allRows.length).toBeGreaterThan(0);
159
274
 
160
275
  // 2. Get subset of rows (Filtering)
161
276
  const tokyoUsers = await table.getAllRows({
@@ -166,40 +281,278 @@ expect(tokyoUsers.length).toBeGreaterThan(0);
166
281
  // 3. Dump data to JSON
167
282
  const data = await table.getAllRows({ asJSON: true });
168
283
  console.log(data); // [{ Name: "Airi Satou", ... }, ...]
284
+ expect(data.length).toBeGreaterThan(0);
285
+ expect(data[0]).toHaveProperty('Name');
169
286
  ```
170
287
  <!-- /embed: get-all-rows -->
171
288
 
172
- 🧩 Pagination Strategies
289
+ #### <a name="getcolumnvalues"></a>`getColumnValues(column, options?)`
173
290
 
174
- This library uses the Strategy Pattern to handle navigation. You can use the built-in strategies or write your own.
291
+ Scans a specific column across all pages and returns values. Supports custom mappers for extracting non-text data.
175
292
 
176
- Built-in Strategies
293
+ **Additional Examples:**
294
+
295
+ Get row data as JSON:
296
+ <!-- embed: get-by-row-json -->
297
+ ```typescript
298
+ // Get row data directly as JSON object
299
+ const data = await table.getByRow({ Name: 'Airi Satou' }, { asJSON: true });
300
+ // Returns: { Name: "Airi Satou", Position: "Accountant", Office: "Tokyo", ... }
177
301
 
178
- clickNext(selector) Best for standard tables (Datatables, lists). Clicks a button and waits for data to change.
302
+ expect(data).toHaveProperty('Name', 'Airi Satou');
303
+ expect(data).toHaveProperty('Position');
304
+ ```
305
+ <!-- /embed: get-by-row-json -->
306
+
307
+ Filter rows with exact match:
308
+ <!-- embed: get-all-rows-exact -->
309
+ ```typescript
310
+ // Get rows with exact match (default is fuzzy/contains match)
311
+ const exactMatches = await table.getAllRows({
312
+ filter: { Office: 'Tokyo' },
313
+ exact: true // Requires exact string match
314
+ });
315
+
316
+ expect(exactMatches.length).toBeGreaterThan(0);
317
+ ```
318
+ <!-- /embed: get-all-rows-exact -->
319
+
320
+ Column scanning with custom mapper:
321
+ <!-- embed: advanced-column-scan-mapper -->
322
+ ```typescript
323
+ // Extract numeric values from a column
324
+ const ages = await table.getColumnValues('Age', {
325
+ mapper: async (cell) => {
326
+ const text = await cell.innerText();
327
+ return parseInt(text, 10);
328
+ }
329
+ });
179
330
 
331
+ // Now ages is an array of numbers
332
+ expect(ages.every(age => typeof age === 'number')).toBe(true);
333
+ expect(ages.length).toBeGreaterThan(0);
334
+ ```
335
+ <!-- /embed: advanced-column-scan-mapper -->
336
+
337
+ #### <a name="reset"></a>`reset()`
338
+
339
+ Resets table state (clears cache, pagination flags) and invokes the `onReset` strategy to return to the first page.
340
+
341
+ #### <a name="getheaders"></a>`getHeaders()`
342
+
343
+ Returns an array of all column names in the table.
344
+
345
+ #### <a name="getheadercell"></a>`getHeaderCell(columnName)`
346
+
347
+ Returns a Playwright `Locator` for the specified header cell.
348
+
349
+ ---
350
+
351
+ ## 🧩 Pagination Strategies
352
+
353
+ This library uses the **Strategy Pattern** for pagination. Use built-in strategies or write custom ones.
354
+
355
+ ### Built-in Strategies
356
+
357
+ #### <a name="tablestrategiesclicknext"></a>`TableStrategies.clickNext(selector)`
358
+
359
+ Best for standard paginated tables (Datatables, lists). Clicks a button/link and waits for table content to change.
360
+
361
+ ```typescript
180
362
  pagination: TableStrategies.clickNext((root) =>
181
- root.page().getByRole('button', { name: 'Next' })
363
+ root.page().getByRole('button', { name: 'Next' })
182
364
  )
365
+ ```
366
+
367
+ #### <a name="tablestrategiesinfinitescroll"></a>`TableStrategies.infiniteScroll()`
183
368
 
184
- infiniteScroll() Best for Virtualized Grids (AG-Grid, HTMX). Aggressively scrolls to trigger data loading.
369
+ Best for virtualized grids (AG-Grid, HTMX). Aggressively scrolls to trigger data loading.
185
370
 
371
+ ```typescript
186
372
  pagination: TableStrategies.infiniteScroll()
373
+ ```
187
374
 
188
- clickLoadMore(selector) Best for "Load More" buttons. Clicks and waits for row count to increase.
375
+ #### <a name="tablestrategiesclickloadmore"></a>`TableStrategies.clickLoadMore(selector)`
189
376
 
190
- šŸ› ļø Developer Tools
377
+ Best for "Load More" buttons. Clicks and waits for row count to increase.
191
378
 
192
- Don't waste time writing selectors manually. Use the generator tools to create your config.
379
+ ```typescript
380
+ pagination: TableStrategies.clickLoadMore((root) =>
381
+ root.getByRole('button', { name: 'Load More' })
382
+ )
383
+ ```
384
+
385
+ ### Custom Strategies
386
+
387
+ A pagination strategy is a function that receives a `TableContext` and returns `Promise<boolean>` (true if more data loaded, false if no more pages):
388
+
389
+ <!-- embed-type: PaginationStrategy -->
390
+ ```typescript
391
+ export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
392
+ ```
393
+ <!-- /embed-type: PaginationStrategy -->
394
+
395
+ <!-- embed-type: TableContext -->
396
+ ```typescript
397
+ export interface TableContext {
398
+ root: Locator;
399
+ config: Required<TableConfig>;
400
+ page: Page;
401
+ resolve: (selector: Selector, parent: Locator | Page) => Locator;
402
+ }
403
+ ```
404
+ <!-- /embed-type: TableContext -->
193
405
 
194
- generateConfigPrompt(options?)
406
+ ---
195
407
 
196
- Prints a prompt you can paste into ChatGPT/Gemini to generate the TableConfig for your specific HTML.
408
+ ## šŸ› ļø Developer Tools
197
409
 
198
- // Options: 'console' (default), 'error' (Throw error to see prompt in trace/cloud)
410
+ ### <a name="generateconfigprompt"></a>`generateConfigPrompt(options?)`
411
+
412
+ Generates a prompt you can paste into ChatGPT/Gemini to automatically generate the `TableConfig` for your specific HTML.
413
+
414
+ ```typescript
199
415
  await table.generateConfigPrompt({ output: 'console' });
416
+ ```
417
+
418
+ ### <a name="generatestrategyprompt"></a>`generateStrategyPrompt(options?)`
419
+
420
+ Generates a prompt to help you write a custom pagination strategy.
421
+
422
+ ```typescript
423
+ await table.generateStrategyPrompt({ output: 'console' });
424
+ ```
425
+
426
+ **Options:**
427
+ <!-- embed-type: PromptOptions -->
428
+ ```typescript
429
+ export interface PromptOptions {
430
+ /**
431
+ * Output Strategy:
432
+ * - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).
433
+ * - 'console': Standard console logs (Default).
434
+ */
435
+ output?: 'console' | 'error';
436
+ includeTypes?: boolean;
437
+ }
438
+ ```
439
+ <!-- /embed-type: PromptOptions -->
440
+
441
+ ---
442
+
443
+ ## šŸ“š Type Reference
444
+
445
+ ### Core Types
446
+
447
+ #### <a name="smartrow"></a>`SmartRow`
448
+
449
+ A `SmartRow` extends Playwright's `Locator` with table-aware methods.
450
+
451
+ <!-- embed-type: SmartRow -->
452
+ ```typescript
453
+ export type SmartRow = Locator & {
454
+ getCell(column: string): Locator;
455
+ toJSON(): Promise<Record<string, string>>;
456
+ };
457
+ ```
458
+ <!-- /embed-type: SmartRow -->
459
+
460
+ **Methods:**
461
+ - `getCell(column: string)`: Returns a `Locator` for the specified cell in this row
462
+ - `toJSON()`: Extracts all cell data as a key-value object
463
+
464
+ All standard Playwright `Locator` methods (`.click()`, `.isVisible()`, `.textContent()`, etc.) are also available.
465
+
466
+ #### <a name="tableconfig"></a>`TableConfig`
467
+
468
+ Configuration options for `useTable()`.
469
+
470
+ <!-- embed-type: TableConfig -->
471
+ ```typescript
472
+ export interface TableConfig {
473
+ rowSelector?: Selector;
474
+ headerSelector?: Selector;
475
+ cellSelector?: Selector;
476
+ pagination?: PaginationStrategy;
477
+ maxPages?: number;
478
+ /**
479
+ * Hook to rename columns dynamically.
480
+ * * @param args.text - The default innerText of the header.
481
+ * @param args.index - The column index.
482
+ * @param args.locator - The specific header cell locator.
483
+ */
484
+ headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;
485
+ autoScroll?: boolean;
486
+ /**
487
+ * Enable debug mode to log internal state to console.
488
+ */
489
+ debug?: boolean;
490
+ /**
491
+ * Strategy to reset the table to the first page.
492
+ * Called when table.reset() is invoked.
493
+ */
494
+ onReset?: (context: TableContext) => Promise<void>;
495
+ }
496
+ ```
497
+ <!-- /embed-type: TableConfig -->
498
+
499
+ **Property Descriptions:**
500
+
501
+ - `rowSelector`: CSS selector or function for table rows (default: `"tbody tr"`)
502
+ - `headerSelector`: CSS selector or function for header cells (default: `"th"`)
503
+ - `cellSelector`: CSS selector or function for data cells (default: `"td"`)
504
+ - `pagination`: Strategy function for navigating pages (default: no pagination)
505
+ - `maxPages`: Maximum pages to scan when searching (default: `1`)
506
+ - `headerTransformer`: Function to transform/rename column headers dynamically
507
+ - `autoScroll`: Automatically scroll table into view (default: `true`)
508
+ - `debug`: Enable verbose logging (default: `false`)
509
+ - `onReset`: Strategy called when `table.reset()` is invoked
510
+
511
+ #### <a name="selector"></a>`Selector`
512
+
513
+ Flexible selector type supporting strings, functions, or existing locators.
514
+
515
+ <!-- embed-type: Selector -->
516
+ ```typescript
517
+ export type Selector = string | ((root: Locator | Page) => Locator);
518
+ ```
519
+ <!-- /embed-type: Selector -->
520
+
521
+ **Examples:**
522
+ ```typescript
523
+ // String selector
524
+ rowSelector: 'tbody tr'
525
+
526
+ // Function selector (useful for complex cases)
527
+ rowSelector: (root) => root.locator('[role="row"]')
528
+
529
+ // Can also accept a Locator directly
530
+ ```
531
+
532
+ #### <a name="paginationstrategy"></a>`PaginationStrategy`
533
+
534
+ Function signature for custom pagination logic.
535
+
536
+ <!-- embed-type: PaginationStrategy -->
537
+ ```typescript
538
+ export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
539
+ ```
540
+ <!-- /embed-type: PaginationStrategy -->
541
+
542
+ Returns `true` if more data was loaded, `false` if pagination should stop.
543
+
544
+ ---
545
+
546
+ ## šŸš€ Tips & Best Practices
547
+
548
+ 1. **Start Simple**: Try the defaults first - they work for most standard HTML tables
549
+ 2. **Use Debug Mode**: When troubleshooting, enable `debug: true` to see what the library is doing
550
+ 3. **Leverage SmartRow**: Use `.getCell()` instead of manual column indices - your tests will be more maintainable
551
+ 4. **Type Safety**: All methods are fully typed - use TypeScript for the best experience
552
+ 5. **Pagination Strategies**: Create reusable strategies for tables with similar pagination patterns
200
553
 
201
- generateStrategyPrompt(options?)
554
+ ---
202
555
 
203
- Prints a prompt to help you write a custom Pagination Strategy.
556
+ ## šŸ“ License
204
557
 
205
- await table.generateStrategyPrompt({ output: 'console' });
558
+ ISC
@@ -102,11 +102,6 @@ exports.TableStrategies = {
102
102
  return false;
103
103
  // 1. Trigger Scroll
104
104
  yield rows.last().scrollIntoViewIfNeeded();
105
- // Optional: Keyboard press for robust grid handling
106
- try {
107
- yield page.keyboard.press('End');
108
- }
109
- catch (e) { }
110
105
  // 2. Smart Wait (Polling)
111
106
  return yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
112
107
  const newCount = yield rows.count();
package/dist/useTable.js CHANGED
@@ -272,40 +272,6 @@ const useTable = (rootLocator, configOptions = {}) => {
272
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`;
273
273
  yield _handlePrompt('Smart Table Strategy', content, options);
274
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
- // }
309
275
  };
310
276
  };
311
277
  exports.useTable = useTable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "2.1.2",
3
+ "version": "2.1.3",
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",
@@ -17,6 +17,7 @@
17
17
  "build": "npm run generate-types && npm run generate-docs && tsc",
18
18
  "prepublishOnly": "npm run build",
19
19
  "test": "npx playwright test",
20
+ "test:compatibility": "npx playwright test compatibility",
20
21
  "prepare": "husky install"
21
22
  },
22
23
  "keywords": [