@rickcedwhat/playwright-smart-table 6.6.0 → 6.7.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 +94 -5
- package/dist/engine/rowFinder.d.ts +1 -4
- package/dist/engine/rowFinder.js +9 -40
- package/dist/engine/tableMapper.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -18
- package/dist/plugins.d.ts +0 -1
- package/dist/smartRow.js +59 -25
- package/dist/strategies/index.d.ts +6 -8
- package/dist/strategies/pagination.d.ts +2 -18
- package/dist/strategies/pagination.js +2 -17
- package/dist/strategies/rdg.d.ts +0 -5
- package/dist/strategies/rdg.js +1 -18
- package/dist/strategies/sorting.js +15 -33
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +72 -78
- package/dist/types.d.ts +63 -87
- package/dist/useTable.js +87 -321
- package/dist/utils/elementTracker.d.ts +15 -0
- package/dist/utils/elementTracker.js +60 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,13 +76,92 @@ const email = await row.getCell('Email').textContent();
|
|
|
76
76
|
const allActive = await table.findRows({ Status: 'Active' });
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
### Iterating Across Pages
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// forEach — sequential, safe for interactions (parallel: false default)
|
|
83
|
+
await table.forEach(async ({ row, rowIndex, stop }) => {
|
|
84
|
+
if (await row.getCell('Status').innerText() === 'Done') stop();
|
|
85
|
+
await row.getCell('Checkbox').click();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// map — parallel within page, safe for reads (parallel: true default)
|
|
89
|
+
const emails = await table.map(({ row }) => row.getCell('Email').innerText());
|
|
90
|
+
|
|
91
|
+
// filter — async predicate across all pages, returns SmartRowArray
|
|
92
|
+
const active = await table.filter(async ({ row }) =>
|
|
93
|
+
await row.getCell('Status').innerText() === 'Active'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// for await...of — low-level page-by-page iteration
|
|
97
|
+
for await (const { row, rowIndex } of table) {
|
|
98
|
+
console.log(rowIndex, await row.getCell('Name').innerText());
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> **`map` + UI interactions:** `map` defaults to `parallel: true`. If your callback opens popovers,
|
|
103
|
+
> fills inputs, or otherwise mutates UI state, pass `{ parallel: false }` to avoid overlapping interactions.
|
|
104
|
+
|
|
105
|
+
### `filter` vs `findRows`
|
|
106
|
+
|
|
107
|
+
| Use case | Best tool |
|
|
108
|
+
|---|---|
|
|
109
|
+
| Match by column value / regex / locator | `findRows` |
|
|
110
|
+
| Computed value (math, range, derived) | `filter` |
|
|
111
|
+
| Cross-column OR logic | `filter` |
|
|
112
|
+
| Multi-step interaction in predicate (click, read, close) | `filter` |
|
|
113
|
+
| Early exit after N matches | `filter` + `stop()` |
|
|
114
|
+
|
|
115
|
+
**`findRows` is faster** for column-value matches — Playwright evaluates the locator natively with no DOM reads. **`filter` is more flexible** for logic that a CSS selector can't express.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// findRows — structural match, no DOM reads, fast
|
|
119
|
+
const notStarted = await table.findRows({
|
|
120
|
+
Status: (cell) => cell.locator('[class*="gray"]')
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// filter — arbitrary async logic
|
|
124
|
+
const expensive = await table.filter(async ({ row }) => {
|
|
125
|
+
const price = parseFloat(await row.getCell('Price').innerText());
|
|
126
|
+
const qty = parseFloat(await row.getCell('Qty').innerText());
|
|
127
|
+
return price * qty > 1000;
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Advanced: `columnOverrides`
|
|
132
|
+
|
|
133
|
+
For complex DOM structures, custom data extraction, or specialized input widgets, use `columnOverrides` to intercept how Smart Table interacts with specific columns:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const table = useTable(page.locator('#table'), {
|
|
137
|
+
columnOverrides: {
|
|
138
|
+
// Override how data is read from the 'Status' column (e.g., for .toJSON())
|
|
139
|
+
Status: {
|
|
140
|
+
read: async (cell) => {
|
|
141
|
+
const isChecked = await cell.locator('input[type="checkbox"]').isChecked();
|
|
142
|
+
return isChecked ? 'Active' : 'Inactive';
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
// Override how data is written to the 'Tags' column (for .smartFill())
|
|
146
|
+
Tags: {
|
|
147
|
+
write: async (cell, value) => {
|
|
148
|
+
await cell.click();
|
|
149
|
+
await page.keyboard.type(value);
|
|
150
|
+
await page.keyboard.press('Enter');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
79
157
|
## Key Features
|
|
80
158
|
|
|
81
159
|
- 🎯 **Smart Locators** - Find rows by content, not position
|
|
82
|
-
- 🧠 **Fuzzy Matching** - Smart suggestions for typos
|
|
160
|
+
- 🧠 **Fuzzy Matching** - Smart suggestions for typos in column names
|
|
83
161
|
- ⚡ **Smart Initialization** - Handles loading states and dynamic headers automatically
|
|
84
162
|
- 📄 **Auto-Pagination** - Search across all pages automatically
|
|
85
163
|
- 🔍 **Column-Aware Access** - Access cells by column name
|
|
164
|
+
- 🔁 **Iteration Methods** - `forEach`, `map`, `filter`, and `for await...of` across all pages
|
|
86
165
|
- 🛠️ **Debug Mode** - Visual debugging with slow motion and logging
|
|
87
166
|
- 🔌 **[Extensible Strategies](docs/concepts/strategies.md)** - Support any table implementation
|
|
88
167
|
- 💪 **Type-Safe** - Full TypeScript support
|
|
@@ -108,11 +187,21 @@ const allActive = await table.findRows({ Status: 'Active' });
|
|
|
108
187
|
|
|
109
188
|
### ⚠️ Important Note on Pagination & Interactions
|
|
110
189
|
|
|
111
|
-
When
|
|
190
|
+
When `findRows` or `filter` paginates across pages, returned `SmartRow` locators point to rows that may be off the current DOM page.
|
|
191
|
+
|
|
192
|
+
- **Data extraction:** Safe — `toJSON()` and cell reads work while the row is visible during iteration.
|
|
193
|
+
- **Interactions after pagination:** Use `await row.bringIntoView()` first — it navigates back to the page the row was originally found on, then you can safely click/fill.
|
|
112
194
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
195
|
+
```typescript
|
|
196
|
+
const active = await table.filter(async ({ row }) =>
|
|
197
|
+
await row.getCell('Status').innerText() === 'Active'
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
for (const row of active) {
|
|
201
|
+
await row.bringIntoView(); // navigate back to the row's page
|
|
202
|
+
await row.getCell('Checkbox').click(); // safe to interact
|
|
203
|
+
}
|
|
204
|
+
```
|
|
116
205
|
|
|
117
206
|
## Documentation
|
|
118
207
|
|
|
@@ -19,10 +19,7 @@ export declare class RowFinder<T = any> {
|
|
|
19
19
|
exact?: boolean;
|
|
20
20
|
maxPages?: number;
|
|
21
21
|
}): Promise<SmartRow<T>>;
|
|
22
|
-
findRows(
|
|
23
|
-
exact?: boolean;
|
|
24
|
-
maxPages?: number;
|
|
25
|
-
}), legacyOptions?: {
|
|
22
|
+
findRows(filters?: Partial<T> | Record<string, FilterValue>, options?: {
|
|
26
23
|
exact?: boolean;
|
|
27
24
|
maxPages?: number;
|
|
28
25
|
}): Promise<SmartRowArray<T>>;
|
package/dist/engine/rowFinder.js
CHANGED
|
@@ -8,17 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
-
var t = {};
|
|
13
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
-
t[p] = s[p];
|
|
15
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
-
t[p[i]] = s[p[i]];
|
|
19
|
-
}
|
|
20
|
-
return t;
|
|
21
|
-
};
|
|
22
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
12
|
exports.RowFinder = void 0;
|
|
24
13
|
const debugUtils_1 = require("../utils/debugUtils");
|
|
@@ -51,46 +40,26 @@ class RowFinder {
|
|
|
51
40
|
yield (0, debugUtils_1.debugDelay)(this.config, 'findRow');
|
|
52
41
|
const sentinel = this.resolve(this.config.rowSelector, this.rootLocator)
|
|
53
42
|
.filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
|
|
54
|
-
|
|
43
|
+
const smartRow = this.makeSmartRow(sentinel, yield this.tableMapper.getMap(), 0);
|
|
44
|
+
smartRow._isSentinel = true;
|
|
45
|
+
return smartRow;
|
|
55
46
|
});
|
|
56
47
|
}
|
|
57
|
-
findRows(
|
|
58
|
-
// Deprecated: verify legacy usage pattern support
|
|
59
|
-
legacyOptions) {
|
|
48
|
+
findRows(filters, options) {
|
|
60
49
|
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
-
// Detect argument pattern:
|
|
62
|
-
// Pattern A: findRows({ Name: 'Alice' }, { maxPages: 5 })
|
|
63
|
-
// Pattern B: findRows({ maxPages: 5 }) <-- No filters, just options
|
|
64
|
-
// Pattern C: findRows({ Name: 'Alice' }) <-- Only filters
|
|
65
50
|
var _a, _b;
|
|
66
|
-
|
|
67
|
-
let options = {};
|
|
68
|
-
if (legacyOptions) {
|
|
69
|
-
// Pattern A
|
|
70
|
-
filters = filtersOrOptions;
|
|
71
|
-
options = legacyOptions;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
// Pattern B or C
|
|
75
|
-
// We need to separate unknown keys (filters) from known options (exact, maxPages)
|
|
76
|
-
// However, filtersOrOptions can be null/undefined
|
|
77
|
-
if (filtersOrOptions) {
|
|
78
|
-
const _c = filtersOrOptions, { exact, maxPages } = _c, rest = __rest(_c, ["exact", "maxPages"]);
|
|
79
|
-
options = { exact, maxPages };
|
|
80
|
-
filters = rest;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
51
|
+
const filtersRecord = filters || {};
|
|
83
52
|
const map = yield this.tableMapper.getMap();
|
|
84
53
|
const allRows = [];
|
|
85
|
-
const effectiveMaxPages = (_b = (_a = options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
|
|
54
|
+
const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : this.config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
|
|
86
55
|
let pagesScanned = 1;
|
|
87
56
|
const collectMatches = () => __awaiter(this, void 0, void 0, function* () {
|
|
88
57
|
var _a, _b;
|
|
89
58
|
// ... logic ...
|
|
90
59
|
let rowLocators = this.resolve(this.config.rowSelector, this.rootLocator);
|
|
91
60
|
// Only apply filters if we have them
|
|
92
|
-
if (Object.keys(
|
|
93
|
-
rowLocators = this.filterEngine.applyFilters(rowLocators,
|
|
61
|
+
if (Object.keys(filtersRecord).length > 0) {
|
|
62
|
+
rowLocators = this.filterEngine.applyFilters(rowLocators, filtersRecord, map, (_a = options === null || options === void 0 ? void 0 : options.exact) !== null && _a !== void 0 ? _a : false, this.rootLocator.page());
|
|
94
63
|
}
|
|
95
64
|
const currentRows = yield rowLocators.all();
|
|
96
65
|
const isRowLoading = (_b = this.config.strategies.loading) === null || _b === void 0 ? void 0 : _b.isRowLoading;
|
|
@@ -125,7 +94,7 @@ class RowFinder {
|
|
|
125
94
|
}
|
|
126
95
|
paginationResult = yield this.config.strategies.pagination.goNext(context);
|
|
127
96
|
}
|
|
128
|
-
const didPaginate =
|
|
97
|
+
const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
129
98
|
if (!didPaginate)
|
|
130
99
|
break;
|
|
131
100
|
this.tableState.currentPageIndex++;
|
|
@@ -105,7 +105,7 @@ class TableMapper {
|
|
|
105
105
|
text = yield this.config.headerTransformer({
|
|
106
106
|
text,
|
|
107
107
|
index: i,
|
|
108
|
-
locator: this.
|
|
108
|
+
locator: this.resolve(this.config.headerSelector, this.rootLocator).nth(i),
|
|
109
109
|
seenHeaders
|
|
110
110
|
});
|
|
111
111
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
1
|
+
export { useTable } from './useTable';
|
|
2
|
+
export type { TableConfig, TableResult, SmartRow, Selector, FilterValue, PaginationPrimitives, SortingStrategy, FillOptions, RowIterationContext, RowIterationOptions, TableContext, StrategyContext, BeforeCellReadFn, GetCellLocatorFn, GetActiveCellFn, } from './types';
|
|
3
|
+
export { Strategies } from './strategies';
|
|
4
|
+
export { Plugins } from './plugins';
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
exports.Plugins = exports.Strategies = exports.useTable = void 0;
|
|
4
|
+
var useTable_1 = require("./useTable");
|
|
5
|
+
Object.defineProperty(exports, "useTable", { enumerable: true, get: function () { return useTable_1.useTable; } });
|
|
6
|
+
// Export namespace-like strategy collections
|
|
7
|
+
var strategies_1 = require("./strategies");
|
|
8
|
+
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
9
|
+
var plugins_1 = require("./plugins");
|
|
10
|
+
Object.defineProperty(exports, "Plugins", { enumerable: true, get: function () { return plugins_1.Plugins; } });
|
package/dist/plugins.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export declare const Plugins: {
|
|
|
3
3
|
Strategies: {
|
|
4
4
|
header: (context: import("./types").TableContext) => Promise<string[]>;
|
|
5
5
|
getCellLocator: ({ row, columnIndex }: any) => any;
|
|
6
|
-
cellNavigation: ({ root, page, index }: any) => Promise<void>;
|
|
7
6
|
navigation: {
|
|
8
7
|
goRight: ({ root, page }: any) => Promise<void>;
|
|
9
8
|
goLeft: ({ root, page }: any) => Promise<void>;
|
package/dist/smartRow.js
CHANGED
|
@@ -16,7 +16,6 @@ const debugUtils_1 = require("./utils/debugUtils");
|
|
|
16
16
|
/**
|
|
17
17
|
* Internal helper to navigate to a cell with active cell optimization.
|
|
18
18
|
* Uses navigation primitives (goUp, goDown, goLeft, goRight, goHome) for orchestration.
|
|
19
|
-
* Falls back to cellNavigation for backward compatibility.
|
|
20
19
|
* Returns the target cell locator after navigation.
|
|
21
20
|
*/
|
|
22
21
|
const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -91,7 +90,6 @@ const _navigateToCell = (params) => __awaiter(void 0, void 0, void 0, function*
|
|
|
91
90
|
}
|
|
92
91
|
return null;
|
|
93
92
|
}
|
|
94
|
-
;
|
|
95
93
|
return null;
|
|
96
94
|
});
|
|
97
95
|
/**
|
|
@@ -121,24 +119,30 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
121
119
|
}
|
|
122
120
|
return resolve(config.cellSelector, rowLocator).nth(idx);
|
|
123
121
|
};
|
|
122
|
+
smart.wasFound = () => {
|
|
123
|
+
return !smart._isSentinel;
|
|
124
|
+
};
|
|
124
125
|
smart.toJSON = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
-
var _a
|
|
126
|
+
var _a;
|
|
126
127
|
const result = {};
|
|
127
128
|
const page = rootLocator.page();
|
|
129
|
+
// Build a getHeaderCell helper for the beforeCellRead context.
|
|
130
|
+
// Uses the table reference if available, otherwise falls back to index-based lookup.
|
|
131
|
+
const getHeaderCell = (table === null || table === void 0 ? void 0 : table.getHeaderCell)
|
|
132
|
+
? table.getHeaderCell.bind(table)
|
|
133
|
+
: (colName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
134
|
+
const idx = map.get(colName);
|
|
135
|
+
if (idx === undefined)
|
|
136
|
+
throw new Error(`Column "${colName}" not found`);
|
|
137
|
+
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
138
|
+
});
|
|
128
139
|
for (const [col, idx] of map.entries()) {
|
|
129
140
|
if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(col)) {
|
|
130
141
|
continue;
|
|
131
142
|
}
|
|
132
|
-
// Check if we have a column override
|
|
143
|
+
// Check if we have a column override for this column
|
|
133
144
|
const columnOverride = (_a = config.columnOverrides) === null || _a === void 0 ? void 0 : _a[col];
|
|
134
|
-
const mapper =
|
|
135
|
-
if (mapper) {
|
|
136
|
-
// Use custom mapper
|
|
137
|
-
// Ensure we have the cell first (same navigation logic)
|
|
138
|
-
// ... wait, the navigation logic below assumes we need to navigate.
|
|
139
|
-
// If we have a mapper, we still need the cell locator.
|
|
140
|
-
// Let's reuse the navigation logic to get targetCell
|
|
141
|
-
}
|
|
145
|
+
const mapper = columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.read;
|
|
142
146
|
// --- Navigation Logic Start ---
|
|
143
147
|
const cell = config.strategies.getCellLocator
|
|
144
148
|
? config.strategies.getCellLocator({
|
|
@@ -168,6 +172,19 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
// --- Navigation Logic End ---
|
|
175
|
+
// Call beforeCellRead hook if configured.
|
|
176
|
+
// Fires for BOTH columnOverrides.read and the default innerText path.
|
|
177
|
+
if (config.strategies.beforeCellRead) {
|
|
178
|
+
yield config.strategies.beforeCellRead({
|
|
179
|
+
cell: targetCell,
|
|
180
|
+
columnName: col,
|
|
181
|
+
columnIndex: idx,
|
|
182
|
+
row: rowLocator,
|
|
183
|
+
page,
|
|
184
|
+
root: rootLocator,
|
|
185
|
+
getHeaderCell,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
171
188
|
if (mapper) {
|
|
172
189
|
// Apply mapper
|
|
173
190
|
const mappedValue = yield mapper(targetCell);
|
|
@@ -182,6 +199,7 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
182
199
|
return result;
|
|
183
200
|
});
|
|
184
201
|
smart.smartFill = (data, fillOptions) => __awaiter(void 0, void 0, void 0, function* () {
|
|
202
|
+
var _a;
|
|
185
203
|
(0, debugUtils_1.logDebug)(config, 'info', 'Filling row', data);
|
|
186
204
|
for (const [colName, value] of Object.entries(data)) {
|
|
187
205
|
if (value === undefined)
|
|
@@ -200,19 +218,35 @@ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve,
|
|
|
200
218
|
rowLocator,
|
|
201
219
|
rowIndex
|
|
202
220
|
});
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
221
|
+
const columnOverride = (_a = config.columnOverrides) === null || _a === void 0 ? void 0 : _a[colName];
|
|
222
|
+
if (columnOverride === null || columnOverride === void 0 ? void 0 : columnOverride.write) {
|
|
223
|
+
const cellLocator = smart.getCell(colName);
|
|
224
|
+
let currentValue;
|
|
225
|
+
if (columnOverride.read) {
|
|
226
|
+
currentValue = yield columnOverride.read(cellLocator);
|
|
227
|
+
}
|
|
228
|
+
yield columnOverride.write({
|
|
229
|
+
cell: cellLocator,
|
|
230
|
+
targetValue: value,
|
|
231
|
+
currentValue,
|
|
232
|
+
row: smart
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
const strategy = config.strategies.fill || fill_1.FillStrategies.default;
|
|
237
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
|
|
238
|
+
yield strategy({
|
|
239
|
+
row: smart,
|
|
240
|
+
columnName: colName,
|
|
241
|
+
value,
|
|
242
|
+
index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
|
|
243
|
+
page: rowLocator.page(),
|
|
244
|
+
rootLocator,
|
|
245
|
+
config,
|
|
246
|
+
table: table,
|
|
247
|
+
fillOptions
|
|
248
|
+
});
|
|
249
|
+
}
|
|
216
250
|
// Delay after filling
|
|
217
251
|
yield (0, debugUtils_1.debugDelay)(config, 'getCell');
|
|
218
252
|
}
|
|
@@ -8,25 +8,23 @@ export * from './dedupe';
|
|
|
8
8
|
export * from './loading';
|
|
9
9
|
export declare const Strategies: {
|
|
10
10
|
Pagination: {
|
|
11
|
-
clickNext: (nextButtonSelector: import("..").Selector, options?: {
|
|
12
|
-
stabilization?: import("./stabilization").StabilizationStrategy;
|
|
13
|
-
timeout?: number;
|
|
14
|
-
}) => import("..").PaginationStrategy;
|
|
15
11
|
click: (selectors: {
|
|
16
12
|
next?: import("..").Selector;
|
|
17
13
|
previous?: import("..").Selector;
|
|
14
|
+
nextBulk?: import("..").Selector;
|
|
15
|
+
previousBulk?: import("..").Selector;
|
|
18
16
|
first?: import("..").Selector;
|
|
19
17
|
}, options?: {
|
|
20
18
|
stabilization?: import("./stabilization").StabilizationStrategy;
|
|
21
19
|
timeout?: number;
|
|
22
|
-
}) => import("
|
|
20
|
+
}) => import("../types").PaginationStrategy;
|
|
23
21
|
infiniteScroll: (options?: {
|
|
24
22
|
action?: "scroll" | "js-scroll";
|
|
25
23
|
scrollTarget?: import("..").Selector;
|
|
26
24
|
scrollAmount?: number;
|
|
27
25
|
stabilization?: import("./stabilization").StabilizationStrategy;
|
|
28
26
|
timeout?: number;
|
|
29
|
-
}) => import("
|
|
27
|
+
}) => import("../types").PaginationStrategy;
|
|
30
28
|
};
|
|
31
29
|
Sorting: {
|
|
32
30
|
AriaSort: () => import("..").SortingStrategy;
|
|
@@ -38,13 +36,13 @@ export declare const Strategies: {
|
|
|
38
36
|
visible: ({ config, resolve, root }: import("..").StrategyContext) => Promise<string[]>;
|
|
39
37
|
};
|
|
40
38
|
Fill: {
|
|
41
|
-
default: ({ row, columnName, value, fillOptions, config, table }: Parameters<import("
|
|
39
|
+
default: ({ row, columnName, value, fillOptions, config, table }: Parameters<import("../types").FillStrategy>[0]) => Promise<void>;
|
|
42
40
|
};
|
|
43
41
|
Resolution: {
|
|
44
42
|
default: import("./resolution").ColumnResolutionStrategy;
|
|
45
43
|
};
|
|
46
44
|
Dedupe: {
|
|
47
|
-
byTopPosition: (tolerance?: number) => import("
|
|
45
|
+
byTopPosition: (tolerance?: number) => import("../types").DedupeStrategy;
|
|
48
46
|
};
|
|
49
47
|
Loading: {
|
|
50
48
|
Table: {
|
|
@@ -1,27 +1,11 @@
|
|
|
1
1
|
import type { PaginationStrategy, Selector } from '../types';
|
|
2
2
|
import { StabilizationStrategy } from './stabilization';
|
|
3
3
|
export declare const PaginationStrategies: {
|
|
4
|
-
/**
|
|
5
|
-
* Strategy: Clicks a "Next" button and waits for stabilization.
|
|
6
|
-
* Backward compatibility for when only a single 'next' selector was needed.
|
|
7
|
-
* @deprecated Use `click` with `{ next: selector }` instead.
|
|
8
|
-
*/
|
|
9
|
-
clickNext: (nextButtonSelector: Selector, options?: {
|
|
10
|
-
stabilization?: StabilizationStrategy;
|
|
11
|
-
timeout?: number;
|
|
12
|
-
}) => PaginationStrategy;
|
|
13
|
-
/**
|
|
14
|
-
* Strategy: Classic Pagination Buttons.
|
|
15
|
-
* Clicks 'Next', 'Previous', or 'First' buttons and waits for stabilization.
|
|
16
|
-
*
|
|
17
|
-
* @param selectors Selectors for pagination buttons.
|
|
18
|
-
* @param options.stabilization Strategy to determine when the page has updated.
|
|
19
|
-
* Defaults to `contentChanged({ scope: 'first' })`.
|
|
20
|
-
* @param options.timeout Timeout for the click action.
|
|
21
|
-
*/
|
|
22
4
|
click: (selectors: {
|
|
23
5
|
next?: Selector;
|
|
24
6
|
previous?: Selector;
|
|
7
|
+
nextBulk?: Selector;
|
|
8
|
+
previousBulk?: Selector;
|
|
25
9
|
first?: Selector;
|
|
26
10
|
}, options?: {
|
|
27
11
|
stabilization?: StabilizationStrategy;
|
|
@@ -12,23 +12,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.PaginationStrategies = void 0;
|
|
13
13
|
const stabilization_1 = require("./stabilization");
|
|
14
14
|
exports.PaginationStrategies = {
|
|
15
|
-
/**
|
|
16
|
-
* Strategy: Clicks a "Next" button and waits for stabilization.
|
|
17
|
-
* Backward compatibility for when only a single 'next' selector was needed.
|
|
18
|
-
* @deprecated Use `click` with `{ next: selector }` instead.
|
|
19
|
-
*/
|
|
20
|
-
clickNext: (nextButtonSelector, options = {}) => {
|
|
21
|
-
return exports.PaginationStrategies.click({ next: nextButtonSelector }, options);
|
|
22
|
-
},
|
|
23
|
-
/**
|
|
24
|
-
* Strategy: Classic Pagination Buttons.
|
|
25
|
-
* Clicks 'Next', 'Previous', or 'First' buttons and waits for stabilization.
|
|
26
|
-
*
|
|
27
|
-
* @param selectors Selectors for pagination buttons.
|
|
28
|
-
* @param options.stabilization Strategy to determine when the page has updated.
|
|
29
|
-
* Defaults to `contentChanged({ scope: 'first' })`.
|
|
30
|
-
* @param options.timeout Timeout for the click action.
|
|
31
|
-
*/
|
|
32
15
|
click: (selectors, options = {}) => {
|
|
33
16
|
var _a;
|
|
34
17
|
const defaultStabilize = (_a = options.stabilization) !== null && _a !== void 0 ? _a : stabilization_1.StabilizationStrategies.contentChanged({ scope: 'first', timeout: options.timeout });
|
|
@@ -49,6 +32,8 @@ exports.PaginationStrategies = {
|
|
|
49
32
|
return {
|
|
50
33
|
goNext: createClicker(selectors.next),
|
|
51
34
|
goPrevious: createClicker(selectors.previous),
|
|
35
|
+
goNextBulk: createClicker(selectors.nextBulk),
|
|
36
|
+
goPreviousBulk: createClicker(selectors.previousBulk),
|
|
52
37
|
goToFirst: createClicker(selectors.first)
|
|
53
38
|
};
|
|
54
39
|
},
|
package/dist/strategies/rdg.d.ts
CHANGED
|
@@ -9,10 +9,6 @@ export declare const scrollRightHeaderRDG: (context: TableContext) => Promise<st
|
|
|
9
9
|
* changing during pagination/scrolling.
|
|
10
10
|
*/
|
|
11
11
|
export declare const rdgGetCellLocator: ({ row, columnIndex }: any) => any;
|
|
12
|
-
/**
|
|
13
|
-
* Scrolls virtualized columns into view before reading.
|
|
14
|
-
*/
|
|
15
|
-
export declare const rdgCellNavigation: ({ root, page, index }: any) => Promise<void>;
|
|
16
12
|
/**
|
|
17
13
|
* Scrolls the grid vertically to load more virtualized rows.
|
|
18
14
|
*/
|
|
@@ -27,7 +23,6 @@ export declare const rdgNavigation: {
|
|
|
27
23
|
export declare const RDGStrategies: {
|
|
28
24
|
header: (context: TableContext) => Promise<string[]>;
|
|
29
25
|
getCellLocator: ({ row, columnIndex }: any) => any;
|
|
30
|
-
cellNavigation: ({ root, page, index }: any) => Promise<void>;
|
|
31
26
|
navigation: {
|
|
32
27
|
goRight: ({ root, page }: any) => Promise<void>;
|
|
33
28
|
goLeft: ({ root, page }: any) => Promise<void>;
|
package/dist/strategies/rdg.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.RDGStrategies = exports.rdgNavigation = exports.rdgPaginationStrategy = exports.
|
|
12
|
+
exports.RDGStrategies = exports.rdgNavigation = exports.rdgPaginationStrategy = exports.rdgGetCellLocator = exports.scrollRightHeaderRDG = void 0;
|
|
13
13
|
/**
|
|
14
14
|
* Scrolls the grid horizontally to collect all column headers.
|
|
15
15
|
* Handles empty headers by labeling them (e.g. "Checkbox").
|
|
@@ -64,22 +64,6 @@ const rdgGetCellLocator = ({ row, columnIndex }) => {
|
|
|
64
64
|
return row.locator(`[role="gridcell"][aria-colindex="${ariaColIndex}"]`);
|
|
65
65
|
};
|
|
66
66
|
exports.rdgGetCellLocator = rdgGetCellLocator;
|
|
67
|
-
/**
|
|
68
|
-
* Scrolls virtualized columns into view before reading.
|
|
69
|
-
*/
|
|
70
|
-
const rdgCellNavigation = (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page, index }) {
|
|
71
|
-
// Check if the column header is visible and scroll horizontally if needed
|
|
72
|
-
const headerCell = root.locator(`[role="columnheader"][aria-colindex="${index + 1}"]`);
|
|
73
|
-
const isVisible = yield headerCell.isVisible().catch(() => false);
|
|
74
|
-
if (!isVisible) {
|
|
75
|
-
const estimatedScroll = index * 150;
|
|
76
|
-
yield root.evaluate((el, scrollAmount) => {
|
|
77
|
-
el.scrollLeft = scrollAmount;
|
|
78
|
-
}, estimatedScroll);
|
|
79
|
-
yield page.waitForTimeout(300);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
exports.rdgCellNavigation = rdgCellNavigation;
|
|
83
67
|
/**
|
|
84
68
|
* Scrolls the grid vertically to load more virtualized rows.
|
|
85
69
|
*/
|
|
@@ -136,7 +120,6 @@ exports.rdgNavigation = {
|
|
|
136
120
|
exports.RDGStrategies = {
|
|
137
121
|
header: exports.scrollRightHeaderRDG,
|
|
138
122
|
getCellLocator: exports.rdgGetCellLocator,
|
|
139
|
-
cellNavigation: exports.rdgCellNavigation,
|
|
140
123
|
navigation: exports.rdgNavigation,
|
|
141
124
|
pagination: exports.rdgPaginationStrategy
|
|
142
125
|
};
|
|
@@ -23,44 +23,26 @@ exports.SortingStrategies = {
|
|
|
23
23
|
return {
|
|
24
24
|
doSort(_a) {
|
|
25
25
|
return __awaiter(this, arguments, void 0, function* ({ columnName, direction, context }) {
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const columnIndex = headerTexts.findIndex(text => text.trim() === columnName);
|
|
31
|
-
if (columnIndex === -1) {
|
|
32
|
-
throw new Error(`[AriaSort] Header with text "${columnName}" not found.`);
|
|
33
|
-
}
|
|
34
|
-
const targetHeader = headers[columnIndex];
|
|
35
|
-
// Click repeatedly to cycle through sort states
|
|
36
|
-
for (let i = 0; i < 3; i++) { // Max 3 clicks to prevent infinite loops (none -> asc -> desc)
|
|
37
|
-
const currentState = yield targetHeader.getAttribute('aria-sort');
|
|
38
|
-
const mappedState = currentState === 'ascending' ? 'asc' : currentState === 'descending' ? 'desc' : 'none';
|
|
39
|
-
if (mappedState === direction) {
|
|
40
|
-
return; // Desired state achieved
|
|
41
|
-
}
|
|
42
|
-
yield targetHeader.click();
|
|
43
|
-
}
|
|
44
|
-
throw new Error(`[AriaSort] Could not achieve sort direction "${direction}" for column "${columnName}" after 3 clicks.`);
|
|
26
|
+
// getHeaderCell is always present on TableContext after table is initialized
|
|
27
|
+
const targetHeader = yield context.getHeaderCell(columnName);
|
|
28
|
+
// The table engine handles verify-and-retry. We only provide the trigger here.
|
|
29
|
+
yield targetHeader.click();
|
|
45
30
|
});
|
|
46
31
|
},
|
|
47
32
|
getSortState(_a) {
|
|
48
33
|
return __awaiter(this, arguments, void 0, function* ({ columnName, context }) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
34
|
+
try {
|
|
35
|
+
const targetHeader = yield context.getHeaderCell(columnName);
|
|
36
|
+
const ariaSort = yield targetHeader.getAttribute('aria-sort');
|
|
37
|
+
if (ariaSort === 'ascending')
|
|
38
|
+
return 'asc';
|
|
39
|
+
if (ariaSort === 'descending')
|
|
40
|
+
return 'desc';
|
|
41
|
+
return 'none';
|
|
42
|
+
}
|
|
43
|
+
catch (_b) {
|
|
44
|
+
return 'none'; // Header not found, treat as unsorted
|
|
56
45
|
}
|
|
57
|
-
const targetHeader = headers[columnIndex];
|
|
58
|
-
const ariaSort = yield targetHeader.getAttribute('aria-sort');
|
|
59
|
-
if (ariaSort === 'ascending')
|
|
60
|
-
return 'asc';
|
|
61
|
-
if (ariaSort === 'descending')
|
|
62
|
-
return 'desc';
|
|
63
|
-
return 'none';
|
|
64
46
|
});
|
|
65
47
|
},
|
|
66
48
|
};
|