@rickcedwhat/playwright-smart-table 6.8.2 → 6.10.0
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 +0 -2
- package/dist/engine/rowFinder.js +153 -168
- package/dist/engine/tableIteration.js +105 -123
- package/dist/engine/tableMapper.js +88 -103
- 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 +163 -96
- 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 +4 -0
- package/dist/strategies/index.js +3 -0
- package/dist/strategies/loading.js +24 -33
- package/dist/strategies/pagination.js +21 -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 +100 -9
- package/dist/types.d.ts +100 -9
- package/dist/useTable.js +98 -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.js +102 -115
- package/dist/utils/smartRowArray.js +2 -11
- package/dist/utils.js +4 -13
- package/package.json +9 -5
package/dist/smartRow.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
|
const fill_1 = require("./strategies/fill");
|
|
13
4
|
const stringUtils_1 = require("./utils/stringUtils");
|
|
@@ -19,13 +10,14 @@ const sentinel_1 = require("./utils/sentinel");
|
|
|
19
10
|
* Uses `strategies.navigation` primitives (goUp/goDown/goLeft/goRight, optional snap/seek/goHome).
|
|
20
11
|
* Returns the target cell locator after navigation.
|
|
21
12
|
*/
|
|
22
|
-
const _navigateToCell = (params) =>
|
|
13
|
+
const _navigateToCell = async (params) => {
|
|
23
14
|
const { config, rootLocator, page, resolve, column, index, rowLocator, rowIndex, barrier } = params;
|
|
24
|
-
|
|
15
|
+
const rowLabel = typeof rowIndex === 'number' ? `row ${rowIndex}` : 'row ?';
|
|
16
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: resolving column "${column}" (colIndex ${index}), ${rowLabel}`);
|
|
25
17
|
// Get active cell if strategy is available
|
|
26
18
|
let activeCell = null;
|
|
27
19
|
if (config.strategies.getActiveCell) {
|
|
28
|
-
activeCell =
|
|
20
|
+
activeCell = await config.strategies.getActiveCell({
|
|
29
21
|
config,
|
|
30
22
|
root: rootLocator,
|
|
31
23
|
page,
|
|
@@ -38,18 +30,107 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
38
30
|
}
|
|
39
31
|
}
|
|
40
32
|
const context = { config, root: rootLocator, page, resolve };
|
|
33
|
+
// Shared helper: is the target cell currently in the DOM?
|
|
34
|
+
const targetReached = async () => {
|
|
35
|
+
if (config.strategies.getActiveCell) {
|
|
36
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
37
|
+
if (ac && ac.rowIndex === rowIndex && ac.columnIndex === index)
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
// Locator presence: virtualized grids (e.g. Glide) often attach the target `td` after scroll
|
|
41
|
+
// while focus id lags; do not require getActiveCell to match for this to count as reached.
|
|
42
|
+
const cell = config.strategies.getCellLocator
|
|
43
|
+
? config.strategies.getCellLocator({ row: rowLocator, root: rootLocator, columnName: column, columnIndex: index, rowIndex, page, config })
|
|
44
|
+
: resolve(config.cellSelector, rowLocator).nth(index);
|
|
45
|
+
return (await cell.count() > 0);
|
|
46
|
+
};
|
|
47
|
+
const getCellLocator = () => config.strategies.getCellLocator
|
|
48
|
+
? config.strategies.getCellLocator({ row: rowLocator, root: rootLocator, columnName: column, columnIndex: index, rowIndex, page, config })
|
|
49
|
+
: resolve(config.cellSelector, rowLocator).nth(index);
|
|
50
|
+
// Viewport-oracle phase: direct positioning for 2D virtualized tables.
|
|
51
|
+
// Runs before navigation primitives; falls through to them if cell still not accessible.
|
|
52
|
+
const viewport = config.strategies.viewport;
|
|
53
|
+
if (viewport && typeof rowIndex === 'number') {
|
|
54
|
+
// Fast path via range oracle: if the user provides getVisibleColumnRange (and memoizes it),
|
|
55
|
+
// this check is a near-instant JS lookup — no DOM count() round-trip needed.
|
|
56
|
+
// Rows do NOT sync at the barrier here — only scroll boundaries need coordination.
|
|
57
|
+
if (viewport.getVisibleColumnRange) {
|
|
58
|
+
const colRange = await viewport.getVisibleColumnRange(context);
|
|
59
|
+
if (colRange && index >= colRange.first && index <= colRange.last) {
|
|
60
|
+
const rowRange = viewport.getVisibleRowRange
|
|
61
|
+
? await viewport.getVisibleRowRange(context)
|
|
62
|
+
: null;
|
|
63
|
+
const rowVisible = !rowRange || (rowIndex >= rowRange.first && rowIndex <= rowRange.last);
|
|
64
|
+
if (rowVisible && await targetReached()) {
|
|
65
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: col ${index} in visible range [${colRange.first}-${colRange.last}], reading directly`);
|
|
66
|
+
return getCellLocator();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (await targetReached()) {
|
|
71
|
+
// No range oracle — fall back to DOM count() check.
|
|
72
|
+
if (barrier)
|
|
73
|
+
await barrier.sync(index);
|
|
74
|
+
return getCellLocator();
|
|
75
|
+
}
|
|
76
|
+
// Column is out of view. Scroll to bring it in.
|
|
77
|
+
// In synchronized mode, the last row to arrive triggers the scroll once for all rows.
|
|
78
|
+
const scrollIntoViewport = async () => {
|
|
79
|
+
const colRange = viewport.getVisibleColumnRange
|
|
80
|
+
? await viewport.getVisibleColumnRange(context)
|
|
81
|
+
: null;
|
|
82
|
+
const colOutOfView = !colRange || index < colRange.first || index > colRange.last;
|
|
83
|
+
if (colOutOfView) {
|
|
84
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: col ${index} out of view, scrolling`);
|
|
85
|
+
if (viewport.scrollToColumn) {
|
|
86
|
+
await viewport.scrollToColumn(context, index);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Some row locators (notably rows returned by findRow()) have a logical
|
|
90
|
+
// rowIndex that is not the grid's viewport coordinate. If the target cell
|
|
91
|
+
// is already mounted after any column scroll, avoid a misleading row jump.
|
|
92
|
+
if (await targetReached()) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// 2D scroll hazard guard: scrolling horizontally may have unmounted the target row.
|
|
96
|
+
const rowRange = viewport.getVisibleRowRange
|
|
97
|
+
? await viewport.getVisibleRowRange(context)
|
|
98
|
+
: null;
|
|
99
|
+
const rowOutOfView = rowRange && (rowIndex < rowRange.first || rowIndex > rowRange.last);
|
|
100
|
+
if (rowOutOfView) {
|
|
101
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: row ${rowIndex} out of view after col scroll, recovering`);
|
|
102
|
+
if (viewport.scrollToRow) {
|
|
103
|
+
await viewport.scrollToRow(context, rowIndex);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
if (barrier) {
|
|
108
|
+
await barrier.sync(index, scrollIntoViewport);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
await scrollIntoViewport();
|
|
112
|
+
}
|
|
113
|
+
if (await targetReached()) {
|
|
114
|
+
return getCellLocator();
|
|
115
|
+
}
|
|
116
|
+
// Cell still not accessible — fall through to navigation primitives if configured.
|
|
117
|
+
if (!config.strategies.navigation) {
|
|
118
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: viewport phase could not reach cell, no navigation fallback configured');
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
41
122
|
// Use navigation primitives if available
|
|
42
123
|
if (config.strategies.navigation) {
|
|
43
124
|
const nav = config.strategies.navigation;
|
|
44
125
|
if (typeof rowIndex !== 'number') {
|
|
45
126
|
throw new Error('Row index is required for navigation');
|
|
46
127
|
}
|
|
47
|
-
const navigateOnce = () =>
|
|
128
|
+
const navigateOnce = async () => {
|
|
48
129
|
// Get current position again to be sure
|
|
49
130
|
let currRow = 0;
|
|
50
131
|
let currCol = 0;
|
|
51
132
|
if (config.strategies.getActiveCell) {
|
|
52
|
-
const ac =
|
|
133
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
53
134
|
if (ac) {
|
|
54
135
|
currRow = ac.rowIndex;
|
|
55
136
|
currCol = ac.columnIndex;
|
|
@@ -60,67 +141,51 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
60
141
|
// Move one step vertically
|
|
61
142
|
if (rDiff > 0 && nav.goDown) {
|
|
62
143
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving down');
|
|
63
|
-
|
|
144
|
+
await nav.goDown(context);
|
|
64
145
|
}
|
|
65
146
|
else if (rDiff < 0 && nav.goUp) {
|
|
66
147
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving up');
|
|
67
|
-
|
|
148
|
+
await nav.goUp(context);
|
|
68
149
|
}
|
|
69
150
|
// Move one step horizontally
|
|
70
151
|
if (cDiff > 0 && nav.goRight) {
|
|
71
152
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving right');
|
|
72
|
-
|
|
153
|
+
await nav.goRight(context);
|
|
73
154
|
}
|
|
74
155
|
else if (cDiff < 0 && nav.goLeft) {
|
|
75
156
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving left');
|
|
76
|
-
|
|
157
|
+
await nav.goLeft(context);
|
|
77
158
|
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (config.strategies.getActiveCell) {
|
|
81
|
-
const ac = yield config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
82
|
-
if (ac && ac.rowIndex === rowIndex && ac.columnIndex === index)
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
// Locator presence: virtualized grids (e.g. Glide) often attach the target `td` after scroll
|
|
86
|
-
// while focus id lags; do not require getActiveCell to match for this to count as reached.
|
|
87
|
-
const cell = config.strategies.getCellLocator
|
|
88
|
-
? config.strategies.getCellLocator({ row: rowLocator, columnName: column, columnIndex: index, page })
|
|
89
|
-
: resolve(config.cellSelector, rowLocator).nth(index);
|
|
90
|
-
return ((yield cell.count()) > 0);
|
|
91
|
-
});
|
|
92
|
-
if (yield targetReached()) {
|
|
159
|
+
};
|
|
160
|
+
if (await targetReached()) {
|
|
93
161
|
// Already there. If we have a barrier, check-in to stay in lock-step.
|
|
94
162
|
if (barrier)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
? config.strategies.getCellLocator({ row: rowLocator, columnName: column, columnIndex: index, page })
|
|
98
|
-
: resolve(config.cellSelector, rowLocator).nth(index);
|
|
99
|
-
return cell;
|
|
163
|
+
await barrier.sync(index);
|
|
164
|
+
return getCellLocator();
|
|
100
165
|
}
|
|
101
166
|
// If getActiveCell is present but returns null (no focus), and cell is in DOM,
|
|
102
167
|
// try to focus it once before entering navigation loop.
|
|
103
168
|
if (config.strategies.getActiveCell) {
|
|
104
|
-
const ac =
|
|
169
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
105
170
|
if (!ac) {
|
|
106
171
|
const cell = resolve(config.cellSelector, rowLocator).nth(index);
|
|
107
|
-
if (
|
|
172
|
+
if (await cell.count() > 0) {
|
|
108
173
|
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: cell "${column}" found but not focused; focusing.`);
|
|
109
|
-
|
|
174
|
+
await cell.focus();
|
|
110
175
|
// Re-check target
|
|
111
|
-
if (
|
|
176
|
+
if (await targetReached()) {
|
|
112
177
|
if (barrier)
|
|
113
|
-
|
|
178
|
+
await barrier.sync(index);
|
|
114
179
|
return cell;
|
|
115
180
|
}
|
|
116
181
|
}
|
|
117
182
|
}
|
|
118
183
|
}
|
|
119
|
-
const navigateUntilReached = () =>
|
|
184
|
+
const navigateUntilReached = async () => {
|
|
120
185
|
let currRow = 0;
|
|
121
186
|
let currCol = 0;
|
|
122
187
|
if (config.strategies.getActiveCell) {
|
|
123
|
-
const ac =
|
|
188
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
124
189
|
if (ac) {
|
|
125
190
|
currRow = ac.rowIndex;
|
|
126
191
|
currCol = ac.columnIndex;
|
|
@@ -130,12 +195,12 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
130
195
|
// Navigate vertically (tight loop)
|
|
131
196
|
for (let i = 0; i < Math.abs(rDiff); i++) {
|
|
132
197
|
if (rDiff > 0 && nav.goDown)
|
|
133
|
-
|
|
198
|
+
await nav.goDown(context);
|
|
134
199
|
else if (rDiff < 0 && nav.goUp)
|
|
135
|
-
|
|
200
|
+
await nav.goUp(context);
|
|
136
201
|
}
|
|
137
202
|
if (config.strategies.getActiveCell) {
|
|
138
|
-
const ac =
|
|
203
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
139
204
|
if (ac) {
|
|
140
205
|
currRow = ac.rowIndex;
|
|
141
206
|
currCol = ac.columnIndex;
|
|
@@ -143,9 +208,9 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
143
208
|
}
|
|
144
209
|
if (index === 0 && nav.snapFirstColumnIntoView) {
|
|
145
210
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: snapFirstColumnIntoView for column index 0');
|
|
146
|
-
|
|
211
|
+
await nav.snapFirstColumnIntoView(context);
|
|
147
212
|
if (config.strategies.getActiveCell) {
|
|
148
|
-
const ac =
|
|
213
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
149
214
|
if (ac) {
|
|
150
215
|
currRow = ac.rowIndex;
|
|
151
216
|
currCol = ac.columnIndex;
|
|
@@ -154,7 +219,7 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
154
219
|
// Home moves a11y focus within the current row to column 0. If focus row !== target row
|
|
155
220
|
// (e.g. still on previous row), Home jumps to grid origin and breaks `tr.nth(k)` reads.
|
|
156
221
|
if (typeof rowIndex === 'number' && currRow === rowIndex) {
|
|
157
|
-
|
|
222
|
+
await rootLocator.evaluate((el) => {
|
|
158
223
|
var _a;
|
|
159
224
|
const canvas = el.closest('canvas') || ((_a = el.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('canvas'));
|
|
160
225
|
if (canvas instanceof HTMLCanvasElement) {
|
|
@@ -162,10 +227,10 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
162
227
|
canvas.focus();
|
|
163
228
|
}
|
|
164
229
|
});
|
|
165
|
-
|
|
166
|
-
|
|
230
|
+
await page.keyboard.press('Home');
|
|
231
|
+
await page.waitForTimeout(120);
|
|
167
232
|
if (config.strategies.getActiveCell) {
|
|
168
|
-
const ac =
|
|
233
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
169
234
|
if (ac) {
|
|
170
235
|
currRow = ac.rowIndex;
|
|
171
236
|
currCol = ac.columnIndex;
|
|
@@ -176,9 +241,9 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
176
241
|
let cDiff = index - currCol;
|
|
177
242
|
if (Math.abs(cDiff) > 12 && nav.seekColumnIndex) {
|
|
178
243
|
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: seekColumnIndex approx for column ${index}`);
|
|
179
|
-
|
|
244
|
+
await nav.seekColumnIndex(context, index);
|
|
180
245
|
if (config.strategies.getActiveCell) {
|
|
181
|
-
const ac =
|
|
246
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
182
247
|
if (ac) {
|
|
183
248
|
currRow = ac.rowIndex;
|
|
184
249
|
currCol = ac.columnIndex;
|
|
@@ -188,14 +253,14 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
188
253
|
}
|
|
189
254
|
for (let i = 0; i < Math.abs(cDiff); i++) {
|
|
190
255
|
if (cDiff > 0 && nav.goRight)
|
|
191
|
-
|
|
256
|
+
await nav.goRight(context);
|
|
192
257
|
else if (cDiff < 0 && nav.goLeft)
|
|
193
|
-
|
|
258
|
+
await nav.goLeft(context);
|
|
194
259
|
}
|
|
195
260
|
const horizontalSteps = Math.abs(cDiff);
|
|
196
261
|
if (horizontalSteps > 0) {
|
|
197
262
|
const settleMs = Math.min(2500, 60 + horizontalSteps * 12);
|
|
198
|
-
|
|
263
|
+
await page.waitForTimeout(settleMs);
|
|
199
264
|
}
|
|
200
265
|
// Wait for active cell to match target: poll getActiveCell or fallback to fixed delay
|
|
201
266
|
// This is the "Midas Touch" buffer needed for Glide's async accessibility updates.
|
|
@@ -204,40 +269,38 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
204
269
|
const start = Date.now();
|
|
205
270
|
while (Date.now() - start < maxWaitMs) {
|
|
206
271
|
if (config.strategies.getActiveCell) {
|
|
207
|
-
const ac =
|
|
272
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
208
273
|
if (ac && ac.rowIndex === rowIndex && ac.columnIndex === index) {
|
|
209
274
|
return ac.locator;
|
|
210
275
|
}
|
|
211
276
|
}
|
|
212
|
-
if (
|
|
277
|
+
if (await targetReached()) {
|
|
213
278
|
break;
|
|
214
279
|
}
|
|
215
|
-
|
|
280
|
+
await page.waitForTimeout(pollIntervalMs);
|
|
216
281
|
}
|
|
217
|
-
}
|
|
282
|
+
};
|
|
218
283
|
// Not there, perform (coordinated) navigation
|
|
219
284
|
let navResult;
|
|
220
285
|
if (barrier) {
|
|
221
|
-
navResult =
|
|
286
|
+
navResult = await barrier.sync(index, navigateUntilReached);
|
|
222
287
|
}
|
|
223
288
|
else {
|
|
224
|
-
navResult =
|
|
289
|
+
navResult = await navigateUntilReached();
|
|
225
290
|
}
|
|
226
291
|
if (navResult instanceof Object && navResult._isLocator) {
|
|
227
292
|
const loc = navResult;
|
|
228
|
-
if (
|
|
293
|
+
if (await loc.count() > 0)
|
|
229
294
|
return loc;
|
|
230
295
|
}
|
|
231
296
|
// Return final locator if reached
|
|
232
|
-
const finalCell =
|
|
233
|
-
|
|
234
|
-
: resolve(config.cellSelector, rowLocator).nth(index);
|
|
235
|
-
if ((yield finalCell.count()) > 0)
|
|
297
|
+
const finalCell = getCellLocator();
|
|
298
|
+
if (await finalCell.count() > 0)
|
|
236
299
|
return finalCell;
|
|
237
300
|
return null;
|
|
238
301
|
}
|
|
239
302
|
return null;
|
|
240
|
-
}
|
|
303
|
+
};
|
|
241
304
|
/**
|
|
242
305
|
* Factory to create a SmartRow by extending a Playwright Locator.
|
|
243
306
|
* We avoid Class/Proxy to ensure full compatibility with Playwright's expect(locator) matchers.
|
|
@@ -262,10 +325,12 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
262
325
|
if (config.strategies.getCellLocator) {
|
|
263
326
|
return config.strategies.getCellLocator({
|
|
264
327
|
row: rowLocator,
|
|
328
|
+
root: rootLocator,
|
|
265
329
|
columnName: colName,
|
|
266
330
|
columnIndex: idx,
|
|
267
331
|
rowIndex: rowIndex,
|
|
268
|
-
page: rootLocator.page()
|
|
332
|
+
page: rootLocator.page(),
|
|
333
|
+
config
|
|
269
334
|
});
|
|
270
335
|
}
|
|
271
336
|
return resolve(config.cellSelector, rowLocator).nth(idx);
|
|
@@ -273,7 +338,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
273
338
|
smart.wasFound = () => {
|
|
274
339
|
return !smart[sentinel_1.SENTINEL_ROW];
|
|
275
340
|
};
|
|
276
|
-
smart.toJSON = (options) =>
|
|
341
|
+
smart.toJSON = async (options) => {
|
|
277
342
|
var _a;
|
|
278
343
|
const result = {};
|
|
279
344
|
const page = rootLocator.page();
|
|
@@ -281,12 +346,12 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
281
346
|
// Uses the table reference if available, otherwise falls back to index-based lookup.
|
|
282
347
|
const getHeaderCell = (table === null || table === void 0 ? void 0 : table.getHeaderCell)
|
|
283
348
|
? table.getHeaderCell.bind(table)
|
|
284
|
-
: (colName) =>
|
|
349
|
+
: async (colName) => {
|
|
285
350
|
const idx = map.get(colName);
|
|
286
351
|
if (idx === undefined)
|
|
287
352
|
throw new Error(`Column "${colName}" not found`);
|
|
288
353
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
289
|
-
}
|
|
354
|
+
};
|
|
290
355
|
for (const [col, idx] of map.entries()) {
|
|
291
356
|
if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(col)) {
|
|
292
357
|
continue;
|
|
@@ -298,16 +363,18 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
298
363
|
const cell = config.strategies.getCellLocator
|
|
299
364
|
? config.strategies.getCellLocator({
|
|
300
365
|
row: rowLocator,
|
|
366
|
+
root: rootLocator,
|
|
301
367
|
columnName: col,
|
|
302
368
|
columnIndex: idx,
|
|
303
369
|
rowIndex: rowIndex,
|
|
304
|
-
page: page
|
|
370
|
+
page: page,
|
|
371
|
+
config
|
|
305
372
|
})
|
|
306
373
|
: resolve(config.cellSelector, rowLocator).nth(idx);
|
|
307
374
|
let targetCell = cell;
|
|
308
375
|
// Always call _navigateToCell to ensure Lock-Step synchronization
|
|
309
376
|
// even if the cell is already present (it handles check-in inside)
|
|
310
|
-
const navigatedCell =
|
|
377
|
+
const navigatedCell = await _navigateToCell({
|
|
311
378
|
config,
|
|
312
379
|
rootLocator,
|
|
313
380
|
page,
|
|
@@ -325,7 +392,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
325
392
|
// Call beforeCellRead hook if configured.
|
|
326
393
|
// Fires for BOTH columnOverrides.read and the default innerText path.
|
|
327
394
|
if (config.strategies.beforeCellRead) {
|
|
328
|
-
|
|
395
|
+
await config.strategies.beforeCellRead({
|
|
329
396
|
cell: targetCell,
|
|
330
397
|
columnName: col,
|
|
331
398
|
columnIndex: idx,
|
|
@@ -337,18 +404,18 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
337
404
|
}
|
|
338
405
|
if (mapper) {
|
|
339
406
|
// Apply mapper
|
|
340
|
-
const mappedValue =
|
|
407
|
+
const mappedValue = await mapper(targetCell);
|
|
341
408
|
result[col] = mappedValue;
|
|
342
409
|
}
|
|
343
410
|
else {
|
|
344
411
|
// Default string extraction
|
|
345
|
-
const text =
|
|
412
|
+
const text = await targetCell.innerText();
|
|
346
413
|
result[col] = (text || '').trim();
|
|
347
414
|
}
|
|
348
415
|
}
|
|
349
416
|
return result;
|
|
350
|
-
}
|
|
351
|
-
smart.smartFill = (data, fillOptions) =>
|
|
417
|
+
};
|
|
418
|
+
smart.smartFill = async (data, fillOptions) => {
|
|
352
419
|
var _a;
|
|
353
420
|
(0, debugUtils_1.logDebug)(config, 'info', 'Filling row', data);
|
|
354
421
|
for (const [colName, value] of Object.entries(data)) {
|
|
@@ -358,7 +425,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
358
425
|
if (colIdx === undefined) {
|
|
359
426
|
throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
|
|
360
427
|
}
|
|
361
|
-
|
|
428
|
+
await _navigateToCell({
|
|
362
429
|
config,
|
|
363
430
|
rootLocator,
|
|
364
431
|
page: rootLocator.page(),
|
|
@@ -374,9 +441,9 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
374
441
|
const cellLocator = smart.getCell(colName);
|
|
375
442
|
let currentValue;
|
|
376
443
|
if (columnOverride.read) {
|
|
377
|
-
currentValue =
|
|
444
|
+
currentValue = await columnOverride.read(cellLocator);
|
|
378
445
|
}
|
|
379
|
-
|
|
446
|
+
await columnOverride.write({
|
|
380
447
|
cell: cellLocator,
|
|
381
448
|
targetValue: value,
|
|
382
449
|
currentValue,
|
|
@@ -386,7 +453,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
386
453
|
else {
|
|
387
454
|
const strategy = config.strategies.fill || fill_1.FillStrategies.default;
|
|
388
455
|
(0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
|
|
389
|
-
|
|
456
|
+
await strategy({
|
|
390
457
|
row: smart,
|
|
391
458
|
columnName: colName,
|
|
392
459
|
value,
|
|
@@ -399,11 +466,11 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
399
466
|
});
|
|
400
467
|
}
|
|
401
468
|
// Delay after filling
|
|
402
|
-
|
|
469
|
+
await (0, debugUtils_1.debugDelay)(config, 'getCell');
|
|
403
470
|
}
|
|
404
471
|
(0, debugUtils_1.logDebug)(config, 'info', 'Row fill complete');
|
|
405
|
-
}
|
|
406
|
-
smart.bringIntoView = () =>
|
|
472
|
+
};
|
|
473
|
+
smart.bringIntoView = async () => {
|
|
407
474
|
if (rowIndex === undefined) {
|
|
408
475
|
throw new Error('Cannot bring row into view - row index is unknown. Use getRowByIndex() instead of getRow().');
|
|
409
476
|
}
|
|
@@ -416,19 +483,19 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
416
483
|
const setCurrent = (n) => { parentTable.currentPageIndex = n; };
|
|
417
484
|
if (primitives.goToPage) {
|
|
418
485
|
(0, debugUtils_1.logDebug)(config, 'info', `bringIntoView: Navigating to page ${tablePageIndex} (goToPage retry loop)`);
|
|
419
|
-
|
|
486
|
+
await (0, paginationPath_1.executeNavigationWithGoToPageRetry)(tablePageIndex, primitives, context, getCurrent, setCurrent);
|
|
420
487
|
}
|
|
421
488
|
else {
|
|
422
489
|
const path = (0, paginationPath_1.planNavigationPath)(getCurrent(), tablePageIndex, primitives);
|
|
423
490
|
if (path.length > 0) {
|
|
424
491
|
(0, debugUtils_1.logDebug)(config, 'info', `bringIntoView: Executing navigation path to page ${tablePageIndex} (${path.length} step(s))`);
|
|
425
|
-
|
|
492
|
+
await (0, paginationPath_1.executeNavigationPath)(path, primitives, context, getCurrent, setCurrent);
|
|
426
493
|
}
|
|
427
494
|
else if (primitives.goToFirst && primitives.goNext && tablePageIndex >= 0) {
|
|
428
495
|
(0, debugUtils_1.logDebug)(config, 'info', `bringIntoView: going to first page and looping goNext until we reach page ${tablePageIndex}`);
|
|
429
|
-
|
|
496
|
+
await primitives.goToFirst(context);
|
|
430
497
|
for (let i = 0; i < tablePageIndex; i++) {
|
|
431
|
-
const ok =
|
|
498
|
+
const ok = await primitives.goNext(context);
|
|
432
499
|
if (!ok)
|
|
433
500
|
throw new Error(`bringIntoView: goNext failed before reaching page ${tablePageIndex}.`);
|
|
434
501
|
}
|
|
@@ -441,10 +508,10 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
441
508
|
}
|
|
442
509
|
}
|
|
443
510
|
// Delay after pagination/finding before scrolling
|
|
444
|
-
|
|
511
|
+
await (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
445
512
|
// Scroll row into view using Playwright's built-in method
|
|
446
|
-
|
|
447
|
-
}
|
|
513
|
+
await rowLocator.scrollIntoViewIfNeeded();
|
|
514
|
+
};
|
|
448
515
|
return smart;
|
|
449
516
|
};
|
|
450
517
|
exports.default = createSmartRow;
|
|
@@ -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.CellNavigationStrategies = void 0;
|
|
13
4
|
exports.CellNavigationStrategies = {
|
|
@@ -15,7 +6,7 @@ exports.CellNavigationStrategies = {
|
|
|
15
6
|
* Default strategy: Assumes column is accessible or standard scrolling works.
|
|
16
7
|
* No specific action taken other than what Playwright's default locator handling does.
|
|
17
8
|
*/
|
|
18
|
-
default: () =>
|
|
9
|
+
default: async () => {
|
|
19
10
|
// No-op
|
|
20
|
-
}
|
|
11
|
+
}
|
|
21
12
|
};
|
|
@@ -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.DedupeStrategies = void 0;
|
|
13
4
|
exports.DedupeStrategies = {
|
|
@@ -16,12 +7,12 @@ exports.DedupeStrategies = {
|
|
|
16
7
|
* Useful for virtualized tables where row DOM elements are reused but content changes.
|
|
17
8
|
* @param tolerance Pixel tolerance for position comparison (default: 2)
|
|
18
9
|
*/
|
|
19
|
-
byTopPosition: (tolerance = 2) => (row) =>
|
|
20
|
-
const box =
|
|
10
|
+
byTopPosition: (tolerance = 2) => async (row) => {
|
|
11
|
+
const box = await row.boundingBox();
|
|
21
12
|
if (!box)
|
|
22
13
|
return 'unknown';
|
|
23
14
|
// Round to nearest tolerance
|
|
24
15
|
const y = Math.round(box.y / tolerance) * tolerance;
|
|
25
16
|
return `pos_${y}`;
|
|
26
|
-
}
|
|
17
|
+
}
|
|
27
18
|
};
|