@rickcedwhat/playwright-smart-table 6.5.0 → 6.7.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.
@@ -128,7 +128,12 @@ export type SmartRow<T = any> = Locator & {
128
128
  smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
129
129
  };
130
130
 
131
- export type StrategyContext = TableContext & { rowLocator?: Locator; rowIndex?: number };
131
+ export type StrategyContext = TableContext & {
132
+ rowLocator?: Locator;
133
+ rowIndex?: number;
134
+ /** Helper to reliably get a header cell locator by name */
135
+ getHeaderCell?: (headerName: string) => Promise<Locator>;
136
+ };
132
137
 
133
138
  /**
134
139
  * Defines the contract for a sorting strategy.
@@ -191,6 +196,12 @@ export interface PaginationPrimitives {
191
196
  /** Classic "Previous Page" or "Scroll Up" */
192
197
  goPrevious?: (context: TableContext) => Promise<boolean>;
193
198
 
199
+ /** Bulk skip forward multiple pages at once */
200
+ goNextBulk?: (context: TableContext) => Promise<boolean>;
201
+
202
+ /** Bulk skip backward multiple pages at once */
203
+ goPreviousBulk?: (context: TableContext) => Promise<boolean>;
204
+
194
205
  /** Jump to first page / scroll to top */
195
206
  goToFirst?: (context: TableContext) => Promise<boolean>;
196
207
 
