@rickcedwhat/playwright-smart-table 6.6.0 ā 6.7.1
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 +94 -5
- package/dist/engine/rowFinder.d.ts +1 -4
- package/dist/engine/rowFinder.js +9 -40
- package/dist/engine/tableMapper.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -18
- package/dist/plugins.d.ts +0 -1
- package/dist/smartRow.js +59 -25
- package/dist/strategies/index.d.ts +6 -8
- package/dist/strategies/pagination.d.ts +2 -18
- package/dist/strategies/pagination.js +2 -17
- package/dist/strategies/rdg.d.ts +0 -5
- package/dist/strategies/rdg.js +1 -18
- package/dist/strategies/sorting.js +15 -33
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +72 -78
- package/dist/types.d.ts +63 -87
- package/dist/useTable.js +87 -321
- package/dist/utils/elementTracker.d.ts +15 -0
- package/dist/utils/elementTracker.js +60 -0
- package/package.json +1 -1
package/dist/useTable.js
CHANGED
|
@@ -24,7 +24,6 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
|
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.useTable = void 0;
|
|
26
26
|
const minimalConfigContext_1 = require("./minimalConfigContext");
|
|
27
|
-
const validation_1 = require("./strategies/validation");
|
|
28
27
|
const loading_1 = require("./strategies/loading");
|
|
29
28
|
const fill_1 = require("./strategies/fill");
|
|
30
29
|
const headers_1 = require("./strategies/headers");
|
|
@@ -116,21 +115,30 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
116
115
|
return clone.outerHTML;
|
|
117
116
|
});
|
|
118
117
|
});
|
|
119
|
-
const _handlePrompt = (
|
|
120
|
-
const { output = 'console', includeTypes = true } = options;
|
|
118
|
+
const _handlePrompt = (promptName, content) => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
119
|
let finalPrompt = content;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (output === 'error') {
|
|
126
|
-
console.log(`ā ļø Throwing error to display [${promptName}] cleanly...`);
|
|
127
|
-
throw new Error(finalPrompt);
|
|
128
|
-
}
|
|
129
|
-
console.log(finalPrompt);
|
|
120
|
+
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
|
|
121
|
+
console.log(`ā ļø Throwing error to display [${promptName}] cleanly...`);
|
|
122
|
+
throw new Error(finalPrompt);
|
|
130
123
|
});
|
|
131
|
-
const
|
|
124
|
+
const _autoInit = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
125
|
yield tableMapper.getMap();
|
|
133
126
|
});
|
|
127
|
+
const _advancePage = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
+
var _a;
|
|
129
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell, getHeaders: result.getHeaders, scrollToColumn: result.scrollToColumn };
|
|
130
|
+
let advanced;
|
|
131
|
+
if (typeof config.strategies.pagination === 'function') {
|
|
132
|
+
advanced = !!(yield config.strategies.pagination(context));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
advanced = !!(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
136
|
+
}
|
|
137
|
+
if (advanced) {
|
|
138
|
+
tableState.currentPageIndex++;
|
|
139
|
+
}
|
|
140
|
+
return advanced;
|
|
141
|
+
});
|
|
134
142
|
const result = {
|
|
135
143
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
136
144
|
set currentPageIndex(v) { tableState.currentPageIndex = v; },
|
|
@@ -165,74 +173,41 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
165
173
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
166
174
|
}),
|
|
167
175
|
reset: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
176
|
+
var _a;
|
|
168
177
|
log("Resetting table...");
|
|
169
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
178
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
|
|
170
179
|
yield config.onReset(context);
|
|
180
|
+
if (typeof config.strategies.pagination !== 'function' && ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst)) {
|
|
181
|
+
log("Auto-navigating to first page...");
|
|
182
|
+
yield config.strategies.pagination.goToFirst(context);
|
|
183
|
+
}
|
|
184
|
+
else if (hasPaginationInConfig) {
|
|
185
|
+
log("No goToFirst strategy configured. Table may not be on page 1.");
|
|
186
|
+
}
|
|
171
187
|
_hasPaginated = false;
|
|
188
|
+
tableState.currentPageIndex = 0;
|
|
172
189
|
tableMapper.clear();
|
|
173
|
-
log("Table reset complete.");
|
|
190
|
+
log("Table reset complete. Calling autoInit to restore state.");
|
|
191
|
+
yield _autoInit();
|
|
174
192
|
}),
|
|
175
193
|
revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
176
194
|
log("Revalidating table structure...");
|
|
177
195
|
yield tableMapper.remapHeaders();
|
|
178
196
|
log("Table revalidated.");
|
|
179
197
|
}),
|
|
180
|
-
getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
181
|
-
var _a, _b;
|
|
182
|
-
const map = yield tableMapper.getMap();
|
|
183
|
-
const colIdx = map.get(column);
|
|
184
|
-
if (colIdx === undefined)
|
|
185
|
-
throw _createColumnError(column, map);
|
|
186
|
-
const mapper = (_a = options === null || options === void 0 ? void 0 : options.mapper) !== null && _a !== void 0 ? _a : ((c) => c.innerText());
|
|
187
|
-
const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
|
|
188
|
-
let pagesScanned = 1;
|
|
189
|
-
const results = [];
|
|
190
|
-
log(`Getting column values for '${column}' (Pages: ${effectiveMaxPages})`);
|
|
191
|
-
while (true) {
|
|
192
|
-
const rows = yield resolve(config.rowSelector, rootLocator).all();
|
|
193
|
-
for (const row of rows) {
|
|
194
|
-
const cell = typeof config.cellSelector === 'string'
|
|
195
|
-
? row.locator(config.cellSelector).nth(colIdx)
|
|
196
|
-
: resolve(config.cellSelector, row).nth(colIdx);
|
|
197
|
-
results.push(yield mapper(cell));
|
|
198
|
-
}
|
|
199
|
-
if (pagesScanned < effectiveMaxPages) {
|
|
200
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
201
|
-
let pageRes;
|
|
202
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
203
|
-
pageRes = yield config.strategies.pagination(context);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
if (!config.strategies.pagination.goNext) {
|
|
207
|
-
log('Cannot paginate: no goNext primitive found.');
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
pageRes = yield config.strategies.pagination.goNext(context);
|
|
211
|
-
}
|
|
212
|
-
if (yield (0, validation_1.validatePaginationResult)(pageRes, 'Pagination Strategy')) {
|
|
213
|
-
_hasPaginated = true;
|
|
214
|
-
tableState.currentPageIndex++;
|
|
215
|
-
pagesScanned++;
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
return results;
|
|
222
|
-
}),
|
|
223
198
|
getRow: (filters, options = { exact: false }) => {
|
|
224
199
|
const map = tableMapper.getMapSync();
|
|
225
200
|
if (!map)
|
|
226
|
-
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.
|
|
201
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.findRows() which auto-initialize.');
|
|
227
202
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
228
203
|
const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
|
|
229
204
|
const rowLocator = matchedRows.first();
|
|
230
205
|
return _makeSmart(rowLocator, map, 0); // fallback index 0
|
|
231
206
|
},
|
|
232
|
-
getRowByIndex: (index
|
|
207
|
+
getRowByIndex: (index) => {
|
|
233
208
|
const map = tableMapper.getMapSync();
|
|
234
209
|
if (!map)
|
|
235
|
-
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.
|
|
210
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.findRows() which auto-initialize.');
|
|
236
211
|
const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
|
|
237
212
|
return _makeSmart(rowLocator, map, index);
|
|
238
213
|
},
|
|
@@ -248,26 +223,47 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
248
223
|
},
|
|
249
224
|
sorting: {
|
|
250
225
|
apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
251
|
-
|
|
226
|
+
var _a;
|
|
227
|
+
yield _autoInit();
|
|
252
228
|
if (!config.strategies.sorting)
|
|
253
229
|
throw new Error('No sorting strategy has been configured.');
|
|
254
230
|
log(`Applying sort for column "${columnName}" (${direction})`);
|
|
255
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
256
|
-
|
|
231
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
|
|
232
|
+
const maxRetries = 3;
|
|
233
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
234
|
+
const currentState = yield config.strategies.sorting.getSortState({ columnName, context });
|
|
235
|
+
if (currentState === direction) {
|
|
236
|
+
log(`Sort for "${columnName}" is already "${direction}".`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
yield config.strategies.sorting.doSort({ columnName, direction, context });
|
|
240
|
+
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
241
|
+
yield config.strategies.loading.isTableLoading(context);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
yield rootLocator.page().waitForTimeout(200);
|
|
245
|
+
}
|
|
246
|
+
yield (0, debugUtils_1.debugDelay)(config, 'default');
|
|
247
|
+
const newState = yield config.strategies.sorting.getSortState({ columnName, context });
|
|
248
|
+
if (newState === direction) {
|
|
249
|
+
log(`Successfully sorted "${columnName}" to "${direction}".`);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
|
|
257
254
|
}),
|
|
258
255
|
getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
259
|
-
yield
|
|
256
|
+
yield _autoInit();
|
|
260
257
|
if (!config.strategies.sorting)
|
|
261
258
|
throw new Error('No sorting strategy has been configured.');
|
|
262
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
259
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
|
|
263
260
|
return config.strategies.sorting.getSortState({ columnName, context });
|
|
264
261
|
})
|
|
265
262
|
},
|
|
266
263
|
// āāā Shared async row iterator āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
267
264
|
[Symbol.asyncIterator]() {
|
|
268
265
|
return __asyncGenerator(this, arguments, function* _a() {
|
|
269
|
-
|
|
270
|
-
yield __await(_ensureInitialized());
|
|
266
|
+
yield __await(_autoInit());
|
|
271
267
|
const map = tableMapper.getMapSync();
|
|
272
268
|
const effectiveMaxPages = config.maxPages;
|
|
273
269
|
let rowIndex = 0;
|
|
@@ -280,17 +276,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
280
276
|
}
|
|
281
277
|
if (pagesScanned >= effectiveMaxPages)
|
|
282
278
|
break;
|
|
283
|
-
|
|
284
|
-
let advanced;
|
|
285
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
286
|
-
advanced = !!(yield __await(config.strategies.pagination(context)));
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
advanced = !!(((_b = config.strategies.pagination) === null || _b === void 0 ? void 0 : _b.goNext) && (yield __await(config.strategies.pagination.goNext(context))));
|
|
290
|
-
}
|
|
291
|
-
if (!advanced)
|
|
279
|
+
if (!(yield __await(_advancePage())))
|
|
292
280
|
break;
|
|
293
|
-
tableState.currentPageIndex++;
|
|
294
281
|
pagesScanned++;
|
|
295
282
|
}
|
|
296
283
|
});
|
|
@@ -298,11 +285,12 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
298
285
|
// āāā Private row-iteration engine āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
299
286
|
forEach: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
|
|
300
287
|
var _a, _b, _c;
|
|
301
|
-
yield
|
|
288
|
+
yield _autoInit();
|
|
302
289
|
const map = tableMapper.getMapSync();
|
|
303
290
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
304
|
-
const
|
|
305
|
-
const
|
|
291
|
+
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
|
|
292
|
+
const dedupeKeys = dedupeStrategy ? new Set() : null;
|
|
293
|
+
const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
|
|
306
294
|
let rowIndex = 0;
|
|
307
295
|
let stopped = false;
|
|
308
296
|
let pagesScanned = 1;
|
|
@@ -315,7 +303,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
315
303
|
if (stopped)
|
|
316
304
|
return;
|
|
317
305
|
if (dedupeKeys) {
|
|
318
|
-
const key = yield
|
|
306
|
+
const key = yield dedupeStrategy(row);
|
|
319
307
|
if (dedupeKeys.has(key))
|
|
320
308
|
return;
|
|
321
309
|
dedupeKeys.add(key);
|
|
@@ -328,7 +316,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
328
316
|
if (stopped)
|
|
329
317
|
break;
|
|
330
318
|
if (dedupeKeys) {
|
|
331
|
-
const key = yield
|
|
319
|
+
const key = yield dedupeStrategy(row);
|
|
332
320
|
if (dedupeKeys.has(key))
|
|
333
321
|
continue;
|
|
334
322
|
dedupeKeys.add(key);
|
|
@@ -339,27 +327,19 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
339
327
|
rowIndex += smartRows.length;
|
|
340
328
|
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
341
329
|
break;
|
|
342
|
-
|
|
343
|
-
let advanced;
|
|
344
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
345
|
-
advanced = !!(yield config.strategies.pagination(context));
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
advanced = !!(((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
349
|
-
}
|
|
350
|
-
if (!advanced)
|
|
330
|
+
if (!(yield _advancePage()))
|
|
351
331
|
break;
|
|
352
|
-
tableState.currentPageIndex++;
|
|
353
332
|
pagesScanned++;
|
|
354
333
|
}
|
|
355
334
|
}),
|
|
356
335
|
map: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
|
|
357
336
|
var _a, _b, _c;
|
|
358
|
-
yield
|
|
337
|
+
yield _autoInit();
|
|
359
338
|
const map = tableMapper.getMapSync();
|
|
360
339
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
361
|
-
const
|
|
362
|
-
const
|
|
340
|
+
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
|
|
341
|
+
const dedupeKeys = dedupeStrategy ? new Set() : null;
|
|
342
|
+
const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : true;
|
|
363
343
|
const results = [];
|
|
364
344
|
let rowIndex = 0;
|
|
365
345
|
let stopped = false;
|
|
@@ -372,7 +352,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
372
352
|
const SKIP = Symbol('skip');
|
|
373
353
|
const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
|
|
374
354
|
if (dedupeKeys) {
|
|
375
|
-
const key = yield
|
|
355
|
+
const key = yield dedupeStrategy(row);
|
|
376
356
|
if (dedupeKeys.has(key))
|
|
377
357
|
return SKIP;
|
|
378
358
|
dedupeKeys.add(key);
|
|
@@ -389,7 +369,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
389
369
|
if (stopped)
|
|
390
370
|
break;
|
|
391
371
|
if (dedupeKeys) {
|
|
392
|
-
const key = yield
|
|
372
|
+
const key = yield dedupeStrategy(row);
|
|
393
373
|
if (dedupeKeys.has(key))
|
|
394
374
|
continue;
|
|
395
375
|
dedupeKeys.add(key);
|
|
@@ -400,28 +380,20 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
400
380
|
rowIndex += smartRows.length;
|
|
401
381
|
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
402
382
|
break;
|
|
403
|
-
|
|
404
|
-
let advanced;
|
|
405
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
406
|
-
advanced = !!(yield config.strategies.pagination(context));
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
advanced = !!(((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
410
|
-
}
|
|
411
|
-
if (!advanced)
|
|
383
|
+
if (!(yield _advancePage()))
|
|
412
384
|
break;
|
|
413
|
-
tableState.currentPageIndex++;
|
|
414
385
|
pagesScanned++;
|
|
415
386
|
}
|
|
416
387
|
return results;
|
|
417
388
|
}),
|
|
418
389
|
filter: (predicate_1, ...args_1) => __awaiter(void 0, [predicate_1, ...args_1], void 0, function* (predicate, options = {}) {
|
|
419
390
|
var _a, _b, _c;
|
|
420
|
-
yield
|
|
391
|
+
yield _autoInit();
|
|
421
392
|
const map = tableMapper.getMapSync();
|
|
422
393
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
423
|
-
const
|
|
424
|
-
const
|
|
394
|
+
const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
|
|
395
|
+
const dedupeKeys = dedupeStrategy ? new Set() : null;
|
|
396
|
+
const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
|
|
425
397
|
const matched = [];
|
|
426
398
|
let rowIndex = 0;
|
|
427
399
|
let stopped = false;
|
|
@@ -433,7 +405,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
433
405
|
if (parallel) {
|
|
434
406
|
const flags = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
|
|
435
407
|
if (dedupeKeys) {
|
|
436
|
-
const key = yield
|
|
408
|
+
const key = yield dedupeStrategy(row);
|
|
437
409
|
if (dedupeKeys.has(key))
|
|
438
410
|
return false;
|
|
439
411
|
dedupeKeys.add(key);
|
|
@@ -448,7 +420,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
448
420
|
if (stopped)
|
|
449
421
|
break;
|
|
450
422
|
if (dedupeKeys) {
|
|
451
|
-
const key = yield
|
|
423
|
+
const key = yield dedupeStrategy(row);
|
|
452
424
|
if (dedupeKeys.has(key))
|
|
453
425
|
continue;
|
|
454
426
|
dedupeKeys.add(key);
|
|
@@ -461,223 +433,17 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
461
433
|
rowIndex += smartRows.length;
|
|
462
434
|
if (stopped || pagesScanned >= effectiveMaxPages)
|
|
463
435
|
break;
|
|
464
|
-
|
|
465
|
-
let advanced;
|
|
466
|
-
if (typeof config.strategies.pagination === 'function') {
|
|
467
|
-
advanced = !!(yield config.strategies.pagination(context));
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
advanced = !!(((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNext) && (yield config.strategies.pagination.goNext(context)));
|
|
471
|
-
}
|
|
472
|
-
if (!advanced)
|
|
436
|
+
if (!(yield _advancePage()))
|
|
473
437
|
break;
|
|
474
|
-
tableState.currentPageIndex++;
|
|
475
438
|
pagesScanned++;
|
|
476
439
|
}
|
|
477
440
|
return (0, smartRowArray_1.createSmartRowArray)(matched);
|
|
478
441
|
}),
|
|
479
|
-
|
|
480
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
481
|
-
yield _ensureInitialized();
|
|
482
|
-
const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.strategies.pagination;
|
|
483
|
-
const hasPaginationInOptions = (options === null || options === void 0 ? void 0 : options.pagination) !== undefined;
|
|
484
|
-
if (!hasPaginationInOptions && !hasPaginationInConfig)
|
|
485
|
-
throw new Error('No pagination strategy provided.');
|
|
486
|
-
yield result.reset();
|
|
487
|
-
yield result.init();
|
|
488
|
-
const map = tableMapper.getMapSync();
|
|
489
|
-
const restrictedTable = {
|
|
490
|
-
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
491
|
-
init: result.init,
|
|
492
|
-
getHeaders: result.getHeaders,
|
|
493
|
-
getHeaderCell: result.getHeaderCell,
|
|
494
|
-
getRow: result.getRow,
|
|
495
|
-
getRowByIndex: result.getRowByIndex,
|
|
496
|
-
findRow: result.findRow,
|
|
497
|
-
findRows: result.findRows,
|
|
498
|
-
getColumnValues: result.getColumnValues,
|
|
499
|
-
isInitialized: result.isInitialized,
|
|
500
|
-
sorting: result.sorting,
|
|
501
|
-
scrollToColumn: result.scrollToColumn,
|
|
502
|
-
revalidate: result.revalidate,
|
|
503
|
-
generateConfigPrompt: result.generateConfigPrompt,
|
|
504
|
-
forEach: result.forEach,
|
|
505
|
-
map: result.map,
|
|
506
|
-
filter: result.filter,
|
|
507
|
-
[Symbol.asyncIterator]: result[Symbol.asyncIterator].bind(result),
|
|
508
|
-
};
|
|
509
|
-
const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
|
|
510
|
-
const getIsLast = (_c = options === null || options === void 0 ? void 0 : options.getIsLast) !== null && _c !== void 0 ? _c : (() => false);
|
|
511
|
-
const allData = [];
|
|
512
|
-
const effectiveMaxIterations = (_d = options === null || options === void 0 ? void 0 : options.maxIterations) !== null && _d !== void 0 ? _d : config.maxPages;
|
|
513
|
-
const batchSize = options === null || options === void 0 ? void 0 : options.batchSize;
|
|
514
|
-
const isBatching = batchSize !== undefined && batchSize > 1;
|
|
515
|
-
const autoFlatten = (_e = options === null || options === void 0 ? void 0 : options.autoFlatten) !== null && _e !== void 0 ? _e : false;
|
|
516
|
-
let index = 0;
|
|
517
|
-
let paginationResult = true;
|
|
518
|
-
let seenKeys = null;
|
|
519
|
-
let batchRows = [];
|
|
520
|
-
let batchStartIndex = 0;
|
|
521
|
-
log(`Starting iterateThroughTable (maxIterations: ${effectiveMaxIterations}, batchSize: ${batchSize !== null && batchSize !== void 0 ? batchSize : 'none'})`);
|
|
522
|
-
while (index < effectiveMaxIterations) {
|
|
523
|
-
const rowLocators = yield resolve(config.rowSelector, rootLocator).all();
|
|
524
|
-
const smartRowsArray = [];
|
|
525
|
-
const isRowLoading = (_f = config.strategies.loading) === null || _f === void 0 ? void 0 : _f.isRowLoading;
|
|
526
|
-
for (let i = 0; i < rowLocators.length; i++) {
|
|
527
|
-
const smartRow = _makeSmart(rowLocators[i], map, i);
|
|
528
|
-
if (isRowLoading && (yield isRowLoading(smartRow)))
|
|
529
|
-
continue;
|
|
530
|
-
smartRowsArray.push(smartRow);
|
|
531
|
-
}
|
|
532
|
-
let rows = (0, smartRowArray_1.createSmartRowArray)(smartRowsArray);
|
|
533
|
-
const dedupeStrategy = (_g = options === null || options === void 0 ? void 0 : options.dedupeStrategy) !== null && _g !== void 0 ? _g : config.strategies.dedupe;
|
|
534
|
-
if (dedupeStrategy && rows.length > 0) {
|
|
535
|
-
if (!seenKeys)
|
|
536
|
-
seenKeys = new Set();
|
|
537
|
-
const deduplicated = [];
|
|
538
|
-
for (const row of rows) {
|
|
539
|
-
const key = yield dedupeStrategy(row);
|
|
540
|
-
if (!seenKeys.has(key)) {
|
|
541
|
-
seenKeys.add(key);
|
|
542
|
-
deduplicated.push(row);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
rows = (0, smartRowArray_1.createSmartRowArray)(deduplicated);
|
|
546
|
-
log(`Deduplicated ${rowLocators.length} rows to ${rows.length} unique rows (total seen: ${seenKeys.size})`);
|
|
547
|
-
}
|
|
548
|
-
// Add rows to batch if batching is enabled
|
|
549
|
-
if (isBatching) {
|
|
550
|
-
batchRows.push(...rows);
|
|
551
|
-
}
|
|
552
|
-
const isLastIteration = index === effectiveMaxIterations - 1;
|
|
553
|
-
// Determine if we should invoke the callback
|
|
554
|
-
const batchComplete = isBatching && (index - batchStartIndex + 1) >= batchSize;
|
|
555
|
-
const shouldInvokeCallback = !isBatching || batchComplete || isLastIteration;
|
|
556
|
-
if (shouldInvokeCallback) {
|
|
557
|
-
const callbackRows = isBatching ? batchRows : rows;
|
|
558
|
-
const callbackIndex = isBatching ? batchStartIndex : index;
|
|
559
|
-
const isFirst = getIsFirst({ index: callbackIndex });
|
|
560
|
-
let isLast = getIsLast({ index: callbackIndex, paginationResult });
|
|
561
|
-
const isLastDueToMax = index === effectiveMaxIterations - 1;
|
|
562
|
-
if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
|
|
563
|
-
yield options.beforeFirst({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows), allData });
|
|
564
|
-
}
|
|
565
|
-
const batchInfo = isBatching ? {
|
|
566
|
-
startIndex: batchStartIndex,
|
|
567
|
-
endIndex: index,
|
|
568
|
-
size: index - batchStartIndex + 1
|
|
569
|
-
} : undefined;
|
|
570
|
-
const returnValue = yield callback({
|
|
571
|
-
index: callbackIndex,
|
|
572
|
-
isFirst,
|
|
573
|
-
isLast,
|
|
574
|
-
rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows),
|
|
575
|
-
allData,
|
|
576
|
-
table: restrictedTable,
|
|
577
|
-
batchInfo
|
|
578
|
-
});
|
|
579
|
-
if (autoFlatten && Array.isArray(returnValue)) {
|
|
580
|
-
allData.push(...returnValue);
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
allData.push(returnValue);
|
|
584
|
-
}
|
|
585
|
-
// Determine if this is truly the last iteration
|
|
586
|
-
let finalIsLast = isLastDueToMax;
|
|
587
|
-
if (!isLastIteration) {
|
|
588
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
589
|
-
if (typeof paginationStrategy === 'function') {
|
|
590
|
-
paginationResult = yield paginationStrategy(context);
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
const pageObj = paginationStrategy;
|
|
594
|
-
if (!pageObj.goNext)
|
|
595
|
-
break;
|
|
596
|
-
paginationResult = yield pageObj.goNext(context);
|
|
597
|
-
}
|
|
598
|
-
(0, debugUtils_1.logDebug)(config, 'info', `Pagination ${paginationResult ? 'succeeded' : 'failed'}`);
|
|
599
|
-
yield (0, debugUtils_1.debugDelay)(config, 'pagination');
|
|
600
|
-
finalIsLast = getIsLast({ index: callbackIndex, paginationResult }) || !paginationResult;
|
|
601
|
-
if (paginationResult)
|
|
602
|
-
tableState.currentPageIndex++;
|
|
603
|
-
}
|
|
604
|
-
if (finalIsLast && (options === null || options === void 0 ? void 0 : options.afterLast)) {
|
|
605
|
-
yield options.afterLast({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows), allData });
|
|
606
|
-
}
|
|
607
|
-
if (finalIsLast || !paginationResult) {
|
|
608
|
-
log(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
|
|
609
|
-
break;
|
|
610
|
-
}
|
|
611
|
-
// Reset batch
|
|
612
|
-
if (isBatching) {
|
|
613
|
-
batchRows = [];
|
|
614
|
-
batchStartIndex = index + 1;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
else {
|
|
618
|
-
// Continue paginating even when batching
|
|
619
|
-
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
620
|
-
if (typeof paginationStrategy === 'function') {
|
|
621
|
-
paginationResult = yield paginationStrategy(context);
|
|
622
|
-
}
|
|
623
|
-
else {
|
|
624
|
-
const pageObj = paginationStrategy;
|
|
625
|
-
if (!pageObj.goNext) {
|
|
626
|
-
log(`Cannot paginate: no goNext primitive found.`);
|
|
627
|
-
break;
|
|
628
|
-
}
|
|
629
|
-
paginationResult = yield pageObj.goNext(context);
|
|
630
|
-
}
|
|
631
|
-
(0, debugUtils_1.logDebug)(config, 'info', `Pagination ${paginationResult ? 'succeeded' : 'failed'} (batching mode)`);
|
|
632
|
-
yield (0, debugUtils_1.debugDelay)(config, 'pagination');
|
|
633
|
-
if (paginationResult)
|
|
634
|
-
tableState.currentPageIndex++;
|
|
635
|
-
if (!paginationResult) {
|
|
636
|
-
// Pagination failed, invoke callback with current batch
|
|
637
|
-
const callbackIndex = batchStartIndex;
|
|
638
|
-
const isFirst = getIsFirst({ index: callbackIndex });
|
|
639
|
-
const isLast = true;
|
|
640
|
-
if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
|
|
641
|
-
yield options.beforeFirst({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(batchRows), allData });
|
|
642
|
-
}
|
|
643
|
-
const batchInfo = {
|
|
644
|
-
startIndex: batchStartIndex,
|
|
645
|
-
endIndex: index,
|
|
646
|
-
size: index - batchStartIndex + 1
|
|
647
|
-
};
|
|
648
|
-
const returnValue = yield callback({
|
|
649
|
-
index: callbackIndex,
|
|
650
|
-
isFirst,
|
|
651
|
-
isLast,
|
|
652
|
-
rows: (0, smartRowArray_1.createSmartRowArray)(batchRows),
|
|
653
|
-
allData,
|
|
654
|
-
table: restrictedTable,
|
|
655
|
-
batchInfo
|
|
656
|
-
});
|
|
657
|
-
if (autoFlatten && Array.isArray(returnValue)) {
|
|
658
|
-
allData.push(...returnValue);
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
allData.push(returnValue);
|
|
662
|
-
}
|
|
663
|
-
if (options === null || options === void 0 ? void 0 : options.afterLast) {
|
|
664
|
-
yield options.afterLast({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(batchRows), allData });
|
|
665
|
-
}
|
|
666
|
-
log(`Pagination failed mid-batch (index: ${index})`);
|
|
667
|
-
break;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
index++;
|
|
671
|
-
log(`Iteration ${index} completed, continuing...`);
|
|
672
|
-
}
|
|
673
|
-
log(`iterateThroughTable completed after ${index + 1} iterations, collected ${allData.length} items`);
|
|
674
|
-
return allData;
|
|
675
|
-
}),
|
|
676
|
-
generateConfigPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
442
|
+
generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
677
443
|
const html = yield _getCleanHtml(rootLocator);
|
|
678
444
|
const separator = "=".repeat(50);
|
|
679
445
|
const content = `\n${separator} \nš¤ COPY INTO GEMINI / ChatGPT š¤\n${separator} \nI am using 'playwright-smart-table'.\nTarget Table Locator: ${rootLocator.toString()} \nGenerate config for: \n\`\`\`html\n${html.substring(0, 10000)} ...\n\`\`\`\n${separator}\n`;
|
|
680
|
-
yield _handlePrompt('Smart Table Config', content
|
|
446
|
+
yield _handlePrompt('Smart Table Config', content);
|
|
681
447
|
}),
|
|
682
448
|
};
|
|
683
449
|
finalTable = result;
|
|
@@ -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;
|