@rickcedwhat/playwright-smart-table 6.4.0 → 6.6.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.
@@ -16,7 +16,7 @@ exports.TYPE_CONTEXT = `
16
16
  * // Function selector
17
17
  * rowSelector: (root) => root.locator('[role="row"]')
18
18
  */
19
- export type Selector = string | ((root: Locator | Page) => Locator);
19
+ export type Selector = string | ((root: Locator | Page) => Locator) | ((root: Locator) => Locator);
20
20
 
21
21
  /**
22
22
  * Value used to filter rows.
@@ -71,6 +71,12 @@ export type SmartRow<T = any> = Locator & {
71
71
  /** Optional row index (0-based) if known */
72
72
  rowIndex?: number;
73
73
 
74
+ /** Optional page index this row was found on (0-based) */
75
+ tablePageIndex?: number;
76
+
77
+ /** Reference to the parent TableResult */
78
+ table: TableResult<T>;
79
+
74
80
  /**
75
81
  * Get a cell locator by column name.
76
82
  * @param column - Column name (case-sensitive)
@@ -178,7 +184,21 @@ export interface TableContext<T = any> {
178
184
  resolve: (selector: Selector, parent: Locator | Page) => Locator;
179
185
  }
180
186
 
181
- export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
187
+ export interface PaginationPrimitives {
188
+ /** Classic "Next Page" or "Scroll Down" */
189
+ goNext?: (context: TableContext) => Promise<boolean>;
190
+
191
+ /** Classic "Previous Page" or "Scroll Up" */
192
+ goPrevious?: (context: TableContext) => Promise<boolean>;
193
+
194
+ /** Jump to first page / scroll to top */
195
+ goToFirst?: (context: TableContext) => Promise<boolean>;
196
+
197
+ /** Jump to specific page index (0-indexed) */
198
+ goToPage?: (pageIndex: number, context: TableContext) => Promise<boolean>;
199
+ }
200
+
201
+ export type PaginationStrategy = ((context: TableContext) => Promise<boolean>) | PaginationPrimitives;
182
202
 
183
203
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
184
204
 
@@ -191,10 +211,29 @@ export type FillStrategy = (options: {
191
211
  index: number;
192
212
  page: Page;
193
213
  rootLocator: Locator;
214
+ config: FinalTableConfig<any>;
194
215
  table: TableResult; // The parent table instance
195
216
  fillOptions?: FillOptions;
196
217
  }) => Promise<void>;
197
218
 
219
+ export interface ColumnOverride<TValue = any> {
220
+ /**
221
+ * How to extract the value from the cell. (Replaces dataMapper logic)
222
+ */
223
+ read?: (cell: Locator) => Promise<TValue> | TValue;
224
+
225
+ /**
226
+ * How to fill the cell with a new value. (Replaces smartFill default logic)
227
+ * Provides the current value (via \`read\`) if a \`write\` wants to check state first.
228
+ */
229
+ write?: (params: {
230
+ cell: Locator;
231
+ targetValue: TValue;
232
+ currentValue?: TValue;
233
+ row: SmartRow<any>;
234
+ }) => Promise<void>;
235
+ }
236
+
198
237
  export type { HeaderStrategy } from './strategies/headers';
199
238
 
200
239
  /**
@@ -275,10 +314,17 @@ export interface TableConfig<T = any> {
275
314
  /** All interaction strategies */
276
315
  strategies?: TableStrategies;
277
316
  /**
317
+ * @deprecated Use \`columnOverrides\` instead. \`dataMapper\` will be removed in v7.0.0.
278
318
  * Custom data mappers for specific columns.
279
319
  * Allows extracting complex data types (boolean, number) instead of just string.
280
320
  */
281
321
  dataMapper?: Partial<Record<keyof T, (cell: Locator) => Promise<T[keyof T]> | T[keyof T]>>;
322
+
323
+ /**
324
+ * Unified interface for reading and writing data to specific columns.
325
+ * Overrides both default extraction (toJSON) and filling (smartFill) logic.
326
+ */
327
+ columnOverrides?: Partial<Record<keyof T, ColumnOverride<T[keyof T]>>>;
282
328
  }
283
329
 
284
330
  export interface FinalTableConfig<T = any> extends TableConfig<T> {
@@ -319,7 +365,36 @@ export interface PromptOptions {
319
365
  includeTypes?: boolean;
320
366
  }
321
367
 
322
- export interface TableResult<T = any> {
368
+ /** Callback context passed to forEach, map, and filter. */
369
+ export type RowIterationContext<T = any> = {
370
+ row: SmartRow<T>;
371
+ rowIndex: number;
372
+ stop: () => void;
373
+ };
374
+
375
+ /** Shared options for forEach, map, and filter. */
376
+ export type RowIterationOptions = {
377
+ /** Maximum number of pages to iterate. Defaults to config.maxPages. */
378
+ maxPages?: number;
379
+ /**
380
+ * Whether to process rows within a page concurrently.
381
+ * @default false for forEach/filter, true for map
382
+ */
383
+ parallel?: boolean;
384
+ /**
385
+ * Deduplication strategy. Use when rows may repeat across iterations
386
+ * (e.g. infinite scroll tables). Returns a unique key per row.
387
+ */
388
+ dedupe?: DedupeStrategy;
389
+ };
390
+
391
+ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>; rowIndex: number }> {
392
+ /**
393
+ * Represents the current page index of the table's DOM.
394
+ * Starts at 0. Automatically maintained by the library during pagination and bringIntoView.
395
+ */
396
+ currentPageIndex: number;
397
+
323
398
  /**
324
399
  * Initializes the table by resolving headers. Must be called before using sync methods.
325
400
  * @param options Optional timeout for header resolution (default: 3000ms)
@@ -395,8 +470,54 @@ export interface TableResult<T = any> {
395
470
  */
396
471
  revalidate: () => Promise<void>;
397
472
 
473
+ /**
474
+ * Iterates every row across all pages, calling the callback for side effects.
475
+ * Execution is sequential by default (safe for interactions like clicking/filling).
476
+ * Call \`stop()\` in the callback to end iteration early.
477
+ *
478
+ * @example
479
+ * await table.forEach(async ({ row, stop }) => {
480
+ * if (await row.getCell('Status').innerText() === 'Done') stop();
481
+ * await row.getCell('Checkbox').click();
482
+ * });
483
+ */
484
+ forEach(
485
+ callback: (ctx: RowIterationContext<T>) => void | Promise<void>,
486
+ options?: RowIterationOptions
487
+ ): Promise<void>;
488
+
489
+ /**
490
+ * Transforms every row across all pages into a value. Returns a flat array.
491
+ * Execution is parallel within each page by default (safe for reads).
492
+ * Call \`stop()\` to halt after the current page finishes.
493
+ *
494
+ * @example
495
+ * const emails = await table.map(({ row }) => row.getCell('Email').innerText());
496
+ */
497
+ map<R>(
498
+ callback: (ctx: RowIterationContext<T>) => R | Promise<R>,
499
+ options?: RowIterationOptions
500
+ ): Promise<R[]>;
501
+
502
+ /**
503
+ * Filters rows across all pages by an async predicate. Returns a SmartRowArray.
504
+ * Rows are returned as-is — call \`bringIntoView()\` on each if needed.
505
+ * Execution is sequential by default.
506
+ *
507
+ * @example
508
+ * const active = await table.filter(async ({ row }) =>
509
+ * await row.getCell('Status').innerText() === 'Active'
510
+ * );
511
+ */
512
+ filter(
513
+ predicate: (ctx: RowIterationContext<T>) => boolean | Promise<boolean>,
514
+ options?: RowIterationOptions
515
+ ): Promise<SmartRowArray<T>>;
516
+
398
517
  /**
399
518
  * Scans a specific column across all pages and returns the values.
519
+ * @deprecated Use \`table.map(({ row }) => row.getCell(column).innerText())\` instead.
520
+ * Will be removed in v7.0.0.
400
521
  */
401
522
  getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;
402
523
 
@@ -421,6 +542,9 @@ export interface TableResult<T = any> {
421
542
  /**
422
543
  * Iterates through paginated table data, calling the callback for each iteration.
423
544
  * Callback return values are automatically appended to allData, which is returned.
545
+ * @deprecated Use \`forEach\`, \`map\`, or \`filter\` instead for cleaner cross-page iteration.
546
+ * Only use this for advanced scenarios (batchSize, beforeFirst/afterLast hooks).
547
+ * Will be removed in v7.0.0.
424
548
  */
425
549
  iterateThroughTable: <T = any>(
426
550
  callback: (context: {
package/dist/types.d.ts CHANGED
@@ -9,7 +9,7 @@ import type { SmartRowArray } from './utils/smartRowArray';
9
9
  * // Function selector
10
10
  * rowSelector: (root) => root.locator('[role="row"]')
11
11
  */
12
- export type Selector = string | ((root: Locator | Page) => Locator);
12
+ export type Selector = string | ((root: Locator | Page) => Locator) | ((root: Locator) => Locator);
13
13
  /**
14
14
  * Value used to filter rows.
15
15
  * - string/number/RegExp: filter by text content of the cell.
@@ -58,6 +58,10 @@ export type GetActiveCellFn = (args: TableContext) => Promise<{
58
58
  export type SmartRow<T = any> = Locator & {
59
59
  /** Optional row index (0-based) if known */
60
60
  rowIndex?: number;
61
+ /** Optional page index this row was found on (0-based) */
62
+ tablePageIndex?: number;
63
+ /** Reference to the parent TableResult */
64
+ table: TableResult<T>;
61
65
  /**
62
66
  * Get a cell locator by column name.
63
67
  * @param column - Column name (case-sensitive)
@@ -161,7 +165,17 @@ export interface TableContext<T = any> {
161
165
  page: Page;
162
166
  resolve: (selector: Selector, parent: Locator | Page) => Locator;
163
167
  }
164
- export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
168
+ export interface PaginationPrimitives {
169
+ /** Classic "Next Page" or "Scroll Down" */
170
+ goNext?: (context: TableContext) => Promise<boolean>;
171
+ /** Classic "Previous Page" or "Scroll Up" */
172
+ goPrevious?: (context: TableContext) => Promise<boolean>;
173
+ /** Jump to first page / scroll to top */
174
+ goToFirst?: (context: TableContext) => Promise<boolean>;
175
+ /** Jump to specific page index (0-indexed) */
176
+ goToPage?: (pageIndex: number, context: TableContext) => Promise<boolean>;
177
+ }
178
+ export type PaginationStrategy = ((context: TableContext) => Promise<boolean>) | PaginationPrimitives;
165
179
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
166
180
  export type FillStrategy = (options: {
167
181
  row: SmartRow;
@@ -170,9 +184,26 @@ export type FillStrategy = (options: {
170
184
  index: number;
171
185
  page: Page;
172
186
  rootLocator: Locator;
187
+ config: FinalTableConfig<any>;
173
188
  table: TableResult;
174
189
  fillOptions?: FillOptions;
175
190
  }) => Promise<void>;
191
+ export interface ColumnOverride<TValue = any> {
192
+ /**
193
+ * How to extract the value from the cell. (Replaces dataMapper logic)
194
+ */
195
+ read?: (cell: Locator) => Promise<TValue> | TValue;
196
+ /**
197
+ * How to fill the cell with a new value. (Replaces smartFill default logic)
198
+ * Provides the current value (via `read`) if a `write` wants to check state first.
199
+ */
200
+ write?: (params: {
201
+ cell: Locator;
202
+ targetValue: TValue;
203
+ currentValue?: TValue;
204
+ row: SmartRow<any>;
205
+ }) => Promise<void>;
206
+ }
176
207
  import { HeaderStrategy } from './strategies/headers';
177
208
  export type { HeaderStrategy } from './strategies/headers';
178
209
  import { NavigationPrimitives } from './strategies/columns';
@@ -263,10 +294,16 @@ export interface TableConfig<T = any> {
263
294
  /** All interaction strategies */
264
295
  strategies?: TableStrategies;
265
296
  /**
297
+ * @deprecated Use `columnOverrides` instead. `dataMapper` will be removed in v7.0.0.
266
298
  * Custom data mappers for specific columns.
267
299
  * Allows extracting complex data types (boolean, number) instead of just string.
268
300
  */
269
301
  dataMapper?: Partial<Record<keyof T, (cell: Locator) => Promise<T[keyof T]> | T[keyof T]>>;
302
+ /**
303
+ * Unified interface for reading and writing data to specific columns.
304
+ * Overrides both default extraction (toJSON) and filling (smartFill) logic.
305
+ */
306
+ columnOverrides?: Partial<Record<keyof T, ColumnOverride<T[keyof T]>>>;
270
307
  }
271
308
  export interface FinalTableConfig<T = any> extends TableConfig<T> {
272
309
  headerSelector: string | ((root: Locator) => Locator);
@@ -307,7 +344,36 @@ export interface PromptOptions {
307
344
  */
308
345
  includeTypes?: boolean;
309
346
  }
310
- export interface TableResult<T = any> {
347
+ /** Callback context passed to forEach, map, and filter. */
348
+ export type RowIterationContext<T = any> = {
349
+ row: SmartRow<T>;
350
+ rowIndex: number;
351
+ stop: () => void;
352
+ };
353
+ /** Shared options for forEach, map, and filter. */
354
+ export type RowIterationOptions = {
355
+ /** Maximum number of pages to iterate. Defaults to config.maxPages. */
356
+ maxPages?: number;
357
+ /**
358
+ * Whether to process rows within a page concurrently.
359
+ * @default false for forEach/filter, true for map
360
+ */
361
+ parallel?: boolean;
362
+ /**
363
+ * Deduplication strategy. Use when rows may repeat across iterations
364
+ * (e.g. infinite scroll tables). Returns a unique key per row.
365
+ */
366
+ dedupe?: DedupeStrategy;
367
+ };
368
+ export interface TableResult<T = any> extends AsyncIterable<{
369
+ row: SmartRow<T>;
370
+ rowIndex: number;
371
+ }> {
372
+ /**
373
+ * Represents the current page index of the table's DOM.
374
+ * Starts at 0. Automatically maintained by the library during pagination and bringIntoView.
375
+ */
376
+ currentPageIndex: number;
311
377
  /**
312
378
  * Initializes the table by resolving headers. Must be called before using sync methods.
313
379
  * @param options Optional timeout for header resolution (default: 3000ms)
@@ -371,8 +437,42 @@ export interface TableResult<T = any> {
371
437
  * Useful when columns change visibility or order dynamically.
372
438
  */
373
439
  revalidate: () => Promise<void>;
440
+ /**
441
+ * Iterates every row across all pages, calling the callback for side effects.
442
+ * Execution is sequential by default (safe for interactions like clicking/filling).
443
+ * Call `stop()` in the callback to end iteration early.
444
+ *
445
+ * @example
446
+ * await table.forEach(async ({ row, stop }) => {
447
+ * if (await row.getCell('Status').innerText() === 'Done') stop();
448
+ * await row.getCell('Checkbox').click();
449
+ * });
450
+ */
451
+ forEach(callback: (ctx: RowIterationContext<T>) => void | Promise<void>, options?: RowIterationOptions): Promise<void>;
452
+ /**
453
+ * Transforms every row across all pages into a value. Returns a flat array.
454
+ * Execution is parallel within each page by default (safe for reads).
455
+ * Call `stop()` to halt after the current page finishes.
456
+ *
457
+ * @example
458
+ * const emails = await table.map(({ row }) => row.getCell('Email').innerText());
459
+ */
460
+ map<R>(callback: (ctx: RowIterationContext<T>) => R | Promise<R>, options?: RowIterationOptions): Promise<R[]>;
461
+ /**
462
+ * Filters rows across all pages by an async predicate. Returns a SmartRowArray.
463
+ * Rows are returned as-is — call `bringIntoView()` on each if needed.
464
+ * Execution is sequential by default.
465
+ *
466
+ * @example
467
+ * const active = await table.filter(async ({ row }) =>
468
+ * await row.getCell('Status').innerText() === 'Active'
469
+ * );
470
+ */
471
+ filter(predicate: (ctx: RowIterationContext<T>) => boolean | Promise<boolean>, options?: RowIterationOptions): Promise<SmartRowArray<T>>;
374
472
  /**
375
473
  * Scans a specific column across all pages and returns the values.
474
+ * @deprecated Use `table.map(({ row }) => row.getCell(column).innerText())` instead.
475
+ * Will be removed in v7.0.0.
376
476
  */
377
477
  getColumnValues: <V = string>(column: string, options?: {
378
478
  mapper?: (cell: Locator) => Promise<V> | V;
@@ -398,6 +498,9 @@ export interface TableResult<T = any> {
398
498
  /**
399
499
  * Iterates through paginated table data, calling the callback for each iteration.
400
500
  * Callback return values are automatically appended to allData, which is returned.
501
+ * @deprecated Use `forEach`, `map`, or `filter` instead for cleaner cross-page iteration.
502
+ * Only use this for advanced scenarios (batchSize, beforeFirst/afterLast hooks).
503
+ * Will be removed in v7.0.0.
401
504
  */
402
505
  iterateThroughTable: <T = any>(callback: (context: {
403
506
  index: number;