@@ -313,12 +324,6 @@ export interface TableConfig<T = any> {
313
324
  onReset?: (context: TableContext) => Promise<void>;
314
325
  /** All interaction strategies */
315
326
  strategies?: TableStrategies;
316
- /**
317
- * @deprecated Use \`columnOverrides\` instead. \`dataMapper\` will be removed in v7.0.0.
318
- * Custom data mappers for specific columns.
319
- * Allows extracting complex data types (boolean, number) instead of just string.
320
- */
321
- dataMapper?: Partial<Record<keyof T, (cell: Locator) => Promise<T[keyof T]> | T[keyof T]>>;
322
327
 
323
328
  /**
324
329
  * Unified interface for reading and writing data to specific columns.
@@ -348,24 +353,32 @@ export interface FillOptions {
348
353
  inputMappers?: Record<string, (cell: Locator) => Locator>;
349
354
  }
350
355
 
351
- /**
352
- * Options for generateConfigPrompt
353
- */
354
- export interface PromptOptions {
356
+
357
+
358
+ /** Callback context passed to forEach, map, and filter. */
359
+ export type RowIterationContext<T = any> = {
360
+ row: SmartRow<T>;
361
+ rowIndex: number;
362
+ stop: () => void;
363
+ };
364
+
365
+ /** Shared options for forEach, map, and filter. */
366
+ export type RowIterationOptions = {
367
+ /** Maximum number of pages to iterate. Defaults to config.maxPages. */
368
+ maxPages?: number;
355
369
  /**
356
- * Output Strategy:
357
- * - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).
358
- * - 'console': Standard console logs (Default).
370
+ * Whether to process rows within a page concurrently.
371
+ * @default false for forEach/filter, true for map
359
372
  */
360
- output?: 'console' | 'error';
373
+ parallel?: boolean;
361
374
  /**
362
- * Include TypeScript type definitions in the prompt
363
- * @default true
375
+ * Deduplication strategy. Use when rows may repeat across iterations
376
+ * (e.g. infinite scroll tables). Returns a unique key per row.
364
377
  */
365
- includeTypes?: boolean;
366
- }
378
+ dedupe?: DedupeStrategy;
379
+ };
367
380
 
368
- export interface TableResult<T = any> {
381
+ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>; rowIndex: number }> {
369
382
  /**
370
383
  * Represents the current page index of the table's DOM.
371
384
  * Starts at 0. Automatically maintained by the library during pagination and bringIntoView.
@@ -448,9 +461,62 @@ export interface TableResult<T = any> {
448
461
  revalidate: () => Promise<void>;
449
462
 
450
463
  /**
451
- * Scans a specific column across all pages and returns the values.
464
+ * Iterates every row across all pages, calling the callback for side effects.
465
+ * Execution is sequential by default (safe for interactions like clicking/filling).
466
+ * Call \`stop()\` in the callback to end iteration early.
467
+ *
468
+ * @example
469
+ * await table.forEach(async ({ row, stop }) => {
470
+ * if (await row.getCell('Status').innerText() === 'Done') stop();
471
+ * await row.getCell('Checkbox').click();
472
+ * });
452
473
  */
453
- getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;
474
+ forEach(
475
+ callback: (ctx: RowIterationContext<T>) => void | Promise<void>,
476
+ options?: RowIterationOptions
477
+ ): Promise<void>;
478
+
479
+ /**
480
+ * Transforms every row across all pages into a value. Returns a flat array.
481
+ * Execution is parallel within each page by default (safe for reads).
482
+ * Call \`stop()\` to halt after the current page finishes.
483
+ *
484
+ * > **⚠️ UI Interactions:** \`map\` defaults to \`parallel: true\`. If your callback opens popovers,
485
+ * > fills inputs, or otherwise mutates UI state, pass \`{ parallel: false }\` to avoid concurrent
486
+ * > interactions interfering with each other.
487
+ *
488
+ * @example
489
+ * // Data extraction — parallel is safe
490
+ * const emails = await table.map(({ row }) => row.getCell('Email').innerText());
491
+ *
492
+ * @example
493
+ * // UI interactions — must use parallel: false
494
+ * const assignees = await table.map(async ({ row }) => {
495
+ * await row.getCell('Assignee').locator('button').click();
496
+ * const name = await page.locator('.popover .name').innerText();
497
+ * await page.keyboard.press('Escape');
498
+ * return name;
499
+ * }, { parallel: false });
500
+ */
501
+ map<R>(
502
+ callback: (ctx: RowIterationContext<T>) => R | Promise<R>,
503
+ options?: RowIterationOptions
504
+ ): Promise<R[]>;
505
+
506
+ /**
507
+ * Filters rows across all pages by an async predicate. Returns a SmartRowArray.
508
+ * Rows are returned as-is — call \`bringIntoView()\` on each if needed.
509
+ * Execution is sequential by default.
510
+ *
511
+ * @example
512
+ * const active = await table.filter(async ({ row }) =>
513
+ * await row.getCell('Status').innerText() === 'Active'
514
+ * );
515
+ */
516
+ filter(
517
+ predicate: (ctx: RowIterationContext<T>) => boolean | Promise<boolean>,
518
+ options?: RowIterationOptions
519
+ ): Promise<SmartRowArray<T>>;
454
520
 
455
521
  /**
456
522
  * Provides access to sorting actions and assertions.
@@ -470,51 +536,11 @@ export interface TableResult<T = any> {
470
536
  getState(columnName: string): Promise<'asc' | 'desc' | 'none'>;
471
537
  };
472
538
 
473
- /**
474
- * Iterates through paginated table data, calling the callback for each iteration.
475
- * Callback return values are automatically appended to allData, which is returned.
476
- */
477
- iterateThroughTable: <T = any>(
478
- callback: (context: {
479
- index: number;
480
- isFirst: boolean;
481
- isLast: boolean;
482
- rows: SmartRowArray;
483
- allData: T[];
484
- table: RestrictedTableResult;
485
- batchInfo?: {
486
- startIndex: number;
487
- endIndex: number;
488
- size: number;
489
- };
490
-
491
- }) => T | T[] | Promise<T | T[]>,
492
- options?: {
493
- pagination?: PaginationStrategy;
494
- dedupeStrategy?: DedupeStrategy;
495
- maxIterations?: number;
496
- batchSize?: number;
497
- getIsFirst?: (context: { index: number }) => boolean;
498
- getIsLast?: (context: { index: number, paginationResult: boolean }) => boolean;
499
- beforeFirst?: (context: { index: number, rows: SmartRowArray, allData: any[] }) => void | Promise<void>;
500
- afterLast?: (context: { index: number, rows: SmartRowArray, allData: any[] }) => void | Promise<void>;
501
- /**
502
- * If true, flattens array results from callback into the main data array.
503
- * If false (default), pushes the return value as-is (preserves batching/arrays).
504
- */
505
- autoFlatten?: boolean;
506
- }
507
- ) => Promise<T[]>;
508
-
509
539
  /**
510
540
  * Generate an AI-friendly configuration prompt for debugging.
511
541
  * Outputs table HTML and TypeScript definitions to help AI assistants generate config.
542
+ * Automatically throws an Error containing the prompt.
512
543
  */
513
- generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
544
+ generateConfigPrompt: () => Promise<void>;
514
545
  }
515
-
516
- /**
517
- * Restricted table result that excludes methods that shouldn't be called during iteration.
518
- */
519
- export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
520
546
  `;
package/dist/types.d.ts CHANGED
@@ -114,6 +114,8 @@ export type SmartRow<T = any> = Locator & {
114
114
  export type StrategyContext = TableContext & {
115
115
  rowLocator?: Locator;
116
116
  rowIndex?: number;
117
+ /** Helper to reliably get a header cell locator by name */
118
+ getHeaderCell?: (headerName: string) => Promise<Locator>;
117
119
  };
118
120
  /**
119
121
  * Defines the contract for a sorting strategy.
@@ -170,6 +172,10 @@ export interface PaginationPrimitives {
170
172
  goNext?: (context: TableContext) => Promise<boolean>;
171
173
  /** Classic "Previous Page" or "Scroll Up" */
172
174
  goPrevious?: (context: TableContext) => Promise<boolean>;
175
+ /** Bulk skip forward multiple pages at once */
176
+ goNextBulk?: (context: TableContext) => Promise<boolean>;
177
+ /** Bulk skip backward multiple pages at once */
178
+ goPreviousBulk?: (context: TableContext) => Promise<boolean>;
173
179
  /** Jump to first page / scroll to top */
174
180
  goToFirst?: (context: TableContext) => Promise<boolean>;
175
181
  /** Jump to specific page index (0-indexed) */
@@ -293,12 +299,6 @@ export interface TableConfig<T = any> {
293
299
  onReset?: (context: TableContext) => Promise<void>;
294
300
  /** All interaction strategies */
295
301
  strategies?: TableStrategies;
296
- /**
297
- * @deprecated Use `columnOverrides` instead. `dataMapper` will be removed in v7.0.0.
298
- * Custom data mappers for specific columns.
299
- * Allows extracting complex data types (boolean, number) instead of just string.
300
- */
301
- dataMapper?: Partial<Record<keyof T, (cell: Locator) => Promise<T[keyof T]> | T[keyof T]>>;
302
302
  /**
303
303
  * Unified interface for reading and writing data to specific columns.
304
304
  * Overrides both default extraction (toJSON) and filling (smartFill) logic.
@@ -328,23 +328,31 @@ export interface FillOptions {
328
328
  */
329
329
  inputMappers?: Record<string, (cell: Locator) => Locator>;
330
330
  }
331
- /**
332
- * Options for generateConfigPrompt
333
- */
334
- export interface PromptOptions {
331
+ /** Callback context passed to forEach, map, and filter. */
332
+ export type RowIterationContext<T = any> = {
333
+ row: SmartRow<T>;
334
+ rowIndex: number;
335
+ stop: () => void;
336
+ };
337
+ /** Shared options for forEach, map, and filter. */
338
+ export type RowIterationOptions = {
339
+ /** Maximum number of pages to iterate. Defaults to config.maxPages. */
340
+ maxPages?: number;
335
341
  /**
336
- * Output Strategy:
337
- * - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).
338
- * - 'console': Standard console logs (Default).
342
+ * Whether to process rows within a page concurrently.
343
+ * @default false for forEach/filter, true for map
339
344
  */
340
- output?: 'console' | 'error';
345
+ parallel?: boolean;
341
346
  /**
342
- * Include TypeScript type definitions in the prompt
343
- * @default true
347
+ * Deduplication strategy. Use when rows may repeat across iterations
348
+ * (e.g. infinite scroll tables). Returns a unique key per row.
344
349
  */
345
- includeTypes?: boolean;
346
- }
347
- export interface TableResult<T = any> {
350
+ dedupe?: DedupeStrategy;
351
+ };
352
+ export interface TableResult<T = any> extends AsyncIterable<{
353
+ row: SmartRow<T>;
354
+ rowIndex: number;
355
+ }> {
348
356
  /**
349
357
  * Represents the current page index of the table's DOM.
350
358
  * Starts at 0. Automatically maintained by the library during pagination and bringIntoView.
@@ -414,12 +422,51 @@ export interface TableResult<T = any> {
414
422
  */
415
423
  revalidate: () => Promise<void>;
416
424
  /**
417
- * Scans a specific column across all pages and returns the values.
425
+ * Iterates every row across all pages, calling the callback for side effects.
426
+ * Execution is sequential by default (safe for interactions like clicking/filling).
427
+ * Call `stop()` in the callback to end iteration early.
428
+ *
429
+ * @example
430
+ * await table.forEach(async ({ row, stop }) => {
431
+ * if (await row.getCell('Status').innerText() === 'Done') stop();
432
+ * await row.getCell('Checkbox').click();
433
+ * });
418
434
  */
419
- getColumnValues: <V = string>(column: string, options?: {
420
- mapper?: (cell: Locator) => Promise<V> | V;
421
- maxPages?: number;
422
- }) => Promise<V[]>;
435
+ forEach(callback: (ctx: RowIterationContext<T>) => void | Promise<void>, options?: RowIterationOptions): Promise<void>;
436
+ /**
437
+ * Transforms every row across all pages into a value. Returns a flat array.
438
+ * Execution is parallel within each page by default (safe for reads).
439
+ * Call `stop()` to halt after the current page finishes.
440
+ *
441
+ * > **⚠️ UI Interactions:** `map` defaults to `parallel: true`. If your callback opens popovers,
442
+ * > fills inputs, or otherwise mutates UI state, pass `{ parallel: false }` to avoid concurrent
443
+ * > interactions interfering with each other.
444
+ *
445
+ * @example
446
+ * // Data extraction — parallel is safe
447
+ * const emails = await table.map(({ row }) => row.getCell('Email').innerText());
448
+ *
449
+ * @example
450
+ * // UI interactions — must use parallel: false
451
+ * const assignees = await table.map(async ({ row }) => {
452
+ * await row.getCell('Assignee').locator('button').click();
453
+ * const name = await page.locator('.popover .name').innerText();
454
+ * await page.keyboard.press('Escape');
455
+ * return name;
456
+ * }, { parallel: false });
457
+ */
458
+ map<R>(callback: (ctx: RowIterationContext<T>) => R | Promise<R>, options?: RowIterationOptions): Promise<R[]>;
459
+ /**
460
+ * Filters rows across all pages by an async predicate. Returns a SmartRowArray.
461
+ * Rows are returned as-is — call `bringIntoView()` on each if needed.
462
+ * Execution is sequential by default.
463
+ *
464
+ * @example
465
+ * const active = await table.filter(async ({ row }) =>
466
+ * await row.getCell('Status').innerText() === 'Active'
467
+ * );
468
+ */
469
+ filter(predicate: (ctx: RowIterationContext<T>) => boolean | Promise<boolean>, options?: RowIterationOptions): Promise<SmartRowArray<T>>;
423
470
  /**
424
471
  * Provides access to sorting actions and assertions.
425
472
  */
@@ -437,57 +484,10 @@ export interface TableResult<T = any> {
437
484
  */
438
485
  getState(columnName: string): Promise<'asc' | 'desc' | 'none'>;
439
486
  };
440
- /**
441
- * Iterates through paginated table data, calling the callback for each iteration.
442
- * Callback return values are automatically appended to allData, which is returned.
443
- */
444
- iterateThroughTable: <T = any>(callback: (context: {
445
- index: number;
446
- isFirst: boolean;
447
- isLast: boolean;
448
- rows: SmartRowArray;
449
- allData: T[];
450
- table: RestrictedTableResult;
451
- batchInfo?: {
452
- startIndex: number;
453
- endIndex: number;
454
- size: number;
455
- };
456
- }) => T | T[] | Promise<T | T[]>, options?: {
457
- pagination?: PaginationStrategy;
458
- dedupeStrategy?: DedupeStrategy;
459
- maxIterations?: number;
460
- batchSize?: number;
461
- getIsFirst?: (context: {
462
- index: number;
463
- }) => boolean;
464
- getIsLast?: (context: {
465
- index: number;
466
- paginationResult: boolean;
467
- }) => boolean;
468
- beforeFirst?: (context: {
469
- index: number;
470
- rows: SmartRowArray;
471
- allData: any[];
472
- }) => void | Promise<void>;
473
- afterLast?: (context: {
474
- index: number;
475
- rows: SmartRowArray;
476
- allData: any[];
477
- }) => void | Promise<void>;
478
- /**
479
- * If true, flattens array results from callback into the main data array.
480
- * If false (default), pushes the return value as-is (preserves batching/arrays).
481
- */
482
- autoFlatten?: boolean;
483
- }) => Promise<T[]>;
484
487
  /**
485
488
  * Generate an AI-friendly configuration prompt for debugging.
486
489
  * Outputs table HTML and TypeScript definitions to help AI assistants generate config.
490
+ * Automatically throws an Error containing the prompt.
487
491
  */
488
- generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
492
+ generateConfigPrompt: () => Promise<void>;
489
493
  }
490
- /**
491
- * Restricted table result that excludes methods that shouldn't be called during iteration.
492
- */
493
- export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;