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