@rickcedwhat/playwright-smart-table 3.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +357 -100
- package/dist/filterEngine.d.ts +11 -0
- package/dist/filterEngine.js +38 -0
- package/dist/smartRow.d.ts +7 -0
- package/dist/smartRow.js +152 -0
- package/dist/strategies/columns.d.ts +5 -17
- package/dist/strategies/columns.js +2 -34
- package/dist/strategies/headers.d.ts +0 -16
- package/dist/strategies/headers.js +1 -113
- package/dist/strategies/index.d.ts +25 -0
- package/dist/strategies/index.js +19 -1
- package/dist/strategies/pagination.d.ts +0 -21
- package/dist/strategies/pagination.js +1 -23
- package/dist/strategies/resolution.d.ts +22 -0
- package/dist/strategies/resolution.js +30 -0
- package/dist/strategies/validation.d.ts +22 -0
- package/dist/strategies/validation.js +54 -0
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +188 -58
- package/dist/types.d.ts +177 -66
- package/dist/useTable.d.ts +5 -9
- package/dist/useTable.js +139 -268
- package/package.json +2 -2
package/dist/useTable.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.
|
|
12
|
+
exports.Strategies = exports.ResolutionStrategies = exports.CellNavigationStrategies = exports.HeaderStrategies = exports.FillStrategies = exports.SortingStrategies = exports.PaginationStrategies = exports.useTable = void 0;
|
|
13
13
|
const typeContext_1 = require("./typeContext");
|
|
14
14
|
const sorting_1 = require("./strategies/sorting");
|
|
15
15
|
const pagination_1 = require("./strategies/pagination");
|
|
@@ -18,14 +18,29 @@ Object.defineProperty(exports, "FillStrategies", { enumerable: true, get: functi
|
|
|
18
18
|
const headers_1 = require("./strategies/headers");
|
|
19
19
|
Object.defineProperty(exports, "HeaderStrategies", { enumerable: true, get: function () { return headers_1.HeaderStrategies; } });
|
|
20
20
|
const columns_1 = require("./strategies/columns");
|
|
21
|
-
Object.defineProperty(exports, "
|
|
21
|
+
Object.defineProperty(exports, "CellNavigationStrategies", { enumerable: true, get: function () { return columns_1.CellNavigationStrategies; } });
|
|
22
|
+
const smartRow_1 = require("./smartRow");
|
|
23
|
+
const filterEngine_1 = require("./filterEngine");
|
|
24
|
+
const resolution_1 = require("./strategies/resolution");
|
|
25
|
+
Object.defineProperty(exports, "ResolutionStrategies", { enumerable: true, get: function () { return resolution_1.ResolutionStrategies; } });
|
|
26
|
+
const strategies_1 = require("./strategies");
|
|
27
|
+
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
28
|
+
const validation_1 = require("./strategies/validation");
|
|
22
29
|
/**
|
|
23
30
|
* Main hook to interact with a table.
|
|
24
31
|
*/
|
|
25
32
|
const useTable = (rootLocator, configOptions = {}) => {
|
|
33
|
+
var _a;
|
|
26
34
|
// Store whether pagination was explicitly provided in config
|
|
27
|
-
const hasPaginationInConfig = configOptions.pagination !== undefined;
|
|
28
|
-
|
|
35
|
+
const hasPaginationInConfig = ((_a = configOptions.strategies) === null || _a === void 0 ? void 0 : _a.pagination) !== undefined;
|
|
36
|
+
// Default strategies
|
|
37
|
+
const defaultStrategies = {
|
|
38
|
+
fill: fill_1.FillStrategies.default,
|
|
39
|
+
header: headers_1.HeaderStrategies.visible,
|
|
40
|
+
cellNavigation: columns_1.CellNavigationStrategies.default,
|
|
41
|
+
pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
42
|
+
};
|
|
43
|
+
const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text, index, locator }) => text, autoScroll: true, debug: false, onReset: () => __awaiter(void 0, void 0, void 0, function* () { }) }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
|
|
29
44
|
const resolve = (item, parent) => {
|
|
30
45
|
if (typeof item === 'string')
|
|
31
46
|
return parent.locator(item);
|
|
@@ -37,31 +52,28 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
37
52
|
let _headerMap = null;
|
|
38
53
|
let _hasPaginated = false;
|
|
39
54
|
let _isInitialized = false;
|
|
55
|
+
// Helpers
|
|
40
56
|
const logDebug = (msg) => {
|
|
41
57
|
if (config.debug)
|
|
42
58
|
console.log(`🔎 [SmartTable Debug] ${msg}`);
|
|
43
59
|
};
|
|
44
|
-
const
|
|
45
|
-
|
|
60
|
+
const _createColumnError = (colName, map, context) => {
|
|
61
|
+
const availableColumns = Array.from(map.keys());
|
|
62
|
+
// Use Suggestion Logic from ResolutionStrategy (if we had a fuzzy one, for now manual suggest)
|
|
46
63
|
const lowerCol = colName.toLowerCase();
|
|
47
64
|
const suggestions = availableColumns.filter(col => col.toLowerCase().includes(lowerCol) ||
|
|
48
65
|
lowerCol.includes(col.toLowerCase()) ||
|
|
49
66
|
col.toLowerCase().replace(/\s+/g, '') === lowerCol.replace(/\s+/g, ''));
|
|
67
|
+
let suggestion = '.';
|
|
50
68
|
if (suggestions.length > 0 && suggestions[0] !== colName) {
|
|
51
|
-
|
|
69
|
+
suggestion = `. Did you mean "${suggestions[0]}"?`;
|
|
52
70
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return `. Available columns: ${availableColumns.map(c => `"${c}"`).join(', ')}`;
|
|
71
|
+
else if (availableColumns.length > 0 && availableColumns.length <= 10) {
|
|
72
|
+
suggestion = `. Available columns: ${availableColumns.map(c => `"${c}"`).join(', ')}`;
|
|
56
73
|
}
|
|
57
74
|
else if (availableColumns.length > 0) {
|
|
58
|
-
|
|
75
|
+
suggestion = `. Available columns (first 5): ${availableColumns.slice(0, 5).map(c => `"${c}"`).join(', ')}, ...`;
|
|
59
76
|
}
|
|
60
|
-
return '.';
|
|
61
|
-
};
|
|
62
|
-
const _createColumnError = (colName, map, context) => {
|
|
63
|
-
const availableColumns = Array.from(map.keys());
|
|
64
|
-
const suggestion = _suggestColumnName(colName, availableColumns);
|
|
65
77
|
const contextMsg = context ? ` (${context})` : '';
|
|
66
78
|
return new Error(`Column "${colName}" not found${contextMsg}${suggestion}`);
|
|
67
79
|
};
|
|
@@ -81,12 +93,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
81
93
|
yield headerLoc.first().waitFor({ state: 'visible', timeout: headerTimeout });
|
|
82
94
|
}
|
|
83
95
|
catch (e) { /* Ignore hydration */ }
|
|
84
|
-
|
|
85
|
-
const strategy = config.headerStrategy || headers_1.HeaderStrategies.visible;
|
|
86
|
-
// We need to construct context - but wait, HeaderStrategies definition I made imports StrategyContext
|
|
87
|
-
// We need to import HeaderStrategies in useTable.ts first (it was imported as HeaderStrategies?).
|
|
88
|
-
// Wait, useTable.ts imports `FillStrategies`.
|
|
89
|
-
// I need to import `HeaderStrategies`.
|
|
96
|
+
const strategy = config.strategies.header || headers_1.HeaderStrategies.visible;
|
|
90
97
|
const context = {
|
|
91
98
|
root: rootLocator,
|
|
92
99
|
config: config,
|
|
@@ -94,20 +101,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
94
101
|
resolve: resolve
|
|
95
102
|
};
|
|
96
103
|
const rawHeaders = yield strategy(context);
|
|
97
|
-
// 2. Map Headers (Async)
|
|
98
|
-
// Note: We lose locator access here for transformer unless strategy provides it.
|
|
99
|
-
// For now assuming transformer handles missing locator or we don't pass it if generic strategy.
|
|
100
104
|
const entries = yield Promise.all(rawHeaders.map((t, i) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
105
|
let text = t.trim() || `__col_${i}`;
|
|
102
106
|
if (config.headerTransformer) {
|
|
103
107
|
text = yield config.headerTransformer({
|
|
104
108
|
text,
|
|
105
109
|
index: i,
|
|
106
|
-
locator: rootLocator.locator(config.headerSelector).nth(i)
|
|
107
|
-
// Danger: scanning strategy implies nth(i) might not map to visual i if scrolled.
|
|
108
|
-
// But map index maps to logical index.
|
|
109
|
-
// If scanning returns 100 headers, nth(99) might not exist in DOM.
|
|
110
|
-
// Passing fallback locator or stub.
|
|
110
|
+
locator: rootLocator.locator(config.headerSelector).nth(i)
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
return [text, i];
|
|
@@ -116,89 +116,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
116
116
|
logDebug(`Mapped ${entries.length} columns: ${JSON.stringify(entries.map(e => e[0]))}`);
|
|
117
117
|
return _headerMap;
|
|
118
118
|
});
|
|
119
|
-
// Placeholder for the final table object
|
|
119
|
+
// Placeholder for the final table object
|
|
120
120
|
let finalTable = null;
|
|
121
|
+
const filterEngine = new filterEngine_1.FilterEngine(config, resolve);
|
|
122
|
+
// Helper factory
|
|
121
123
|
const _makeSmart = (rowLocator, map, rowIndex) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
smart.rowIndex = rowIndex;
|
|
125
|
-
smart.getCell = (colName) => {
|
|
126
|
-
const idx = map.get(colName);
|
|
127
|
-
if (idx === undefined) {
|
|
128
|
-
const availableColumns = Array.from(map.keys());
|
|
129
|
-
const suggestion = _suggestColumnName(colName, availableColumns);
|
|
130
|
-
throw new Error(`Column "${colName}" not found${suggestion}`);
|
|
131
|
-
}
|
|
132
|
-
if (config.cellResolver) {
|
|
133
|
-
return config.cellResolver({ row: rowLocator, columnName: colName, columnIndex: idx, rowIndex });
|
|
134
|
-
}
|
|
135
|
-
return resolve(config.cellSelector, rowLocator).nth(idx);
|
|
136
|
-
};
|
|
137
|
-
smart.toJSON = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
138
|
-
const result = {};
|
|
139
|
-
const cells = typeof config.cellSelector === 'string'
|
|
140
|
-
? rowLocator.locator(config.cellSelector)
|
|
141
|
-
: resolve(config.cellSelector, rowLocator);
|
|
142
|
-
const texts = yield cells.allInnerTexts();
|
|
143
|
-
for (const [col, idx] of map.entries()) {
|
|
144
|
-
result[col] = (texts[idx] || '').trim();
|
|
145
|
-
}
|
|
146
|
-
return result;
|
|
147
|
-
});
|
|
148
|
-
// @ts-ignore - Intentionally overriding Locator's fill method to accept object
|
|
149
|
-
smart.fill = (data, fillOptions) => __awaiter(void 0, void 0, void 0, function* () {
|
|
150
|
-
logDebug(`Filling row with data: ${JSON.stringify(data)}`);
|
|
151
|
-
// Fill each column
|
|
152
|
-
for (const [colName, value] of Object.entries(data)) {
|
|
153
|
-
const colIdx = map.get(colName);
|
|
154
|
-
if (colIdx === undefined) {
|
|
155
|
-
throw _createColumnError(colName, map, 'in fill data');
|
|
156
|
-
}
|
|
157
|
-
// Execute Column Strategy BEFORE filling
|
|
158
|
-
yield config.columnStrategy({
|
|
159
|
-
config: config,
|
|
160
|
-
root: rootLocator,
|
|
161
|
-
page: rootLocator.page(),
|
|
162
|
-
resolve,
|
|
163
|
-
column: colName,
|
|
164
|
-
index: colIdx,
|
|
165
|
-
rowLocator: rowLocator,
|
|
166
|
-
rowIndex: rowIndex
|
|
167
|
-
});
|
|
168
|
-
// Use configured strategy or default to internal DOM logic
|
|
169
|
-
const strategy = config.fillStrategy || fill_1.FillStrategies.default;
|
|
170
|
-
yield strategy({
|
|
171
|
-
row: smart,
|
|
172
|
-
columnName: colName,
|
|
173
|
-
value,
|
|
174
|
-
index: -1,
|
|
175
|
-
page: rootLocator.page(),
|
|
176
|
-
rootLocator,
|
|
177
|
-
table: finalTable,
|
|
178
|
-
fillOptions
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
logDebug('Fill operation completed');
|
|
182
|
-
});
|
|
183
|
-
// Alias for explicit usage avoiding Locator.fill conflict
|
|
184
|
-
smart.smartFill = smart.fill;
|
|
185
|
-
return smart;
|
|
186
|
-
};
|
|
187
|
-
const _applyFilters = (baseRows, filters, map, exact) => {
|
|
188
|
-
let filtered = baseRows;
|
|
189
|
-
const page = rootLocator.page();
|
|
190
|
-
for (const [colName, value] of Object.entries(filters)) {
|
|
191
|
-
const colIndex = map.get(colName);
|
|
192
|
-
if (colIndex === undefined) {
|
|
193
|
-
throw _createColumnError(colName, map, 'in filter');
|
|
194
|
-
}
|
|
195
|
-
const filterVal = typeof value === 'number' ? String(value) : value;
|
|
196
|
-
const cellTemplate = resolve(config.cellSelector, page);
|
|
197
|
-
filtered = filtered.filter({
|
|
198
|
-
has: cellTemplate.nth(colIndex).getByText(filterVal, { exact }),
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
return filtered;
|
|
124
|
+
// Use the wrapped SmartRow logic
|
|
125
|
+
return (0, smartRow_1.createSmartRow)(rowLocator, map, rowIndex, config, rootLocator, resolve, finalTable);
|
|
202
126
|
};
|
|
203
127
|
const _findRowLocator = (filters_1, ...args_1) => __awaiter(void 0, [filters_1, ...args_1], void 0, function* (filters, options = {}) {
|
|
204
128
|
var _a;
|
|
@@ -208,11 +132,12 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
208
132
|
logDebug(`Looking for row: ${JSON.stringify(filters)} (MaxPages: ${effectiveMaxPages})`);
|
|
209
133
|
while (true) {
|
|
210
134
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
211
|
-
|
|
135
|
+
// Use FilterEngine
|
|
136
|
+
const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
|
|
212
137
|
const count = yield matchedRows.count();
|
|
213
138
|
logDebug(`Page ${currentPage}: Found ${count} matches.`);
|
|
214
139
|
if (count > 1) {
|
|
215
|
-
//
|
|
140
|
+
// Sample data logic (simplified for refactor, kept inline or moved to util if needed)
|
|
216
141
|
const sampleData = [];
|
|
217
142
|
try {
|
|
218
143
|
const firstFewRows = yield matchedRows.all();
|
|
@@ -222,12 +147,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
222
147
|
sampleData.push(JSON.stringify(rowData));
|
|
223
148
|
}
|
|
224
149
|
}
|
|
225
|
-
catch (e) {
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
const sampleMsg = sampleData.length > 0
|
|
229
|
-
? `\nSample matching rows:\n${sampleData.map((d, i) => ` ${i + 1}. ${d}`).join('\n')}`
|
|
230
|
-
: '';
|
|
150
|
+
catch (e) { }
|
|
151
|
+
const sampleMsg = sampleData.length > 0 ? `\nSample matching rows:\n${sampleData.map((d, i) => ` ${i + 1}. ${d}`).join('\n')}` : '';
|
|
231
152
|
throw new Error(`Strict Mode Violation: Found ${count} rows matching ${JSON.stringify(filters)} on page ${currentPage}. ` +
|
|
232
153
|
`Expected exactly one match. Try adding more filters to make your query unique.${sampleMsg}`);
|
|
233
154
|
}
|
|
@@ -241,7 +162,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
241
162
|
page: rootLocator.page(),
|
|
242
163
|
resolve: resolve
|
|
243
164
|
};
|
|
244
|
-
const
|
|
165
|
+
const paginationResult = yield config.strategies.pagination(context);
|
|
166
|
+
const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
245
167
|
if (didLoadMore) {
|
|
246
168
|
_hasPaginated = true;
|
|
247
169
|
currentPage++;
|
|
@@ -258,33 +180,27 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
258
180
|
}
|
|
259
181
|
});
|
|
260
182
|
const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
|
|
183
|
+
// ... same logic ...
|
|
261
184
|
const { output = 'console', includeTypes = true } = options;
|
|
262
185
|
let finalPrompt = content;
|
|
263
|
-
if (includeTypes)
|
|
186
|
+
if (includeTypes)
|
|
264
187
|
finalPrompt += `\n\n👇 Useful TypeScript Definitions 👇\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
|
|
265
|
-
}
|
|
266
188
|
if (output === 'error') {
|
|
267
189
|
console.log(`⚠️ Throwing error to display [${promptName}] cleanly...`);
|
|
268
190
|
throw new Error(finalPrompt);
|
|
269
191
|
}
|
|
270
192
|
console.log(finalPrompt);
|
|
271
193
|
});
|
|
272
|
-
// Helper to extract clean HTML for prompts
|
|
273
194
|
const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
274
195
|
return loc.evaluate((el) => {
|
|
275
196
|
const clone = el.cloneNode(true);
|
|
276
|
-
// 1. Remove Heavy/Useless Elements
|
|
277
197
|
const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
|
|
278
198
|
clone.querySelectorAll(removeSelectors).forEach(n => n.remove());
|
|
279
|
-
// 2. Clean Attributes
|
|
280
199
|
const walker = document.createTreeWalker(clone, NodeFilter.SHOW_ELEMENT);
|
|
281
200
|
let currentNode = walker.currentNode;
|
|
282
201
|
while (currentNode) {
|
|
283
|
-
currentNode.removeAttribute('style');
|
|
202
|
+
currentNode.removeAttribute('style');
|
|
284
203
|
currentNode.removeAttribute('data-reactid');
|
|
285
|
-
// 3. Condense Tailwind Classes (Heuristic)
|
|
286
|
-
// If class string is very long (>50 chars), keep the first few tokens and truncate.
|
|
287
|
-
// This preserves "MuiRow" but cuts "text-sm p-4 hover:bg-gray-50 ..."
|
|
288
204
|
const cls = currentNode.getAttribute('class');
|
|
289
205
|
if (cls && cls.length > 80) {
|
|
290
206
|
const tokens = cls.split(' ');
|
|
@@ -297,7 +213,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
297
213
|
return clone.outerHTML;
|
|
298
214
|
});
|
|
299
215
|
});
|
|
300
|
-
// Helper to ensure initialization for async methods
|
|
301
216
|
const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
302
217
|
if (!_isInitialized) {
|
|
303
218
|
yield _getMap();
|
|
@@ -306,9 +221,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
306
221
|
});
|
|
307
222
|
const result = {
|
|
308
223
|
init: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
309
|
-
if (_isInitialized && _headerMap)
|
|
224
|
+
if (_isInitialized && _headerMap)
|
|
310
225
|
return result;
|
|
311
|
-
}
|
|
312
226
|
yield _getMap(options === null || options === void 0 ? void 0 : options.timeout);
|
|
313
227
|
_isInitialized = true;
|
|
314
228
|
return result;
|
|
@@ -316,10 +230,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
316
230
|
scrollToColumn: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
317
231
|
const map = yield _getMap();
|
|
318
232
|
const idx = map.get(columnName);
|
|
319
|
-
if (idx === undefined)
|
|
233
|
+
if (idx === undefined)
|
|
320
234
|
throw _createColumnError(columnName, map);
|
|
321
|
-
|
|
322
|
-
yield config.columnStrategy({
|
|
235
|
+
yield config.strategies.cellNavigation({
|
|
323
236
|
config: config,
|
|
324
237
|
root: rootLocator,
|
|
325
238
|
page: rootLocator.page(),
|
|
@@ -329,15 +242,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
329
242
|
});
|
|
330
243
|
}),
|
|
331
244
|
getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
332
|
-
if (!_isInitialized || !_headerMap)
|
|
333
|
-
throw new Error('Table not initialized. Call await table.init() first.');
|
|
334
|
-
}
|
|
245
|
+
if (!_isInitialized || !_headerMap)
|
|
246
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
335
247
|
return Array.from(_headerMap.keys());
|
|
336
248
|
}),
|
|
337
249
|
getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
338
|
-
if (!_isInitialized || !_headerMap)
|
|
250
|
+
if (!_isInitialized || !_headerMap)
|
|
339
251
|
throw new Error('Table not initialized. Call await table.init() first.');
|
|
340
|
-
}
|
|
341
252
|
const idx = _headerMap.get(columnName);
|
|
342
253
|
if (idx === undefined)
|
|
343
254
|
throw _createColumnError(columnName, _headerMap, 'header cell');
|
|
@@ -345,21 +256,21 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
345
256
|
}),
|
|
346
257
|
reset: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
347
258
|
logDebug("Resetting table...");
|
|
348
|
-
const context = {
|
|
349
|
-
root: rootLocator,
|
|
350
|
-
config: config,
|
|
351
|
-
page: rootLocator.page(),
|
|
352
|
-
resolve: resolve
|
|
353
|
-
};
|
|
259
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
354
260
|
yield config.onReset(context);
|
|
355
261
|
_hasPaginated = false;
|
|
356
262
|
_headerMap = null;
|
|
357
263
|
_isInitialized = false;
|
|
358
264
|
logDebug("Table reset complete.");
|
|
359
265
|
}),
|
|
266
|
+
revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
267
|
+
logDebug("Revalidating table structure...");
|
|
268
|
+
_headerMap = null; // Clear the map to force re-scanning
|
|
269
|
+
yield _getMap(); // Re-scan headers
|
|
270
|
+
logDebug("Table revalidated.");
|
|
271
|
+
}),
|
|
360
272
|
getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
361
273
|
var _a, _b;
|
|
362
|
-
// Auto-init if needed (async methods can auto-init)
|
|
363
274
|
yield _ensureInitialized();
|
|
364
275
|
const colIdx = _headerMap.get(column);
|
|
365
276
|
if (colIdx === undefined)
|
|
@@ -378,10 +289,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
378
289
|
results.push(yield mapper(cell));
|
|
379
290
|
}
|
|
380
291
|
if (currentPage < effectiveMaxPages) {
|
|
381
|
-
const context = {
|
|
382
|
-
|
|
383
|
-
};
|
|
384
|
-
if (yield config.pagination(context)) {
|
|
292
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
293
|
+
if (yield config.strategies.pagination(context)) {
|
|
385
294
|
_hasPaginated = true;
|
|
386
295
|
currentPage++;
|
|
387
296
|
continue;
|
|
@@ -391,44 +300,34 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
391
300
|
}
|
|
392
301
|
return results;
|
|
393
302
|
}),
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (!_isInitialized || !_headerMap) {
|
|
398
|
-
throw new Error('Table not initialized. Call await table.init() first.');
|
|
399
|
-
}
|
|
400
|
-
// Handle Index Overload (1-based index)
|
|
401
|
-
if (typeof filtersOrIndex === 'number') {
|
|
402
|
-
const rowIndex = filtersOrIndex - 1;
|
|
403
|
-
const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
|
|
404
|
-
return _makeSmart(rowLocator, _headerMap, rowIndex);
|
|
405
|
-
}
|
|
406
|
-
// Handle Filter Logic (Sync - lazy)
|
|
407
|
-
const filters = filtersOrIndex;
|
|
303
|
+
getRow: (filters, options = { exact: false }) => {
|
|
304
|
+
if (!_isInitialized || !_headerMap)
|
|
305
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
408
306
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
409
|
-
const matchedRows =
|
|
307
|
+
const matchedRows = filterEngine.applyFilters(allRows, filters, _headerMap, options.exact || false, rootLocator.page());
|
|
410
308
|
const rowLocator = matchedRows.first();
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
309
|
+
return _makeSmart(rowLocator, _headerMap, 0); // fallback index 0
|
|
310
|
+
},
|
|
311
|
+
getRowByIndex: (index, options = {}) => {
|
|
312
|
+
if (!_isInitialized || !_headerMap)
|
|
313
|
+
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
314
|
+
const rowIndex = index - 1; // Convert 1-based to 0-based
|
|
315
|
+
const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
|
|
316
|
+
return _makeSmart(rowLocator, _headerMap, rowIndex);
|
|
415
317
|
},
|
|
416
|
-
|
|
417
|
-
// Auto-init if needed (async methods can auto-init)
|
|
318
|
+
findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
418
319
|
yield _ensureInitialized();
|
|
419
|
-
// Full pagination logic (existing _findRowLocator logic)
|
|
420
320
|
let row = yield _findRowLocator(filters, options);
|
|
421
321
|
if (!row) {
|
|
422
322
|
row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
|
|
423
323
|
}
|
|
424
324
|
return _makeSmart(row, _headerMap, 0);
|
|
425
325
|
}),
|
|
426
|
-
|
|
427
|
-
// Auto-init if needed (async methods can auto-init)
|
|
326
|
+
getRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
428
327
|
yield _ensureInitialized();
|
|
429
328
|
let rowLocators = resolve(config.rowSelector, rootLocator);
|
|
430
329
|
if (options === null || options === void 0 ? void 0 : options.filter) {
|
|
431
|
-
rowLocators =
|
|
330
|
+
rowLocators = filterEngine.applyFilters(rowLocators, options.filter, _headerMap, options.exact || false, rootLocator.page());
|
|
432
331
|
}
|
|
433
332
|
const rows = yield rowLocators.all();
|
|
434
333
|
const smartRows = rows.map((loc, i) => _makeSmart(loc, _headerMap, i));
|
|
@@ -437,103 +336,99 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
437
336
|
}
|
|
438
337
|
return smartRows;
|
|
439
338
|
}),
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
339
|
+
findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
340
|
+
var _a, _b, _c, _d;
|
|
341
|
+
yield _ensureInitialized();
|
|
342
|
+
const allRows = [];
|
|
343
|
+
const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
|
|
344
|
+
let pageCount = 0;
|
|
345
|
+
// Collect rows from current page
|
|
346
|
+
let rowLocators = resolve(config.rowSelector, rootLocator);
|
|
347
|
+
rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false, rootLocator.page());
|
|
348
|
+
let rows = yield rowLocators.all();
|
|
349
|
+
allRows.push(...rows.map((loc, i) => _makeSmart(loc, _headerMap, i)));
|
|
350
|
+
// Paginate and collect more rows
|
|
351
|
+
while (pageCount < effectiveMaxPages && config.strategies.pagination) {
|
|
352
|
+
const paginationResult = yield config.strategies.pagination({
|
|
353
|
+
root: rootLocator,
|
|
354
|
+
config,
|
|
355
|
+
resolve,
|
|
356
|
+
page: rootLocator.page()
|
|
357
|
+
});
|
|
358
|
+
const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
359
|
+
if (!didPaginate)
|
|
360
|
+
break;
|
|
361
|
+
pageCount++;
|
|
362
|
+
_hasPaginated = true;
|
|
363
|
+
// Collect rows from new page
|
|
364
|
+
rowLocators = resolve(config.rowSelector, rootLocator);
|
|
365
|
+
rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_d = options === null || options === void 0 ? void 0 : options.exact) !== null && _d !== void 0 ? _d : false, rootLocator.page());
|
|
366
|
+
rows = yield rowLocators.all();
|
|
367
|
+
allRows.push(...rows.map((loc, i) => _makeSmart(loc, _headerMap, i)));
|
|
368
|
+
}
|
|
369
|
+
if (options === null || options === void 0 ? void 0 : options.asJSON) {
|
|
370
|
+
return Promise.all(allRows.map(r => r.toJSON()));
|
|
371
|
+
}
|
|
372
|
+
return allRows;
|
|
458
373
|
}),
|
|
374
|
+
isInitialized: () => {
|
|
375
|
+
return _isInitialized;
|
|
376
|
+
},
|
|
459
377
|
sorting: {
|
|
460
378
|
apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
461
|
-
// Auto-init if needed (async methods can auto-init)
|
|
462
379
|
yield _ensureInitialized();
|
|
463
|
-
if (!config.sorting)
|
|
464
|
-
throw new Error('No sorting strategy has been configured.
|
|
465
|
-
}
|
|
380
|
+
if (!config.strategies.sorting)
|
|
381
|
+
throw new Error('No sorting strategy has been configured.');
|
|
466
382
|
logDebug(`Applying sort for column "${columnName}" (${direction})`);
|
|
467
|
-
const context = {
|
|
468
|
-
|
|
469
|
-
config: config,
|
|
470
|
-
page: rootLocator.page(),
|
|
471
|
-
resolve: resolve
|
|
472
|
-
};
|
|
473
|
-
yield config.sorting.doSort({ columnName, direction, context });
|
|
383
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
384
|
+
yield config.strategies.sorting.doSort({ columnName, direction, context });
|
|
474
385
|
}),
|
|
475
386
|
getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
476
|
-
// Auto-init if needed (async methods can auto-init)
|
|
477
387
|
yield _ensureInitialized();
|
|
478
|
-
if (!config.sorting)
|
|
479
|
-
throw new Error('No sorting strategy has been configured.
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const context = {
|
|
483
|
-
root: rootLocator,
|
|
484
|
-
config: config,
|
|
485
|
-
page: rootLocator.page(),
|
|
486
|
-
resolve: resolve
|
|
487
|
-
};
|
|
488
|
-
return config.sorting.getSortState({ columnName, context });
|
|
388
|
+
if (!config.strategies.sorting)
|
|
389
|
+
throw new Error('No sorting strategy has been configured.');
|
|
390
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
391
|
+
return config.strategies.sorting.getSortState({ columnName, context });
|
|
489
392
|
})
|
|
490
393
|
},
|
|
491
394
|
iterateThroughTable: (callback, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
492
395
|
var _a, _b, _c, _d;
|
|
493
|
-
// Auto-init if needed (async methods can auto-init)
|
|
494
396
|
yield _ensureInitialized();
|
|
495
|
-
|
|
496
|
-
const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.pagination;
|
|
497
|
-
// Check if pagination was explicitly provided in options or config
|
|
397
|
+
const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.strategies.pagination;
|
|
498
398
|
const hasPaginationInOptions = (options === null || options === void 0 ? void 0 : options.pagination) !== undefined;
|
|
499
|
-
if (!hasPaginationInOptions && !hasPaginationInConfig)
|
|
500
|
-
throw new Error('No pagination strategy provided.
|
|
501
|
-
}
|
|
502
|
-
// Reset to initial page before starting
|
|
399
|
+
if (!hasPaginationInOptions && !hasPaginationInConfig)
|
|
400
|
+
throw new Error('No pagination strategy provided.');
|
|
503
401
|
yield result.reset();
|
|
504
402
|
yield result.init();
|
|
505
|
-
// Create restricted table instance (excludes problematic methods)
|
|
506
403
|
const restrictedTable = {
|
|
507
404
|
init: result.init,
|
|
508
405
|
getHeaders: result.getHeaders,
|
|
509
406
|
getHeaderCell: result.getHeaderCell,
|
|
510
|
-
|
|
511
|
-
|
|
407
|
+
getRow: result.getRow,
|
|
408
|
+
getRowByIndex: result.getRowByIndex,
|
|
409
|
+
findRow: result.findRow,
|
|
410
|
+
getRows: result.getRows,
|
|
411
|
+
findRows: result.findRows,
|
|
512
412
|
getColumnValues: result.getColumnValues,
|
|
513
|
-
|
|
514
|
-
generateStrategyPrompt: result.generateStrategyPrompt,
|
|
413
|
+
isInitialized: result.isInitialized,
|
|
515
414
|
sorting: result.sorting,
|
|
516
|
-
scrollToColumn: result.scrollToColumn,
|
|
415
|
+
scrollToColumn: result.scrollToColumn,
|
|
416
|
+
revalidate: result.revalidate,
|
|
517
417
|
};
|
|
518
|
-
// Default functions
|
|
519
418
|
const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
|
|
520
419
|
const getIsLast = (_c = options === null || options === void 0 ? void 0 : options.getIsLast) !== null && _c !== void 0 ? _c : (() => false);
|
|
521
|
-
// Create allData array (persists across iterations)
|
|
522
420
|
const allData = [];
|
|
523
421
|
const effectiveMaxIterations = (_d = options === null || options === void 0 ? void 0 : options.maxIterations) !== null && _d !== void 0 ? _d : config.maxPages;
|
|
524
422
|
let index = 0;
|
|
525
|
-
let paginationResult = true;
|
|
526
|
-
let seenKeys = null;
|
|
423
|
+
let paginationResult = true;
|
|
424
|
+
let seenKeys = null;
|
|
527
425
|
logDebug(`Starting iterateThroughTable (maxIterations: ${effectiveMaxIterations})`);
|
|
528
426
|
while (index < effectiveMaxIterations) {
|
|
529
|
-
// Get current rows
|
|
530
427
|
const rowLocators = yield resolve(config.rowSelector, rootLocator).all();
|
|
531
428
|
let rows = rowLocators.map((loc, i) => _makeSmart(loc, _headerMap, i));
|
|
532
|
-
// Deduplicate if dedupeStrategy provided (across all iterations)
|
|
533
429
|
if ((options === null || options === void 0 ? void 0 : options.dedupeStrategy) && rows.length > 0) {
|
|
534
|
-
if (!seenKeys)
|
|
430
|
+
if (!seenKeys)
|
|
535
431
|
seenKeys = new Set();
|
|
536
|
-
}
|
|
537
432
|
const deduplicated = [];
|
|
538
433
|
for (const row of rows) {
|
|
539
434
|
const key = yield options.dedupeStrategy(row);
|
|
@@ -545,43 +440,20 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
545
440
|
rows = deduplicated;
|
|
546
441
|
logDebug(`Deduplicated ${rowLocators.length} rows to ${rows.length} unique rows (total seen: ${seenKeys.size})`);
|
|
547
442
|
}
|
|
548
|
-
// Determine flags (isLast will be checked after pagination attempt)
|
|
549
443
|
const isFirst = getIsFirst({ index });
|
|
550
444
|
let isLast = getIsLast({ index, paginationResult });
|
|
551
|
-
// Check if this is the last iteration due to maxIterations (before attempting pagination)
|
|
552
445
|
const isLastDueToMax = index === effectiveMaxIterations - 1;
|
|
553
|
-
|
|
554
|
-
if (isFirst && (options === null || options === void 0 ? void 0 : options.onFirst)) {
|
|
446
|
+
if (isFirst && (options === null || options === void 0 ? void 0 : options.onFirst))
|
|
555
447
|
yield options.onFirst({ index, rows, allData });
|
|
556
|
-
}
|
|
557
|
-
// Call main callback
|
|
558
|
-
const returnValue = yield callback({
|
|
559
|
-
index,
|
|
560
|
-
isFirst,
|
|
561
|
-
isLast,
|
|
562
|
-
rows,
|
|
563
|
-
allData,
|
|
564
|
-
table: restrictedTable,
|
|
565
|
-
});
|
|
566
|
-
// Append return value to allData
|
|
448
|
+
const returnValue = yield callback({ index, isFirst, isLast, rows, allData, table: restrictedTable });
|
|
567
449
|
allData.push(returnValue);
|
|
568
|
-
|
|
569
|
-
const context = {
|
|
570
|
-
root: rootLocator,
|
|
571
|
-
config: config,
|
|
572
|
-
page: rootLocator.page(),
|
|
573
|
-
resolve: resolve
|
|
574
|
-
};
|
|
450
|
+
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
575
451
|
paginationResult = yield paginationStrategy(context);
|
|
576
|
-
// Now check isLast with updated paginationResult
|
|
577
452
|
isLast = getIsLast({ index, paginationResult }) || isLastDueToMax;
|
|
578
|
-
|
|
579
|
-
if (isLast && (options === null || options === void 0 ? void 0 : options.onLast)) {
|
|
453
|
+
if (isLast && (options === null || options === void 0 ? void 0 : options.onLast))
|
|
580
454
|
yield options.onLast({ index, rows, allData });
|
|
581
|
-
}
|
|
582
|
-
// Check if we should continue
|
|
583
455
|
if (isLast || !paginationResult) {
|
|
584
|
-
logDebug(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult}
|
|
456
|
+
logDebug(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
|
|
585
457
|
break;
|
|
586
458
|
}
|
|
587
459
|
index++;
|
|
@@ -596,5 +468,4 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
596
468
|
};
|
|
597
469
|
exports.useTable = useTable;
|
|
598
470
|
exports.PaginationStrategies = pagination_1.PaginationStrategies;
|
|
599
|
-
exports.TableStrategies = pagination_1.TableStrategies;
|
|
600
471
|
exports.SortingStrategies = sorting_1.SortingStrategies;
|