@rickcedwhat/playwright-smart-table 6.24.0 → 6.25.0-next.c784f2c
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 +2 -0
- package/dist/engine/rowFinder.js +13 -4
- package/dist/engine/tableIteration.js +2 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/presets/glide/index.js +0 -1
- package/dist/presets/rdg.js +0 -2
- package/dist/smartRow.js +39 -29
- package/dist/strategies/contentReady.d.ts +0 -1
- package/dist/strategies/index.d.ts +2 -3
- package/dist/strategies/resolution.d.ts +1 -3
- package/dist/strategies/resolution.js +0 -28
- package/dist/types.d.ts +12 -2
- package/dist/useTable.js +10 -2
- package/dist/utils/locatorDescription.d.ts +15 -0
- package/dist/utils/locatorDescription.js +56 -0
- package/dist/utils/navigationBarrier.d.ts +0 -2
- package/dist/utils/navigationBarrier.js +1 -4
- package/package.json +5 -6
- package/dist/typeContext.d.ts +0 -6
- package/dist/typeContext.js +0 -1133
- package/dist/utils.d.ts +0 -7
- package/dist/utils.js +0 -21
|
@@ -25,6 +25,8 @@ export declare class RowFinder<T = any> {
|
|
|
25
25
|
maxPages?: number;
|
|
26
26
|
}): Promise<SmartRow<T>>;
|
|
27
27
|
private waitForTableReady;
|
|
28
|
+
/** Unfiltered rows keep the plain `SmartRow #n` name that `_makeSmart` applies. */
|
|
29
|
+
private nameFilteredRow;
|
|
28
30
|
findRows(filters?: Record<string, FilterValue>, options?: {
|
|
29
31
|
exact?: boolean;
|
|
30
32
|
maxPages?: number;
|
package/dist/engine/rowFinder.js
CHANGED
|
@@ -9,6 +9,7 @@ const navigationBarrier_1 = require("../utils/navigationBarrier");
|
|
|
9
9
|
const rowResolution_1 = require("./rowResolution");
|
|
10
10
|
const resolveCellLocator_1 = require("../utils/resolveCellLocator");
|
|
11
11
|
const scanPages_1 = require("./scanPages");
|
|
12
|
+
const locatorDescription_1 = require("../utils/locatorDescription");
|
|
12
13
|
const loadingWait_1 = require("../utils/loadingWait");
|
|
13
14
|
class RowFinder {
|
|
14
15
|
constructor(rootLocator, config, resolve, filterEngine, tableMapper, makeSmartRow, tableState = { currentPageIndex: 0 }, advancePage = async () => false) {
|
|
@@ -111,12 +112,13 @@ class RowFinder {
|
|
|
111
112
|
await (0, debugUtils_1.debugDelay)(this.config, 'findRow');
|
|
112
113
|
const map = await this.tableMapper.getMap();
|
|
113
114
|
const resolved = await this.resolveRowIndex(rowLocator);
|
|
114
|
-
|
|
115
|
+
const describedRow = (0, locatorDescription_1.describeLocator)(rowLocator, (0, locatorDescription_1.describeRowByFilters)(filters), this.config);
|
|
116
|
+
return this.makeSmartRow(describedRow, map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, undefined, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
|
|
115
117
|
}
|
|
116
118
|
(0, debugUtils_1.logDebug)(this.config, 'error', 'Row not found', filters);
|
|
117
119
|
await (0, debugUtils_1.debugDelay)(this.config, 'findRow');
|
|
118
|
-
const sentinel = this.resolve(this.config.rowSelector, this.rootLocator)
|
|
119
|
-
.filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
|
|
120
|
+
const sentinel = (0, locatorDescription_1.describeLocator)(this.resolve(this.config.rowSelector, this.rootLocator)
|
|
121
|
+
.filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() }), (0, locatorDescription_1.describeSentinelRow)(filters), this.config);
|
|
120
122
|
const smartRow = this.makeSmartRow(sentinel, await this.tableMapper.getMap(), undefined);
|
|
121
123
|
smartRow[sentinel_1.SENTINEL_ROW] = true;
|
|
122
124
|
return smartRow;
|
|
@@ -134,6 +136,12 @@ class RowFinder {
|
|
|
134
136
|
};
|
|
135
137
|
await (0, loadingWait_1.waitWhileTableLoading)(this.config, context, this.rootLocator.page(), 'waitForTableReady');
|
|
136
138
|
}
|
|
139
|
+
/** Unfiltered rows keep the plain `SmartRow #n` name that `_makeSmart` applies. */
|
|
140
|
+
nameFilteredRow(row, resolved, filters) {
|
|
141
|
+
if (!resolved || Object.keys(filters).length === 0)
|
|
142
|
+
return row;
|
|
143
|
+
return (0, locatorDescription_1.describeLocator)(row, (0, locatorDescription_1.describeRowByIndex)(resolved.index, filters), this.config);
|
|
144
|
+
}
|
|
137
145
|
async findRows(filters = {}, options) {
|
|
138
146
|
var _a;
|
|
139
147
|
var _b, _c;
|
|
@@ -168,7 +176,8 @@ class RowFinder {
|
|
|
168
176
|
const barrier = useBarrier ? new navigationBarrier_1.NavigationBarrier(newIndices.length) : undefined;
|
|
169
177
|
for (const idx of newIndices) {
|
|
170
178
|
const resolved = await (0, rowResolution_1.resolveLogicalRowIndex)(currentRows[idx], this.config, () => allRows.length);
|
|
171
|
-
const
|
|
179
|
+
const rowLocator = this.nameFilteredRow(currentRows[idx], resolved, filtersRecord);
|
|
180
|
+
const smartRow = this.makeSmartRow(rowLocator, map, resolved === null || resolved === void 0 ? void 0 : resolved.index, this.tableState.currentPageIndex, barrier, resolved === null || resolved === void 0 ? void 0 : resolved.selector);
|
|
172
181
|
const loadingOutcome = await (0, rowResolution_1.resolveRowLoading)(smartRow, this.config.strategies.loading, 'skip', (msg) => (0, debugUtils_1.logDebug)(this.config, 'verbose', `findRows: ${msg}`));
|
|
173
182
|
if (loadingOutcome !== 'process') {
|
|
174
183
|
barrier === null || barrier === void 0 ? void 0 : barrier.markFinished();
|
|
@@ -45,8 +45,9 @@ async function runMap(env, callback, options = {}, label = 'map') {
|
|
|
45
45
|
let rowIndex = 0;
|
|
46
46
|
let stopped = false;
|
|
47
47
|
let stoppedIndex = Infinity;
|
|
48
|
+
// In parallel/synchronized mode rows can call stop() out of order; the earliest row wins.
|
|
48
49
|
const stop = (idx) => {
|
|
49
|
-
if (
|
|
50
|
+
if (idx < stoppedIndex) {
|
|
50
51
|
log(env.config, `${label}: stop() called at row ${idx} — halting`);
|
|
51
52
|
stopped = true;
|
|
52
53
|
stoppedIndex = idx;
|
package/dist/packageVersion.d.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* Generated by scripts/embed-version.mjs from package.json
|
|
4
4
|
*/
|
|
5
5
|
/** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
|
|
6
|
-
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.
|
|
6
|
+
export declare const PLAYWRIGHT_SMART_TABLE_VERSION: "6.25.0-next.c784f2c";
|
package/dist/packageVersion.js
CHANGED
|
@@ -6,4 +6,4 @@ exports.PLAYWRIGHT_SMART_TABLE_VERSION = void 0;
|
|
|
6
6
|
* Generated by scripts/embed-version.mjs from package.json
|
|
7
7
|
*/
|
|
8
8
|
/** Semver of this package. Use in logs or diagnostics to confirm the installed build. */
|
|
9
|
-
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.
|
|
9
|
+
exports.PLAYWRIGHT_SMART_TABLE_VERSION = "6.25.0-next.c784f2c";
|
|
@@ -102,7 +102,6 @@ const GlideDefaultStrategies = {
|
|
|
102
102
|
getActiveCell: glideGetActiveCell
|
|
103
103
|
};
|
|
104
104
|
/** Strategies only for Glide Data Grid. Includes fillSimple; use when you want to supply your own selectors or override fill. */
|
|
105
|
-
// fallow-ignore-next-line unused-export
|
|
106
105
|
exports.GlideStrategies = Object.assign(Object.assign({}, GlideDefaultStrategies), { fillSimple: glideFillSimple });
|
|
107
106
|
/**
|
|
108
107
|
* Factory that returns a configured Glide preset.
|
package/dist/presets/rdg.js
CHANGED
|
@@ -99,7 +99,6 @@ const RDGDefaultStrategies = {
|
|
|
99
99
|
pagination: rdgPaginationStrategy
|
|
100
100
|
};
|
|
101
101
|
/** Full strategies for React Data Grid. Use when you want to supply your own selectors: `strategies: presets.rdg.Strategies` */
|
|
102
|
-
// fallow-ignore-next-line unused-export
|
|
103
102
|
exports.RDGStrategies = RDGDefaultStrategies;
|
|
104
103
|
/**
|
|
105
104
|
* Full preset for React Data Grid (selectors + default strategies).
|
|
@@ -174,7 +173,6 @@ const rdg2DViewportStrategy = {
|
|
|
174
173
|
};
|
|
175
174
|
/** Default strategies for the RDG2D preset (extends RDG with viewport recovery). */
|
|
176
175
|
const RDG2DDefaultStrategies = Object.assign(Object.assign({}, RDGDefaultStrategies), { viewport: rdg2DViewportStrategy });
|
|
177
|
-
// fallow-ignore-next-line unused-export
|
|
178
176
|
exports.RDG2DStrategies = RDG2DDefaultStrategies;
|
|
179
177
|
/**
|
|
180
178
|
* Full preset for React Data Grid with 2D virtualization (rows + columns both virtualized).
|
package/dist/smartRow.js
CHANGED
|
@@ -7,6 +7,7 @@ const debugUtils_1 = require("./utils/debugUtils");
|
|
|
7
7
|
const paginationPath_1 = require("./utils/paginationPath");
|
|
8
8
|
const sentinel_1 = require("./utils/sentinel");
|
|
9
9
|
const pageIndex_1 = require("./utils/pageIndex");
|
|
10
|
+
const locatorDescription_1 = require("./utils/locatorDescription");
|
|
10
11
|
/**
|
|
11
12
|
* Internal helper to navigate to a cell with active cell optimization.
|
|
12
13
|
* Uses `strategies.navigation` primitives (goUp/goDown/goLeft/goRight, optional snap/seek/goHome).
|
|
@@ -328,7 +329,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
328
329
|
// Attach Methods
|
|
329
330
|
smart.getCell = (colName) => {
|
|
330
331
|
var _a;
|
|
331
|
-
var _b;
|
|
332
|
+
var _b, _c;
|
|
332
333
|
if ((_a = config.syntheticColumns) === null || _a === void 0 ? void 0 : _a[colName]) {
|
|
333
334
|
throw new Error(`Column "${colName}" is synthetic (no DOM cell) — use getValue("${colName}") or toJSON() instead`);
|
|
334
335
|
}
|
|
@@ -351,7 +352,8 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
351
352
|
else {
|
|
352
353
|
baseLocator = resolve(config.cellSelector, rowLocator).nth(idx);
|
|
353
354
|
}
|
|
354
|
-
const
|
|
355
|
+
const cellDescription = `${(_c = (0, locatorDescription_1.getLocatorDescription)(rowLocator)) !== null && _c !== void 0 ? _c : 'SmartRow'} › "${colName}" cell`;
|
|
356
|
+
const smartCell = (0, locatorDescription_1.describeLocator)(baseLocator, cellDescription, config);
|
|
355
357
|
smartCell.bringIntoView = async () => {
|
|
356
358
|
const page = rootLocator.page();
|
|
357
359
|
const navigatedCell = await _navigateToCell({
|
|
@@ -850,36 +852,44 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
850
852
|
barrier: smart._barrier
|
|
851
853
|
});
|
|
852
854
|
const columnOverride = (_b = config.columnOverrides) === null || _b === void 0 ? void 0 : _b[colName];
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
855
|
+
try {
|
|
856
|
+
if (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) {
|
|
857
|
+
const cellLocator = smart.getCell(colName);
|
|
858
|
+
let currentValue;
|
|
859
|
+
if (columnOverride.read) {
|
|
860
|
+
currentValue = await columnOverride.read(cellLocator, {
|
|
861
|
+
row: smart, columnName: colName, columnIndex: colIdx,
|
|
862
|
+
getCell: (name) => smart.getCell(name),
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
await columnOverride.write({
|
|
866
|
+
cell: cellLocator,
|
|
867
|
+
targetValue: value,
|
|
868
|
+
currentValue,
|
|
869
|
+
row: smart
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
const strategy = config.strategies.fill || fill_1.FillStrategies.default;
|
|
874
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
|
|
875
|
+
await strategy({
|
|
876
|
+
row: smart,
|
|
877
|
+
columnName: colName,
|
|
878
|
+
value,
|
|
879
|
+
index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
|
|
880
|
+
page: rowLocator.page(),
|
|
881
|
+
rootLocator,
|
|
882
|
+
config,
|
|
883
|
+
table: table,
|
|
884
|
+
fillOptions
|
|
860
885
|
});
|
|
861
886
|
}
|
|
862
|
-
await columnOverride.write({
|
|
863
|
-
cell: cellLocator,
|
|
864
|
-
targetValue: value,
|
|
865
|
-
currentValue,
|
|
866
|
-
row: smart
|
|
867
|
-
});
|
|
868
887
|
}
|
|
869
|
-
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
columnName: colName,
|
|
875
|
-
value,
|
|
876
|
-
index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
|
|
877
|
-
page: rowLocator.page(),
|
|
878
|
-
rootLocator,
|
|
879
|
-
config,
|
|
880
|
-
table: table,
|
|
881
|
-
fillOptions
|
|
882
|
-
});
|
|
888
|
+
catch (err) {
|
|
889
|
+
const source = (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) ? 'columnOverrides.write' : 'fill strategy';
|
|
890
|
+
const wrapped = new Error(`[SmartTable] smartFill: ${source} for "${colName}" failed — ${err instanceof Error ? err.message : String(err)}`);
|
|
891
|
+
wrapped.cause = err;
|
|
892
|
+
throw wrapped;
|
|
883
893
|
}
|
|
884
894
|
// Delay after filling
|
|
885
895
|
await (0, debugUtils_1.debugDelay)(config, 'getCell');
|
|
@@ -3,7 +3,6 @@ export * from './sorting';
|
|
|
3
3
|
export * from './columns';
|
|
4
4
|
export * from './headers';
|
|
5
5
|
export * from './fill';
|
|
6
|
-
export type { ColumnResolutionStrategy } from './resolution';
|
|
7
6
|
export * from './dedupe';
|
|
8
7
|
export * from './loading';
|
|
9
8
|
export * from './stabilization';
|
|
@@ -102,10 +101,10 @@ export declare const Strategies: {
|
|
|
102
101
|
textStable: (options?: {
|
|
103
102
|
timeout?: number;
|
|
104
103
|
interval?: number;
|
|
105
|
-
}) => import("
|
|
104
|
+
}) => import("..").ContentReadyStrategy;
|
|
106
105
|
mutationSettled: (options?: {
|
|
107
106
|
timeout?: number;
|
|
108
107
|
quietPeriod?: number;
|
|
109
|
-
}) => import("
|
|
108
|
+
}) => import("..").ContentReadyStrategy;
|
|
110
109
|
};
|
|
111
110
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { StrategyContext } from '../types';
|
|
2
|
+
/** @deprecated Not used by the library. Will be removed in v7. */
|
|
2
3
|
export interface ColumnResolutionStrategy {
|
|
3
4
|
/**
|
|
4
5
|
* Resolves a column name (string or Regex) to a column index.
|
|
@@ -17,6 +18,3 @@ export interface ColumnResolutionStrategy {
|
|
|
17
18
|
headerMap: Map<string, number>;
|
|
18
19
|
}): string;
|
|
19
20
|
}
|
|
20
|
-
export declare const ResolutionStrategies: {
|
|
21
|
-
default: ColumnResolutionStrategy;
|
|
22
|
-
};
|
|
@@ -1,30 +1,2 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ResolutionStrategies = void 0;
|
|
4
|
-
exports.ResolutionStrategies = {
|
|
5
|
-
default: {
|
|
6
|
-
resolveIndex: ({ query, headerMap }) => {
|
|
7
|
-
// 1. Exact / String Match
|
|
8
|
-
if (typeof query === 'string') {
|
|
9
|
-
if (headerMap.has(query))
|
|
10
|
-
return headerMap.get(query);
|
|
11
|
-
}
|
|
12
|
-
// 2. Regex Match
|
|
13
|
-
if (query instanceof RegExp) {
|
|
14
|
-
for (const [colName, idx] of headerMap.entries()) {
|
|
15
|
-
if (query.test(colName))
|
|
16
|
-
return idx;
|
|
17
|
-
}
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
// 3. (Optional) Fuzzy String Match fallback could go here
|
|
21
|
-
// But for strict default strategy, we might want to keep it simple first
|
|
22
|
-
// The original code didn't do fuzzy *resolution* logic inside the get(), it just did strict get().
|
|
23
|
-
// The fuzzy logic was only for *suggestions* on error.
|
|
24
|
-
return undefined;
|
|
25
|
-
},
|
|
26
|
-
resolveName: ({ query }) => {
|
|
27
|
-
return query.toString();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
};
|
package/dist/types.d.ts
CHANGED
|
@@ -760,6 +760,14 @@ export type RowIterationContext<T = any> = {
|
|
|
760
760
|
index: number;
|
|
761
761
|
/** 0-based page index — which page this row was collected from. */
|
|
762
762
|
pageIndex: number;
|
|
763
|
+
/**
|
|
764
|
+
* Ends the iteration. No further pages are loaded, and the result of `map` / `filter`
|
|
765
|
+
* includes this row and nothing after it. If several rows call `stop()`, the earliest one wins.
|
|
766
|
+
*
|
|
767
|
+
* In `'sequential'` mode (the default) no callback after this row runs. In `'parallel'` and
|
|
768
|
+
* `'synchronized'` mode the rest of the current page is already in flight, so later rows'
|
|
769
|
+
* callbacks may still run (and their side effects happen) even though their results are dropped.
|
|
770
|
+
*/
|
|
763
771
|
stop: () => void;
|
|
764
772
|
};
|
|
765
773
|
/** Concurrency modes for row iteration. */
|
|
@@ -935,7 +943,8 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
935
943
|
/**
|
|
936
944
|
* Iterates every row across all pages, calling the callback for side effects.
|
|
937
945
|
* Execution is sequential by default (safe for interactions like clicking/filling).
|
|
938
|
-
* Call `stop()` in the callback to end iteration early.
|
|
946
|
+
* Call `stop()` in the callback to end iteration early. In sequential mode no later row runs;
|
|
947
|
+
* in parallel mode the rest of the current page may still run (see `RowIterationContext.stop`).
|
|
939
948
|
*
|
|
940
949
|
* @param callback - Function receiving { row, rowIndex, stop }
|
|
941
950
|
* @param options - maxPages, concurrency, dedupe, useBulkPagination
|
|
@@ -952,7 +961,8 @@ export interface TableResult<T = any> extends AsyncIterable<{
|
|
|
952
961
|
* Defaults to `concurrency: 'sequential'` (safe for clicks/fills). Pass
|
|
953
962
|
* `concurrency: 'parallel'` for read-only extraction, or `'synchronized'` when
|
|
954
963
|
* navigation must stay lock-step on virtualized grids.
|
|
955
|
-
* Call `stop()` to
|
|
964
|
+
* Call `stop()` to end early: the result includes the row that called it and nothing after it
|
|
965
|
+
* (in parallel mode, later rows on the same page may still run; see `RowIterationContext.stop`).
|
|
956
966
|
*
|
|
957
967
|
* @param callback - Function receiving { row, rowIndex, stop }
|
|
958
968
|
* @param options - maxPages, concurrency, dedupe, useBulkPagination
|
package/dist/useTable.js
CHANGED
|
@@ -35,6 +35,7 @@ const elementTracker_1 = require("./utils/elementTracker");
|
|
|
35
35
|
const loadingWait_1 = require("./utils/loadingWait");
|
|
36
36
|
const pageIndex_1 = require("./utils/pageIndex");
|
|
37
37
|
const smartRowInternals_1 = require("./utils/smartRowInternals");
|
|
38
|
+
const locatorDescription_1 = require("./utils/locatorDescription");
|
|
38
39
|
// Helper to safely serialize objects containing functions for logging
|
|
39
40
|
const safeStringify = (obj) => {
|
|
40
41
|
try {
|
|
@@ -153,7 +154,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
153
154
|
const effectiveLocator = healSelector
|
|
154
155
|
? rootLocator.locator(healSelector)
|
|
155
156
|
: rowLocator;
|
|
156
|
-
|
|
157
|
+
// Keep a caller-supplied description (e.g. the filters) across self-healing; otherwise name the row by index.
|
|
158
|
+
const existingDescription = (0, locatorDescription_1.getLocatorDescription)(rowLocator);
|
|
159
|
+
const description = existingDescription !== null && existingDescription !== void 0 ? existingDescription : (rowIndex !== undefined ? (0, locatorDescription_1.describeRowByIndex)(rowIndex) : undefined);
|
|
160
|
+
const describedLocator = description !== undefined && (healSelector !== undefined || existingDescription === undefined)
|
|
161
|
+
? (0, locatorDescription_1.describeLocator)(effectiveLocator, description, config)
|
|
162
|
+
: effectiveLocator;
|
|
163
|
+
const sr = (0, smartRow_1.default)(describedLocator, map, rowIndex, config, rootLocator, resolve, finalTable, tablePageIndex, barrier, renderWindowPosition);
|
|
157
164
|
if (healSelector)
|
|
158
165
|
(0, smartRowInternals_1.internals)(sr)._selfHealing = true;
|
|
159
166
|
return sr;
|
|
@@ -514,7 +521,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
514
521
|
throw new Error('Initialization Error: You attempted to access a row before the table structure was mapped. Please call "await table.init()" once before using synchronous row access.');
|
|
515
522
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
516
523
|
const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page(), rootLocator);
|
|
517
|
-
|
|
524
|
+
const describedRows = (0, locatorDescription_1.describeLocator)(matchedRows, (0, locatorDescription_1.describeRowByFilters)(filters), config);
|
|
525
|
+
return _makeSmart(describedRows, map, undefined); // sync path cannot compute real index
|
|
518
526
|
},
|
|
519
527
|
getRowByIndex: (index) => {
|
|
520
528
|
log(`getRowByIndex: index=${index}`);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import type { FilterValue, FinalTableConfig } from '../types';
|
|
3
|
+
/** `Name="Alice", Status=/act/i` — readable in traces, unlike JSON.stringify (which turns RegExp into `{}`). */
|
|
4
|
+
export declare const formatFilters: (filters: Record<string, FilterValue>) => string;
|
|
5
|
+
export declare const describeRowByFilters: (filters: Record<string, FilterValue>) => string;
|
|
6
|
+
/** `SmartRow #3`, or `SmartRow #3 where Dept="Eng"` when the row came from a filtered search. */
|
|
7
|
+
export declare const describeRowByIndex: (rowIndex: number, filters?: Record<string, FilterValue>) => string;
|
|
8
|
+
export declare const describeSentinelRow: (filters: Record<string, FilterValue>) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Applies `locator.describe()` (Playwright 1.53+) so traces, HTML reports and error messages show a
|
|
11
|
+
* readable name instead of the raw selector chain. Older Playwright versions get the locator back unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export declare const describeLocator: (locator: Locator, description: string, config?: FinalTableConfig) => Locator;
|
|
14
|
+
/** The description already applied to a locator, or undefined (unsupported or never described). */
|
|
15
|
+
export declare const getLocatorDescription: (locator: Locator) => string | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLocatorDescription = exports.describeLocator = exports.describeSentinelRow = exports.describeRowByIndex = exports.describeRowByFilters = exports.formatFilters = void 0;
|
|
4
|
+
const debugUtils_1 = require("./debugUtils");
|
|
5
|
+
const MAX_VALUE_LENGTH = 40;
|
|
6
|
+
const locatorDescriptions = new WeakMap();
|
|
7
|
+
let loggedUnsupported = false;
|
|
8
|
+
const formatFilterValue = (value) => {
|
|
9
|
+
if (value instanceof RegExp)
|
|
10
|
+
return value.toString();
|
|
11
|
+
if (typeof value === 'function')
|
|
12
|
+
return '<locator filter>';
|
|
13
|
+
if (typeof value === 'number')
|
|
14
|
+
return String(value);
|
|
15
|
+
const text = value.length > MAX_VALUE_LENGTH ? `${value.slice(0, MAX_VALUE_LENGTH)}…` : value;
|
|
16
|
+
return JSON.stringify(text);
|
|
17
|
+
};
|
|
18
|
+
/** `Name="Alice", Status=/act/i` — readable in traces, unlike JSON.stringify (which turns RegExp into `{}`). */
|
|
19
|
+
const formatFilters = (filters) => Object.entries(filters).map(([col, value]) => `${col}=${formatFilterValue(value)}`).join(', ');
|
|
20
|
+
exports.formatFilters = formatFilters;
|
|
21
|
+
const describeRowByFilters = (filters) => {
|
|
22
|
+
const formatted = (0, exports.formatFilters)(filters);
|
|
23
|
+
return formatted ? `SmartRow where ${formatted}` : 'SmartRow';
|
|
24
|
+
};
|
|
25
|
+
exports.describeRowByFilters = describeRowByFilters;
|
|
26
|
+
/** `SmartRow #3`, or `SmartRow #3 where Dept="Eng"` when the row came from a filtered search. */
|
|
27
|
+
const describeRowByIndex = (rowIndex, filters = {}) => {
|
|
28
|
+
const formatted = (0, exports.formatFilters)(filters);
|
|
29
|
+
return formatted ? `SmartRow #${rowIndex} where ${formatted}` : `SmartRow #${rowIndex}`;
|
|
30
|
+
};
|
|
31
|
+
exports.describeRowByIndex = describeRowByIndex;
|
|
32
|
+
const describeSentinelRow = (filters) => {
|
|
33
|
+
const formatted = (0, exports.formatFilters)(filters);
|
|
34
|
+
return formatted ? `SmartRow not found: ${formatted}` : 'SmartRow not found';
|
|
35
|
+
};
|
|
36
|
+
exports.describeSentinelRow = describeSentinelRow;
|
|
37
|
+
/**
|
|
38
|
+
* Applies `locator.describe()` (Playwright 1.53+) so traces, HTML reports and error messages show a
|
|
39
|
+
* readable name instead of the raw selector chain. Older Playwright versions get the locator back unchanged.
|
|
40
|
+
*/
|
|
41
|
+
const describeLocator = (locator, description, config) => {
|
|
42
|
+
if (typeof locator.describe === 'function') {
|
|
43
|
+
const described = locator.describe(description);
|
|
44
|
+
locatorDescriptions.set(described, description);
|
|
45
|
+
return described;
|
|
46
|
+
}
|
|
47
|
+
if ((config === null || config === void 0 ? void 0 : config.debug) && !loggedUnsupported) {
|
|
48
|
+
loggedUnsupported = true;
|
|
49
|
+
(0, debugUtils_1.logDebug)(config, 'info', 'Upgrade @playwright/test to 1.53+ to see SmartRow / SmartCell names in traces and error messages');
|
|
50
|
+
}
|
|
51
|
+
return locator;
|
|
52
|
+
};
|
|
53
|
+
exports.describeLocator = describeLocator;
|
|
54
|
+
/** The description already applied to a locator, or undefined (unsupported or never described). */
|
|
55
|
+
const getLocatorDescription = (locator) => { var _a; return typeof locator.description === 'function' ? (_a = locator.description()) !== null && _a !== void 0 ? _a : undefined : locatorDescriptions.get(locator); };
|
|
56
|
+
exports.getLocatorDescription = getLocatorDescription;
|
|
@@ -4,8 +4,6 @@ export declare class NavigationBarrier {
|
|
|
4
4
|
private buckets;
|
|
5
5
|
private recoveryMutex;
|
|
6
6
|
constructor(total: number);
|
|
7
|
-
/** True when more than one row shares this barrier (scrollToRow would evict peers). */
|
|
8
|
-
isParallel(): boolean;
|
|
9
7
|
/**
|
|
10
8
|
* Synchronize all active rows at a specific column index.
|
|
11
9
|
* The last row to arrive (or the last markFinished call) triggers the `moveAction`.
|
|
@@ -9,10 +9,6 @@ class NavigationBarrier {
|
|
|
9
9
|
this.recoveryMutex = null;
|
|
10
10
|
this.total = total;
|
|
11
11
|
}
|
|
12
|
-
/** True when more than one row shares this barrier (scrollToRow would evict peers). */
|
|
13
|
-
isParallel() {
|
|
14
|
-
return this.total > 1;
|
|
15
|
-
}
|
|
16
12
|
/**
|
|
17
13
|
* Synchronize all active rows at a specific column index.
|
|
18
14
|
* The last row to arrive (or the last markFinished call) triggers the `moveAction`.
|
|
@@ -55,6 +51,7 @@ class NavigationBarrier {
|
|
|
55
51
|
* The fn receives a fresh `targetReached` check; if the previous row's scroll
|
|
56
52
|
* already brought this row into view, fn can no-op immediately.
|
|
57
53
|
*/
|
|
54
|
+
// fallow-ignore-next-line unused-class-member
|
|
58
55
|
async runExclusiveRecovery(fn) {
|
|
59
56
|
if (!this.recoveryMutex)
|
|
60
57
|
this.recoveryMutex = new mutex_1.Mutex();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.25.0-next.c784f2c",
|
|
4
4
|
"description": "Smart, column-aware table interactions for Playwright",
|
|
5
5
|
"author": "Cedrick Catalan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"generate-types": "node scripts/embed-types.mjs",
|
|
22
21
|
"generate-config-types": "node scripts/embed-config-types.mjs",
|
|
23
22
|
"generate-docs": "node scripts/generate-readme.mjs",
|
|
24
23
|
"generate-all-api-docs": "node scripts/generate-all-api-docs.mjs",
|
|
@@ -29,7 +28,7 @@
|
|
|
29
28
|
"docs:verify-links": "node scripts/verify-vitepress-links.mjs",
|
|
30
29
|
"embed-version": "node scripts/embed-version.mjs",
|
|
31
30
|
"generate-og": "node scripts/generate-og.mjs",
|
|
32
|
-
"build": "pnpm run embed-version && pnpm run generate-
|
|
31
|
+
"build": "pnpm run embed-version && pnpm run generate-config-types && pnpm run generate-docs && pnpm run generate-all-api-docs && pnpm run update-all-api-signatures && tsc && node scripts/check-no-runtime-playwright.mjs",
|
|
33
32
|
"prepublishOnly": "pnpm run build",
|
|
34
33
|
"clean-port": "lsof -ti:3000 | xargs kill -9 || true",
|
|
35
34
|
"pretest": "pnpm run clean-port",
|
|
@@ -38,6 +37,7 @@
|
|
|
38
37
|
"test:unit": "vitest run --coverage --reporter=verbose --reporter=html",
|
|
39
38
|
"test:unit:ui": "vitest --ui",
|
|
40
39
|
"test:mutate": "pnpm exec stryker run",
|
|
40
|
+
"fallow": "fallow dead-code",
|
|
41
41
|
"pretest:e2e": "pnpm run clean-port",
|
|
42
42
|
"posttest:e2e": "pnpm run clean-port",
|
|
43
43
|
"test:e2e": "pnpm exec playwright test",
|
|
@@ -53,9 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"./types": {
|
|
55
55
|
"types": "./dist/types.d.ts",
|
|
56
|
-
"
|
|
57
|
-
"require": "./dist/index.js",
|
|
58
|
-
"default": "./dist/index.js"
|
|
56
|
+
"default": "./dist/types.js"
|
|
59
57
|
},
|
|
60
58
|
"./presets": {
|
|
61
59
|
"types": "./dist/presets/index.d.ts",
|
|
@@ -124,6 +122,7 @@
|
|
|
124
122
|
"@types/node": "^26.6.2",
|
|
125
123
|
"@vitest/coverage-v8": "^3.2.6",
|
|
126
124
|
"@vitest/ui": "^3.2.6",
|
|
125
|
+
"fallow": "3.31.0",
|
|
127
126
|
"happy-dom": "^20.10.6",
|
|
128
127
|
"husky": "^9.1.7",
|
|
129
128
|
"typescript": "^7.0.2",
|