@rickcedwhat/playwright-smart-table 6.7.0 → 6.7.3
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/README.md +26 -0
- package/dist/engine/rowFinder.d.ts +1 -4
- package/dist/engine/rowFinder.js +72 -88
- package/dist/engine/tableMapper.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/plugins.d.ts +3 -1
- package/dist/smartRow.js +56 -22
- package/dist/strategies/glide.d.ts +3 -0
- package/dist/strategies/glide.js +3 -0
- package/dist/strategies/index.d.ts +6 -4
- package/dist/strategies/pagination.d.ts +2 -0
- package/dist/strategies/pagination.js +5 -5
- package/dist/strategies/rdg.d.ts +0 -5
- package/dist/strategies/rdg.js +1 -18
- package/dist/strategies/sorting.js +4 -9
- package/dist/strategies/validation.js +5 -2
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +50 -10
- package/dist/types.d.ts +48 -11
- package/dist/useTable.js +186 -174
- package/dist/utils/elementTracker.d.ts +15 -0
- package/dist/utils/elementTracker.js +60 -0
- package/package.json +7 -2
package/dist/useTable.js
CHANGED
|
@@ -33,6 +33,7 @@ const tableMapper_1 = require("./engine/tableMapper");
|
|
|
33
33
|
const rowFinder_1 = require("./engine/rowFinder");
|
|
34
34
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
35
35
|
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
36
|
+
const elementTracker_1 = require("./utils/elementTracker");
|
|
36
37
|
/**
|
|
37
38
|
* Main hook to interact with a table.
|
|
38
39
|
*/
|
|
@@ -121,9 +122,24 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
121
122
|
console.log(`⚠️ Throwing error to display [${promptName}] cleanly...`);
|
|
122
123
|
throw new Error(finalPrompt);
|
|
123
124
|
});
|
|
124
|
-
const
|
|
125
|
+
const _autoInit = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
126
|
yield tableMapper.getMap();
|
|
126
127
|
});
|
|
128
|
+
const _advancePage = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
129
|
+
var _a;
|
|
130
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell, getHeaders: result.getHeaders, scrollToColumn: result.scrollToColumn };
|
|
131
|
+
let advanced;
|
|
132
|
+
if (typeof config.strategies.pagination === 'function') {
|
|
133
|
+
advanced = !!(yield config.strategies.pagination(context));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
advanced = !!(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
137
|
+
}
|
|
138
|
+
if (advanced) {
|
|
139
|
+
tableState.currentPageIndex++;
|
|
140
|
+
}
|
|
141
|
+
return advanced;
|
|
142
|
+
});
|
|
127
143
|
const result = {
|
|
128
144
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
129
145
|
set currentPageIndex(v) { tableState.currentPageIndex = v; },
|
|
@@ -160,7 +176,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
160
176
|
reset: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
177
|
var _a;
|
|
162
178
|
log("Resetting table...");
|
|
163
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
179
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
|
|
164
180
|
yield config.onReset(context);
|
|
165
181
|
if (typeof config.strategies.pagination !== 'function' && ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst)) {
|
|
166
182
|
log("Auto-navigating to first page...");
|
|
@@ -172,7 +188,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
172
188
|
_hasPaginated = false;
|
|
173
189
|
tableState.currentPageIndex = 0;
|
|
174
190
|
tableMapper.clear();
|
|
175
|
-
log("Table reset complete.");
|
|
191
|
+
log("Table reset complete. Calling autoInit to restore state.");
|
|
192
|
+
yield _autoInit();
|
|
176
193
|
}),
|
|
177
194
|
revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
178
195
|
log("Revalidating table structure...");
|
|
@@ -182,16 +199,16 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
182
199
|
getRow: (filters, options = { exact: false }) => {
|
|
183
200
|
const map = tableMapper.getMapSync();
|
|
184
201
|
if (!map)
|
|
185
|
-
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.
|
|
202
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.findRows() which auto-initialize.');
|
|
186
203
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
187
204
|
const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
|
|
188
205
|
const rowLocator = matchedRows.first();
|
|
189
206
|
return _makeSmart(rowLocator, map, 0); // fallback index 0
|
|
190
207
|
},
|
|
191
|
-
getRowByIndex: (index
|
|
208
|
+
getRowByIndex: (index) => {
|
|
192
209
|
const map = tableMapper.getMapSync();
|
|
193
210
|
if (!map)
|
|
194
|
-
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.
|
|
211
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.findRows() which auto-initialize.');
|
|
195
212
|
const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
|
|
196
213
|
return _makeSmart(rowLocator, map, index);
|
|
197
214
|
},
|
|
@@ -208,7 +225,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
208
225
|
sorting: {
|
|
209
226
|
apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
210
227
|
var _a;
|
|
211
|
-
yield
|
|
228
|
+
yield _autoInit();
|
|
212
229
|
if (!config.strategies.sorting)
|
|
213
230
|
throw new Error('No sorting strategy has been configured.');
|
|
214
231
|
log(`Applying sort for column "${columnName}" (${direction})`);
|
|
@@ -237,7 +254,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
237
254
|
throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
|
|
238
255
|
}),
|
|
239
256
|
getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
240
|
-
yield
|
|
257
|
+
yield _autoInit();
|
|
241
258
|
if (!config.strategies.sorting)
|
|
242
259
|
throw new Error('No sorting strategy has been configured.');
|
|
243
260
|
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
|
|
@@ -247,216 +264,211 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
247
264
|
// ─── Shared async row iterator ───────────────────────────────────────────
|
|
248
265
|
[Symbol.asyncIterator]() {
|
|
249
266
|
return __asyncGenerator(this, arguments, function* _a() {
|
|
250
|
-
|
|
251
|
-
yield __await(_ensureInitialized());
|
|
267
|
+
yield __await(_autoInit());
|
|
252
268
|
const map = tableMapper.getMapSync();
|
|
253
269
|
const effectiveMaxPages = config.maxPages;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
270
|
+
const tracker = new elementTracker_1.ElementTracker('iterator');
|
|
271
|
+
try {
|
|
272
|
+
let rowIndex = 0;
|
|
273
|
+
let pagesScanned = 1;
|
|
274
|
+
while (true) {
|
|
275
|
+
const rowLocators = resolve(config.rowSelector, rootLocator);
|
|
276
|
+
const newIndices = yield __await(tracker.getUnseenIndices(rowLocators));
|
|
277
|
+
const pageRows = yield __await(rowLocators.all());
|
|
278
|
+
for (const idx of newIndices) {
|
|
279
|
+
yield yield __await({ row: _makeSmart(pageRows[idx], map, rowIndex), rowIndex });
|
|
280
|
+
rowIndex++;
|
|
281
|
+
}
|
|
282
|
+
if (pagesScanned >= effectiveMaxPages)
|
|
283
|
+
break;
|
|
284
|
+
if (!(yield __await(_advancePage())))
|
|
285
|
+
break;
|
|
286
|
+
pagesScanned++;
|
|
271
287
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
pagesScanned++;
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
yield __await(tracker.cleanup(rootLocator.page()));
|
|
276
291
|
}
|
|
277
292
|
});
|
|
278
293
|
},
|
|
279
294
|
// ─── Private row-iteration engine ────────────────────────────────────────
|
|
280
295
|
forEach: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
|
|
281
|
-
var _a, _b, _c
|
|
282
|
-
yield
|
|
296
|
+
var _a, _b, _c;
|
|
297
|
+
yield _autoInit();
|
|
283
298
|
const map = tableMapper.getMapSync();
|
|
284
299
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
285
300
|
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
|
|
286
301
|
const dedupeKeys = dedupeStrategy ? new Set() : null;
|
|
287
302
|
const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (
|
|
303
|
+
const tracker = new elementTracker_1.ElementTracker('forEach');
|
|
304
|
+
try {
|
|
305
|
+
let rowIndex = 0;
|
|
306
|
+
let stopped = false;
|
|
307
|
+
let pagesScanned = 1;
|
|
308
|
+
const stop = () => { stopped = true; };
|
|
309
|
+
while (!stopped) {
|
|
310
|
+
const rowLocators = resolve(config.rowSelector, rootLocator);
|
|
311
|
+
const newIndices = yield tracker.getUnseenIndices(rowLocators);
|
|
312
|
+
const pageRows = yield rowLocators.all();
|
|
313
|
+
const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i));
|
|
314
|
+
if (parallel) {
|
|
315
|
+
yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
|
|
316
|
+
if (stopped)
|
|
302
317
|
return;
|
|
303
|
-
dedupeKeys
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (
|
|
315
|
-
|
|
316
|
-
dedupeKeys
|
|
318
|
+
if (dedupeKeys) {
|
|
319
|
+
const key = yield dedupeStrategy(row);
|
|
320
|
+
if (dedupeKeys.has(key))
|
|
321
|
+
return;
|
|
322
|
+
dedupeKeys.add(key);
|
|
323
|
+
}
|
|
324
|
+
yield callback({ row, rowIndex: row.rowIndex, stop });
|
|
325
|
+
})));
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
for (const row of smartRows) {
|
|
329
|
+
if (stopped)
|
|
330
|
+
break;
|
|
331
|
+
if (dedupeKeys) {
|
|
332
|
+
const key = yield dedupeStrategy(row);
|
|
333
|
+
if (dedupeKeys.has(key))
|
|
334
|
+
continue;
|
|
335
|
+
dedupeKeys.add(key);
|
|
336
|
+
}
|
|
337
|
+
yield callback({ row, rowIndex: row.rowIndex, stop });
|
|
317
338
|
}
|
|
318
|
-
yield callback({ row, rowIndex: row.rowIndex, stop });
|
|
319
339
|
}
|
|
340
|
+
rowIndex += smartRows.length;
|
|
341
|
+
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
342
|
+
break;
|
|
343
|
+
if (!(yield _advancePage()))
|
|
344
|
+
break;
|
|
345
|
+
pagesScanned++;
|
|
320
346
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
325
|
-
let advanced;
|
|
326
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
327
|
-
advanced = !!(yield config.strategies.pagination(context));
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
advanced = !!(((_d = config.strategies.pagination) === null || _d === void 0 ? void 0 : _d.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
331
|
-
}
|
|
332
|
-
if (!advanced)
|
|
333
|
-
break;
|
|
334
|
-
tableState.currentPageIndex++;
|
|
335
|
-
pagesScanned++;
|
|
347
|
+
}
|
|
348
|
+
finally {
|
|
349
|
+
yield tracker.cleanup(rootLocator.page());
|
|
336
350
|
}
|
|
337
351
|
}),
|
|
338
352
|
map: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
|
|
339
|
-
var _a, _b, _c
|
|
340
|
-
yield
|
|
353
|
+
var _a, _b, _c;
|
|
354
|
+
yield _autoInit();
|
|
341
355
|
const map = tableMapper.getMapSync();
|
|
342
356
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
343
357
|
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
|
|
344
358
|
const dedupeKeys = dedupeStrategy ? new Set() : null;
|
|
345
359
|
const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : true;
|
|
360
|
+
const tracker = new elementTracker_1.ElementTracker('map');
|
|
346
361
|
const results = [];
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
+
try {
|
|
363
|
+
let rowIndex = 0;
|
|
364
|
+
let stopped = false;
|
|
365
|
+
let pagesScanned = 1;
|
|
366
|
+
const stop = () => { stopped = true; };
|
|
367
|
+
while (!stopped) {
|
|
368
|
+
const rowLocators = resolve(config.rowSelector, rootLocator);
|
|
369
|
+
const newIndices = yield tracker.getUnseenIndices(rowLocators);
|
|
370
|
+
const pageRows = yield rowLocators.all();
|
|
371
|
+
const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i));
|
|
372
|
+
if (parallel) {
|
|
373
|
+
const SKIP = Symbol('skip');
|
|
374
|
+
const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
|
|
375
|
+
if (dedupeKeys) {
|
|
376
|
+
const key = yield dedupeStrategy(row);
|
|
377
|
+
if (dedupeKeys.has(key))
|
|
378
|
+
return SKIP;
|
|
379
|
+
dedupeKeys.add(key);
|
|
380
|
+
}
|
|
381
|
+
return callback({ row, rowIndex: row.rowIndex, stop });
|
|
382
|
+
})));
|
|
383
|
+
for (const r of pageResults) {
|
|
384
|
+
if (r !== SKIP)
|
|
385
|
+
results.push(r);
|
|
362
386
|
}
|
|
363
|
-
return callback({ row, rowIndex: row.rowIndex, stop });
|
|
364
|
-
})));
|
|
365
|
-
for (const r of pageResults) {
|
|
366
|
-
if (r !== SKIP)
|
|
367
|
-
results.push(r);
|
|
368
387
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
388
|
+
else {
|
|
389
|
+
for (const row of smartRows) {
|
|
390
|
+
if (stopped)
|
|
391
|
+
break;
|
|
392
|
+
if (dedupeKeys) {
|
|
393
|
+
const key = yield dedupeStrategy(row);
|
|
394
|
+
if (dedupeKeys.has(key))
|
|
395
|
+
continue;
|
|
396
|
+
dedupeKeys.add(key);
|
|
397
|
+
}
|
|
398
|
+
results.push(yield callback({ row, rowIndex: row.rowIndex, stop }));
|
|
379
399
|
}
|
|
380
|
-
results.push(yield callback({ row, rowIndex: row.rowIndex, stop }));
|
|
381
400
|
}
|
|
401
|
+
rowIndex += smartRows.length;
|
|
402
|
+
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
403
|
+
break;
|
|
404
|
+
if (!(yield _advancePage()))
|
|
405
|
+
break;
|
|
406
|
+
pagesScanned++;
|
|
382
407
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
387
|
-
let advanced;
|
|
388
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
389
|
-
advanced = !!(yield config.strategies.pagination(context));
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
advanced = !!(((_d = config.strategies.pagination) === null || _d === void 0 ? void 0 : _d.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
393
|
-
}
|
|
394
|
-
if (!advanced)
|
|
395
|
-
break;
|
|
396
|
-
tableState.currentPageIndex++;
|
|
397
|
-
pagesScanned++;
|
|
408
|
+
}
|
|
409
|
+
finally {
|
|
410
|
+
yield tracker.cleanup(rootLocator.page());
|
|
398
411
|
}
|
|
399
412
|
return results;
|
|
400
413
|
}),
|
|
401
414
|
filter: (predicate_1, ...args_1) => __awaiter(void 0, [predicate_1, ...args_1], void 0, function* (predicate, options = {}) {
|
|
402
|
-
var _a, _b, _c
|
|
403
|
-
yield
|
|
415
|
+
var _a, _b, _c;
|
|
416
|
+
yield _autoInit();
|
|
404
417
|
const map = tableMapper.getMapSync();
|
|
405
418
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
406
419
|
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
|
|
407
420
|
const dedupeKeys = dedupeStrategy ? new Set() : null;
|
|
408
421
|
const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
|
|
422
|
+
const tracker = new elementTracker_1.ElementTracker('filter');
|
|
409
423
|
const matched = [];
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
424
|
+
try {
|
|
425
|
+
let rowIndex = 0;
|
|
426
|
+
let stopped = false;
|
|
427
|
+
let pagesScanned = 1;
|
|
428
|
+
const stop = () => { stopped = true; };
|
|
429
|
+
while (!stopped) {
|
|
430
|
+
const rowLocators = resolve(config.rowSelector, rootLocator);
|
|
431
|
+
const newIndices = yield tracker.getUnseenIndices(rowLocators);
|
|
432
|
+
const pageRows = yield rowLocators.all();
|
|
433
|
+
const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i, pagesScanned - 1));
|
|
434
|
+
if (parallel) {
|
|
435
|
+
const flags = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
|
|
436
|
+
if (dedupeKeys) {
|
|
437
|
+
const key = yield dedupeStrategy(row);
|
|
438
|
+
if (dedupeKeys.has(key))
|
|
439
|
+
return false;
|
|
440
|
+
dedupeKeys.add(key);
|
|
441
|
+
}
|
|
442
|
+
return predicate({ row, rowIndex: row.rowIndex, stop });
|
|
443
|
+
})));
|
|
444
|
+
smartRows.forEach((row, i) => { if (flags[i])
|
|
445
|
+
matched.push(row); });
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
for (const row of smartRows) {
|
|
449
|
+
if (stopped)
|
|
450
|
+
break;
|
|
451
|
+
if (dedupeKeys) {
|
|
452
|
+
const key = yield dedupeStrategy(row);
|
|
453
|
+
if (dedupeKeys.has(key))
|
|
454
|
+
continue;
|
|
455
|
+
dedupeKeys.add(key);
|
|
456
|
+
}
|
|
457
|
+
if (yield predicate({ row, rowIndex: row.rowIndex, stop })) {
|
|
458
|
+
matched.push(row);
|
|
459
|
+
}
|
|
442
460
|
}
|
|
443
461
|
}
|
|
462
|
+
rowIndex += smartRows.length;
|
|
463
|
+
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
464
|
+
break;
|
|
465
|
+
if (!(yield _advancePage()))
|
|
466
|
+
break;
|
|
467
|
+
pagesScanned++;
|
|
444
468
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
449
|
-
let advanced;
|
|
450
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
451
|
-
advanced = !!(yield config.strategies.pagination(context));
|
|
452
|
-
}
|
|
453
|
-
else {
|
|
454
|
-
advanced = !!(((_d = config.strategies.pagination) === null || _d === void 0 ? void 0 : _d.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
455
|
-
}
|
|
456
|
-
if (!advanced)
|
|
457
|
-
break;
|
|
458
|
-
tableState.currentPageIndex++;
|
|
459
|
-
pagesScanned++;
|
|
469
|
+
}
|
|
470
|
+
finally {
|
|
471
|
+
yield tracker.cleanup(rootLocator.page());
|
|
460
472
|
}
|
|
461
473
|
return (0, smartRowArray_1.createSmartRowArray)(matched);
|
|
462
474
|
}),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Locator } from '@playwright/test';
|
|
2
|
+
export declare class ElementTracker {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
constructor(prefix?: string);
|
|
5
|
+
/**
|
|
6
|
+
* Finds the indices of newly seen elements in the browser, storing their text signature
|
|
7
|
+
* in a WeakMap. This gracefully handles both append-only DOMs (by identity) and
|
|
8
|
+
* virtualized DOMs (by text signature if nodes are recycled).
|
|
9
|
+
*/
|
|
10
|
+
getUnseenIndices(locators: Locator): Promise<number[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Cleans up the tracking map from the browser window object.
|
|
13
|
+
*/
|
|
14
|
+
cleanup(page: any): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ElementTracker = void 0;
|
|
13
|
+
class ElementTracker {
|
|
14
|
+
constructor(prefix = 'tracker') {
|
|
15
|
+
this.id = `__smartTable_${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Finds the indices of newly seen elements in the browser, storing their text signature
|
|
19
|
+
* in a WeakMap. This gracefully handles both append-only DOMs (by identity) and
|
|
20
|
+
* virtualized DOMs (by text signature if nodes are recycled).
|
|
21
|
+
*/
|
|
22
|
+
getUnseenIndices(locators) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
return yield locators.evaluateAll((elements, trackerId) => {
|
|
25
|
+
const win = window;
|
|
26
|
+
if (!win[trackerId]) {
|
|
27
|
+
win[trackerId] = new WeakMap();
|
|
28
|
+
}
|
|
29
|
+
const seenMap = win[trackerId];
|
|
30
|
+
const newIndices = [];
|
|
31
|
+
elements.forEach((el, index) => {
|
|
32
|
+
// Determine a lightweight signature for the row (textContent strips HTML, fast)
|
|
33
|
+
const signature = el.textContent || '';
|
|
34
|
+
// If it's a new element, OR a recycled element with new data
|
|
35
|
+
if (seenMap.get(el) !== signature) {
|
|
36
|
+
seenMap.set(el, signature);
|
|
37
|
+
newIndices.push(index);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return newIndices;
|
|
41
|
+
}, this.id);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Cleans up the tracking map from the browser window object.
|
|
46
|
+
*/
|
|
47
|
+
cleanup(page) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
try {
|
|
50
|
+
yield page.evaluate((trackerId) => {
|
|
51
|
+
delete window[trackerId];
|
|
52
|
+
}, this.id);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
// Ignore context destroyed errors during cleanup
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.ElementTracker = ElementTracker;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "6.7.
|
|
3
|
+
"version": "6.7.3",
|
|
4
4
|
"description": "Smart, column-aware table interactions for Playwright",
|
|
5
5
|
"author": "Cedrick Catalan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,11 +23,16 @@
|
|
|
23
23
|
"docs:build": "vitepress build docs",
|
|
24
24
|
"build": "npm run generate-types && npm run generate-config-types && npm run generate-docs && npm run generate-all-api-docs && npm run update-all-api-signatures && tsc",
|
|
25
25
|
"prepublishOnly": "npm run build",
|
|
26
|
+
"clean-port": "lsof -ti:3000 | xargs kill -9 || true",
|
|
27
|
+
"pretest": "npm run clean-port",
|
|
28
|
+
"posttest": "npm run clean-port",
|
|
26
29
|
"test": "npm run test:unit && npx playwright test",
|
|
27
30
|
"test:unit": "vitest run --reporter=verbose --reporter=html",
|
|
28
31
|
"test:unit:ui": "vitest --ui",
|
|
32
|
+
"pretest:e2e": "npm run clean-port",
|
|
33
|
+
"posttest:e2e": "npm run clean-port",
|
|
29
34
|
"test:e2e": "npx playwright test",
|
|
30
|
-
"test:compatibility": "npx playwright test compatibility",
|
|
35
|
+
"test:compatibility": "npm run clean-port && npx playwright test compatibility",
|
|
31
36
|
"prepare": "husky install"
|
|
32
37
|
},
|
|
33
38
|
"keywords": [
|