@rickcedwhat/playwright-smart-table 6.21.0 ā 6.22.0-next.28cae60
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.d.ts +3 -3
- package/dist/engine/rowFinder.js +4 -6
- package/dist/engine/tableIteration.d.ts +1 -1
- package/dist/engine/tableIteration.js +4 -3
- package/dist/index.d.ts +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/plugins/index.d.ts +9 -9
- package/dist/presets/mui.js +33 -36
- package/dist/smartRow.js +14 -3
- package/dist/strategies/filter.js +2 -3
- package/dist/strategies/viewport.js +26 -10
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +68 -32
- package/dist/types.d.ts +66 -32
- package/dist/useTable.js +35 -26
- package/dist/utils/cssSelector.d.ts +14 -0
- package/dist/utils/cssSelector.js +28 -0
- package/dist/utils/loadingWait.d.ts +15 -0
- package/dist/utils/loadingWait.js +43 -0
- package/dist/utils/pageIndex.d.ts +12 -0
- package/dist/utils/pageIndex.js +19 -0
- package/dist/utils/resolveCellLocator.d.ts +2 -2
- package/dist/utils/resolveCellLocator.js +4 -4
- package/dist/utils/smartRowInternals.d.ts +14 -0
- package/dist/utils/smartRowInternals.js +6 -0
- package/package.json +1 -1
package/dist/useTable.js
CHANGED
|
@@ -32,6 +32,9 @@ const scanPages_1 = require("./engine/scanPages");
|
|
|
32
32
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
33
33
|
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
34
34
|
const elementTracker_1 = require("./utils/elementTracker");
|
|
35
|
+
const loadingWait_1 = require("./utils/loadingWait");
|
|
36
|
+
const pageIndex_1 = require("./utils/pageIndex");
|
|
37
|
+
const smartRowInternals_1 = require("./utils/smartRowInternals");
|
|
35
38
|
// Helper to safely serialize objects containing functions for logging
|
|
36
39
|
const safeStringify = (obj) => {
|
|
37
40
|
try {
|
|
@@ -145,13 +148,14 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
145
148
|
// Placeholder for the final table object
|
|
146
149
|
let finalTable = null;
|
|
147
150
|
// Helper factory
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
// healSelector: the per-row selector returned by resolveRowIndex (not config.rowSelector).
|
|
152
|
+
const _makeSmart = (rowLocator, map, rowIndex, tablePageIndex, barrier, healSelector, renderWindowPosition = false) => {
|
|
153
|
+
const effectiveLocator = healSelector
|
|
154
|
+
? rootLocator.locator(healSelector)
|
|
151
155
|
: rowLocator;
|
|
152
156
|
const sr = (0, smartRow_1.default)(effectiveLocator, map, rowIndex, config, rootLocator, resolve, finalTable, tablePageIndex, barrier, renderWindowPosition);
|
|
153
|
-
if (
|
|
154
|
-
sr._selfHealing = true;
|
|
157
|
+
if (healSelector)
|
|
158
|
+
(0, smartRowInternals_1.internals)(sr)._selfHealing = true;
|
|
155
159
|
return sr;
|
|
156
160
|
};
|
|
157
161
|
const tableState = { currentPageIndex: 0, empty: false };
|
|
@@ -183,7 +187,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
183
187
|
const _handlePrompt = async (promptName, content) => {
|
|
184
188
|
let finalPrompt = content;
|
|
185
189
|
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
|
|
186
|
-
|
|
190
|
+
(0, debugUtils_1.logDebug)(config, 'info', `Throwing error to display [${promptName}] cleanly...`);
|
|
187
191
|
throw new Error(finalPrompt);
|
|
188
192
|
};
|
|
189
193
|
const _autoInit = async () => {
|
|
@@ -229,9 +233,12 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
229
233
|
};
|
|
230
234
|
const result = {
|
|
231
235
|
get currentPageIndex() { return tableState.currentPageIndex; },
|
|
232
|
-
set currentPageIndex(v) {
|
|
236
|
+
set currentPageIndex(v) {
|
|
237
|
+
console.warn('[SmartTable] Manually assigning table.currentPageIndex can desync pagination path planning. Prefer reset() / library navigation; writable access may become read-only in v7.');
|
|
238
|
+
tableState.currentPageIndex = v;
|
|
239
|
+
},
|
|
233
240
|
init: async (options) => {
|
|
234
|
-
var _a;
|
|
241
|
+
var _a, _b, _c;
|
|
235
242
|
if (tableMapper.isInitialized() || tableState.empty)
|
|
236
243
|
return result;
|
|
237
244
|
if (config.strategies.sorting)
|
|
@@ -240,6 +247,10 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
240
247
|
(0, validation_1.validateFillStrategy)(config.strategies.fill);
|
|
241
248
|
(0, debugUtils_1.warnIfDebugInCI)(config);
|
|
242
249
|
(0, debugUtils_1.logDebug)(config, 'info', 'Initializing table');
|
|
250
|
+
if (config.maxPages === 1 &&
|
|
251
|
+
(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) || ((_b = config.strategies.pagination) === null || _b === void 0 ? void 0 : _b.goNextBulk))) {
|
|
252
|
+
console.warn('[SmartTable] maxPages is 1 but a pagination strategy is configured ā iteration/find will never leave page 1. Pass maxPages: N (or Infinity) to scan further pages.');
|
|
253
|
+
}
|
|
243
254
|
let map;
|
|
244
255
|
try {
|
|
245
256
|
map = await tableMapper.getMap(options === null || options === void 0 ? void 0 : options.timeout);
|
|
@@ -256,7 +267,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
256
267
|
throw headerError;
|
|
257
268
|
}
|
|
258
269
|
(0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${map.size} columns`, Array.from(map.keys()));
|
|
259
|
-
if ((
|
|
270
|
+
if ((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.detectCurrentPage) {
|
|
260
271
|
try {
|
|
261
272
|
const detected = await config.strategies.pagination.detectCurrentPage(rootLocator);
|
|
262
273
|
if (Number.isInteger(detected) && detected >= 0) {
|
|
@@ -370,10 +381,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
370
381
|
const isTableLoading = (_e = config.strategies.loading) === null || _e === void 0 ? void 0 : _e.isTableLoading;
|
|
371
382
|
if (isTableLoading) {
|
|
372
383
|
const ctx = createStrategyContext();
|
|
373
|
-
|
|
374
|
-
log('countRows: table is loading... waiting');
|
|
375
|
-
await rootLocator.page().waitForTimeout(200);
|
|
376
|
-
}
|
|
384
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
377
385
|
}
|
|
378
386
|
if (!hasPagination) {
|
|
379
387
|
log(`countRows: counting rows in current viewport (no pagination)${hasFilters ? ` filters=${safeStringify(filtersRecord)}` : ''}`);
|
|
@@ -396,10 +404,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
396
404
|
if (!isTableLoading)
|
|
397
405
|
return;
|
|
398
406
|
const ctx = createStrategyContext();
|
|
399
|
-
|
|
400
|
-
log('countRows: table is loading... waiting');
|
|
401
|
-
await rootLocator.page().waitForTimeout(200);
|
|
402
|
-
}
|
|
407
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, ctx, rootLocator.page(), 'countRows');
|
|
403
408
|
},
|
|
404
409
|
}, async ({ pagesScanned: pageNum }) => {
|
|
405
410
|
const rowLocators = resolveRows();
|
|
@@ -592,7 +597,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
592
597
|
},
|
|
593
598
|
sorting: {
|
|
594
599
|
apply: async (columnName, direction) => {
|
|
595
|
-
var _a, _b, _c, _d, _e;
|
|
600
|
+
var _a, _b, _c, _d, _e, _f;
|
|
596
601
|
await _autoInit();
|
|
597
602
|
if (!config.strategies.sorting)
|
|
598
603
|
throw new Error('No sorting strategy has been configured.');
|
|
@@ -607,15 +612,15 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
607
612
|
}
|
|
608
613
|
await config.strategies.sorting.doSort({ columnName, direction, context });
|
|
609
614
|
if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
|
|
610
|
-
const timeout = (_b = config.strategies.loading.sortStabilizationTimeout) !== null && _b !== void 0 ? _b : 10000;
|
|
611
|
-
const poll = (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
}
|
|
615
|
+
const timeout = (_c = (_b = config.strategies.loading.sortStabilizationTimeout) !== null && _b !== void 0 ? _b : config.strategies.loading.loadingTimeout) !== null && _c !== void 0 ? _c : 10000;
|
|
616
|
+
const poll = (_d = config.strategies.loading.sortStabilizationPollInterval) !== null && _d !== void 0 ? _d : 100;
|
|
617
|
+
await (0, loadingWait_1.waitWhileTableLoading)(config, context, rootLocator.page(), 'sorting.apply', {
|
|
618
|
+
timeout,
|
|
619
|
+
pollInterval: poll,
|
|
620
|
+
});
|
|
616
621
|
}
|
|
617
622
|
else {
|
|
618
|
-
const fallback = (
|
|
623
|
+
const fallback = (_f = (_e = config.strategies.loading) === null || _e === void 0 ? void 0 : _e.sortStabilizationFallbackDelay) !== null && _f !== void 0 ? _f : 200;
|
|
619
624
|
await rootLocator.page().waitForTimeout(fallback);
|
|
620
625
|
}
|
|
621
626
|
await (0, debugUtils_1.debugDelay)(config, 'default');
|
|
@@ -767,10 +772,14 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
767
772
|
await _handlePrompt('Smart Table Config', content);
|
|
768
773
|
},
|
|
769
774
|
generateConfigPrompt: async () => {
|
|
770
|
-
|
|
775
|
+
(0, debugUtils_1.logDebug)(config, 'error', 'generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
|
|
771
776
|
return result.generateConfig();
|
|
772
777
|
},
|
|
773
778
|
};
|
|
779
|
+
Object.defineProperty(result, pageIndex_1.SET_CURRENT_PAGE_INDEX, {
|
|
780
|
+
value: (v) => { tableState.currentPageIndex = v; },
|
|
781
|
+
enumerable: false,
|
|
782
|
+
});
|
|
774
783
|
createStrategyContext = () => ({
|
|
775
784
|
root: rootLocator,
|
|
776
785
|
config,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TableSelector } from '../types';
|
|
2
|
+
type SelectorKey = 'headerSelector' | 'rowSelector' | 'cellSelector';
|
|
3
|
+
/** Viewport range meaning "can't measure": callers treat everything as possibly visible and verify in the DOM. */
|
|
4
|
+
export declare const UNKNOWN_RANGE: {
|
|
5
|
+
first: number;
|
|
6
|
+
last: number;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Returns the configured selector when it is a CSS string. Features that run
|
|
10
|
+
* `querySelectorAll` in the browser can't use a function selector; for those this warns
|
|
11
|
+
* once per config + feature and returns `null` so the caller can skip that step.
|
|
12
|
+
*/
|
|
13
|
+
export declare function cssSelectorOrWarn(config: Partial<Record<SelectorKey, TableSelector>>, key: SelectorKey, feature: string): string | null;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UNKNOWN_RANGE = void 0;
|
|
4
|
+
exports.cssSelectorOrWarn = cssSelectorOrWarn;
|
|
5
|
+
/** Viewport range meaning "can't measure": callers treat everything as possibly visible and verify in the DOM. */
|
|
6
|
+
exports.UNKNOWN_RANGE = { first: 0, last: Number.POSITIVE_INFINITY };
|
|
7
|
+
const warned = new WeakMap();
|
|
8
|
+
/**
|
|
9
|
+
* Returns the configured selector when it is a CSS string. Features that run
|
|
10
|
+
* `querySelectorAll` in the browser can't use a function selector; for those this warns
|
|
11
|
+
* once per config + feature and returns `null` so the caller can skip that step.
|
|
12
|
+
*/
|
|
13
|
+
function cssSelectorOrWarn(config, key, feature) {
|
|
14
|
+
const selector = config[key];
|
|
15
|
+
if (typeof selector === 'string')
|
|
16
|
+
return selector;
|
|
17
|
+
if (selector === undefined)
|
|
18
|
+
return null;
|
|
19
|
+
let seen = warned.get(config);
|
|
20
|
+
if (!seen)
|
|
21
|
+
warned.set(config, (seen = new Set()));
|
|
22
|
+
const id = `${key}:${feature}`;
|
|
23
|
+
if (!seen.has(id)) {
|
|
24
|
+
seen.add(id);
|
|
25
|
+
console.warn(`[SmartTable] ${feature} needs ${key} to be a CSS string, but a function was configured ā skipping it. Use a string ${key} to enable ${feature}.`);
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Page } from '@playwright/test';
|
|
2
|
+
import type { FinalTableConfig, TableContext } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Poll `loading.isTableLoading` until false or timeout.
|
|
5
|
+
* Shared by countRows, findRow(s), and sort stabilization (#434).
|
|
6
|
+
*
|
|
7
|
+
* Timeout resolution (first defined wins):
|
|
8
|
+
* 1. Explicit `options.timeout` ā e.g. the sort caller's stabilization timeout
|
|
9
|
+
* 2. `loadingTimeout` ā general gate for any isTableLoading poll
|
|
10
|
+
* 3. 10_000 ms default
|
|
11
|
+
*/
|
|
12
|
+
export declare function waitWhileTableLoading(config: FinalTableConfig, context: TableContext, page: Page, label: string, options?: {
|
|
13
|
+
timeout?: number;
|
|
14
|
+
pollInterval?: number;
|
|
15
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitWhileTableLoading = waitWhileTableLoading;
|
|
4
|
+
const debugUtils_1 = require("./debugUtils");
|
|
5
|
+
/**
|
|
6
|
+
* Poll `loading.isTableLoading` until false or timeout.
|
|
7
|
+
* Shared by countRows, findRow(s), and sort stabilization (#434).
|
|
8
|
+
*
|
|
9
|
+
* Timeout resolution (first defined wins):
|
|
10
|
+
* 1. Explicit `options.timeout` ā e.g. the sort caller's stabilization timeout
|
|
11
|
+
* 2. `loadingTimeout` ā general gate for any isTableLoading poll
|
|
12
|
+
* 3. 10_000 ms default
|
|
13
|
+
*/
|
|
14
|
+
async function waitWhileTableLoading(config, context, page, label, options) {
|
|
15
|
+
var _a, _b, _c, _d, _e;
|
|
16
|
+
const isTableLoading = (_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading;
|
|
17
|
+
if (!isTableLoading)
|
|
18
|
+
return;
|
|
19
|
+
const loading = config.strategies.loading;
|
|
20
|
+
const timeout = (_c = (_b = options === null || options === void 0 ? void 0 : options.timeout) !== null && _b !== void 0 ? _b : loading.loadingTimeout) !== null && _c !== void 0 ? _c : 10000;
|
|
21
|
+
const poll = (_e = (_d = options === null || options === void 0 ? void 0 : options.pollInterval) !== null && _d !== void 0 ? _d : loading.sortStabilizationPollInterval) !== null && _e !== void 0 ? _e : 200;
|
|
22
|
+
const deadline = Date.now() + timeout;
|
|
23
|
+
while (Date.now() < deadline) {
|
|
24
|
+
let timer;
|
|
25
|
+
try {
|
|
26
|
+
// A custom predicate may never settle. Stop waiting when the budget expires;
|
|
27
|
+
// Promise.race also handles any later rejection from that predicate.
|
|
28
|
+
const expired = new Promise(resolve => {
|
|
29
|
+
timer = setTimeout(() => resolve(false), Math.max(0, deadline - Date.now()));
|
|
30
|
+
});
|
|
31
|
+
if (!(await Promise.race([isTableLoading(context), expired])))
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
}
|
|
37
|
+
const remaining = deadline - Date.now();
|
|
38
|
+
if (remaining <= 0)
|
|
39
|
+
return;
|
|
40
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `${label}: table is loading... waiting`);
|
|
41
|
+
await page.waitForTimeout(Math.min(poll, remaining));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal setter for `table.currentPageIndex` used by library navigation
|
|
3
|
+
* (bringIntoView path planner, etc.) so the public setter can warn on
|
|
4
|
+
* external writes without spamming on every library update (#434).
|
|
5
|
+
*/
|
|
6
|
+
export declare const SET_CURRENT_PAGE_INDEX: unique symbol;
|
|
7
|
+
export type PageIndexWritable = {
|
|
8
|
+
currentPageIndex: number;
|
|
9
|
+
[SET_CURRENT_PAGE_INDEX]?: (n: number) => void;
|
|
10
|
+
};
|
|
11
|
+
/** Prefer the internal setter when present; fall back to the public property. */
|
|
12
|
+
export declare function setCurrentPageIndex(table: PageIndexWritable, n: number): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SET_CURRENT_PAGE_INDEX = void 0;
|
|
4
|
+
exports.setCurrentPageIndex = setCurrentPageIndex;
|
|
5
|
+
/**
|
|
6
|
+
* Internal setter for `table.currentPageIndex` used by library navigation
|
|
7
|
+
* (bringIntoView path planner, etc.) so the public setter can warn on
|
|
8
|
+
* external writes without spamming on every library update (#434).
|
|
9
|
+
*/
|
|
10
|
+
exports.SET_CURRENT_PAGE_INDEX = Symbol.for('playwright-smart-table.setCurrentPageIndex');
|
|
11
|
+
/** Prefer the internal setter when present; fall back to the public property. */
|
|
12
|
+
function setCurrentPageIndex(table, n) {
|
|
13
|
+
const internal = table[exports.SET_CURRENT_PAGE_INDEX];
|
|
14
|
+
if (typeof internal === 'function') {
|
|
15
|
+
internal(n);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
table.currentPageIndex = n;
|
|
19
|
+
}
|
|
@@ -22,8 +22,8 @@ export declare function resolveCellLocator(args: {
|
|
|
22
22
|
* `filter({ has })`. Using the rows collection or a document-rooted parent nests
|
|
23
23
|
* the row selector and matches nothing.
|
|
24
24
|
*
|
|
25
|
-
* Without `getCellLocator`,
|
|
26
|
-
*
|
|
25
|
+
* Without `getCellLocator`, resolves `cellSelector` against the same `:scope` row so a
|
|
26
|
+
* function `cellSelector` receives a Locator, and Playwright re-bases it into each row.
|
|
27
27
|
*/
|
|
28
28
|
export declare function resolveCellLocatorForFilter(args: {
|
|
29
29
|
config: FinalTableConfig;
|
|
@@ -31,14 +31,14 @@ function resolveCellLocator(args) {
|
|
|
31
31
|
* `filter({ has })`. Using the rows collection or a document-rooted parent nests
|
|
32
32
|
* the row selector and matches nothing.
|
|
33
33
|
*
|
|
34
|
-
* Without `getCellLocator`,
|
|
35
|
-
*
|
|
34
|
+
* Without `getCellLocator`, resolves `cellSelector` against the same `:scope` row so a
|
|
35
|
+
* function `cellSelector` receives a Locator, and Playwright re-bases it into each row.
|
|
36
36
|
*/
|
|
37
37
|
function resolveCellLocatorForFilter(args) {
|
|
38
38
|
var _a;
|
|
39
39
|
const { config, resolve, page, root, columnName, columnIndex } = args;
|
|
40
|
+
const scopeRow = page.locator(':scope');
|
|
40
41
|
if ((_a = config.strategies) === null || _a === void 0 ? void 0 : _a.getCellLocator) {
|
|
41
|
-
const scopeRow = page.locator(':scope');
|
|
42
42
|
return config.strategies.getCellLocator({
|
|
43
43
|
row: scopeRow,
|
|
44
44
|
root: root !== null && root !== void 0 ? root : scopeRow,
|
|
@@ -48,5 +48,5 @@ function resolveCellLocatorForFilter(args) {
|
|
|
48
48
|
config,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
return resolve(config.cellSelector,
|
|
51
|
+
return resolve(config.cellSelector, scopeRow).nth(columnIndex);
|
|
52
52
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SmartRow } from '../types';
|
|
2
|
+
import type { NavigationBarrier } from './navigationBarrier';
|
|
3
|
+
/**
|
|
4
|
+
* Library-private state stored on SmartRow instances. Not part of the public `SmartRow` type.
|
|
5
|
+
*/
|
|
6
|
+
export interface SmartRowInternals {
|
|
7
|
+
/** Row re-resolves itself from the `resolveRowIndex` selector on every use (DOM recycling). */
|
|
8
|
+
_selfHealing?: boolean;
|
|
9
|
+
/** Row is being processed inside a batch (map/forEach/filter or findRows). */
|
|
10
|
+
_inBatch?: boolean;
|
|
11
|
+
/** Barrier coordinating column scrolls across rows in `synchronized` iteration. */
|
|
12
|
+
_barrier?: NavigationBarrier;
|
|
13
|
+
}
|
|
14
|
+
export declare function internals(row: SmartRow<any>): SmartRowInternals;
|