@rickcedwhat/playwright-smart-table 6.9.0 → 6.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/dist/engine/rowFinder.js +153 -168
- package/dist/engine/tableIteration.js +105 -120
- package/dist/engine/tableMapper.js +88 -103
- package/dist/minimalConfigContext.d.ts +1 -1
- package/dist/minimalConfigContext.js +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/presets/glide/columns.js +23 -32
- package/dist/presets/glide/headers.js +18 -27
- package/dist/presets/glide/index.d.ts +9 -9
- package/dist/presets/glide/index.js +24 -33
- package/dist/presets/mui.js +197 -124
- package/dist/presets/rdg.d.ts +1 -1
- package/dist/presets/rdg.js +32 -41
- package/dist/smartRow.js +209 -101
- package/dist/strategies/columns.js +2 -11
- package/dist/strategies/dedupe.js +3 -12
- package/dist/strategies/fill.js +20 -29
- package/dist/strategies/headers.js +22 -31
- package/dist/strategies/index.d.ts +7 -0
- package/dist/strategies/index.js +3 -0
- package/dist/strategies/loading.js +24 -33
- package/dist/strategies/pagination.d.ts +10 -0
- package/dist/strategies/pagination.js +49 -30
- package/dist/strategies/sorting.js +18 -31
- package/dist/strategies/stabilization.js +28 -37
- package/dist/strategies/viewport.d.ts +52 -0
- package/dist/strategies/viewport.js +123 -0
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +160 -5
- package/dist/types.d.ts +153 -5
- package/dist/useTable.js +142 -76
- package/dist/utils/debugUtils.js +5 -16
- package/dist/utils/elementTracker.js +27 -40
- package/dist/utils/mutex.js +5 -16
- package/dist/utils/navigationBarrier.js +25 -36
- package/dist/utils/paginationPath.d.ts +4 -1
- package/dist/utils/paginationPath.js +164 -141
- package/dist/utils/smartRowArray.js +2 -11
- package/dist/utils.js +4 -13
- package/package.json +13 -8
package/dist/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,120 @@ 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
|
+
const colRange = viewport.getVisibleColumnRange ? await viewport.getVisibleColumnRange(context) : null;
|
|
119
|
+
const rowRange = viewport.getVisibleRowRange ? await viewport.getVisibleRowRange(context) : null;
|
|
120
|
+
let errMsg = `SmartTable: could not reach cell for column "${column}" (colIndex ${index}) at row ${rowIndex}.\n`;
|
|
121
|
+
if (colRange) {
|
|
122
|
+
errMsg += ` Visible column range: [${colRange.first}–${colRange.last}]. Column is out of view and no navigation fallback is configured.\n`;
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
errMsg += ` Column is out of view and no navigation fallback is configured.\n`;
|
|
126
|
+
}
|
|
127
|
+
if (rowRange && rowIndex !== undefined) {
|
|
128
|
+
const inView = rowIndex >= rowRange.first && rowIndex <= rowRange.last;
|
|
129
|
+
errMsg += ` Visible row range: [${rowRange.first}–${rowRange.last}]. ${inView ? 'Row is in view.' : 'Row is out of view.'}\n`;
|
|
130
|
+
}
|
|
131
|
+
errMsg += ` → Add a \`strategies.navigation\` or \`strategies.viewport.scrollToColumn\` to handle off-screen columns.`;
|
|
132
|
+
throw new Error(errMsg);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
42
135
|
// Use navigation primitives if available
|
|
43
136
|
if (config.strategies.navigation) {
|
|
44
137
|
const nav = config.strategies.navigation;
|
|
45
138
|
if (typeof rowIndex !== 'number') {
|
|
46
139
|
throw new Error('Row index is required for navigation');
|
|
47
140
|
}
|
|
48
|
-
const navigateOnce = () =>
|
|
141
|
+
const navigateOnce = async () => {
|
|
49
142
|
// Get current position again to be sure
|
|
50
143
|
let currRow = 0;
|
|
51
144
|
let currCol = 0;
|
|
52
145
|
if (config.strategies.getActiveCell) {
|
|
53
|
-
const ac =
|
|
146
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
54
147
|
if (ac) {
|
|
55
148
|
currRow = ac.rowIndex;
|
|
56
149
|
currCol = ac.columnIndex;
|
|
@@ -61,67 +154,51 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
61
154
|
// Move one step vertically
|
|
62
155
|
if (rDiff > 0 && nav.goDown) {
|
|
63
156
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving down');
|
|
64
|
-
|
|
157
|
+
await nav.goDown(context);
|
|
65
158
|
}
|
|
66
159
|
else if (rDiff < 0 && nav.goUp) {
|
|
67
160
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving up');
|
|
68
|
-
|
|
161
|
+
await nav.goUp(context);
|
|
69
162
|
}
|
|
70
163
|
// Move one step horizontally
|
|
71
164
|
if (cDiff > 0 && nav.goRight) {
|
|
72
165
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving right');
|
|
73
|
-
|
|
166
|
+
await nav.goRight(context);
|
|
74
167
|
}
|
|
75
168
|
else if (cDiff < 0 && nav.goLeft) {
|
|
76
169
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: moving left');
|
|
77
|
-
|
|
170
|
+
await nav.goLeft(context);
|
|
78
171
|
}
|
|
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()) {
|
|
172
|
+
};
|
|
173
|
+
if (await targetReached()) {
|
|
94
174
|
// Already there. If we have a barrier, check-in to stay in lock-step.
|
|
95
175
|
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;
|
|
176
|
+
await barrier.sync(index);
|
|
177
|
+
return getCellLocator();
|
|
101
178
|
}
|
|
102
179
|
// If getActiveCell is present but returns null (no focus), and cell is in DOM,
|
|
103
180
|
// try to focus it once before entering navigation loop.
|
|
104
181
|
if (config.strategies.getActiveCell) {
|
|
105
|
-
const ac =
|
|
182
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
106
183
|
if (!ac) {
|
|
107
184
|
const cell = resolve(config.cellSelector, rowLocator).nth(index);
|
|
108
|
-
if (
|
|
185
|
+
if (await cell.count() > 0) {
|
|
109
186
|
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: cell "${column}" found but not focused; focusing.`);
|
|
110
|
-
|
|
187
|
+
await cell.focus();
|
|
111
188
|
// Re-check target
|
|
112
|
-
if (
|
|
189
|
+
if (await targetReached()) {
|
|
113
190
|
if (barrier)
|
|
114
|
-
|
|
191
|
+
await barrier.sync(index);
|
|
115
192
|
return cell;
|
|
116
193
|
}
|
|
117
194
|
}
|
|
118
195
|
}
|
|
119
196
|
}
|
|
120
|
-
const navigateUntilReached = () =>
|
|
197
|
+
const navigateUntilReached = async () => {
|
|
121
198
|
let currRow = 0;
|
|
122
199
|
let currCol = 0;
|
|
123
200
|
if (config.strategies.getActiveCell) {
|
|
124
|
-
const ac =
|
|
201
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
125
202
|
if (ac) {
|
|
126
203
|
currRow = ac.rowIndex;
|
|
127
204
|
currCol = ac.columnIndex;
|
|
@@ -131,12 +208,12 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
131
208
|
// Navigate vertically (tight loop)
|
|
132
209
|
for (let i = 0; i < Math.abs(rDiff); i++) {
|
|
133
210
|
if (rDiff > 0 && nav.goDown)
|
|
134
|
-
|
|
211
|
+
await nav.goDown(context);
|
|
135
212
|
else if (rDiff < 0 && nav.goUp)
|
|
136
|
-
|
|
213
|
+
await nav.goUp(context);
|
|
137
214
|
}
|
|
138
215
|
if (config.strategies.getActiveCell) {
|
|
139
|
-
const ac =
|
|
216
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
140
217
|
if (ac) {
|
|
141
218
|
currRow = ac.rowIndex;
|
|
142
219
|
currCol = ac.columnIndex;
|
|
@@ -144,9 +221,9 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
144
221
|
}
|
|
145
222
|
if (index === 0 && nav.snapFirstColumnIntoView) {
|
|
146
223
|
(0, debugUtils_1.logDebug)(config, 'verbose', '_navigateToCell: snapFirstColumnIntoView for column index 0');
|
|
147
|
-
|
|
224
|
+
await nav.snapFirstColumnIntoView(context);
|
|
148
225
|
if (config.strategies.getActiveCell) {
|
|
149
|
-
const ac =
|
|
226
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
150
227
|
if (ac) {
|
|
151
228
|
currRow = ac.rowIndex;
|
|
152
229
|
currCol = ac.columnIndex;
|
|
@@ -155,7 +232,7 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
155
232
|
// Home moves a11y focus within the current row to column 0. If focus row !== target row
|
|
156
233
|
// (e.g. still on previous row), Home jumps to grid origin and breaks `tr.nth(k)` reads.
|
|
157
234
|
if (typeof rowIndex === 'number' && currRow === rowIndex) {
|
|
158
|
-
|
|
235
|
+
await rootLocator.evaluate((el) => {
|
|
159
236
|
var _a;
|
|
160
237
|
const canvas = el.closest('canvas') || ((_a = el.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('canvas'));
|
|
161
238
|
if (canvas instanceof HTMLCanvasElement) {
|
|
@@ -163,10 +240,10 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
163
240
|
canvas.focus();
|
|
164
241
|
}
|
|
165
242
|
});
|
|
166
|
-
|
|
167
|
-
|
|
243
|
+
await page.keyboard.press('Home');
|
|
244
|
+
await page.waitForTimeout(120);
|
|
168
245
|
if (config.strategies.getActiveCell) {
|
|
169
|
-
const ac =
|
|
246
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
170
247
|
if (ac) {
|
|
171
248
|
currRow = ac.rowIndex;
|
|
172
249
|
currCol = ac.columnIndex;
|
|
@@ -177,9 +254,9 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
177
254
|
let cDiff = index - currCol;
|
|
178
255
|
if (Math.abs(cDiff) > 12 && nav.seekColumnIndex) {
|
|
179
256
|
(0, debugUtils_1.logDebug)(config, 'verbose', `_navigateToCell: seekColumnIndex approx for column ${index}`);
|
|
180
|
-
|
|
257
|
+
await nav.seekColumnIndex(context, index);
|
|
181
258
|
if (config.strategies.getActiveCell) {
|
|
182
|
-
const ac =
|
|
259
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
183
260
|
if (ac) {
|
|
184
261
|
currRow = ac.rowIndex;
|
|
185
262
|
currCol = ac.columnIndex;
|
|
@@ -189,14 +266,14 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
189
266
|
}
|
|
190
267
|
for (let i = 0; i < Math.abs(cDiff); i++) {
|
|
191
268
|
if (cDiff > 0 && nav.goRight)
|
|
192
|
-
|
|
269
|
+
await nav.goRight(context);
|
|
193
270
|
else if (cDiff < 0 && nav.goLeft)
|
|
194
|
-
|
|
271
|
+
await nav.goLeft(context);
|
|
195
272
|
}
|
|
196
273
|
const horizontalSteps = Math.abs(cDiff);
|
|
197
274
|
if (horizontalSteps > 0) {
|
|
198
275
|
const settleMs = Math.min(2500, 60 + horizontalSteps * 12);
|
|
199
|
-
|
|
276
|
+
await page.waitForTimeout(settleMs);
|
|
200
277
|
}
|
|
201
278
|
// Wait for active cell to match target: poll getActiveCell or fallback to fixed delay
|
|
202
279
|
// This is the "Midas Touch" buffer needed for Glide's async accessibility updates.
|
|
@@ -205,40 +282,40 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
205
282
|
const start = Date.now();
|
|
206
283
|
while (Date.now() - start < maxWaitMs) {
|
|
207
284
|
if (config.strategies.getActiveCell) {
|
|
208
|
-
const ac =
|
|
285
|
+
const ac = await config.strategies.getActiveCell({ config, root: rootLocator, page, resolve });
|
|
209
286
|
if (ac && ac.rowIndex === rowIndex && ac.columnIndex === index) {
|
|
210
287
|
return ac.locator;
|
|
211
288
|
}
|
|
212
289
|
}
|
|
213
|
-
if (
|
|
290
|
+
if (await targetReached()) {
|
|
214
291
|
break;
|
|
215
292
|
}
|
|
216
|
-
|
|
293
|
+
await page.waitForTimeout(pollIntervalMs);
|
|
217
294
|
}
|
|
218
|
-
}
|
|
295
|
+
};
|
|
219
296
|
// Not there, perform (coordinated) navigation
|
|
220
297
|
let navResult;
|
|
221
298
|
if (barrier) {
|
|
222
|
-
navResult =
|
|
299
|
+
navResult = await barrier.sync(index, navigateUntilReached);
|
|
223
300
|
}
|
|
224
301
|
else {
|
|
225
|
-
navResult =
|
|
302
|
+
navResult = await navigateUntilReached();
|
|
226
303
|
}
|
|
227
304
|
if (navResult instanceof Object && navResult._isLocator) {
|
|
228
305
|
const loc = navResult;
|
|
229
|
-
if (
|
|
306
|
+
if (await loc.count() > 0)
|
|
230
307
|
return loc;
|
|
231
308
|
}
|
|
232
309
|
// Return final locator if reached
|
|
233
|
-
const finalCell =
|
|
234
|
-
|
|
235
|
-
: resolve(config.cellSelector, rowLocator).nth(index);
|
|
236
|
-
if ((yield finalCell.count()) > 0)
|
|
310
|
+
const finalCell = getCellLocator();
|
|
311
|
+
if (await finalCell.count() > 0)
|
|
237
312
|
return finalCell;
|
|
238
|
-
|
|
313
|
+
throw new Error(`SmartTable: could not reach cell for column "${column}" (colIndex ${index}) at row ${rowIndex} after exhausting navigation strategies. Ensure navigation primitives are correctly implemented.`);
|
|
239
314
|
}
|
|
240
|
-
|
|
241
|
-
|
|
315
|
+
// No horizontal strategies configured; assume table is 1D or fully rendered,
|
|
316
|
+
// and let standard Playwright auto-waiting handle any lazy-loading delays.
|
|
317
|
+
return getCellLocator();
|
|
318
|
+
};
|
|
242
319
|
/**
|
|
243
320
|
* Factory to create a SmartRow by extending a Playwright Locator.
|
|
244
321
|
* We avoid Class/Proxy to ensure full compatibility with Playwright's expect(locator) matchers.
|
|
@@ -260,21 +337,44 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
260
337
|
if (idx === undefined) {
|
|
261
338
|
throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
|
|
262
339
|
}
|
|
340
|
+
let baseLocator;
|
|
263
341
|
if (config.strategies.getCellLocator) {
|
|
264
|
-
|
|
342
|
+
baseLocator = config.strategies.getCellLocator({
|
|
265
343
|
row: rowLocator,
|
|
344
|
+
root: rootLocator,
|
|
266
345
|
columnName: colName,
|
|
267
346
|
columnIndex: idx,
|
|
268
347
|
rowIndex: rowIndex,
|
|
269
|
-
page: rootLocator.page()
|
|
348
|
+
page: rootLocator.page(),
|
|
349
|
+
config
|
|
270
350
|
});
|
|
271
351
|
}
|
|
272
|
-
|
|
352
|
+
else {
|
|
353
|
+
baseLocator = resolve(config.cellSelector, rowLocator).nth(idx);
|
|
354
|
+
}
|
|
355
|
+
const smartCell = baseLocator;
|
|
356
|
+
smartCell.bringIntoView = async () => {
|
|
357
|
+
const navigatedCell = await _navigateToCell({
|
|
358
|
+
config,
|
|
359
|
+
rootLocator,
|
|
360
|
+
page: rootLocator.page(),
|
|
361
|
+
resolve,
|
|
362
|
+
column: colName,
|
|
363
|
+
index: idx,
|
|
364
|
+
rowLocator,
|
|
365
|
+
rowIndex,
|
|
366
|
+
barrier: smart._barrier
|
|
367
|
+
});
|
|
368
|
+
if (navigatedCell && navigatedCell._locator) {
|
|
369
|
+
smartCell._locator = navigatedCell._locator;
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
return smartCell;
|
|
273
373
|
};
|
|
274
374
|
smart.wasFound = () => {
|
|
275
375
|
return !smart[sentinel_1.SENTINEL_ROW];
|
|
276
376
|
};
|
|
277
|
-
smart.toJSON = (options) =>
|
|
377
|
+
smart.toJSON = async (options) => {
|
|
278
378
|
var _a;
|
|
279
379
|
const result = {};
|
|
280
380
|
const page = rootLocator.page();
|
|
@@ -282,12 +382,12 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
282
382
|
// Uses the table reference if available, otherwise falls back to index-based lookup.
|
|
283
383
|
const getHeaderCell = (table === null || table === void 0 ? void 0 : table.getHeaderCell)
|
|
284
384
|
? table.getHeaderCell.bind(table)
|
|
285
|
-
: (colName) =>
|
|
385
|
+
: async (colName) => {
|
|
286
386
|
const idx = map.get(colName);
|
|
287
387
|
if (idx === undefined)
|
|
288
388
|
throw new Error(`Column "${colName}" not found`);
|
|
289
389
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
290
|
-
}
|
|
390
|
+
};
|
|
291
391
|
for (const [col, idx] of map.entries()) {
|
|
292
392
|
if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(col)) {
|
|
293
393
|
continue;
|
|
@@ -299,16 +399,18 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
299
399
|
const cell = config.strategies.getCellLocator
|
|
300
400
|
? config.strategies.getCellLocator({
|
|
301
401
|
row: rowLocator,
|
|
402
|
+
root: rootLocator,
|
|
302
403
|
columnName: col,
|
|
303
404
|
columnIndex: idx,
|
|
304
405
|
rowIndex: rowIndex,
|
|
305
|
-
page: page
|
|
406
|
+
page: page,
|
|
407
|
+
config
|
|
306
408
|
})
|
|
307
409
|
: resolve(config.cellSelector, rowLocator).nth(idx);
|
|
308
410
|
let targetCell = cell;
|
|
309
411
|
// Always call _navigateToCell to ensure Lock-Step synchronization
|
|
310
412
|
// even if the cell is already present (it handles check-in inside)
|
|
311
|
-
const navigatedCell =
|
|
413
|
+
const navigatedCell = await _navigateToCell({
|
|
312
414
|
config,
|
|
313
415
|
rootLocator,
|
|
314
416
|
page,
|
|
@@ -326,7 +428,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
326
428
|
// Call beforeCellRead hook if configured.
|
|
327
429
|
// Fires for BOTH columnOverrides.read and the default innerText path.
|
|
328
430
|
if (config.strategies.beforeCellRead) {
|
|
329
|
-
|
|
431
|
+
await config.strategies.beforeCellRead({
|
|
330
432
|
cell: targetCell,
|
|
331
433
|
columnName: col,
|
|
332
434
|
columnIndex: idx,
|
|
@@ -338,18 +440,18 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
338
440
|
}
|
|
339
441
|
if (mapper) {
|
|
340
442
|
// Apply mapper
|
|
341
|
-
const mappedValue =
|
|
443
|
+
const mappedValue = await mapper(targetCell);
|
|
342
444
|
result[col] = mappedValue;
|
|
343
445
|
}
|
|
344
446
|
else {
|
|
345
447
|
// Default string extraction
|
|
346
|
-
const text =
|
|
448
|
+
const text = await targetCell.innerText();
|
|
347
449
|
result[col] = (text || '').trim();
|
|
348
450
|
}
|
|
349
451
|
}
|
|
350
452
|
return result;
|
|
351
|
-
}
|
|
352
|
-
smart.smartFill = (data, fillOptions) =>
|
|
453
|
+
};
|
|
454
|
+
smart.smartFill = async (data, fillOptions) => {
|
|
353
455
|
var _a;
|
|
354
456
|
(0, debugUtils_1.logDebug)(config, 'info', 'Filling row', data);
|
|
355
457
|
for (const [colName, value] of Object.entries(data)) {
|
|
@@ -359,7 +461,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
359
461
|
if (colIdx === undefined) {
|
|
360
462
|
throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
|
|
361
463
|
}
|
|
362
|
-
|
|
464
|
+
await _navigateToCell({
|
|
363
465
|
config,
|
|
364
466
|
rootLocator,
|
|
365
467
|
page: rootLocator.page(),
|
|
@@ -375,9 +477,9 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
375
477
|
const cellLocator = smart.getCell(colName);
|
|
376
478
|
let currentValue;
|
|
377
479
|
if (columnOverride.read) {
|
|
378
|
-
currentValue =
|
|
480
|
+
currentValue = await columnOverride.read(cellLocator);
|
|
379
481
|
}
|
|
380
|
-
|
|
482
|
+
await columnOverride.write({
|
|
381
483
|
cell: cellLocator,
|
|
382
484
|
targetValue: value,
|
|
383
485
|
currentValue,
|
|
@@ -387,7 +489,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
387
489
|
else {
|
|
388
490
|
const strategy = config.strategies.fill || fill_1.FillStrategies.default;
|
|
389
491
|
(0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
|
|
390
|
-
|
|
492
|
+
await strategy({
|
|
391
493
|
row: smart,
|
|
392
494
|
columnName: colName,
|
|
393
495
|
value,
|
|
@@ -400,11 +502,11 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
400
502
|
});
|
|
401
503
|
}
|
|
402
504
|
// Delay after filling
|
|
403
|
-
|
|
505
|
+
await (0, debugUtils_1.debugDelay)(config, 'getCell');
|
|
404
506
|
}
|
|
405
507
|
(0, debugUtils_1.logDebug)(config, 'info', 'Row fill complete');
|
|
406
|
-
}
|
|
407
|
-
smart.bringIntoView = () =>
|
|
508
|
+
};
|
|
509
|
+
smart.bringIntoView = async () => {
|
|
408
510
|
if (rowIndex === undefined) {
|
|
409
511
|
throw new Error('Cannot bring row into view - row index is unknown. Use getRowByIndex() instead of getRow().');
|
|
410
512
|
}
|
|
@@ -417,19 +519,25 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
417
519
|
const setCurrent = (n) => { parentTable.currentPageIndex = n; };
|
|
418
520
|
if (primitives.goToPage) {
|
|
419
521
|
(0, debugUtils_1.logDebug)(config, 'info', `bringIntoView: Navigating to page ${tablePageIndex} (goToPage retry loop)`);
|
|
420
|
-
|
|
522
|
+
await (0, paginationPath_1.executeNavigationWithGoToPageRetry)(tablePageIndex, primitives, context, getCurrent, setCurrent);
|
|
421
523
|
}
|
|
422
524
|
else {
|
|
423
|
-
|
|
525
|
+
let totalPages;
|
|
526
|
+
if (primitives.getTotalPages) {
|
|
527
|
+
const tp = await primitives.getTotalPages(context);
|
|
528
|
+
if (tp !== null)
|
|
529
|
+
totalPages = tp;
|
|
530
|
+
}
|
|
531
|
+
const path = (0, paginationPath_1.planNavigationPath)(getCurrent(), tablePageIndex, primitives, totalPages);
|
|
424
532
|
if (path.length > 0) {
|
|
425
533
|
(0, debugUtils_1.logDebug)(config, 'info', `bringIntoView: Executing navigation path to page ${tablePageIndex} (${path.length} step(s))`);
|
|
426
|
-
|
|
534
|
+
await (0, paginationPath_1.executeNavigationPath)(path, primitives, context, getCurrent, setCurrent);
|
|
427
535
|
}
|
|
428
536
|
else if (primitives.goToFirst && primitives.goNext && tablePageIndex >= 0) {
|
|
429
537
|
(0, debugUtils_1.logDebug)(config, 'info', `bringIntoView: going to first page and looping goNext until we reach page ${tablePageIndex}`);
|
|
430
|
-
|
|
538
|
+
await primitives.goToFirst(context);
|
|
431
539
|
for (let i = 0; i < tablePageIndex; i++) {
|
|
432
|
-
const ok =
|
|
540
|
+
const ok = await primitives.goNext(context);
|
|
433
541
|
if (!ok)
|
|
434
542
|
throw new Error(`bringIntoView: goNext failed before reaching page ${tablePageIndex}.`);
|
|
435
543
|
}
|
|
@@ -442,10 +550,10 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
442
550
|
}
|
|
443
551
|
}
|
|
444
552
|
// Delay after pagination/finding before scrolling
|
|
445
|
-
|
|
553
|
+
await (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
446
554
|
// Scroll row into view using Playwright's built-in method
|
|
447
|
-
|
|
448
|
-
}
|
|
555
|
+
await rowLocator.scrollIntoViewIfNeeded();
|
|
556
|
+
};
|
|
449
557
|
return smart;
|
|
450
558
|
};
|
|
451
559
|
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
|
};
|