@rickcedwhat/playwright-smart-table 6.25.0-next.3dfcc94 → 6.25.0-next.49ce2de
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/countRows.d.ts +27 -0
- package/dist/engine/countRows.js +139 -0
- package/dist/engine/rowByIndex.d.ts +19 -0
- package/dist/engine/rowByIndex.js +70 -0
- package/dist/engine/scanPages.d.ts +2 -2
- package/dist/engine/sorting.d.ts +11 -0
- package/dist/engine/sorting.js +51 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/useTable.js +41 -239
- package/dist/utils/safeStringify.d.ts +2 -0
- package/dist/utils/safeStringify.js +18 -0
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import type { FilterValue, FinalTableConfig, TableContext } from '../types';
|
|
3
|
+
import type { FilterEngine } from '../filterEngine';
|
|
4
|
+
import type { TableMapper } from './tableMapper';
|
|
5
|
+
import type { RowFinder } from './rowFinder';
|
|
6
|
+
export interface CountRowsEnv<T = any> {
|
|
7
|
+
config: FinalTableConfig<T>;
|
|
8
|
+
rootLocator: Locator;
|
|
9
|
+
getRowLocators: () => Locator;
|
|
10
|
+
tableMapper: TableMapper;
|
|
11
|
+
filterEngine: FilterEngine;
|
|
12
|
+
rowFinder: RowFinder<T>;
|
|
13
|
+
advancePage: (useBulk: boolean) => Promise<boolean>;
|
|
14
|
+
getCurrentPageIndex: () => number;
|
|
15
|
+
getContext: () => TableContext;
|
|
16
|
+
/** Restores page 1 after a paginated count. */
|
|
17
|
+
reset: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Counts matching rows on the current page, or across pages when pagination and
|
|
21
|
+
* `maxPages > 1` allow it. A paginated count always resets to page 1 afterwards (#348).
|
|
22
|
+
* Callers must have initialized the table.
|
|
23
|
+
*/
|
|
24
|
+
export declare function countRows<T>(env: CountRowsEnv<T>, filters?: Record<string, FilterValue>, options?: {
|
|
25
|
+
exact?: boolean;
|
|
26
|
+
maxPages?: number;
|
|
27
|
+
}): Promise<number>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.countRows = countRows;
|
|
4
|
+
const scanPages_1 = require("./scanPages");
|
|
5
|
+
const elementTracker_1 = require("../utils/elementTracker");
|
|
6
|
+
const debugUtils_1 = require("../utils/debugUtils");
|
|
7
|
+
const loadingWait_1 = require("../utils/loadingWait");
|
|
8
|
+
const safeStringify_1 = require("../utils/safeStringify");
|
|
9
|
+
function log(config, msg) {
|
|
10
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', msg);
|
|
11
|
+
}
|
|
12
|
+
/** Splits filters by how they're evaluated: DOM text, `columnOverrides.read`, or synthetic columns. */
|
|
13
|
+
function partitionFilters(config, filters) {
|
|
14
|
+
var _a, _b, _c;
|
|
15
|
+
const domFilters = {};
|
|
16
|
+
const overrideFilters = {};
|
|
17
|
+
const syntheticFilters = {};
|
|
18
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
19
|
+
if ((_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[key]) {
|
|
20
|
+
syntheticFilters[key] = value;
|
|
21
|
+
}
|
|
22
|
+
else if ((_c = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[key]) === null || _c === void 0 ? void 0 : _c.read) {
|
|
23
|
+
overrideFilters[key] = value;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
domFilters[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { domFilters, overrideFilters, syntheticFilters };
|
|
30
|
+
}
|
|
31
|
+
function requireMap(tableMapper) {
|
|
32
|
+
const map = tableMapper.getMapSync();
|
|
33
|
+
if (!map)
|
|
34
|
+
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
35
|
+
return map;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Counts matching rows on the current page, or across pages when pagination and
|
|
39
|
+
* `maxPages > 1` allow it. A paginated count always resets to page 1 afterwards (#348).
|
|
40
|
+
* Callers must have initialized the table.
|
|
41
|
+
*/
|
|
42
|
+
async function countRows(env, filters, options) {
|
|
43
|
+
var _a;
|
|
44
|
+
var _b, _c;
|
|
45
|
+
const { config, rootLocator, tableMapper, rowFinder } = env;
|
|
46
|
+
const filtersRecord = filters !== null && filters !== void 0 ? filters : {};
|
|
47
|
+
const hasFilters = Object.keys(filtersRecord).length > 0;
|
|
48
|
+
const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
|
|
49
|
+
const pag = config.strategies.pagination;
|
|
50
|
+
const hasPagination = effectiveMaxPages > 1 && !!((pag === null || pag === void 0 ? void 0 : pag.goNext) || (pag === null || pag === void 0 ? void 0 : pag.goNextBulk));
|
|
51
|
+
const exact = (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false;
|
|
52
|
+
const { domFilters, overrideFilters, syntheticFilters } = partitionFilters(config, filtersRecord);
|
|
53
|
+
const hasOverrides = Object.keys(overrideFilters).length > 0;
|
|
54
|
+
const hasSynthetics = Object.keys(syntheticFilters).length > 0;
|
|
55
|
+
const hasPostFilters = hasOverrides || hasSynthetics;
|
|
56
|
+
const resolveRows = () => {
|
|
57
|
+
const rows = env.getRowLocators();
|
|
58
|
+
if (Object.keys(domFilters).length === 0)
|
|
59
|
+
return rows;
|
|
60
|
+
return env.filterEngine.applyFilters(rows, domFilters, requireMap(tableMapper), exact, rootLocator.page(), rootLocator);
|
|
61
|
+
};
|
|
62
|
+
const countPostFilterMatches = async (candidates, indices) => {
|
|
63
|
+
if (!hasPostFilters)
|
|
64
|
+
return indices.length;
|
|
65
|
+
const map = requireMap(tableMapper);
|
|
66
|
+
let count = 0;
|
|
67
|
+
for (const idx of indices) {
|
|
68
|
+
const row = candidates[idx];
|
|
69
|
+
if (hasOverrides && !await rowFinder.matchesOverrideFilters(row, overrideFilters, map, exact))
|
|
70
|
+
continue;
|
|
71
|
+
if (hasSynthetics && !await rowFinder.matchesSyntheticFilters(row, syntheticFilters, map, exact))
|
|
72
|
+
continue;
|
|
73
|
+
count++;
|
|
74
|
+
}
|
|
75
|
+
return count;
|
|
76
|
+
};
|
|
77
|
+
const isTableLoading = (_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading;
|
|
78
|
+
const waitForReady = async () => {
|
|
79
|
+
if (!isTableLoading)
|
|
80
|
+
return;
|
|
81
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, env.getContext(), rootLocator.page(), 'countRows');
|
|
82
|
+
};
|
|
83
|
+
await waitForReady();
|
|
84
|
+
const filterSuffix = hasFilters ? ` filters=${(0, safeStringify_1.safeStringify)(filtersRecord)}` : '';
|
|
85
|
+
if (!hasPagination) {
|
|
86
|
+
log(config, `countRows: counting rows in current viewport (no pagination)${filterSuffix}`);
|
|
87
|
+
if (!hasPostFilters)
|
|
88
|
+
return resolveRows().count();
|
|
89
|
+
const all = await resolveRows().all();
|
|
90
|
+
return countPostFilterMatches(all, all.map((_, i) => i));
|
|
91
|
+
}
|
|
92
|
+
log(config, `countRows: paginating up to ${effectiveMaxPages} page(s)${filterSuffix}`);
|
|
93
|
+
const tracker = new elementTracker_1.ElementTracker('countRows');
|
|
94
|
+
let total = 0;
|
|
95
|
+
let pagesScanned = 1;
|
|
96
|
+
let paginationError;
|
|
97
|
+
try {
|
|
98
|
+
const scanned = await (0, scanPages_1.scanPages)({
|
|
99
|
+
advancePage: env.advancePage,
|
|
100
|
+
getCurrentPageIndex: env.getCurrentPageIndex,
|
|
101
|
+
config,
|
|
102
|
+
waitForReady,
|
|
103
|
+
}, async ({ pagesScanned: pageNum }) => {
|
|
104
|
+
const rowLocators = resolveRows();
|
|
105
|
+
const newIndices = await tracker.getUnseenIndices(rowLocators);
|
|
106
|
+
const candidates = await rowLocators.all();
|
|
107
|
+
const matched = await countPostFilterMatches(candidates, newIndices);
|
|
108
|
+
total += matched;
|
|
109
|
+
log(config, `countRows: page ${pageNum} — ${matched} row(s) (running total: ${total})`);
|
|
110
|
+
return 'continue';
|
|
111
|
+
}, {
|
|
112
|
+
maxPages: effectiveMaxPages,
|
|
113
|
+
useBulk: false,
|
|
114
|
+
label: 'countRows',
|
|
115
|
+
finalScanOnEof: true,
|
|
116
|
+
});
|
|
117
|
+
pagesScanned = scanned.pagesScanned;
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
paginationError = e;
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
await tracker.cleanup(rootLocator.page());
|
|
125
|
+
// Always restore page 1, even if pagination threw mid-count (#348), so the table
|
|
126
|
+
// stays usable for callers that catch the error. If reset() itself fails, only let
|
|
127
|
+
// it surface when pagination succeeded — otherwise preserve the original error.
|
|
128
|
+
log(config, `countRows: ${total} total row(s) across ${pagesScanned} page(s) — resetting`);
|
|
129
|
+
try {
|
|
130
|
+
await env.reset();
|
|
131
|
+
}
|
|
132
|
+
catch (resetError) {
|
|
133
|
+
if (paginationError === undefined)
|
|
134
|
+
throw resetError;
|
|
135
|
+
(0, debugUtils_1.logDebug)(config, 'error', 'countRows: reset() failed after pagination error', resetError);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return total;
|
|
139
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import type { FinalTableConfig, SmartRow, TableContext } from '../types';
|
|
3
|
+
export interface RowByIndexEnv<T = any> {
|
|
4
|
+
config: FinalTableConfig<T>;
|
|
5
|
+
getRowLocators: () => Locator;
|
|
6
|
+
getMap: () => Map<string, number>;
|
|
7
|
+
advancePage: (useBulk: boolean) => Promise<boolean>;
|
|
8
|
+
getCurrentPageIndex: () => number;
|
|
9
|
+
getContext: () => TableContext;
|
|
10
|
+
makeSmartRow: (rowLocator: Locator, map: Map<string, number>, rowIndex: number, tablePageIndex: number, healSelector?: string) => SmartRow<T>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Finds the row whose logical index (per `strategies.resolveRowIndex`) is `index`:
|
|
14
|
+
* already mounted, else via `viewport.scrollToRow`, else by advancing pages until it
|
|
15
|
+
* mounts (bounded by `maxPages`). Callers must have initialized the table.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findRowByIndex<T>(env: RowByIndexEnv<T>, index: number, options?: {
|
|
18
|
+
maxPages?: number;
|
|
19
|
+
}): Promise<SmartRow<T>>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findRowByIndex = findRowByIndex;
|
|
4
|
+
const rowResolution_1 = require("./rowResolution");
|
|
5
|
+
const scanPages_1 = require("./scanPages");
|
|
6
|
+
const debugUtils_1 = require("../utils/debugUtils");
|
|
7
|
+
const safeStringify_1 = require("../utils/safeStringify");
|
|
8
|
+
/**
|
|
9
|
+
* Finds the row whose logical index (per `strategies.resolveRowIndex`) is `index`:
|
|
10
|
+
* already mounted, else via `viewport.scrollToRow`, else by advancing pages until it
|
|
11
|
+
* mounts (bounded by `maxPages`). Callers must have initialized the table.
|
|
12
|
+
*/
|
|
13
|
+
async function findRowByIndex(env, index, options) {
|
|
14
|
+
var _a;
|
|
15
|
+
const { config } = env;
|
|
16
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `findRowByIndex: index=${index} options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
17
|
+
const resolveRI = config.strategies.resolveRowIndex;
|
|
18
|
+
const viewport = config.strategies.viewport;
|
|
19
|
+
// Identifying a row by its logical index requires a resolveRowIndex strategy. Without one
|
|
20
|
+
// the library can only offer render-window-relative getRowByIndex, or filter-based findRow.
|
|
21
|
+
if (!resolveRI) {
|
|
22
|
+
throw new Error(`findRowByIndex(${index}) requires a strategies.resolveRowIndex to identify rows by their logical index. ` +
|
|
23
|
+
`Configure one (e.g. read the grid's row-index attribute), or use getRowByIndex() (current render window) / findRow(filters).`);
|
|
24
|
+
}
|
|
25
|
+
let foundSelector;
|
|
26
|
+
// The currently-mounted row whose logical index === index, if any.
|
|
27
|
+
const findMounted = async () => {
|
|
28
|
+
const rows = await env.getRowLocators().all();
|
|
29
|
+
for (const r of rows) {
|
|
30
|
+
const result = await resolveRI(r);
|
|
31
|
+
if (result === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
const normalized = (0, rowResolution_1.normalizeRowIndexResult)(result);
|
|
34
|
+
if (normalized.index === index) {
|
|
35
|
+
foundSelector = normalized.selector;
|
|
36
|
+
return r;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
};
|
|
41
|
+
// 1. Already mounted (no navigation needed)?
|
|
42
|
+
let loc = await findMounted();
|
|
43
|
+
// 2. Fast path: jump straight to it via the viewport's random-access scroll.
|
|
44
|
+
if (!loc && (viewport === null || viewport === void 0 ? void 0 : viewport.scrollToRow)) {
|
|
45
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `findRowByIndex: scrolling to row ${index}`);
|
|
46
|
+
await viewport.scrollToRow(env.getContext(), index);
|
|
47
|
+
loc = await findMounted();
|
|
48
|
+
}
|
|
49
|
+
// 3. Fallback: advance pages (on infinite-scroll tables, a "page" is a scroll step) until
|
|
50
|
+
// the row is mounted. Page 1 was already searched above.
|
|
51
|
+
const maxPages = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
52
|
+
const scanForMounted = async () => {
|
|
53
|
+
let hit = null;
|
|
54
|
+
await (0, scanPages_1.scanPages)({ advancePage: env.advancePage, getCurrentPageIndex: env.getCurrentPageIndex, config }, async ({ pagesScanned }) => {
|
|
55
|
+
if (pagesScanned > 1)
|
|
56
|
+
hit = await findMounted();
|
|
57
|
+
return hit ? 'stop' : 'continue';
|
|
58
|
+
}, { maxPages, useBulk: false, label: 'findRowByIndex', finalScanOnEof: false });
|
|
59
|
+
return hit;
|
|
60
|
+
};
|
|
61
|
+
if (!loc && config.strategies.pagination) {
|
|
62
|
+
loc = await scanForMounted();
|
|
63
|
+
}
|
|
64
|
+
if (!loc) {
|
|
65
|
+
throw new Error(`findRowByIndex(${index}): could not reach row ${index}. ` +
|
|
66
|
+
`Ensure a viewport.scrollToRow or pagination can bring it into view (searched up to maxPages=${maxPages}).`);
|
|
67
|
+
}
|
|
68
|
+
await (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
69
|
+
return env.makeSmartRow(loc, env.getMap(), index, env.getCurrentPageIndex(), foundSelector);
|
|
70
|
+
}
|
|
@@ -21,10 +21,10 @@ import type { FinalTableConfig } from '../types';
|
|
|
21
21
|
* | findRow | yes | options.useBulkPagination | yes | DOM/post filters only |
|
|
22
22
|
* | countRows | yes | no (single-step) | yes | count-only / post filters |
|
|
23
23
|
* | async iterator | yes | no | via runMap path | same as map |
|
|
24
|
-
* | findRowByIndex | no
|
|
24
|
+
* | findRowByIndex | no | no | no | stops once the target index is mounted* |
|
|
25
25
|
* | bringIntoView | n/a | path planner | n/a | not a page scan |
|
|
26
26
|
*
|
|
27
|
-
* \* findRowByIndex
|
|
27
|
+
* \* findRowByIndex only falls back to scanPages after the mounted / viewport.scrollToRow paths miss.
|
|
28
28
|
*/
|
|
29
29
|
export type ScanDecision = 'continue' | 'stop';
|
|
30
30
|
export interface ScanPageContext {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Page } from '@playwright/test';
|
|
2
|
+
import type { FinalTableConfig, StrategyContext } from '../types';
|
|
3
|
+
export interface SortingEnv<T = any> {
|
|
4
|
+
config: FinalTableConfig<T>;
|
|
5
|
+
getPage: () => Page;
|
|
6
|
+
/** Strategy context including `getHeaderCell`. */
|
|
7
|
+
getContext: () => StrategyContext;
|
|
8
|
+
}
|
|
9
|
+
/** Drives the sorting strategy until `columnName` reports `direction`, retrying up to 3 times. */
|
|
10
|
+
export declare function applySort<T>(env: SortingEnv<T>, columnName: string, direction: 'asc' | 'desc'): Promise<void>;
|
|
11
|
+
export declare function getSortState<T>(env: SortingEnv<T>, columnName: string): Promise<'asc' | 'desc' | 'none'>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applySort = applySort;
|
|
4
|
+
exports.getSortState = getSortState;
|
|
5
|
+
const debugUtils_1 = require("../utils/debugUtils");
|
|
6
|
+
const loadingWait_1 = require("../utils/loadingWait");
|
|
7
|
+
const MAX_SORT_ATTEMPTS = 3;
|
|
8
|
+
function requireSorting(config) {
|
|
9
|
+
if (!config.strategies.sorting)
|
|
10
|
+
throw new Error('No sorting strategy has been configured.');
|
|
11
|
+
return config.strategies.sorting;
|
|
12
|
+
}
|
|
13
|
+
/** Waits for the table to settle after `doSort`: `isTableLoading` when configured, otherwise a fixed delay. */
|
|
14
|
+
async function waitForSortToSettle(env, context) {
|
|
15
|
+
var _a, _b, _c, _d;
|
|
16
|
+
const loading = env.config.strategies.loading;
|
|
17
|
+
if (loading === null || loading === void 0 ? void 0 : loading.isTableLoading) {
|
|
18
|
+
const timeout = (_b = (_a = loading.sortStabilizationTimeout) !== null && _a !== void 0 ? _a : loading.loadingTimeout) !== null && _b !== void 0 ? _b : 10000;
|
|
19
|
+
const pollInterval = (_c = loading.sortStabilizationPollInterval) !== null && _c !== void 0 ? _c : 100;
|
|
20
|
+
await (0, loadingWait_1.waitWhileTableLoading)(env.config, context, env.getPage(), 'sorting.apply', { timeout, pollInterval });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
await env.getPage().waitForTimeout((_d = loading === null || loading === void 0 ? void 0 : loading.sortStabilizationFallbackDelay) !== null && _d !== void 0 ? _d : 200);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Drives the sorting strategy until `columnName` reports `direction`, retrying up to 3 times. */
|
|
27
|
+
async function applySort(env, columnName, direction) {
|
|
28
|
+
const sorting = requireSorting(env.config);
|
|
29
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `Applying sort for column "${columnName}" (${direction})`);
|
|
30
|
+
const context = env.getContext();
|
|
31
|
+
for (let i = 0; i < MAX_SORT_ATTEMPTS; i++) {
|
|
32
|
+
const currentState = await sorting.getSortState({ columnName, context });
|
|
33
|
+
if (currentState === direction) {
|
|
34
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `Sort for "${columnName}" is already "${direction}".`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await sorting.doSort({ columnName, direction, context });
|
|
38
|
+
await waitForSortToSettle(env, context);
|
|
39
|
+
await (0, debugUtils_1.debugDelay)(env.config, 'default');
|
|
40
|
+
const newState = await sorting.getSortState({ columnName, context });
|
|
41
|
+
if (newState === direction) {
|
|
42
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `Successfully sorted "${columnName}" to "${direction}".`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${MAX_SORT_ATTEMPTS} attempts.`);
|
|
47
|
+
}
|
|
48
|
+
async function getSortState(env, columnName) {
|
|
49
|
+
const sorting = requireSorting(env.config);
|
|
50
|
+
return sorting.getSortState({ columnName, context: env.getContext() });
|
|
51
|
+
}
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Generated by scripts/embed-version.mjs from package.json
|
|
4
4
|
*/
|
|
5
5
|
/** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
|
|
6
|
-
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.25.0-next.
|
|
6
|
+
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.25.0-next.49ce2de";
|
package/dist/packageVersion.js
CHANGED
|
@@ -6,4 +6,4 @@ exports.PLAYWRIGHT_SMART_TABLE_VERSION = void 0;
|
|
|
6
6
|
* Generated by scripts/embed-version.mjs from package.json
|
|
7
7
|
*/
|
|
8
8
|
/** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
|
|
9
|
-
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.25.0-next.
|
|
9
|
+
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.25.0-next.49ce2de";
|
package/dist/useTable.js
CHANGED
|
@@ -27,29 +27,15 @@ const filterEngine_1 = require("./filterEngine");
|
|
|
27
27
|
const tableMapper_1 = require("./engine/tableMapper");
|
|
28
28
|
const rowFinder_1 = require("./engine/rowFinder");
|
|
29
29
|
const tableIteration_1 = require("./engine/tableIteration");
|
|
30
|
-
const
|
|
31
|
-
const
|
|
30
|
+
const countRows_1 = require("./engine/countRows");
|
|
31
|
+
const sorting_1 = require("./engine/sorting");
|
|
32
|
+
const rowByIndex_1 = require("./engine/rowByIndex");
|
|
33
|
+
const safeStringify_1 = require("./utils/safeStringify");
|
|
32
34
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
33
35
|
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
34
|
-
const elementTracker_1 = require("./utils/elementTracker");
|
|
35
|
-
const loadingWait_1 = require("./utils/loadingWait");
|
|
36
36
|
const pageIndex_1 = require("./utils/pageIndex");
|
|
37
37
|
const smartRowInternals_1 = require("./utils/smartRowInternals");
|
|
38
38
|
const locatorDescription_1 = require("./utils/locatorDescription");
|
|
39
|
-
// Helper to safely serialize objects containing functions for logging
|
|
40
|
-
const safeStringify = (obj) => {
|
|
41
|
-
try {
|
|
42
|
-
return JSON.stringify(obj, (_key, value) => {
|
|
43
|
-
if (typeof value === 'function') {
|
|
44
|
-
return '[Function]';
|
|
45
|
-
}
|
|
46
|
-
return value;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
return '[unserializable]';
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
39
|
/**
|
|
54
40
|
* Main hook to interact with a table.
|
|
55
41
|
*/
|
|
@@ -238,6 +224,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
238
224
|
}
|
|
239
225
|
return didAdvance;
|
|
240
226
|
};
|
|
227
|
+
const sortingEnv = () => ({
|
|
228
|
+
config,
|
|
229
|
+
getPage: () => rootLocator.page(),
|
|
230
|
+
getContext: () => (Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell })),
|
|
231
|
+
});
|
|
241
232
|
const result = {
|
|
242
233
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
243
234
|
set currentPageIndex(v) {
|
|
@@ -326,133 +317,24 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
326
317
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
327
318
|
},
|
|
328
319
|
countRows: async (filters, options) => {
|
|
329
|
-
var _a, _b, _c, _d;
|
|
330
|
-
var _e;
|
|
331
320
|
if (tableState.empty)
|
|
332
321
|
return 0;
|
|
333
322
|
await _autoInit();
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
else if ((_c = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[key]) === null || _c === void 0 ? void 0 : _c.read) {
|
|
348
|
-
overrideFilters[key] = value;
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
domFilters[key] = value;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
const hasPostFilters = Object.keys(overrideFilters).length > 0 || Object.keys(syntheticFilters).length > 0;
|
|
356
|
-
const resolveRows = () => {
|
|
357
|
-
var _a;
|
|
358
|
-
let rows = resolve(config.rowSelector, rootLocator);
|
|
359
|
-
if (Object.keys(domFilters).length > 0) {
|
|
360
|
-
const map = tableMapper.getMapSync();
|
|
361
|
-
if (!map)
|
|
362
|
-
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
363
|
-
rows = filterEngine.applyFilters(rows, domFilters, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, rootLocator.page(), rootLocator);
|
|
364
|
-
}
|
|
365
|
-
return rows;
|
|
366
|
-
};
|
|
367
|
-
const countPostFilterMatches = async (candidates, indices) => {
|
|
368
|
-
var _a;
|
|
369
|
-
if (!hasPostFilters)
|
|
370
|
-
return indices.length;
|
|
371
|
-
const map = tableMapper.getMapSync();
|
|
372
|
-
if (!map)
|
|
373
|
-
throw new Error('Initialization Error: Table map not available. Call "await table.init()" first.');
|
|
374
|
-
const exact = (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false;
|
|
375
|
-
const hasOverrides = Object.keys(overrideFilters).length > 0;
|
|
376
|
-
const hasSynthetics = Object.keys(syntheticFilters).length > 0;
|
|
377
|
-
let count = 0;
|
|
378
|
-
for (const idx of indices) {
|
|
379
|
-
const row = candidates[idx];
|
|
380
|
-
if (hasOverrides && !await rowFinder.matchesOverrideFilters(row, overrideFilters, map, exact))
|
|
381
|
-
continue;
|
|
382
|
-
if (hasSynthetics && !await rowFinder.matchesSyntheticFilters(row, syntheticFilters, map, exact))
|
|
383
|
-
continue;
|
|
384
|
-
count++;
|
|
385
|
-
}
|
|
386
|
-
return count;
|
|
387
|
-
};
|
|
388
|
-
// Wait for table to finish loading before counting
|
|
389
|
-
const isTableLoading = (_d = config.strategies.loading) === null || _d === void 0 ? void 0 : _d.isTableLoading;
|
|
390
|
-
if (isTableLoading) {
|
|
391
|
-
const ctx = createStrategyContext();
|
|
392
|
-
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
393
|
-
}
|
|
394
|
-
if (!hasPagination) {
|
|
395
|
-
log(`countRows: counting rows in current viewport (no pagination)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
396
|
-
if (!hasPostFilters)
|
|
397
|
-
return resolveRows().count();
|
|
398
|
-
const all = await resolveRows().all();
|
|
399
|
-
return countPostFilterMatches(all, all.map((_, i) => i));
|
|
400
|
-
}
|
|
401
|
-
log(`countRows: paginating up to ${effectiveMaxPages} page(s)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
402
|
-
const tracker = new elementTracker_1.ElementTracker('countRows');
|
|
403
|
-
let total = 0;
|
|
404
|
-
let pagesScanned = 1;
|
|
405
|
-
let paginationError;
|
|
406
|
-
try {
|
|
407
|
-
const scanned = await (0, scanPages_1.scanPages)({
|
|
408
|
-
advancePage: _advancePage,
|
|
409
|
-
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
410
|
-
config,
|
|
411
|
-
waitForReady: async () => {
|
|
412
|
-
if (!isTableLoading)
|
|
413
|
-
return;
|
|
414
|
-
const ctx = createStrategyContext();
|
|
415
|
-
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
416
|
-
},
|
|
417
|
-
}, async ({ pagesScanned: pageNum }) => {
|
|
418
|
-
const rowLocators = resolveRows();
|
|
419
|
-
const newIndices = await tracker.getUnseenIndices(rowLocators);
|
|
420
|
-
const candidates = await rowLocators.all();
|
|
421
|
-
const matched = await countPostFilterMatches(candidates, newIndices);
|
|
422
|
-
total += matched;
|
|
423
|
-
log(`countRows: page ${pageNum} — ${matched} row(s) (running total: ${total})`);
|
|
424
|
-
return 'continue';
|
|
425
|
-
}, {
|
|
426
|
-
maxPages: effectiveMaxPages,
|
|
427
|
-
useBulk: false,
|
|
428
|
-
label: 'countRows',
|
|
429
|
-
finalScanOnEof: true,
|
|
430
|
-
});
|
|
431
|
-
pagesScanned = scanned.pagesScanned;
|
|
432
|
-
}
|
|
433
|
-
catch (e) {
|
|
434
|
-
paginationError = e;
|
|
435
|
-
throw e;
|
|
436
|
-
}
|
|
437
|
-
finally {
|
|
438
|
-
await tracker.cleanup(rootLocator.page());
|
|
439
|
-
// Always restore page 1, even if pagination threw mid-count (#348), so the table
|
|
440
|
-
// stays usable for callers that catch the error. If reset() itself fails, only let
|
|
441
|
-
// it surface when pagination succeeded — otherwise preserve the original error.
|
|
442
|
-
log(`countRows: ${total} total row(s) across ${pagesScanned} page(s) — resetting`);
|
|
443
|
-
try {
|
|
444
|
-
await result.reset();
|
|
445
|
-
}
|
|
446
|
-
catch (resetError) {
|
|
447
|
-
if (paginationError === undefined)
|
|
448
|
-
throw resetError;
|
|
449
|
-
(0, debugUtils_1.logDebug)(config, 'error', 'countRows: reset() failed after pagination error', resetError);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return total;
|
|
323
|
+
return (0, countRows_1.countRows)({
|
|
324
|
+
config,
|
|
325
|
+
rootLocator,
|
|
326
|
+
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
327
|
+
tableMapper,
|
|
328
|
+
filterEngine,
|
|
329
|
+
rowFinder,
|
|
330
|
+
advancePage: _advancePage,
|
|
331
|
+
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
332
|
+
getContext: () => createStrategyContext(),
|
|
333
|
+
reset: () => result.reset(),
|
|
334
|
+
}, filters, options);
|
|
453
335
|
},
|
|
454
336
|
mapColumn: async (columnName, options = {}) => {
|
|
455
|
-
log(`mapColumn: column="${columnName}" options=${safeStringify(options)}`);
|
|
337
|
+
log(`mapColumn: column="${columnName}" options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
456
338
|
await _autoInit();
|
|
457
339
|
const map = await tableMapper.getMap();
|
|
458
340
|
if (!map.has(columnName))
|
|
@@ -477,7 +359,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
477
359
|
* Iterates over rows and extracts the value of a single column as strings.
|
|
478
360
|
*/
|
|
479
361
|
getColumnValues: async (columnName, options = {}) => {
|
|
480
|
-
log(`getColumnValues: column="${columnName}" options=${safeStringify(options)}`);
|
|
362
|
+
log(`getColumnValues: column="${columnName}" options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
481
363
|
const values = await result.mapColumn(columnName, options);
|
|
482
364
|
return values.map(v => String(v));
|
|
483
365
|
},
|
|
@@ -505,7 +387,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
505
387
|
log("Table revalidated.");
|
|
506
388
|
},
|
|
507
389
|
getRow: (filters, options = { exact: false }) => {
|
|
508
|
-
log(`getRow: filters=${safeStringify(filters)} exact=${options.exact}`);
|
|
390
|
+
log(`getRow: filters=${(0, safeStringify_1.safeStringify)(filters)} exact=${options.exact}`);
|
|
509
391
|
const syntheticKeys = Object.keys(filters).filter(k => { var _a; return (_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[k]; });
|
|
510
392
|
if (syntheticKeys.length > 0) {
|
|
511
393
|
throw new Error(`getRow() cannot filter by synthetic column(s): ${syntheticKeys.join(', ')}. ` +
|
|
@@ -533,67 +415,23 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
533
415
|
return _makeSmart(rowLocator, map, index, undefined, undefined, undefined, true);
|
|
534
416
|
},
|
|
535
417
|
findRowByIndex: async (index, options) => {
|
|
536
|
-
var _a, _b;
|
|
537
|
-
log(`findRowByIndex: index=${index} options=${safeStringify(options)}`);
|
|
538
418
|
await _autoInit();
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
let foundSelector;
|
|
549
|
-
// The currently-mounted row whose logical index === index, if any.
|
|
550
|
-
const findMounted = async () => {
|
|
551
|
-
const rows = await resolve(config.rowSelector, rootLocator).all();
|
|
552
|
-
for (const r of rows) {
|
|
553
|
-
const result = await resolveRI(r);
|
|
554
|
-
if (result === undefined)
|
|
555
|
-
continue;
|
|
556
|
-
const normalized = (0, rowResolution_1.normalizeRowIndexResult)(result);
|
|
557
|
-
if (normalized.index === index) {
|
|
558
|
-
foundSelector = normalized.selector;
|
|
559
|
-
return r;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
return null;
|
|
563
|
-
};
|
|
564
|
-
// 1. Already mounted (no navigation needed)?
|
|
565
|
-
let loc = await findMounted();
|
|
566
|
-
// 2. Fast path: jump straight to it via the viewport's random-access scroll.
|
|
567
|
-
if (!loc && (viewport === null || viewport === void 0 ? void 0 : viewport.scrollToRow)) {
|
|
568
|
-
log(`findRowByIndex: scrolling to row ${index}`);
|
|
569
|
-
await viewport.scrollToRow(createStrategyContext(), index);
|
|
570
|
-
loc = await findMounted();
|
|
571
|
-
}
|
|
572
|
-
// 3. Fallback: advance pages (on infinite-scroll tables, a "page" is a scroll step) until
|
|
573
|
-
// the row is reached. Bounded by maxPages so an unreachable index can't loop forever.
|
|
574
|
-
if (!loc && config.strategies.pagination) {
|
|
575
|
-
const effectiveMaxPages = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
576
|
-
let pagesScanned = 1;
|
|
577
|
-
while (!loc && pagesScanned < effectiveMaxPages) {
|
|
578
|
-
if (!await _advancePage(false))
|
|
579
|
-
break;
|
|
580
|
-
pagesScanned++;
|
|
581
|
-
loc = await findMounted();
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
if (!loc) {
|
|
585
|
-
throw new Error(`findRowByIndex(${index}): could not reach row ${index}. ` +
|
|
586
|
-
`Ensure a viewport.scrollToRow or pagination can bring it into view (searched up to maxPages=${(_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages}).`);
|
|
587
|
-
}
|
|
588
|
-
await (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
589
|
-
return _makeSmart(loc, map, index, tableState.currentPageIndex, undefined, foundSelector);
|
|
419
|
+
return (0, rowByIndex_1.findRowByIndex)({
|
|
420
|
+
config,
|
|
421
|
+
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
422
|
+
getMap: () => tableMapper.getMapSync(),
|
|
423
|
+
advancePage: _advancePage,
|
|
424
|
+
getCurrentPageIndex: () => tableState.currentPageIndex,
|
|
425
|
+
getContext: () => createStrategyContext(),
|
|
426
|
+
makeSmartRow: (loc, map, idx, pageIdx, sel) => _makeSmart(loc, map, idx, pageIdx, undefined, sel),
|
|
427
|
+
}, index, options);
|
|
590
428
|
},
|
|
591
429
|
findRow: async (filters, options) => {
|
|
592
|
-
log(`findRow: filters=${safeStringify(filters)} options=${safeStringify(options)}`);
|
|
430
|
+
log(`findRow: filters=${(0, safeStringify_1.safeStringify)(filters)} options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
593
431
|
return rowFinder.findRow(filters, options);
|
|
594
432
|
},
|
|
595
433
|
findRows: async (filters, options) => {
|
|
596
|
-
log(`findRows: filters=${safeStringify(filters !== null && filters !== void 0 ? filters : {})} options=${safeStringify(options)}`);
|
|
434
|
+
log(`findRows: filters=${(0, safeStringify_1.safeStringify)(filters !== null && filters !== void 0 ? filters : {})} options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
597
435
|
return rowFinder.findRows(filters !== null && filters !== void 0 ? filters : {}, options);
|
|
598
436
|
},
|
|
599
437
|
isInitialized: () => {
|
|
@@ -606,49 +444,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
606
444
|
},
|
|
607
445
|
sorting: {
|
|
608
446
|
apply: async (columnName, direction) => {
|
|
609
|
-
var _a, _b;
|
|
610
|
-
var _c, _d, _e, _f;
|
|
611
447
|
await _autoInit();
|
|
612
|
-
|
|
613
|
-
throw new Error('No sorting strategy has been configured.');
|
|
614
|
-
log(`Applying sort for column "${columnName}" (${direction})`);
|
|
615
|
-
const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
|
|
616
|
-
const maxRetries = 3;
|
|
617
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
618
|
-
const currentState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
619
|
-
if (currentState === direction) {
|
|
620
|
-
log(`Sort for "${columnName}" is already "${direction}".`);
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
await config.strategies.sorting.doSort({ columnName, direction, context });
|
|
624
|
-
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
625
|
-
const timeout = (_d = (_c = config.strategies.loading.sortStabilizationTimeout) !== null && _c !== void 0 ? _c : config.strategies.loading.loadingTimeout) !== null && _d !== void 0 ? _d : 10000;
|
|
626
|
-
const poll = (_e = config.strategies.loading.sortStabilizationPollInterval) !== null && _e !== void 0 ? _e : 100;
|
|
627
|
-
await (0, loadingWait_1.waitWhileTableLoading)(config, context, rootLocator.page(), 'sorting.apply', {
|
|
628
|
-
timeout,
|
|
629
|
-
pollInterval: poll,
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
const fallback = (_f = (_b = config.strategies.loading) === null || _b === void 0 ? void 0 : _b.sortStabilizationFallbackDelay) !== null && _f !== void 0 ? _f : 200;
|
|
634
|
-
await rootLocator.page().waitForTimeout(fallback);
|
|
635
|
-
}
|
|
636
|
-
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
637
|
-
const newState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
638
|
-
if (newState === direction) {
|
|
639
|
-
log(`Successfully sorted "${columnName}" to "${direction}".`);
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
|
|
448
|
+
return (0, sorting_1.applySort)(sortingEnv(), columnName, direction);
|
|
644
449
|
},
|
|
645
450
|
getState: async (columnName) => {
|
|
646
451
|
log(`sorting.getState: column="${columnName}"`);
|
|
647
452
|
await _autoInit();
|
|
648
|
-
|
|
649
|
-
throw new Error('No sorting strategy has been configured.');
|
|
650
|
-
const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
|
|
651
|
-
return config.strategies.sorting.getSortState({ columnName, context });
|
|
453
|
+
return (0, sorting_1.getSortState)(sortingEnv(), columnName);
|
|
652
454
|
}
|
|
653
455
|
},
|
|
654
456
|
// ─── Shared async row iterator ───────────────────────────────────────────
|
|
@@ -716,7 +518,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
716
518
|
},
|
|
717
519
|
// ─── Row iteration (delegated to engine/tableIteration) ──────────────────
|
|
718
520
|
forEach: async (callback, options = {}) => {
|
|
719
|
-
log(`forEach: options=${safeStringify(options)}`);
|
|
521
|
+
log(`forEach: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
720
522
|
await _autoInit();
|
|
721
523
|
await (0, tableIteration_1.runForEach)({
|
|
722
524
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
@@ -731,7 +533,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
731
533
|
}, callback, options);
|
|
732
534
|
},
|
|
733
535
|
map: async (callback, options = {}) => {
|
|
734
|
-
log(`map: options=${safeStringify(options)}`);
|
|
536
|
+
log(`map: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
735
537
|
await _autoInit();
|
|
736
538
|
return (0, tableIteration_1.runMap)({
|
|
737
539
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
@@ -746,7 +548,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
746
548
|
}, callback, options);
|
|
747
549
|
},
|
|
748
550
|
toArray: async (callback, options = {}) => {
|
|
749
|
-
log(`toArray: options=${safeStringify(options)}`);
|
|
551
|
+
log(`toArray: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
750
552
|
const cb = callback !== null && callback !== void 0 ? callback : (({ row }) => row.toJSON());
|
|
751
553
|
try {
|
|
752
554
|
return await result.map(cb, options);
|
|
@@ -756,7 +558,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
756
558
|
}
|
|
757
559
|
},
|
|
758
560
|
filter: async (predicate, options = {}) => {
|
|
759
|
-
log(`filter: options=${safeStringify(options)}`);
|
|
561
|
+
log(`filter: options=${(0, safeStringify_1.safeStringify)(options)}`);
|
|
760
562
|
await _autoInit();
|
|
761
563
|
return (0, tableIteration_1.runFilter)({
|
|
762
564
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeStringify = void 0;
|
|
4
|
+
/** JSON.stringify for debug logs: functions become "[Function]" and cycles don't throw. */
|
|
5
|
+
const safeStringify = (obj) => {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.stringify(obj, (_key, value) => {
|
|
8
|
+
if (typeof value === 'function') {
|
|
9
|
+
return '[Function]';
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
return '[unserializable]';
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
exports.safeStringify = safeStringify;
|
package/package.json
CHANGED