@rickcedwhat/playwright-smart-table 6.18.0 → 6.20.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/engine/rowFinder.d.ts +16 -1
- package/dist/engine/rowFinder.js +156 -83
- package/dist/engine/rowResolution.d.ts +35 -0
- package/dist/engine/rowResolution.js +78 -0
- package/dist/engine/tableIteration.d.ts +4 -2
- package/dist/engine/tableIteration.js +72 -57
- package/dist/engine/tableMapper.js +10 -3
- package/dist/index.d.ts +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +9 -3
- package/dist/presets/glide/headers.js +6 -3
- package/dist/presets/mui.js +28 -1
- package/dist/smartRow.js +240 -48
- package/dist/strategies/fill.js +5 -2
- package/dist/strategies/headers.js +6 -3
- package/dist/strategies/viewport.js +46 -3
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +146 -16
- package/dist/types.d.ts +140 -14
- package/dist/useTable.js +202 -26
- package/dist/utils/mergeTableConfig.js +4 -0
- package/package.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ import type { SmartRowArray } from './utils/smartRowArray';
|
|
|
10
10
|
* headerSelector: (root) => root.locator('[role="columnheader"]')
|
|
11
11
|
*/
|
|
12
12
|
export type Selector = string | ((root: Locator | Page) => Locator) | ((root: Locator) => Locator);
|
|
13
|
+
/**
|
|
14
|
+
* Return type for `resolveRowIndex`. A plain number gives the logical index only;
|
|
15
|
+
* `{ index, selector }` additionally provides a CSS selector the library uses to
|
|
16
|
+
* build a self-healing row locator that survives virtual-scroll DOM recycling.
|
|
17
|
+
*/
|
|
18
|
+
export type RowIndexResult = number | {
|
|
19
|
+
index: number;
|
|
20
|
+
selector: string;
|
|
21
|
+
};
|
|
13
22
|
/**
|
|
14
23
|
* Value used to filter rows.
|
|
15
24
|
* - string/number/RegExp: filter by text content of the cell.
|
|
@@ -157,6 +166,15 @@ export interface ViewportStrategy {
|
|
|
157
166
|
first: number;
|
|
158
167
|
last: number;
|
|
159
168
|
}>;
|
|
169
|
+
/**
|
|
170
|
+
* Returns the DOM positions (0-based, document order — indices into the resolved
|
|
171
|
+
* `rowSelector` set) of rows currently within the scroll container's visible bounds.
|
|
172
|
+
* Unlike `getVisibleRowRange` (a logical min/max), this identifies the exact rows, so
|
|
173
|
+
* `map`/`forEach`/`filter` can skip overscan rows during collection (see #353 / #357).
|
|
174
|
+
* Geometry-based and inclusive (any overlap counts as visible). When the container can't
|
|
175
|
+
* be measured, return all positions so no rows are filtered.
|
|
176
|
+
*/
|
|
177
|
+
getVisibleRowIndices?: (context: TableContext) => Promise<number[]>;
|
|
160
178
|
/**
|
|
161
179
|
* Returns the 0-based index range of columns currently rendered in the DOM.
|
|
162
180
|
* Used to detect when a target column is not yet mounted before reading.
|
|
@@ -236,9 +254,16 @@ export type SmartRow<T = any> = Locator & {
|
|
|
236
254
|
*
|
|
237
255
|
* const partial = await row.toJSON({ columns: ['Name', 'Email'] });
|
|
238
256
|
* // { Name: 'John', Email: 'john@example.com' }
|
|
257
|
+
*
|
|
258
|
+
* // Atomic: snapshot all cell values in a single evaluate — zero inter-column stagger.
|
|
259
|
+
* // Requires cellSelector to be a CSS string (not a function).
|
|
260
|
+
* // Column overrides ARE supported (run against a frozen off-screen reconstruction).
|
|
261
|
+
* // Uses textContent (not layout-dependent innerText) for non-override columns.
|
|
262
|
+
* const coherent = await row.toJSON({ atomic: true });
|
|
239
263
|
*/
|
|
240
264
|
toJSON(options?: {
|
|
241
265
|
columns?: string[];
|
|
266
|
+
atomic?: boolean;
|
|
242
267
|
}): Promise<T>;
|
|
243
268
|
/**
|
|
244
269
|
* Scrolls/paginates to bring this row into view.
|
|
@@ -265,6 +290,12 @@ export type SmartRow<T = any> = Locator & {
|
|
|
265
290
|
* );
|
|
266
291
|
*/
|
|
267
292
|
smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
|
|
293
|
+
/**
|
|
294
|
+
* Get the resolved value of any column — real, override, or synthetic.
|
|
295
|
+
* @param column - Column name (case-sensitive)
|
|
296
|
+
* @returns The column value as a string
|
|
297
|
+
*/
|
|
298
|
+
getValue(column: string): Promise<string>;
|
|
268
299
|
/**
|
|
269
300
|
* Returns whether the row exists in the DOM (i.e. is not a sentinel row).
|
|
270
301
|
*/
|
|
@@ -383,12 +414,45 @@ export type FillStrategy = (options: {
|
|
|
383
414
|
table: TableResult;
|
|
384
415
|
fillOptions?: FillOptions;
|
|
385
416
|
}) => Promise<void>;
|
|
417
|
+
/** Context passed as the second argument to {@link ColumnOverride.read}. */
|
|
418
|
+
export interface ColumnOverrideReadContext {
|
|
419
|
+
/**
|
|
420
|
+
* The parent row. Use for multi-cell or row-derived values — e.g. a synthetic column
|
|
421
|
+
* that reads an `a[href]` or a `data-*` attribute from the row rather than a cell:
|
|
422
|
+
* `read: (_cell, { row }) => row.evaluate(el => el.querySelector('a')?.href)`.
|
|
423
|
+
*/
|
|
424
|
+
row: SmartRow;
|
|
425
|
+
/** The column being read. */
|
|
426
|
+
columnName: string;
|
|
427
|
+
/** The column's 0-based index in the resolved header map. */
|
|
428
|
+
columnIndex: number;
|
|
429
|
+
/**
|
|
430
|
+
* Get a Locator for another cell in the same row by column name.
|
|
431
|
+
* Returns raw cell Locators, not override-processed values.
|
|
432
|
+
*
|
|
433
|
+
* In atomic mode, the Locator points at the frozen reconstructed cell — coherent with
|
|
434
|
+
* every other cell from the same snapshot. In non-atomic mode, it points at the live cell.
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* read: async (_cell, { getCell }) => {
|
|
438
|
+
* const name = (await getCell('Name').innerText()).trim();
|
|
439
|
+
* const href = await getCell('Name').locator('a').getAttribute('href') || '';
|
|
440
|
+
* return `${name} | ${href}`;
|
|
441
|
+
* }
|
|
442
|
+
*/
|
|
443
|
+
getCell: (columnName: string) => Locator;
|
|
444
|
+
}
|
|
445
|
+
export interface SyntheticColumnDef<T = any> {
|
|
446
|
+
compute: (row: SmartRow<T>) => Promise<string | number> | string | number;
|
|
447
|
+
}
|
|
386
448
|
export interface ColumnOverride<TValue = any> {
|
|
387
449
|
/**
|
|
388
450
|
* How to extract the value from the cell.
|
|
389
|
-
* `context` provides
|
|
451
|
+
* The second `context` argument provides the parent `row` (for multi-cell or row-derived
|
|
452
|
+
* values), plus `columnName` and `columnIndex`. Backwards-compatible: existing
|
|
453
|
+
* single-argument `read(cell)` implementations keep working.
|
|
390
454
|
*/
|
|
391
|
-
read?: (cell: Locator) => Promise<TValue> | TValue;
|
|
455
|
+
read?: (cell: Locator, context: ColumnOverrideReadContext) => Promise<TValue> | TValue;
|
|
392
456
|
/**
|
|
393
457
|
* How to fill the cell with a new value. (Replaces smartFill default logic)
|
|
394
458
|
* Provides the current value (via `read`) if a `write` wants to check state first.
|
|
@@ -466,6 +530,12 @@ export interface LoadingStrategy {
|
|
|
466
530
|
* Defaults to 'read-as-is' when cellLoadingTimeout is set.
|
|
467
531
|
*/
|
|
468
532
|
onCellLoadingTimeout?: 'skip' | 'read-as-is' | 'throw' | ((cell: import('@playwright/test').Locator, columnName: string, row: SmartRow) => Promise<string>);
|
|
533
|
+
/** Max ms to wait for sort stabilization when isTableLoading is set. @default 10000 */
|
|
534
|
+
sortStabilizationTimeout?: number;
|
|
535
|
+
/** Polling interval (ms) while waiting for sort stabilization. @default 100 */
|
|
536
|
+
sortStabilizationPollInterval?: number;
|
|
537
|
+
/** Fallback delay (ms) after sort when no isTableLoading is configured. @default 200 */
|
|
538
|
+
sortStabilizationFallbackDelay?: number;
|
|
469
539
|
}
|
|
470
540
|
/**
|
|
471
541
|
* Organized container for all table interaction strategies.
|
|
@@ -513,14 +583,20 @@ export interface TableStrategies {
|
|
|
513
583
|
*
|
|
514
584
|
* Return `undefined` to fall back to DOM position.
|
|
515
585
|
*
|
|
586
|
+
* When the index maps to a DOM attribute, return `{ index, selector }` instead of
|
|
587
|
+
* a plain number. The library uses the CSS selector to build a **self-healing row
|
|
588
|
+
* locator** that re-queries the DOM on every action — surviving virtual-scroll
|
|
589
|
+
* recycling for `getCell`, `smartFill`, and `toJSON` without manual re-pinning.
|
|
590
|
+
*
|
|
516
591
|
* @example
|
|
517
|
-
* // MUI DataGrid:
|
|
592
|
+
* // MUI DataGrid: self-healing via data-rowindex attribute
|
|
518
593
|
* resolveRowIndex: async (row) => {
|
|
519
594
|
* const v = await row.getAttribute('data-rowindex').catch(() => null);
|
|
520
|
-
*
|
|
595
|
+
* if (v === null || isNaN(Number(v))) return undefined;
|
|
596
|
+
* return { index: Number(v), selector: `[data-rowindex="${v}"]` };
|
|
521
597
|
* }
|
|
522
598
|
*/
|
|
523
|
-
resolveRowIndex?: (row: Locator) => Promise<
|
|
599
|
+
resolveRowIndex?: (row: Locator) => Promise<RowIndexResult | undefined>;
|
|
524
600
|
/**
|
|
525
601
|
* Viewport oracle strategies for 2D virtualized tables (e.g. MUI DataGrid, AG Grid,
|
|
526
602
|
* Braintrust-style grids where both rows and columns are virtualized simultaneously).
|
|
@@ -558,7 +634,7 @@ export interface TableConfig<T = any> {
|
|
|
558
634
|
autoScroll?: boolean;
|
|
559
635
|
/** Debug options for development and troubleshooting */
|
|
560
636
|
debug?: DebugConfig;
|
|
561
|
-
/**
|
|
637
|
+
/** Hook called after reset completes (after goToFirst, cache clear, and autoInit). */
|
|
562
638
|
onReset?: (context: TableContext) => Promise<void>;
|
|
563
639
|
/** All interaction strategies */
|
|
564
640
|
strategies?: TableStrategies;
|
|
@@ -567,6 +643,19 @@ export interface TableConfig<T = any> {
|
|
|
567
643
|
* Overrides both default extraction (toJSON) and filling (smartFill) logic.
|
|
568
644
|
*/
|
|
569
645
|
columnOverrides?: Partial<Record<keyof T, ColumnOverride<T[keyof T]>>>;
|
|
646
|
+
/**
|
|
647
|
+
* Computed columns with no DOM presence. Each key becomes a virtual column name
|
|
648
|
+
* available in `toJSON()`, `getValue()`, and `findRow()`/`findRows()` filters.
|
|
649
|
+
* The `compute` function receives the full SmartRow and must only read real or
|
|
650
|
+
* override columns (no chaining between synthetics).
|
|
651
|
+
*/
|
|
652
|
+
syntheticColumns?: Record<string, SyntheticColumnDef<T>>;
|
|
653
|
+
/**
|
|
654
|
+
* Locator for an empty-state element that replaces the table when there are no results.
|
|
655
|
+
* If header resolution fails during init() and this locator is visible, init() succeeds
|
|
656
|
+
* and isEmpty() returns true. All row operations still throw normally.
|
|
657
|
+
*/
|
|
658
|
+
emptyState?: Locator;
|
|
570
659
|
}
|
|
571
660
|
export interface FinalTableConfig<T = any> extends TableConfig<T> {
|
|
572
661
|
headerSelector: string | ((root: Locator) => Locator);
|
|
@@ -595,9 +684,17 @@ export interface FillOptions {
|
|
|
595
684
|
/** Callback context passed to forEach, map, and filter. */
|
|
596
685
|
export type RowIterationContext<T = any> = {
|
|
597
686
|
row: SmartRow<T>;
|
|
598
|
-
/**
|
|
687
|
+
/**
|
|
688
|
+
* The row's logical/data-model index. When a `resolveRowIndex` strategy is configured
|
|
689
|
+
* (e.g. MUI DataGrid's `data-rowindex`) this is the grid's true row index; otherwise it
|
|
690
|
+
* equals `index`. Use this for `row.bringIntoView()` and position math on virtualized
|
|
691
|
+
* tables — it is stable across scrolling/dedupe, unlike the visit-order `index`.
|
|
692
|
+
*/
|
|
599
693
|
rowIndex: number;
|
|
600
|
-
/**
|
|
694
|
+
/**
|
|
695
|
+
* 0-based enumeration counter — the order this row was visited (contiguous within the run).
|
|
696
|
+
* Not a DOM position or grid identity. Use `rowIndex` for the row's data-model index.
|
|
697
|
+
*/
|
|
601
698
|
index: number;
|
|
602
699
|
/** 0-based page index — which page this row was collected from. */
|
|
603
700
|
pageIndex: number;
|
|
@@ -651,6 +748,12 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
651
748
|
* @returns true if init() has been called and completed, false otherwise
|
|
652
749
|
*/
|
|
653
750
|
isInitialized(): boolean;
|
|
751
|
+
/**
|
|
752
|
+
* SYNC: Returns true if init() resolved via the emptyState path — the table's
|
|
753
|
+
* empty-state locator was visible when header resolution failed.
|
|
754
|
+
* Row operations still throw normally; use this to branch before calling them.
|
|
755
|
+
*/
|
|
756
|
+
isEmpty(): boolean;
|
|
654
757
|
getHeaders: () => Promise<string[]>;
|
|
655
758
|
getHeaderCell: (columnName: string) => Promise<Locator>;
|
|
656
759
|
/**
|
|
@@ -670,8 +773,9 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
670
773
|
* For button-paginated tables this is the i-th row on the current page. For virtualized
|
|
671
774
|
* tables the render window shifts as you scroll, so `getRowByIndex(i)` returns whatever
|
|
672
775
|
* row currently sits at DOM position `i` — NOT the logical/absolute row `i` in the dataset
|
|
673
|
-
* once the list has scrolled.
|
|
674
|
-
* `
|
|
776
|
+
* once the list has scrolled. For the row with a specific logical/data-model index on a
|
|
777
|
+
* virtualized table, use the async `findRowByIndex(i)` instead (it scrolls to the row);
|
|
778
|
+
* to iterate by logical identity, use `map` / `findRows` (with a `dedupe` strategy).
|
|
675
779
|
*
|
|
676
780
|
* Resolution is lazy: an out-of-range index yields a SmartRow whose operations fail when
|
|
677
781
|
* it is used, rather than throwing here.
|
|
@@ -679,6 +783,24 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
679
783
|
* @param index 0-based position within the current render window
|
|
680
784
|
*/
|
|
681
785
|
getRowByIndex: (index: number) => SmartRow<T>;
|
|
786
|
+
/**
|
|
787
|
+
* ASYNC: Returns the row with a specific logical/data-model index, scrolling/paginating to
|
|
788
|
+
* reach it. Unlike the sync `getRowByIndex` (render-window position), this resolves the true
|
|
789
|
+
* data-model row `index` on virtualized tables.
|
|
790
|
+
*
|
|
791
|
+
* Reaches the row via, in order: a currently-mounted match, the viewport's random-access
|
|
792
|
+
* `scrollToRow` fast path, then advancing pages (on infinite-scroll tables a "page" is a
|
|
793
|
+
* scroll step) up to `maxPages`.
|
|
794
|
+
*
|
|
795
|
+
* Requires a `strategies.resolveRowIndex` to identify rows by logical index — throws if one
|
|
796
|
+
* is not configured. Also throws if the row cannot be reached (no silent wrong-row fallback).
|
|
797
|
+
*
|
|
798
|
+
* @param index 0-based logical/data-model row index
|
|
799
|
+
* @param options - `maxPages` bounds how far to scroll/paginate (defaults to config.maxPages)
|
|
800
|
+
*/
|
|
801
|
+
findRowByIndex: (index: number, options?: {
|
|
802
|
+
maxPages?: number;
|
|
803
|
+
}) => Promise<SmartRow<T>>;
|
|
682
804
|
/**
|
|
683
805
|
* ASYNC: Searches for a single row across pages using pagination.
|
|
684
806
|
* Auto-initializes the table if not already initialized.
|
|
@@ -705,10 +827,14 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
705
827
|
*/
|
|
706
828
|
scrollToColumn: (columnName: string) => Promise<void>;
|
|
707
829
|
/**
|
|
708
|
-
* Counts the number of rows
|
|
709
|
-
*
|
|
830
|
+
* Counts the number of rows, optionally filtered.
|
|
831
|
+
* Without arguments, counts all rows. With filters, counts only matching rows.
|
|
832
|
+
* Paginates when pagination is configured.
|
|
710
833
|
*/
|
|
711
|
-
countRows: (
|
|
834
|
+
countRows: (filters?: Record<string, FilterValue>, options?: {
|
|
835
|
+
exact?: boolean;
|
|
836
|
+
maxPages?: number;
|
|
837
|
+
}) => Promise<number>;
|
|
712
838
|
/**
|
|
713
839
|
* Iterates over rows and extracts the value of a single column.
|
|
714
840
|
* More efficient than map + toJSON for single-column extraction.
|
|
@@ -723,7 +849,7 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
723
849
|
*/
|
|
724
850
|
getColumnValues(columnName: string, options?: RowIterationOptions): Promise<string[]>;
|
|
725
851
|
/**
|
|
726
|
-
* Resets the table
|
|
852
|
+
* Resets the table: calls goToFirst (if configured), clears cache, re-inits headers, then calls onReset.
|
|
727
853
|
*/
|
|
728
854
|
reset: () => Promise<void>;
|
|
729
855
|
/**
|