@rickcedwhat/playwright-smart-table 6.9.0 → 6.10.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 +9 -6
- package/dist/engine/rowFinder.js +153 -168
- package/dist/engine/tableIteration.js +105 -120
- package/dist/engine/tableMapper.js +88 -103
- package/dist/minimalConfigContext.d.ts +1 -1
- package/dist/minimalConfigContext.js +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/presets/glide/columns.js +23 -32
- package/dist/presets/glide/headers.js +18 -27
- package/dist/presets/glide/index.d.ts +9 -9
- package/dist/presets/glide/index.js +24 -33
- package/dist/presets/mui.js +197 -124
- package/dist/presets/rdg.d.ts +1 -1
- package/dist/presets/rdg.js +32 -41
- package/dist/smartRow.js +209 -101
- package/dist/strategies/columns.js +2 -11
- package/dist/strategies/dedupe.js +3 -12
- package/dist/strategies/fill.js +20 -29
- package/dist/strategies/headers.js +22 -31
- package/dist/strategies/index.d.ts +7 -0
- package/dist/strategies/index.js +3 -0
- package/dist/strategies/loading.js +24 -33
- package/dist/strategies/pagination.d.ts +10 -0
- package/dist/strategies/pagination.js +49 -30
- package/dist/strategies/sorting.js +18 -31
- package/dist/strategies/stabilization.js +28 -37
- package/dist/strategies/viewport.d.ts +52 -0
- package/dist/strategies/viewport.js +123 -0
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +160 -5
- package/dist/types.d.ts +153 -5
- package/dist/useTable.js +142 -76
- package/dist/utils/debugUtils.js +5 -16
- package/dist/utils/elementTracker.js +27 -40
- package/dist/utils/mutex.js +5 -16
- package/dist/utils/navigationBarrier.js +25 -36
- package/dist/utils/paginationPath.d.ts +4 -1
- package/dist/utils/paginationPath.js +164 -141
- package/dist/utils/smartRowArray.js +2 -11
- package/dist/utils.js +4 -13
- package/package.json +13 -8
package/dist/useTable.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
|
12
3
|
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
13
4
|
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
@@ -56,7 +47,38 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
56
47
|
isHeaderLoading: loading_1.LoadingStrategies.Headers.stable(200)
|
|
57
48
|
}
|
|
58
49
|
};
|
|
59
|
-
const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "thead th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text }) => text, autoScroll: true, onReset: () =>
|
|
50
|
+
const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "thead th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text }) => text, autoScroll: true, onReset: async () => { } }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
|
|
51
|
+
// Automatically memoize viewport range oracles so user-provided functions are
|
|
52
|
+
// pure DOM queries with no cache bookkeeping. Invalidated after each scroll call.
|
|
53
|
+
if (config.strategies.viewport && !config.strategies.viewport.disableCache) {
|
|
54
|
+
const vp = config.strategies.viewport;
|
|
55
|
+
let colRangeCache = null;
|
|
56
|
+
let rowRangeCache = null;
|
|
57
|
+
config.strategies.viewport = Object.assign(Object.assign({}, vp), { getVisibleColumnRange: vp.getVisibleColumnRange
|
|
58
|
+
? async (ctx) => {
|
|
59
|
+
if (!colRangeCache)
|
|
60
|
+
colRangeCache = await vp.getVisibleColumnRange(ctx);
|
|
61
|
+
return colRangeCache;
|
|
62
|
+
}
|
|
63
|
+
: undefined, getVisibleRowRange: vp.getVisibleRowRange
|
|
64
|
+
? async (ctx) => {
|
|
65
|
+
if (!rowRangeCache)
|
|
66
|
+
rowRangeCache = await vp.getVisibleRowRange(ctx);
|
|
67
|
+
return rowRangeCache;
|
|
68
|
+
}
|
|
69
|
+
: undefined, scrollToColumn: vp.scrollToColumn
|
|
70
|
+
? async (ctx, colIndex) => {
|
|
71
|
+
colRangeCache = null;
|
|
72
|
+
rowRangeCache = null;
|
|
73
|
+
await vp.scrollToColumn(ctx, colIndex);
|
|
74
|
+
}
|
|
75
|
+
: undefined, scrollToRow: vp.scrollToRow
|
|
76
|
+
? async (ctx, rowIndex) => {
|
|
77
|
+
rowRangeCache = null;
|
|
78
|
+
await vp.scrollToRow(ctx, rowIndex);
|
|
79
|
+
}
|
|
80
|
+
: undefined });
|
|
81
|
+
}
|
|
60
82
|
const resolve = (item, parent) => {
|
|
61
83
|
if (typeof item === 'string')
|
|
62
84
|
return parent.locator(item);
|
|
@@ -102,7 +124,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
102
124
|
const rowFinder = new rowFinder_1.RowFinder(rootLocator, config, resolve, filterEngine, tableMapper, _makeSmart, tableState);
|
|
103
125
|
/** Builds a full TableContext/StrategyContext with getHeaderCell, getHeaders, scrollToColumn. Set after result is created. */
|
|
104
126
|
let createStrategyContext = () => ({ root: rootLocator, config, page: rootLocator.page(), resolve });
|
|
105
|
-
const _getCleanHtml = (loc) =>
|
|
127
|
+
const _getCleanHtml = async (loc) => {
|
|
106
128
|
return loc.evaluate((el) => {
|
|
107
129
|
const clone = el.cloneNode(true);
|
|
108
130
|
const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
|
|
@@ -123,33 +145,33 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
123
145
|
}
|
|
124
146
|
return clone.outerHTML;
|
|
125
147
|
});
|
|
126
|
-
}
|
|
127
|
-
const _handlePrompt = (promptName, content) =>
|
|
148
|
+
};
|
|
149
|
+
const _handlePrompt = async (promptName, content) => {
|
|
128
150
|
let finalPrompt = content;
|
|
129
151
|
finalPrompt += `\n\n👇 Useful TypeScript Definitions 👇\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
|
|
130
152
|
console.log(`⚠️ Throwing error to display [${promptName}] cleanly...`);
|
|
131
153
|
throw new Error(finalPrompt);
|
|
132
|
-
}
|
|
133
|
-
const _autoInit = () =>
|
|
134
|
-
|
|
135
|
-
}
|
|
154
|
+
};
|
|
155
|
+
const _autoInit = async () => {
|
|
156
|
+
await tableMapper.getMap();
|
|
157
|
+
};
|
|
136
158
|
// Default: goNext (one page). Pass useBulk true to prefer goNextBulk. "How far" uses numeric return when strategy provides it.
|
|
137
|
-
const _advancePage =
|
|
159
|
+
const _advancePage = async (useBulk = false) => {
|
|
138
160
|
const context = createStrategyContext();
|
|
139
161
|
const pagination = config.strategies.pagination;
|
|
140
162
|
let rawResult;
|
|
141
163
|
let primitive;
|
|
142
164
|
if (useBulk && (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk)) {
|
|
143
165
|
primitive = 'goNextBulk';
|
|
144
|
-
rawResult =
|
|
166
|
+
rawResult = await pagination.goNextBulk(context);
|
|
145
167
|
}
|
|
146
168
|
else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNext) {
|
|
147
169
|
primitive = 'goNext';
|
|
148
|
-
rawResult =
|
|
170
|
+
rawResult = await pagination.goNext(context);
|
|
149
171
|
}
|
|
150
172
|
else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk) {
|
|
151
173
|
primitive = 'goNextBulk (fallback)';
|
|
152
|
-
rawResult =
|
|
174
|
+
rawResult = await pagination.goNextBulk(context);
|
|
153
175
|
}
|
|
154
176
|
else {
|
|
155
177
|
log('_advancePage: no pagination primitive available — returning false');
|
|
@@ -165,47 +187,91 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
165
187
|
log(`_advancePage: ${primitive} returned false — end of data`);
|
|
166
188
|
}
|
|
167
189
|
return didAdvance;
|
|
168
|
-
}
|
|
190
|
+
};
|
|
169
191
|
const result = {
|
|
170
192
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
171
193
|
set currentPageIndex(v) { tableState.currentPageIndex = v; },
|
|
172
|
-
init: (options) =>
|
|
194
|
+
init: async (options) => {
|
|
195
|
+
var _a;
|
|
173
196
|
if (tableMapper.isInitialized())
|
|
174
197
|
return result;
|
|
175
198
|
(0, debugUtils_1.warnIfDebugInCI)(config);
|
|
176
199
|
(0, debugUtils_1.logDebug)(config, 'info', 'Initializing table');
|
|
177
|
-
const map =
|
|
200
|
+
const map = await tableMapper.getMap(options === null || options === void 0 ? void 0 : options.timeout);
|
|
178
201
|
(0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${map.size} columns`, Array.from(map.keys()));
|
|
179
|
-
|
|
202
|
+
if ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.detectCurrentPage) {
|
|
203
|
+
try {
|
|
204
|
+
const detected = await config.strategies.pagination.detectCurrentPage(rootLocator);
|
|
205
|
+
if (Number.isInteger(detected) && detected >= 0) {
|
|
206
|
+
tableState.currentPageIndex = detected;
|
|
207
|
+
(0, debugUtils_1.logDebug)(config, 'info', `init: detected starting page index ${detected}`);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
tableState.currentPageIndex = 0;
|
|
211
|
+
(0, debugUtils_1.logDebug)(config, 'error', `init: detectCurrentPage returned invalid index (${detected}); defaulting to 0`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
tableState.currentPageIndex = 0;
|
|
216
|
+
(0, debugUtils_1.logDebug)(config, 'error', `init: detectCurrentPage failed`, e);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
180
220
|
return result;
|
|
181
|
-
}
|
|
182
|
-
scrollToColumn: (columnName) =>
|
|
183
|
-
const map =
|
|
221
|
+
},
|
|
222
|
+
scrollToColumn: async (columnName) => {
|
|
223
|
+
const map = await tableMapper.getMap();
|
|
184
224
|
const idx = map.get(columnName);
|
|
185
225
|
if (idx === undefined)
|
|
186
226
|
throw _createColumnError(columnName, map);
|
|
187
227
|
// Use header cell for scrolling
|
|
188
228
|
const headerCell = resolve(config.headerSelector, rootLocator).nth(idx);
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
getHeaders: () =>
|
|
192
|
-
const map =
|
|
229
|
+
await headerCell.scrollIntoViewIfNeeded();
|
|
230
|
+
},
|
|
231
|
+
getHeaders: async () => {
|
|
232
|
+
const map = await tableMapper.getMap();
|
|
193
233
|
return Array.from(map.keys());
|
|
194
|
-
}
|
|
195
|
-
getHeaderCell: (columnName) =>
|
|
196
|
-
const map =
|
|
234
|
+
},
|
|
235
|
+
getHeaderCell: async (columnName) => {
|
|
236
|
+
const map = await tableMapper.getMap();
|
|
197
237
|
const idx = map.get(columnName);
|
|
198
238
|
if (idx === undefined)
|
|
199
239
|
throw _createColumnError(columnName, map, 'header cell');
|
|
200
240
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
201
|
-
}
|
|
202
|
-
|
|
241
|
+
},
|
|
242
|
+
countRows: async () => {
|
|
243
|
+
await _autoInit();
|
|
244
|
+
const allRows = resolve(config.rowSelector, rootLocator);
|
|
245
|
+
return allRows.count();
|
|
246
|
+
},
|
|
247
|
+
mapColumn: async (columnName, options = {}) => {
|
|
248
|
+
await _autoInit();
|
|
249
|
+
const map = await tableMapper.getMap();
|
|
250
|
+
if (!map.has(columnName))
|
|
251
|
+
throw _createColumnError(columnName, map, 'mapColumn iteration');
|
|
252
|
+
return result.map(async ({ row }) => {
|
|
253
|
+
var _a;
|
|
254
|
+
const cell = row.getCell(columnName);
|
|
255
|
+
await cell.bringIntoView();
|
|
256
|
+
const columnOverride = (_a = config.columnOverrides) === null || _a === void 0 ? void 0 : _a[columnName];
|
|
257
|
+
if (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.read) {
|
|
258
|
+
return await columnOverride.read(cell);
|
|
259
|
+
}
|
|
260
|
+
const text = await cell.innerText();
|
|
261
|
+
return (text || '').trim();
|
|
262
|
+
}, options);
|
|
263
|
+
},
|
|
264
|
+
getColumnValues: async (columnName, options = {}) => {
|
|
265
|
+
const values = await result.mapColumn(columnName, options);
|
|
266
|
+
return values.map(v => String(v));
|
|
267
|
+
},
|
|
268
|
+
reset: async () => {
|
|
203
269
|
var _a;
|
|
204
270
|
log("Resetting table...");
|
|
205
|
-
|
|
271
|
+
await config.onReset(createStrategyContext());
|
|
206
272
|
if ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst) {
|
|
207
273
|
log("Auto-navigating to first page...");
|
|
208
|
-
|
|
274
|
+
await config.strategies.pagination.goToFirst(createStrategyContext());
|
|
209
275
|
}
|
|
210
276
|
else if (hasPaginationInConfig) {
|
|
211
277
|
log("No goToFirst strategy configured. Table may not be on page 1.");
|
|
@@ -214,13 +280,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
214
280
|
tableState.currentPageIndex = 0;
|
|
215
281
|
tableMapper.clear();
|
|
216
282
|
log("Table reset complete. Calling autoInit to restore state.");
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
revalidate: () =>
|
|
283
|
+
await _autoInit();
|
|
284
|
+
},
|
|
285
|
+
revalidate: async () => {
|
|
220
286
|
log("Revalidating table structure...");
|
|
221
|
-
|
|
287
|
+
await tableMapper.remapHeaders();
|
|
222
288
|
log("Table revalidated.");
|
|
223
|
-
}
|
|
289
|
+
},
|
|
224
290
|
getRow: (filters, options = { exact: false }) => {
|
|
225
291
|
const map = tableMapper.getMapSync();
|
|
226
292
|
if (!map)
|
|
@@ -237,53 +303,53 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
237
303
|
const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
|
|
238
304
|
return _makeSmart(rowLocator, map, index);
|
|
239
305
|
},
|
|
240
|
-
findRow: (filters, options) =>
|
|
306
|
+
findRow: async (filters, options) => {
|
|
241
307
|
return rowFinder.findRow(filters, options);
|
|
242
|
-
}
|
|
243
|
-
findRows: (filters, options) =>
|
|
308
|
+
},
|
|
309
|
+
findRows: async (filters, options) => {
|
|
244
310
|
return rowFinder.findRows(filters !== null && filters !== void 0 ? filters : {}, options);
|
|
245
|
-
}
|
|
311
|
+
},
|
|
246
312
|
isInitialized: () => {
|
|
247
313
|
return tableMapper.isInitialized();
|
|
248
314
|
},
|
|
249
315
|
sorting: {
|
|
250
|
-
apply: (columnName, direction) =>
|
|
316
|
+
apply: async (columnName, direction) => {
|
|
251
317
|
var _a;
|
|
252
|
-
|
|
318
|
+
await _autoInit();
|
|
253
319
|
if (!config.strategies.sorting)
|
|
254
320
|
throw new Error('No sorting strategy has been configured.');
|
|
255
321
|
log(`Applying sort for column "${columnName}" (${direction})`);
|
|
256
322
|
const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
|
|
257
323
|
const maxRetries = 3;
|
|
258
324
|
for (let i = 0; i < maxRetries; i++) {
|
|
259
|
-
const currentState =
|
|
325
|
+
const currentState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
260
326
|
if (currentState === direction) {
|
|
261
327
|
log(`Sort for "${columnName}" is already "${direction}".`);
|
|
262
328
|
return;
|
|
263
329
|
}
|
|
264
|
-
|
|
330
|
+
await config.strategies.sorting.doSort({ columnName, direction, context });
|
|
265
331
|
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
266
|
-
|
|
332
|
+
await config.strategies.loading.isTableLoading(context);
|
|
267
333
|
}
|
|
268
334
|
else {
|
|
269
|
-
|
|
335
|
+
await rootLocator.page().waitForTimeout(200);
|
|
270
336
|
}
|
|
271
|
-
|
|
272
|
-
const newState =
|
|
337
|
+
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
338
|
+
const newState = await config.strategies.sorting.getSortState({ columnName, context });
|
|
273
339
|
if (newState === direction) {
|
|
274
340
|
log(`Successfully sorted "${columnName}" to "${direction}".`);
|
|
275
341
|
return;
|
|
276
342
|
}
|
|
277
343
|
}
|
|
278
344
|
throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
|
|
279
|
-
}
|
|
280
|
-
getState: (columnName) =>
|
|
281
|
-
|
|
345
|
+
},
|
|
346
|
+
getState: async (columnName) => {
|
|
347
|
+
await _autoInit();
|
|
282
348
|
if (!config.strategies.sorting)
|
|
283
349
|
throw new Error('No sorting strategy has been configured.');
|
|
284
350
|
const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
|
|
285
351
|
return config.strategies.sorting.getSortState({ columnName, context });
|
|
286
|
-
}
|
|
352
|
+
}
|
|
287
353
|
},
|
|
288
354
|
// ─── Shared async row iterator ───────────────────────────────────────────
|
|
289
355
|
[Symbol.asyncIterator]() {
|
|
@@ -318,9 +384,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
318
384
|
});
|
|
319
385
|
},
|
|
320
386
|
// ─── Row iteration (delegated to engine/tableIteration) ──────────────────
|
|
321
|
-
forEach:
|
|
322
|
-
|
|
323
|
-
|
|
387
|
+
forEach: async (callback, options = {}) => {
|
|
388
|
+
await _autoInit();
|
|
389
|
+
await (0, tableIteration_1.runForEach)({
|
|
324
390
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
325
391
|
getMap: () => tableMapper.getMapSync(),
|
|
326
392
|
advancePage: _advancePage,
|
|
@@ -329,9 +395,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
329
395
|
config,
|
|
330
396
|
getPage: () => rootLocator.page(),
|
|
331
397
|
}, callback, options);
|
|
332
|
-
}
|
|
333
|
-
map:
|
|
334
|
-
|
|
398
|
+
},
|
|
399
|
+
map: async (callback, options = {}) => {
|
|
400
|
+
await _autoInit();
|
|
335
401
|
return (0, tableIteration_1.runMap)({
|
|
336
402
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
337
403
|
getMap: () => tableMapper.getMapSync(),
|
|
@@ -341,9 +407,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
341
407
|
config,
|
|
342
408
|
getPage: () => rootLocator.page(),
|
|
343
409
|
}, callback, options);
|
|
344
|
-
}
|
|
345
|
-
filter:
|
|
346
|
-
|
|
410
|
+
},
|
|
411
|
+
filter: async (predicate, options = {}) => {
|
|
412
|
+
await _autoInit();
|
|
347
413
|
return (0, tableIteration_1.runFilter)({
|
|
348
414
|
getRowLocators: () => resolve(config.rowSelector, rootLocator),
|
|
349
415
|
getMap: () => tableMapper.getMapSync(),
|
|
@@ -353,17 +419,17 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
353
419
|
config,
|
|
354
420
|
getPage: () => rootLocator.page(),
|
|
355
421
|
}, predicate, options);
|
|
356
|
-
}
|
|
357
|
-
generateConfig: () =>
|
|
358
|
-
const html =
|
|
422
|
+
},
|
|
423
|
+
generateConfig: async () => {
|
|
424
|
+
const html = await _getCleanHtml(rootLocator);
|
|
359
425
|
const separator = "=".repeat(50);
|
|
360
426
|
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`;
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
generateConfigPrompt: () =>
|
|
427
|
+
await _handlePrompt('Smart Table Config', content);
|
|
428
|
+
},
|
|
429
|
+
generateConfigPrompt: async () => {
|
|
364
430
|
console.warn('⚠️ [playwright-smart-table] generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
|
|
365
431
|
return result.generateConfig();
|
|
366
|
-
}
|
|
432
|
+
},
|
|
367
433
|
};
|
|
368
434
|
createStrategyContext = () => ({
|
|
369
435
|
root: rootLocator,
|
package/dist/utils/debugUtils.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.getDebugDelay = getDebugDelay;
|
|
13
4
|
exports.debugDelay = debugDelay;
|
|
@@ -28,13 +19,11 @@ function getDebugDelay(config, actionType) {
|
|
|
28
19
|
/**
|
|
29
20
|
* Add debug delay for specific action type
|
|
30
21
|
*/
|
|
31
|
-
function debugDelay(config, actionType) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
});
|
|
22
|
+
async function debugDelay(config, actionType) {
|
|
23
|
+
const delay = getDebugDelay(config, actionType);
|
|
24
|
+
if (delay > 0) {
|
|
25
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
26
|
+
}
|
|
38
27
|
}
|
|
39
28
|
/**
|
|
40
29
|
* Log debug message based on log level
|
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.ElementTracker = void 0;
|
|
13
4
|
class ElementTracker {
|
|
@@ -19,42 +10,38 @@ class ElementTracker {
|
|
|
19
10
|
* in a WeakMap. This gracefully handles both append-only DOMs (by identity) and
|
|
20
11
|
* virtualized DOMs (by text signature if nodes are recycled).
|
|
21
12
|
*/
|
|
22
|
-
getUnseenIndices(locators) {
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
async getUnseenIndices(locators) {
|
|
14
|
+
return await locators.evaluateAll((elements, trackerId) => {
|
|
15
|
+
const win = window;
|
|
16
|
+
if (!win[trackerId]) {
|
|
17
|
+
win[trackerId] = new WeakMap();
|
|
18
|
+
}
|
|
19
|
+
const seenMap = win[trackerId];
|
|
20
|
+
const newIndices = [];
|
|
21
|
+
elements.forEach((el, index) => {
|
|
22
|
+
// Determine a lightweight signature for the row (textContent strips HTML, fast)
|
|
23
|
+
const signature = el.textContent || '';
|
|
24
|
+
// If it's a new element, OR a recycled element with new data
|
|
25
|
+
if (seenMap.get(el) !== signature) {
|
|
26
|
+
seenMap.set(el, signature);
|
|
27
|
+
newIndices.push(index);
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
});
|
|
29
|
+
});
|
|
30
|
+
return newIndices;
|
|
31
|
+
}, this.id);
|
|
43
32
|
}
|
|
44
33
|
/**
|
|
45
34
|
* Cleans up the tracking map from the browser window object.
|
|
46
35
|
*/
|
|
47
|
-
cleanup(page) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
});
|
|
36
|
+
async cleanup(page) {
|
|
37
|
+
try {
|
|
38
|
+
await page.evaluate((trackerId) => {
|
|
39
|
+
delete window[trackerId];
|
|
40
|
+
}, this.id);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
// Ignore context destroyed errors during cleanup
|
|
44
|
+
}
|
|
58
45
|
}
|
|
59
46
|
}
|
|
60
47
|
exports.ElementTracker = ElementTracker;
|
package/dist/utils/mutex.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.Mutex = void 0;
|
|
13
4
|
/**
|
|
@@ -18,14 +9,12 @@ class Mutex {
|
|
|
18
9
|
constructor() {
|
|
19
10
|
this.queue = Promise.resolve();
|
|
20
11
|
}
|
|
21
|
-
run(action) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return yield action();
|
|
25
|
-
}));
|
|
26
|
-
this.queue = next.catch(() => { }); // prevent chain breakage
|
|
27
|
-
return yield next;
|
|
12
|
+
async run(action) {
|
|
13
|
+
const next = this.queue.then(async () => {
|
|
14
|
+
return await action();
|
|
28
15
|
});
|
|
16
|
+
this.queue = next.catch(() => { }); // prevent chain breakage
|
|
17
|
+
return await next;
|
|
29
18
|
}
|
|
30
19
|
}
|
|
31
20
|
exports.Mutex = Mutex;
|
|
@@ -1,13 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.NavigationBarrier = void 0;
|
|
13
4
|
/**
|
|
@@ -25,34 +16,32 @@ class NavigationBarrier {
|
|
|
25
16
|
* Synchronize all active rows at a specific column index.
|
|
26
17
|
* The last row to arrive will trigger the `moveAction`.
|
|
27
18
|
*/
|
|
28
|
-
sync(colIndex, moveAction) {
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
async sync(colIndex, moveAction) {
|
|
20
|
+
if (this.total <= 1) {
|
|
21
|
+
if (moveAction)
|
|
22
|
+
return await moveAction();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
let bucket = this.buckets.get(colIndex);
|
|
27
|
+
if (!bucket) {
|
|
28
|
+
bucket = new Set();
|
|
29
|
+
this.buckets.set(colIndex, bucket);
|
|
30
|
+
}
|
|
31
|
+
bucket.add(resolve);
|
|
32
|
+
if (bucket.size + this.finishedCount >= this.total) {
|
|
33
|
+
// Use a self-executing async function to handle the moveAction synchronously in this context
|
|
34
|
+
(async () => {
|
|
35
|
+
let result;
|
|
36
|
+
try {
|
|
37
|
+
if (moveAction)
|
|
38
|
+
result = await moveAction();
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
this.broadcast(colIndex, result);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
34
44
|
}
|
|
35
|
-
return new Promise((resolve) => {
|
|
36
|
-
let bucket = this.buckets.get(colIndex);
|
|
37
|
-
if (!bucket) {
|
|
38
|
-
bucket = new Set();
|
|
39
|
-
this.buckets.set(colIndex, bucket);
|
|
40
|
-
}
|
|
41
|
-
bucket.add(resolve);
|
|
42
|
-
if (bucket.size + this.finishedCount >= this.total) {
|
|
43
|
-
// Use a self-executing async function to handle the moveAction synchronously in this context
|
|
44
|
-
(() => __awaiter(this, void 0, void 0, function* () {
|
|
45
|
-
let result;
|
|
46
|
-
try {
|
|
47
|
-
if (moveAction)
|
|
48
|
-
result = yield moveAction();
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
this.broadcast(colIndex, result);
|
|
52
|
-
}
|
|
53
|
-
}))();
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
45
|
});
|
|
57
46
|
}
|
|
58
47
|
/**
|
|
@@ -3,6 +3,9 @@ import type { PaginationPrimitives, TableContext } from '../types';
|
|
|
3
3
|
export type NavigationStep = {
|
|
4
4
|
type: 'goToPage';
|
|
5
5
|
pageIndex: number;
|
|
6
|
+
} | {
|
|
7
|
+
type: 'goToLast';
|
|
8
|
+
targetIndex: number;
|
|
6
9
|
} | {
|
|
7
10
|
type: 'goNextBulk';
|
|
8
11
|
count: number;
|
|
@@ -22,7 +25,7 @@ export type NavigationStep = {
|
|
|
22
25
|
* single steps (goNext / goPrevious). May choose to overshoot with bulk then step back when
|
|
23
26
|
* that reduces total primitive calls (e.g. page 3 → 12 with bulk 10: goNextBulk once, goPrevious once).
|
|
24
27
|
*/
|
|
25
|
-
export declare function planNavigationPath(currentPageIndex: number, targetPageIndex: number, primitives: PaginationPrimitives): NavigationStep[];
|
|
28
|
+
export declare function planNavigationPath(currentPageIndex: number, targetPageIndex: number, primitives: PaginationPrimitives, totalPages?: number): NavigationStep[];
|
|
26
29
|
/**
|
|
27
30
|
* Navigate to targetPageIndex when goToPage is available but may be "windowed"
|
|
28
31
|
* (e.g. only works for visible page links 6–14). Tries goToPage(target); on false,
|