@rickcedwhat/playwright-smart-table 6.20.1-next.19d3cd3 → 6.20.1-next.1d11eb1
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.js +58 -62
- package/dist/engine/scanPages.d.ts +58 -0
- package/dist/engine/scanPages.js +46 -0
- package/dist/engine/tableIteration.d.ts +1 -0
- package/dist/engine/tableIteration.js +24 -20
- package/dist/filterEngine.d.ts +4 -0
- package/dist/filterEngine.js +15 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +51 -10
- package/dist/plugins/index.js +14 -6
- package/dist/presets/glide/index.d.ts +1 -1
- package/dist/presets/glide/index.js +2 -2
- package/dist/presets/rdg.d.ts +1 -1
- package/dist/presets/rdg.js +4 -4
- package/dist/smartRow.js +4 -23
- package/dist/strategies/columns.d.ts +5 -9
- package/dist/strategies/columns.js +0 -10
- package/dist/strategies/contentReady.d.ts +9 -2
- package/dist/strategies/contentReady.js +35 -2
- package/dist/strategies/filter.d.ts +0 -4
- package/dist/strategies/filter.js +0 -16
- package/dist/strategies/index.d.ts +16 -15
- package/dist/strategies/index.js +6 -5
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +14 -1
- package/dist/types.d.ts +14 -1
- package/dist/useTable.js +84 -38
- package/dist/utils/resolveCellLocator.d.ts +35 -0
- package/dist/utils/resolveCellLocator.js +52 -0
- package/package.json +1 -1
package/dist/engine/rowFinder.js
CHANGED
|
@@ -7,6 +7,8 @@ const elementTracker_1 = require("../utils/elementTracker");
|
|
|
7
7
|
const sentinel_1 = require("../utils/sentinel");
|
|
8
8
|
const navigationBarrier_1 = require("../utils/navigationBarrier");
|
|
9
9
|
const rowResolution_1 = require("./rowResolution");
|
|
10
|
+
const resolveCellLocator_1 = require("../utils/resolveCellLocator");
|
|
11
|
+
const scanPages_1 = require("./scanPages");
|
|
10
12
|
class RowFinder {
|
|
11
13
|
constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }, advancePage = async () => false) {
|
|
12
14
|
this.rootLocator = rootLocator;
|
|
@@ -56,12 +58,26 @@ class RowFinder {
|
|
|
56
58
|
if (colIndex === undefined)
|
|
57
59
|
continue;
|
|
58
60
|
const override = this.config.columnOverrides[colName];
|
|
59
|
-
const cell =
|
|
61
|
+
const cell = (0, resolveCellLocator_1.resolveCellLocator)({
|
|
62
|
+
config: this.config,
|
|
63
|
+
resolve: this.resolve,
|
|
64
|
+
row: rowLocator,
|
|
65
|
+
root: this.rootLocator,
|
|
66
|
+
columnName: colName,
|
|
67
|
+
columnIndex: colIndex,
|
|
68
|
+
});
|
|
60
69
|
const getCell = (name) => {
|
|
61
70
|
const idx = map.get(name);
|
|
62
71
|
if (idx === undefined)
|
|
63
72
|
throw new Error(`Column "${name}" not found`);
|
|
64
|
-
return
|
|
73
|
+
return (0, resolveCellLocator_1.resolveCellLocator)({
|
|
74
|
+
config: this.config,
|
|
75
|
+
resolve: this.resolve,
|
|
76
|
+
row: rowLocator,
|
|
77
|
+
root: this.rootLocator,
|
|
78
|
+
columnName: name,
|
|
79
|
+
columnIndex: idx,
|
|
80
|
+
});
|
|
65
81
|
};
|
|
66
82
|
const context = {
|
|
67
83
|
row: this.makeSmartRow(rowLocator, map, undefined),
|
|
@@ -126,39 +142,40 @@ class RowFinder {
|
|
|
126
142
|
const map = await this.tableMapper.getMap();
|
|
127
143
|
const allRows = [];
|
|
128
144
|
const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
|
|
129
|
-
|
|
145
|
+
const useBulk = (options === null || options === void 0 ? void 0 : options.useBulkPagination) === true && !!((_c = this.config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNextBulk);
|
|
146
|
+
const hasPagination = !!this.config.strategies.pagination;
|
|
130
147
|
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: starting (maxPages=${effectiveMaxPages}, filters=${JSON.stringify(filtersRecord)})`);
|
|
131
148
|
const tracker = new elementTracker_1.ElementTracker('findRows');
|
|
132
149
|
try {
|
|
133
|
-
await this.waitForTableReady();
|
|
134
150
|
const { domFilters, overrideFilters, syntheticFilters } = this.splitFilters(filtersRecord);
|
|
135
151
|
const hasOverrideFilters = Object.keys(overrideFilters).length > 0;
|
|
136
152
|
const hasSyntheticFilters = Object.keys(syntheticFilters).length > 0;
|
|
137
|
-
const
|
|
153
|
+
const { pagesScanned } = await (0, scanPages_1.scanPages)({
|
|
154
|
+
advancePage: this.advancePage,
|
|
155
|
+
getCurrentPageIndex: () => this.tableState.currentPageIndex,
|
|
156
|
+
config: this.config,
|
|
157
|
+
waitForReady: () => this.waitForTableReady(),
|
|
158
|
+
}, async () => {
|
|
138
159
|
var _a, _b, _c, _d;
|
|
139
160
|
let rowLocators = this.resolve(this.config.rowSelector, this.rootLocator);
|
|
140
161
|
if (Object.keys(domFilters).length > 0) {
|
|
141
162
|
rowLocators = this.filterEngine.applyFilters(rowLocators, domFilters, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, this.rootLocator.page(), this.rootLocator);
|
|
142
163
|
}
|
|
143
|
-
// Get only newly seen matched rows
|
|
144
164
|
const newIndices = await tracker.getUnseenIndices(rowLocators);
|
|
145
165
|
const currentRows = await rowLocators.all();
|
|
146
166
|
let added = 0;
|
|
147
|
-
// One barrier per batch — synchronizes cell navigation across all rows in this page's results
|
|
148
167
|
const useBarrier = this.config.concurrency === 'synchronized' && newIndices.length > 1;
|
|
149
168
|
const barrier = useBarrier ? new navigationBarrier_1.NavigationBarrier(newIndices.length) : undefined;
|
|
150
169
|
for (const idx of newIndices) {
|
|
151
170
|
const resolved = await (0, rowResolution_1.resolveLogicalRowIndex)(currentRows[idx], this.config, () => allRows.length);
|
|
152
171
|
const smartRow = this.makeSmartRow(currentRows[idx], map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
|
|
153
|
-
// findRows skips a still-loading row when no timeout is configured (legacy
|
|
154
|
-
// behavior) — see resolveRowLoading's `noTimeoutAction: 'skip'`.
|
|
155
172
|
const loadingOutcome = await (0, rowResolution_1.resolveRowLoading)(smartRow, this.config.strategies.loading, 'skip', (msg) => (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: ${msg}`));
|
|
156
173
|
if (loadingOutcome !== 'process') {
|
|
157
174
|
barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
|
|
158
175
|
if (loadingOutcome === 'throw') {
|
|
159
176
|
throw new Error(`[SmartTable] Row ${allRows.length} did not finish loading within ${(_b = this.config.strategies.loading) === null || _b === void 0 ? void 0 : _b.rowLoadingTimeout}ms`);
|
|
160
177
|
}
|
|
161
|
-
continue;
|
|
178
|
+
continue;
|
|
162
179
|
}
|
|
163
180
|
if (hasOverrideFilters && !await this.matchesOverrideFilters(currentRows[idx], overrideFilters, map, (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false)) {
|
|
164
181
|
barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
|
|
@@ -172,44 +189,33 @@ class RowFinder {
|
|
|
172
189
|
added++;
|
|
173
190
|
}
|
|
174
191
|
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: page ${this.tableState.currentPageIndex} — ${added} new match(es) (total: ${allRows.length})`);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// still falls back to it.
|
|
184
|
-
const useBulk = (options === null || options === void 0 ? void 0 : options.useBulkPagination) === true && !!((_c = this.config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNextBulk);
|
|
185
|
-
const prevPage = this.tableState.currentPageIndex;
|
|
186
|
-
const didPaginate = await this.advancePage(useBulk);
|
|
187
|
-
if (!didPaginate) {
|
|
188
|
-
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: pagination returned false — final scan`);
|
|
189
|
-
await collectMatches();
|
|
190
|
-
break;
|
|
191
|
-
}
|
|
192
|
-
const pagesJumped = this.tableState.currentPageIndex - prevPage;
|
|
193
|
-
pagesScanned += pagesJumped;
|
|
194
|
-
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: advanced ${pagesJumped} page(s), now at page ${this.tableState.currentPageIndex}`);
|
|
195
|
-
await (0, debugUtils_1.debugDelay)(this.config, 'pagination');
|
|
196
|
-
await collectMatches();
|
|
197
|
-
}
|
|
192
|
+
return 'continue';
|
|
193
|
+
}, {
|
|
194
|
+
maxPages: hasPagination ? effectiveMaxPages : 1,
|
|
195
|
+
useBulk,
|
|
196
|
+
label: 'findRows',
|
|
197
|
+
finalScanOnEof: hasPagination,
|
|
198
|
+
});
|
|
199
|
+
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: done — ${allRows.length} row(s) collected across ${pagesScanned} page(s)`);
|
|
198
200
|
}
|
|
199
201
|
finally {
|
|
200
202
|
await tracker.cleanup(this.rootLocator.page());
|
|
201
203
|
}
|
|
202
|
-
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: done — ${allRows.length} row(s) collected across ${pagesScanned} page(s)`);
|
|
203
204
|
return (0, smartRowArray_1.createSmartRowArray)(allRows);
|
|
204
205
|
}
|
|
205
206
|
async findRowLocator(filters, options = {}) {
|
|
206
207
|
var _a, _b;
|
|
207
208
|
const map = await this.tableMapper.getMap();
|
|
208
209
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages;
|
|
209
|
-
|
|
210
|
+
const useBulk = options.useBulkPagination === true && !!((_b = this.config.strategies.pagination) === null || _b === void 0 ? void 0 : _b.goNextBulk);
|
|
211
|
+
let found = null;
|
|
210
212
|
(0, debugUtils_1.logDebug)(this.config, 'verbose', `Looking for row: ${JSON.stringify(filters)} (MaxPages: ${effectiveMaxPages})`);
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
await (0, scanPages_1.scanPages)({
|
|
214
|
+
advancePage: this.advancePage,
|
|
215
|
+
getCurrentPageIndex: () => this.tableState.currentPageIndex,
|
|
216
|
+
config: this.config,
|
|
217
|
+
waitForReady: () => this.waitForTableReady(),
|
|
218
|
+
}, async () => {
|
|
213
219
|
const allRows = this.resolve(this.config.rowSelector, this.rootLocator);
|
|
214
220
|
const { domFilters, overrideFilters, syntheticFilters } = this.splitFilters(filters);
|
|
215
221
|
const hasPostFilters = Object.keys(overrideFilters).length > 0 || Object.keys(syntheticFilters).length > 0;
|
|
@@ -222,8 +228,10 @@ class RowFinder {
|
|
|
222
228
|
(0, debugUtils_1.logDebug)(this.config, 'verbose', `Page ${this.tableState.currentPageIndex}: Found ${count} matches.`);
|
|
223
229
|
if (count > 1)
|
|
224
230
|
await this.throwIfAmbiguous(await matchedRows.all(), filters, map);
|
|
225
|
-
if (count === 1)
|
|
226
|
-
|
|
231
|
+
if (count === 1) {
|
|
232
|
+
found = matchedRows.first();
|
|
233
|
+
return 'stop';
|
|
234
|
+
}
|
|
227
235
|
}
|
|
228
236
|
else {
|
|
229
237
|
const candidates = await matchedRows.all();
|
|
@@ -239,31 +247,19 @@ class RowFinder {
|
|
|
239
247
|
(0, debugUtils_1.logDebug)(this.config, 'verbose', `Page ${this.tableState.currentPageIndex}: ${postFilterMatches.length} match(es) after post-filter`);
|
|
240
248
|
if (postFilterMatches.length > 1)
|
|
241
249
|
await this.throwIfAmbiguous(postFilterMatches, filters, map);
|
|
242
|
-
if (postFilterMatches.length === 1)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (pagesScanned < effectiveMaxPages) {
|
|
246
|
-
(0, debugUtils_1.logDebug)(this.config, 'verbose', `Page ${this.tableState.currentPageIndex}: Not found. Attempting pagination...`);
|
|
247
|
-
// Default to single-step goNext; bulk is opt-in via useBulkPagination: true (#349).
|
|
248
|
-
// Bulk-by-default made findRow jump past the page holding the target row.
|
|
249
|
-
const useBulk = options.useBulkPagination === true && !!((_b = this.config.strategies.pagination) === null || _b === void 0 ? void 0 : _b.goNextBulk);
|
|
250
|
-
const prevPage = this.tableState.currentPageIndex;
|
|
251
|
-
const didLoadMore = await this.advancePage(useBulk);
|
|
252
|
-
if (didLoadMore) {
|
|
253
|
-
const pagesJumped = this.tableState.currentPageIndex - prevPage;
|
|
254
|
-
pagesScanned += pagesJumped;
|
|
255
|
-
(0, debugUtils_1.logDebug)(this.config, 'verbose', `findRowLocator: advanced ${pagesJumped} page(s), now at page ${this.tableState.currentPageIndex}`);
|
|
256
|
-
await (0, debugUtils_1.debugDelay)(this.config, 'pagination');
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
(0, debugUtils_1.logDebug)(this.config, 'verbose', `Page ${this.tableState.currentPageIndex}: pagination returned false — final scan`);
|
|
261
|
-
pagesScanned = effectiveMaxPages;
|
|
262
|
-
continue;
|
|
250
|
+
if (postFilterMatches.length === 1) {
|
|
251
|
+
found = postFilterMatches[0];
|
|
252
|
+
return 'stop';
|
|
263
253
|
}
|
|
264
254
|
}
|
|
265
|
-
return
|
|
266
|
-
}
|
|
255
|
+
return 'continue';
|
|
256
|
+
}, {
|
|
257
|
+
maxPages: effectiveMaxPages,
|
|
258
|
+
useBulk,
|
|
259
|
+
label: 'findRow',
|
|
260
|
+
finalScanOnEof: true,
|
|
261
|
+
});
|
|
262
|
+
return found;
|
|
267
263
|
}
|
|
268
264
|
resolveRowIndex(rowLocator) {
|
|
269
265
|
// Shared resolver: resolveRowIndex strategy first, else the DOM-position fallback.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { FinalTableConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Page-walk primitive (#427).
|
|
4
|
+
*
|
|
5
|
+
* Owns the shared pagination shell used by map/forEach/filter, findRow(s),
|
|
6
|
+
* countRows, and the public async iterator:
|
|
7
|
+
* - `maxPages` budget
|
|
8
|
+
* - `advancePage(useBulk)` with bulk page-jump accounting
|
|
9
|
+
* - EOF final scan (one more `onPage` after `advancePage` returns false)
|
|
10
|
+
* - optional `waitForReady` before each page
|
|
11
|
+
*
|
|
12
|
+
* Per-page collection (overscan, dedupe, loading, filters) stays in the caller
|
|
13
|
+
* so find vs map can keep their different row policies.
|
|
14
|
+
*
|
|
15
|
+
* ## Feature matrix (public APIs → this shell)
|
|
16
|
+
*
|
|
17
|
+
* | API | final-scan | bulk opt-in | waitForReady | overscan/dedupe/loading |
|
|
18
|
+
* |-----|------------|-------------|--------------|-------------------------|
|
|
19
|
+
* | map / forEach / filter | yes | options.useBulkPagination | via caller | in runMap |
|
|
20
|
+
* | findRows | yes | options.useBulkPagination | yes | findRows policy (skip loading) |
|
|
21
|
+
* | findRow | yes | options.useBulkPagination | yes | DOM/post filters only |
|
|
22
|
+
* | countRows | yes | no (single-step) | yes | count-only / post filters |
|
|
23
|
+
* | async iterator | yes | no | via runMap path | same as map |
|
|
24
|
+
* | findRowByIndex | no* | no | n/a | scroll/search, not a full scan |
|
|
25
|
+
* | bringIntoView | n/a | path planner | n/a | not a page scan |
|
|
26
|
+
*
|
|
27
|
+
* \* findRowByIndex stops when the target index is mounted; it does not collect rows.
|
|
28
|
+
*/
|
|
29
|
+
export type ScanDecision = 'continue' | 'stop';
|
|
30
|
+
export interface ScanPageContext {
|
|
31
|
+
/** 1-based count of pages visited so far (includes the current page). */
|
|
32
|
+
pagesScanned: number;
|
|
33
|
+
/** `tableState.currentPageIndex` at the start of this page callback. */
|
|
34
|
+
pageIndex: number;
|
|
35
|
+
/** True when this is the post-EOF extra pass after `advancePage` returned false. */
|
|
36
|
+
isFinalScan: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface ScanPagesEnv {
|
|
39
|
+
advancePage: (useBulk: boolean) => Promise<boolean>;
|
|
40
|
+
getCurrentPageIndex: () => number;
|
|
41
|
+
config: FinalTableConfig;
|
|
42
|
+
/** Optional gate before each page (e.g. `isTableLoading` poll). */
|
|
43
|
+
waitForReady?: () => Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export interface ScanPagesOptions {
|
|
46
|
+
maxPages: number;
|
|
47
|
+
/** Prefer goNextBulk when the advance helper supports it. @default false */
|
|
48
|
+
useBulk?: boolean;
|
|
49
|
+
label?: string;
|
|
50
|
+
/**
|
|
51
|
+
* When `advancePage` returns false, invoke `onPage` once more before stopping.
|
|
52
|
+
* Matches runMap / findRows EOF behavior. @default true
|
|
53
|
+
*/
|
|
54
|
+
finalScanOnEof?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export declare function scanPages(env: ScanPagesEnv, onPage: (ctx: ScanPageContext) => Promise<ScanDecision>, options: ScanPagesOptions): Promise<{
|
|
57
|
+
pagesScanned: number;
|
|
58
|
+
}>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scanPages = scanPages;
|
|
4
|
+
const debugUtils_1 = require("../utils/debugUtils");
|
|
5
|
+
async function scanPages(env, onPage, options) {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
const maxPages = options.maxPages;
|
|
8
|
+
const useBulk = (_a = options.useBulk) !== null && _a !== void 0 ? _a : false;
|
|
9
|
+
const finalScanOnEof = options.finalScanOnEof !== false;
|
|
10
|
+
const label = (_b = options.label) !== null && _b !== void 0 ? _b : 'scanPages';
|
|
11
|
+
let pagesScanned = 1;
|
|
12
|
+
let reachedEnd = false;
|
|
13
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: starting (maxPages=${maxPages}, useBulk=${useBulk}, finalScanOnEof=${finalScanOnEof})`);
|
|
14
|
+
while (true) {
|
|
15
|
+
if (env.waitForReady) {
|
|
16
|
+
await env.waitForReady();
|
|
17
|
+
}
|
|
18
|
+
const decision = await onPage({
|
|
19
|
+
pagesScanned,
|
|
20
|
+
pageIndex: env.getCurrentPageIndex(),
|
|
21
|
+
isFinalScan: reachedEnd,
|
|
22
|
+
});
|
|
23
|
+
if (decision === 'stop') {
|
|
24
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: onPage returned stop at page ${env.getCurrentPageIndex()}`);
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
if (pagesScanned >= maxPages || reachedEnd) {
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
const prevPage = env.getCurrentPageIndex();
|
|
31
|
+
const didAdvance = await env.advancePage(useBulk);
|
|
32
|
+
if (!didAdvance) {
|
|
33
|
+
if (finalScanOnEof) {
|
|
34
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: pagination returned false — final scan`);
|
|
35
|
+
reachedEnd = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
const pagesJumped = env.getCurrentPageIndex() - prevPage;
|
|
41
|
+
pagesScanned += pagesJumped > 0 ? pagesJumped : 1;
|
|
42
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: advanced ${pagesJumped > 0 ? pagesJumped : 1} page(s), now at page ${env.getCurrentPageIndex()} (scanned=${pagesScanned})`);
|
|
43
|
+
}
|
|
44
|
+
(0, debugUtils_1.logDebug)(env.config, 'verbose', `${label}: done — ${pagesScanned} page(s) scanned`);
|
|
45
|
+
return { pagesScanned };
|
|
46
|
+
}
|
|
@@ -20,6 +20,7 @@ export declare function runForEach<T>(env: TableIterationEnv<T>, callback: (ctx:
|
|
|
20
20
|
/**
|
|
21
21
|
* Row iteration for map (and forEach/filter via label).
|
|
22
22
|
* Concurrency: `parallel` | `synchronized` | `sequential` (see RowIterationOptions).
|
|
23
|
+
* Page walking is owned by {@link scanPages} (#427).
|
|
23
24
|
*/
|
|
24
25
|
export declare function runMap<T, R>(env: TableIterationEnv<T>, callback: (ctx: RowIterationContext<T>) => R | Promise<R>, options?: RowIterationOptions, label?: string): Promise<R[]>;
|
|
25
26
|
/** Filter via map; returns matched rows as {@link SmartRowArray}. */
|
|
@@ -8,6 +8,7 @@ const debugUtils_1 = require("../utils/debugUtils");
|
|
|
8
8
|
const navigationBarrier_1 = require("../utils/navigationBarrier");
|
|
9
9
|
const mutex_1 = require("../utils/mutex");
|
|
10
10
|
const rowResolution_1 = require("./rowResolution");
|
|
11
|
+
const scanPages_1 = require("./scanPages");
|
|
11
12
|
function log(config, msg) {
|
|
12
13
|
(0, debugUtils_1.logDebug)(config, 'verbose', msg);
|
|
13
14
|
}
|
|
@@ -19,9 +20,10 @@ async function runForEach(env, callback, options = {}) {
|
|
|
19
20
|
/**
|
|
20
21
|
* Row iteration for map (and forEach/filter via label).
|
|
21
22
|
* Concurrency: `parallel` | `synchronized` | `sequential` (see RowIterationOptions).
|
|
23
|
+
* Page walking is owned by {@link scanPages} (#427).
|
|
22
24
|
*/
|
|
23
25
|
async function runMap(env, callback, options = {}, label = 'map') {
|
|
24
|
-
var _a, _b, _c, _d, _e
|
|
26
|
+
var _a, _b, _c, _d, _e;
|
|
25
27
|
const map = env.getMap();
|
|
26
28
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : env.config.maxPages;
|
|
27
29
|
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : env.config.strategies.dedupe;
|
|
@@ -42,8 +44,6 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
42
44
|
let rowIndex = 0;
|
|
43
45
|
let stopped = false;
|
|
44
46
|
let stoppedIndex = Infinity;
|
|
45
|
-
let pagesScanned = 1;
|
|
46
|
-
let reachedEnd = false;
|
|
47
47
|
const stop = (idx) => {
|
|
48
48
|
if (!stopped) {
|
|
49
49
|
log(env.config, `${label}: stop() called at row ${idx} — halting`);
|
|
@@ -51,7 +51,14 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
51
51
|
stoppedIndex = idx;
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
-
|
|
54
|
+
const { pagesScanned } = await (0, scanPages_1.scanPages)({
|
|
55
|
+
advancePage: env.advancePage,
|
|
56
|
+
getCurrentPageIndex: env.getCurrentPageIndex,
|
|
57
|
+
config: env.config,
|
|
58
|
+
}, async ({ pagesScanned: pageNum, isFinalScan }) => {
|
|
59
|
+
var _a;
|
|
60
|
+
if (stopped)
|
|
61
|
+
return 'stop';
|
|
55
62
|
const rowLocators = env.getRowLocators();
|
|
56
63
|
const allIndices = await tracker.peekUnseenIndices(rowLocators);
|
|
57
64
|
const pageRows = await rowLocators.all();
|
|
@@ -68,8 +75,8 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
68
75
|
// #417 follow-up: skip viewport filtering on the final scan — there are no more pages
|
|
69
76
|
// to defer to, so collect all remaining unseen rows regardless of visibility.
|
|
70
77
|
let candidateIndices = allIndices;
|
|
71
|
-
const getVisibleRowIndices = (
|
|
72
|
-
if (getVisibleRowIndices && !
|
|
78
|
+
const getVisibleRowIndices = (_a = env.config.strategies.viewport) === null || _a === void 0 ? void 0 : _a.getVisibleRowIndices;
|
|
79
|
+
if (getVisibleRowIndices && !isFinalScan) {
|
|
73
80
|
const visible = new Set(await getVisibleRowIndices(env.getContext()));
|
|
74
81
|
const minVisible = visible.size > 0 ? Math.min(...visible) : Infinity;
|
|
75
82
|
candidateIndices = allIndices.filter(idx => visible.has(idx) || idx < minVisible);
|
|
@@ -86,10 +93,10 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
86
93
|
await tracker.commitIndices(rowLocators, newIndices);
|
|
87
94
|
const batchSize = newIndices.length;
|
|
88
95
|
if (batchSize === 0) {
|
|
89
|
-
log(env.config, `${label}: page ${
|
|
96
|
+
log(env.config, `${label}: page ${pageNum} — no new row(s) found`);
|
|
90
97
|
}
|
|
91
98
|
else {
|
|
92
|
-
log(env.config, `${label}: scanning page ${
|
|
99
|
+
log(env.config, `${label}: scanning page ${pageNum} — ${batchSize} new row(s)`);
|
|
93
100
|
const barrier = useBarrier ? new navigationBarrier_1.NavigationBarrier(batchSize) : undefined;
|
|
94
101
|
const actionMutex = useMutex ? new mutex_1.Mutex() : null;
|
|
95
102
|
// `index` (ctx.index) is the enumeration counter — the order rows are visited,
|
|
@@ -169,8 +176,8 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
169
176
|
}
|
|
170
177
|
}
|
|
171
178
|
else {
|
|
172
|
-
const
|
|
173
|
-
pageResults.push(...
|
|
179
|
+
const batchResults = await Promise.all(smartRows.map((row, i) => processRow(row, positionBase + i)));
|
|
180
|
+
pageResults.push(...batchResults);
|
|
174
181
|
}
|
|
175
182
|
for (let i = 0; i < pageResults.length; i++) {
|
|
176
183
|
if (positionBase + i > stoppedIndex)
|
|
@@ -181,16 +188,13 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
181
188
|
}
|
|
182
189
|
rowIndex += batchSize;
|
|
183
190
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
pagesScanned++;
|
|
193
|
-
}
|
|
191
|
+
return stopped ? 'stop' : 'continue';
|
|
192
|
+
}, {
|
|
193
|
+
maxPages: effectiveMaxPages,
|
|
194
|
+
useBulk,
|
|
195
|
+
label,
|
|
196
|
+
finalScanOnEof: true,
|
|
197
|
+
});
|
|
194
198
|
log(env.config, `${label}: complete — ${results.length} result(s) across ${pagesScanned} page(s)`);
|
|
195
199
|
}
|
|
196
200
|
finally {
|
package/dist/filterEngine.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export declare class FilterEngine {
|
|
|
10
10
|
* Note: `rootLocator` is optional for backward compatibility in call sites that already
|
|
11
11
|
* pass only the page. When strategies.filter is present we construct a TableContext
|
|
12
12
|
* using the provided `rootLocator`.
|
|
13
|
+
*
|
|
14
|
+
* When `strategies.getCellLocator` is set, cells are resolved through that strategy
|
|
15
|
+
* (via a `:scope` row stub) so column-virtualized grids that use `aria-colindex` / etc.
|
|
16
|
+
* are not filtered with fragile `.nth(colIndex)` DOM order.
|
|
13
17
|
*/
|
|
14
18
|
applyFilters(baseRows: Locator, filters: Record<string, FilterValue>, map: Map<string, number>, exact: boolean, page: Page, rootLocator?: Locator): Locator;
|
|
15
19
|
}
|
package/dist/filterEngine.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FilterEngine = void 0;
|
|
4
4
|
const stringUtils_1 = require("./utils/stringUtils");
|
|
5
|
+
const resolveCellLocator_1 = require("./utils/resolveCellLocator");
|
|
5
6
|
class FilterEngine {
|
|
6
7
|
constructor(config, resolve) {
|
|
7
8
|
this.config = config;
|
|
@@ -13,6 +14,10 @@ class FilterEngine {
|
|
|
13
14
|
* Note: `rootLocator` is optional for backward compatibility in call sites that already
|
|
14
15
|
* pass only the page. When strategies.filter is present we construct a TableContext
|
|
15
16
|
* using the provided `rootLocator`.
|
|
17
|
+
*
|
|
18
|
+
* When `strategies.getCellLocator` is set, cells are resolved through that strategy
|
|
19
|
+
* (via a `:scope` row stub) so column-virtualized grids that use `aria-colindex` / etc.
|
|
20
|
+
* are not filtered with fragile `.nth(colIndex)` DOM order.
|
|
16
21
|
*/
|
|
17
22
|
applyFilters(baseRows, filters, map, exact, page, rootLocator) {
|
|
18
23
|
var _a;
|
|
@@ -41,10 +46,16 @@ class FilterEngine {
|
|
|
41
46
|
});
|
|
42
47
|
continue;
|
|
43
48
|
}
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
// Prefer getCellLocator (column-virtualized presets); else cellSelector.nth.
|
|
50
|
+
// Playwright re-bases the `has` locator into each candidate row.
|
|
51
|
+
const targetCell = (0, resolveCellLocator_1.resolveCellLocatorForFilter)({
|
|
52
|
+
config: this.config,
|
|
53
|
+
resolve: this.resolve,
|
|
54
|
+
page,
|
|
55
|
+
root: rootLocator,
|
|
56
|
+
columnName: colName,
|
|
57
|
+
columnIndex: colIndex,
|
|
58
|
+
});
|
|
48
59
|
if (typeof filterVal === 'function') {
|
|
49
60
|
// Locator-based filter: (cell) => cell.locator(...)
|
|
50
61
|
filtered = filtered.filter({
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export { PLAYWRIGHT_SMART_TABLE_VERSION } from './packageVersion';
|
|
2
2
|
export { useTable } from './useTable';
|
|
3
|
-
export type { TableConfig, TableResult, SmartRow, SmartCell, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, SyntheticColumnDef, ColumnOverride, ColumnOverrideReadContext, } from './types';
|
|
3
|
+
export type { TableConfig, TableResult, SmartRow, SmartCell, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, DebugConfig, SyntheticColumnDef, ColumnOverride, ColumnOverrideReadContext, TableStrategies, LoadingStrategy, ViewportStrategy, FilterStrategy, NavigationPrimitives, DedupeStrategy, ContentReadyStrategy, FillStrategy, PaginationStrategy, HeaderStrategy, } from './types';
|
|
4
4
|
export { Strategies } from './strategies';
|
|
5
5
|
export * as presets from './presets';
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated Use `presets` instead (`presets.muiDataGrid`, `presets.rdg`, `presets.glide`, …).
|
|
8
|
+
* `Plugins` will be removed in v7.0.0.
|
|
9
|
+
* Note: `Plugins.MUI` is the **DataGrid** preset only — for MUI Table use `presets.muiTable`.
|
|
10
|
+
*/
|
|
7
11
|
export { Plugins } from './plugins';
|
|
8
12
|
export { mergeTableConfig } from './utils/mergeTableConfig';
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,11 @@ Object.defineProperty(exports, "useTable", { enumerable: true, get: function ()
|
|
|
42
42
|
var strategies_1 = require("./strategies");
|
|
43
43
|
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
44
44
|
exports.presets = __importStar(require("./presets"));
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use `presets` instead (`presets.muiDataGrid`, `presets.rdg`, `presets.glide`, …).
|
|
47
|
+
* `Plugins` will be removed in v7.0.0.
|
|
48
|
+
* Note: `Plugins.MUI` is the **DataGrid** preset only — for MUI Table use `presets.muiTable`.
|
|
49
|
+
*/
|
|
46
50
|
var plugins_1 = require("./plugins");
|
|
47
51
|
Object.defineProperty(exports, "Plugins", { enumerable: true, get: function () { return plugins_1.Plugins; } });
|
|
48
52
|
var mergeTableConfig_1 = require("./utils/mergeTableConfig");
|
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.20.1-next.
|
|
6
|
+
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.20.1-next.1d11eb1";
|
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.20.1-next.
|
|
9
|
+
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.20.1-next.1d11eb1";
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -1,13 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @deprecated Use `presets` instead. Plugins will be removed in v7.0.0.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* - Plugins.
|
|
3
|
+
*
|
|
4
|
+
* Compatibility shim over official presets:
|
|
5
|
+
* - `Plugins.MUI` → `presets.muiDataGrid` (**DataGrid only** — not `presets.muiTable`)
|
|
6
|
+
* - `Plugins.RDG` → `presets.rdg`
|
|
7
|
+
* - `Plugins.Glide` → `presets.glide`
|
|
8
|
+
*
|
|
9
|
+
* Prefer: `useTable(loc, { ...presets.muiDataGrid, maxPages: 5 })`.
|
|
10
|
+
* Strategies only: `useTable(loc, { rowSelector: '...', strategies: presets.muiDataGrid.strategies })`.
|
|
6
11
|
*/
|
|
7
12
|
export declare const Plugins: {
|
|
8
13
|
/** @deprecated Use `presets.rdg` */
|
|
9
14
|
RDG: {
|
|
10
|
-
Strategies:
|
|
15
|
+
Strategies: {
|
|
16
|
+
header: (context: import("..").TableContext) => Promise<string[]>;
|
|
17
|
+
getCellLocator: ({ row, columnIndex }: {
|
|
18
|
+
row: import("@playwright/test").Locator;
|
|
19
|
+
columnIndex: number;
|
|
20
|
+
}) => import("@playwright/test").Locator;
|
|
21
|
+
navigation: {
|
|
22
|
+
goRight: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
23
|
+
goLeft: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
24
|
+
goDown: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
25
|
+
goUp: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
26
|
+
goHome: ({ root }: import("..").StrategyContext) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
pagination: import("..").PaginationPrimitives;
|
|
29
|
+
};
|
|
11
30
|
headerSelector?: string | ((root: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
12
31
|
rowSelector?: string | undefined;
|
|
13
32
|
cellSelector?: string | ((row: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
@@ -22,14 +41,33 @@ export declare const Plugins: {
|
|
|
22
41
|
autoScroll?: boolean | undefined;
|
|
23
42
|
debug?: import("..").DebugConfig | undefined;
|
|
24
43
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
25
|
-
strategies?: import("
|
|
44
|
+
strategies?: import("..").TableStrategies | undefined;
|
|
26
45
|
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
27
46
|
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
28
47
|
emptyState?: import("@playwright/test").Locator | undefined;
|
|
29
48
|
};
|
|
30
49
|
/** @deprecated Use `presets.glide` */
|
|
31
50
|
Glide: {
|
|
32
|
-
Strategies:
|
|
51
|
+
Strategies: {
|
|
52
|
+
fillSimple: import("..").FillStrategy;
|
|
53
|
+
fill: import("..").FillStrategy;
|
|
54
|
+
pagination: import("..").PaginationPrimitives;
|
|
55
|
+
header: (context: import("..").StrategyContext, options?: {
|
|
56
|
+
limit?: number;
|
|
57
|
+
selector?: string;
|
|
58
|
+
scrollAmount?: number;
|
|
59
|
+
}) => Promise<string[]>;
|
|
60
|
+
viewport: import("..").ViewportStrategy;
|
|
61
|
+
loading: {
|
|
62
|
+
isHeaderLoading: () => Promise<boolean>;
|
|
63
|
+
};
|
|
64
|
+
getCellLocator: ({ row, columnIndex }: any) => any;
|
|
65
|
+
getActiveCell: ({ page }: any) => Promise<{
|
|
66
|
+
rowIndex: number;
|
|
67
|
+
columnIndex: number;
|
|
68
|
+
locator: any;
|
|
69
|
+
} | null>;
|
|
70
|
+
};
|
|
33
71
|
headerSelector?: string | ((root: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
34
72
|
rowSelector?: string | undefined;
|
|
35
73
|
cellSelector?: string | ((row: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
@@ -44,14 +82,17 @@ export declare const Plugins: {
|
|
|
44
82
|
autoScroll?: boolean | undefined;
|
|
45
83
|
debug?: import("..").DebugConfig | undefined;
|
|
46
84
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
47
|
-
strategies?: import("
|
|
85
|
+
strategies?: import("..").TableStrategies | undefined;
|
|
48
86
|
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
49
87
|
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
50
88
|
emptyState?: import("@playwright/test").Locator | undefined;
|
|
51
89
|
};
|
|
52
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* @deprecated Use `presets.muiDataGrid`.
|
|
92
|
+
* DataGrid only — for MUI `<Table>` use `presets.muiTable`.
|
|
93
|
+
*/
|
|
53
94
|
MUI: {
|
|
54
|
-
Strategies: import("
|
|
95
|
+
Strategies: import("..").TableStrategies | undefined;
|
|
55
96
|
headerSelector?: string | ((root: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
56
97
|
rowSelector?: string | undefined;
|
|
57
98
|
cellSelector?: string | ((row: import("@playwright/test").Locator) => import("@playwright/test").Locator) | undefined;
|
|
@@ -66,7 +107,7 @@ export declare const Plugins: {
|
|
|
66
107
|
autoScroll?: boolean | undefined;
|
|
67
108
|
debug?: import("..").DebugConfig | undefined;
|
|
68
109
|
onReset?: ((context: import("..").TableContext) => Promise<void>) | undefined;
|
|
69
|
-
strategies?: import("
|
|
110
|
+
strategies?: import("..").TableStrategies | undefined;
|
|
70
111
|
columnOverrides?: Partial<Record<string | number | symbol, import("..").ColumnOverride<any>>> | undefined;
|
|
71
112
|
syntheticColumns?: Record<string, import("..").SyntheticColumnDef<any>> | undefined;
|
|
72
113
|
emptyState?: import("@playwright/test").Locator | undefined;
|