@rickcedwhat/playwright-smart-table 6.1.1 → 6.3.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 +4 -2
- package/dist/engine/rowFinder.d.ts +27 -0
- package/dist/engine/rowFinder.js +193 -0
- package/dist/engine/tableMapper.d.ts +16 -0
- package/dist/engine/tableMapper.js +136 -0
- package/dist/filterEngine.d.ts +2 -2
- package/dist/filterEngine.js +15 -4
- package/dist/smartRow.d.ts +1 -1
- package/dist/smartRow.js +57 -36
- package/dist/strategies/index.d.ts +4 -0
- package/dist/strategies/loading.d.ts +14 -0
- package/dist/strategies/loading.js +31 -0
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +38 -14
- package/dist/types.d.ts +34 -14
- package/dist/useTable.d.ts +5 -1
- package/dist/useTable.js +45 -242
- package/dist/utils/stringUtils.js +12 -3
- package/package.json +10 -5
package/dist/useTable.js
CHANGED
|
@@ -23,11 +23,12 @@ const columns_1 = require("./strategies/columns");
|
|
|
23
23
|
Object.defineProperty(exports, "CellNavigationStrategies", { enumerable: true, get: function () { return columns_1.CellNavigationStrategies; } });
|
|
24
24
|
const smartRow_1 = require("./smartRow");
|
|
25
25
|
const filterEngine_1 = require("./filterEngine");
|
|
26
|
+
const tableMapper_1 = require("./engine/tableMapper");
|
|
27
|
+
const rowFinder_1 = require("./engine/rowFinder");
|
|
26
28
|
const resolution_1 = require("./strategies/resolution");
|
|
27
29
|
Object.defineProperty(exports, "ResolutionStrategies", { enumerable: true, get: function () { return resolution_1.ResolutionStrategies; } });
|
|
28
30
|
const strategies_1 = require("./strategies");
|
|
29
31
|
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
30
|
-
const validation_1 = require("./strategies/validation");
|
|
31
32
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
32
33
|
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
33
34
|
/**
|
|
@@ -43,6 +44,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
43
44
|
header: headers_1.HeaderStrategies.visible,
|
|
44
45
|
cellNavigation: columns_1.CellNavigationStrategies.default,
|
|
45
46
|
pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
47
|
+
loading: {
|
|
48
|
+
isHeaderLoading: loading_1.LoadingStrategies.Headers.stable(200)
|
|
49
|
+
}
|
|
46
50
|
};
|
|
47
51
|
const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "thead th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text }) => text, autoScroll: true, onReset: () => __awaiter(void 0, void 0, void 0, function* () { }) }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
|
|
48
52
|
const resolve = (item, parent) => {
|
|
@@ -53,16 +57,13 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
53
57
|
return item;
|
|
54
58
|
};
|
|
55
59
|
// Internal State
|
|
56
|
-
let _headerMap = null;
|
|
57
60
|
let _hasPaginated = false;
|
|
58
|
-
let _isInitialized = false;
|
|
59
61
|
// Helpers
|
|
60
62
|
const log = (msg) => {
|
|
61
|
-
(0, debugUtils_1.logDebug)(config, 'verbose', msg);
|
|
63
|
+
(0, debugUtils_1.logDebug)(config, 'verbose', msg);
|
|
62
64
|
};
|
|
63
65
|
const _createColumnError = (colName, map, context) => {
|
|
64
66
|
const availableColumns = Array.from(map.keys());
|
|
65
|
-
// Use Suggestion Logic from ResolutionStrategy (if we had a fuzzy one, for now manual suggest)
|
|
66
67
|
const lowerCol = colName.toLowerCase();
|
|
67
68
|
const suggestions = availableColumns.filter(col => col.toLowerCase().includes(lowerCol) ||
|
|
68
69
|
lowerCol.includes(col.toLowerCase()) ||
|
|
@@ -80,138 +81,16 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
80
81
|
const contextMsg = context ? ` (${context})` : '';
|
|
81
82
|
return new Error(`Column "${colName}" not found${contextMsg}${suggestion}`);
|
|
82
83
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
log('Mapping headers...');
|
|
87
|
-
const headerTimeout = timeout !== null && timeout !== void 0 ? timeout : 3000;
|
|
88
|
-
if (config.autoScroll) {
|
|
89
|
-
try {
|
|
90
|
-
yield rootLocator.scrollIntoViewIfNeeded({ timeout: 1000 });
|
|
91
|
-
}
|
|
92
|
-
catch (e) { }
|
|
93
|
-
}
|
|
94
|
-
const headerLoc = resolve(config.headerSelector, rootLocator);
|
|
95
|
-
try {
|
|
96
|
-
yield headerLoc.first().waitFor({ state: 'visible', timeout: headerTimeout });
|
|
97
|
-
}
|
|
98
|
-
catch (e) { /* Ignore hydration */ }
|
|
99
|
-
const strategy = config.strategies.header || headers_1.HeaderStrategies.visible;
|
|
100
|
-
const context = {
|
|
101
|
-
root: rootLocator,
|
|
102
|
-
config: config,
|
|
103
|
-
page: rootLocator.page(),
|
|
104
|
-
resolve: resolve
|
|
105
|
-
};
|
|
106
|
-
const rawHeaders = yield strategy(context);
|
|
107
|
-
const seenHeaders = new Set();
|
|
108
|
-
const entries = [];
|
|
109
|
-
for (let i = 0; i < rawHeaders.length; i++) {
|
|
110
|
-
let text = rawHeaders[i].trim() || `__col_${i}`;
|
|
111
|
-
if (config.headerTransformer) {
|
|
112
|
-
text = yield config.headerTransformer({
|
|
113
|
-
text,
|
|
114
|
-
index: i,
|
|
115
|
-
locator: rootLocator.locator(config.headerSelector).nth(i),
|
|
116
|
-
seenHeaders
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
entries.push([text, i]);
|
|
120
|
-
seenHeaders.add(text);
|
|
121
|
-
}
|
|
122
|
-
// Validation: Check for empty table
|
|
123
|
-
if (entries.length === 0) {
|
|
124
|
-
throw new Error(`Initialization Error: No columns found using selector "${config.headerSelector}". Check your selector or ensure the table is visible.`);
|
|
125
|
-
}
|
|
126
|
-
// Validation: Check for duplicates
|
|
127
|
-
const seen = new Set();
|
|
128
|
-
const duplicates = new Set();
|
|
129
|
-
for (const [name] of entries) {
|
|
130
|
-
if (seen.has(name)) {
|
|
131
|
-
duplicates.add(name);
|
|
132
|
-
}
|
|
133
|
-
seen.add(name);
|
|
134
|
-
}
|
|
135
|
-
if (duplicates.size > 0) {
|
|
136
|
-
const dupList = Array.from(duplicates).map(d => `"${d}"`).join(', ');
|
|
137
|
-
throw new Error(`Initialization Error: Duplicate column names found: ${dupList}. Use 'headerTransformer' to rename duplicate columns.`);
|
|
138
|
-
}
|
|
139
|
-
_headerMap = new Map(entries);
|
|
140
|
-
log(`Mapped ${entries.length} columns: ${JSON.stringify(entries.map(e => e[0]))}`);
|
|
141
|
-
return _headerMap;
|
|
142
|
-
});
|
|
84
|
+
// Engines
|
|
85
|
+
const filterEngine = new filterEngine_1.FilterEngine(config, resolve);
|
|
86
|
+
const tableMapper = new tableMapper_1.TableMapper(rootLocator, config, resolve);
|
|
143
87
|
// Placeholder for the final table object
|
|
144
88
|
let finalTable = null;
|
|
145
|
-
const filterEngine = new filterEngine_1.FilterEngine(config, resolve);
|
|
146
89
|
// Helper factory
|
|
147
90
|
const _makeSmart = (rowLocator, map, rowIndex) => {
|
|
148
|
-
// Use the wrapped SmartRow logic
|
|
149
91
|
return (0, smartRow_1.createSmartRow)(rowLocator, map, rowIndex, config, rootLocator, resolve, finalTable);
|
|
150
92
|
};
|
|
151
|
-
const
|
|
152
|
-
var _a, _b;
|
|
153
|
-
const map = yield _getMap();
|
|
154
|
-
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
155
|
-
let currentPage = 1;
|
|
156
|
-
log(`Looking for row: ${JSON.stringify(filters)} (MaxPages: ${effectiveMaxPages})`);
|
|
157
|
-
while (true) {
|
|
158
|
-
// Check for table loading
|
|
159
|
-
if ((_b = config.strategies.loading) === null || _b === void 0 ? void 0 : _b.isTableLoading) {
|
|
160
|
-
const isLoading = yield config.strategies.loading.isTableLoading({ root: rootLocator, config, page: rootLocator.page(), resolve });
|
|
161
|
-
if (isLoading) {
|
|
162
|
-
log('Table is loading... waiting');
|
|
163
|
-
yield rootLocator.page().waitForTimeout(200);
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
const allRows = resolve(config.rowSelector, rootLocator);
|
|
168
|
-
// Use FilterEngine
|
|
169
|
-
const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
|
|
170
|
-
const count = yield matchedRows.count();
|
|
171
|
-
log(`Page ${currentPage}: Found ${count} matches.`);
|
|
172
|
-
if (count > 1) {
|
|
173
|
-
// Sample data logic (simplified for refactor, kept inline or moved to util if needed)
|
|
174
|
-
const sampleData = [];
|
|
175
|
-
try {
|
|
176
|
-
const firstFewRows = yield matchedRows.all();
|
|
177
|
-
const sampleCount = Math.min(firstFewRows.length, 3);
|
|
178
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
179
|
-
const rowData = yield _makeSmart(firstFewRows[i], map).toJSON();
|
|
180
|
-
sampleData.push(JSON.stringify(rowData));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
catch (e) { }
|
|
184
|
-
const sampleMsg = sampleData.length > 0 ? `\nSample matching rows:\n${sampleData.map((d, i) => ` ${i + 1}. ${d}`).join('\n')}` : '';
|
|
185
|
-
throw new Error(`Ambiguous Row: Found ${count} rows matching ${JSON.stringify(filters)} on page ${currentPage}. ` +
|
|
186
|
-
`Expected exactly one match. Try adding more filters to make your query unique.${sampleMsg}`);
|
|
187
|
-
}
|
|
188
|
-
if (count === 1)
|
|
189
|
-
return matchedRows.first();
|
|
190
|
-
if (currentPage < effectiveMaxPages) {
|
|
191
|
-
log(`Page ${currentPage}: Not found. Attempting pagination...`);
|
|
192
|
-
const context = {
|
|
193
|
-
root: rootLocator,
|
|
194
|
-
config: config,
|
|
195
|
-
page: rootLocator.page(),
|
|
196
|
-
resolve: resolve
|
|
197
|
-
};
|
|
198
|
-
const paginationResult = yield config.strategies.pagination(context);
|
|
199
|
-
const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
200
|
-
if (didLoadMore) {
|
|
201
|
-
_hasPaginated = true;
|
|
202
|
-
currentPage++;
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
log(`Page ${currentPage}: Pagination failed (end of data).`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (_hasPaginated) {
|
|
210
|
-
console.warn(`⚠️ [SmartTable] Row not found. The table has been paginated (Current Page: ${currentPage}). You may need to call 'await table.reset()' if the target row is on a previous page.`);
|
|
211
|
-
}
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
});
|
|
93
|
+
const rowFinder = new rowFinder_1.RowFinder(rootLocator, config, resolve, filterEngine, tableMapper, _makeSmart);
|
|
215
94
|
const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
216
95
|
return loc.evaluate((el) => {
|
|
217
96
|
const clone = el.cloneNode(true);
|
|
@@ -247,28 +126,21 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
247
126
|
console.log(finalPrompt);
|
|
248
127
|
});
|
|
249
128
|
const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
250
|
-
|
|
251
|
-
yield _getMap();
|
|
252
|
-
_isInitialized = true;
|
|
253
|
-
}
|
|
129
|
+
yield tableMapper.getMap();
|
|
254
130
|
});
|
|
255
131
|
const result = {
|
|
256
132
|
init: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
257
|
-
if (
|
|
133
|
+
if (tableMapper.isInitialized())
|
|
258
134
|
return result;
|
|
259
135
|
(0, debugUtils_1.warnIfDebugInCI)(config);
|
|
260
136
|
(0, debugUtils_1.logDebug)(config, 'info', 'Initializing table');
|
|
261
|
-
yield
|
|
262
|
-
|
|
263
|
-
if (_headerMap) {
|
|
264
|
-
(0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${_headerMap.size} columns`, Array.from(_headerMap.keys()));
|
|
265
|
-
// Trace event removed - redundant with debug logging
|
|
266
|
-
}
|
|
137
|
+
const map = yield tableMapper.getMap(options === null || options === void 0 ? void 0 : options.timeout);
|
|
138
|
+
(0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${map.size} columns`, Array.from(map.keys()));
|
|
267
139
|
yield (0, debugUtils_1.debugDelay)(config, 'default');
|
|
268
140
|
return result;
|
|
269
141
|
}),
|
|
270
142
|
scrollToColumn: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
271
|
-
const map = yield
|
|
143
|
+
const map = yield tableMapper.getMap();
|
|
272
144
|
const idx = map.get(columnName);
|
|
273
145
|
if (idx === undefined)
|
|
274
146
|
throw _createColumnError(columnName, map);
|
|
@@ -282,14 +154,14 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
282
154
|
});
|
|
283
155
|
}),
|
|
284
156
|
getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
285
|
-
yield
|
|
286
|
-
return Array.from(
|
|
157
|
+
const map = yield tableMapper.getMap();
|
|
158
|
+
return Array.from(map.keys());
|
|
287
159
|
}),
|
|
288
160
|
getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
289
|
-
yield
|
|
290
|
-
const idx =
|
|
161
|
+
const map = yield tableMapper.getMap();
|
|
162
|
+
const idx = map.get(columnName);
|
|
291
163
|
if (idx === undefined)
|
|
292
|
-
throw _createColumnError(columnName,
|
|
164
|
+
throw _createColumnError(columnName, map, 'header cell');
|
|
293
165
|
return resolve(config.headerSelector, rootLocator).nth(idx);
|
|
294
166
|
}),
|
|
295
167
|
reset: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -297,22 +169,20 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
297
169
|
const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
|
|
298
170
|
yield config.onReset(context);
|
|
299
171
|
_hasPaginated = false;
|
|
300
|
-
|
|
301
|
-
_isInitialized = false;
|
|
172
|
+
tableMapper.clear();
|
|
302
173
|
log("Table reset complete.");
|
|
303
174
|
}),
|
|
304
175
|
revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
305
176
|
log("Revalidating table structure...");
|
|
306
|
-
|
|
307
|
-
yield _getMap(); // Re-scan headers
|
|
177
|
+
yield tableMapper.remapHeaders();
|
|
308
178
|
log("Table revalidated.");
|
|
309
179
|
}),
|
|
310
180
|
getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
311
181
|
var _a, _b;
|
|
312
|
-
yield
|
|
313
|
-
const colIdx =
|
|
182
|
+
const map = yield tableMapper.getMap();
|
|
183
|
+
const colIdx = map.get(column);
|
|
314
184
|
if (colIdx === undefined)
|
|
315
|
-
throw _createColumnError(column,
|
|
185
|
+
throw _createColumnError(column, map);
|
|
316
186
|
const mapper = (_a = options === null || options === void 0 ? void 0 : options.mapper) !== null && _a !== void 0 ? _a : ((c) => c.innerText());
|
|
317
187
|
const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
|
|
318
188
|
let currentPage = 1;
|
|
@@ -339,103 +209,35 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
339
209
|
return results;
|
|
340
210
|
}),
|
|
341
211
|
getRow: (filters, options = { exact: false }) => {
|
|
342
|
-
|
|
212
|
+
const map = tableMapper.getMapSync();
|
|
213
|
+
if (!map)
|
|
343
214
|
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
344
215
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
345
|
-
const matchedRows = filterEngine.applyFilters(allRows, filters,
|
|
216
|
+
const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
|
|
346
217
|
const rowLocator = matchedRows.first();
|
|
347
|
-
return _makeSmart(rowLocator,
|
|
218
|
+
return _makeSmart(rowLocator, map, 0); // fallback index 0
|
|
348
219
|
},
|
|
349
220
|
getRowByIndex: (index, options = {}) => {
|
|
350
|
-
|
|
221
|
+
const map = tableMapper.getMapSync();
|
|
222
|
+
if (!map)
|
|
351
223
|
throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
|
|
352
224
|
const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
|
|
353
|
-
return _makeSmart(rowLocator,
|
|
225
|
+
return _makeSmart(rowLocator, map, index);
|
|
354
226
|
},
|
|
355
227
|
findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
let row = yield _findRowLocator(filters, options);
|
|
359
|
-
if (row) {
|
|
360
|
-
(0, debugUtils_1.logDebug)(config, 'info', 'Row found');
|
|
361
|
-
yield (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
362
|
-
return _makeSmart(row, _headerMap, 0);
|
|
363
|
-
}
|
|
364
|
-
(0, debugUtils_1.logDebug)(config, 'error', 'Row not found', filters);
|
|
365
|
-
yield (0, debugUtils_1.debugDelay)(config, 'findRow');
|
|
366
|
-
// Return sentinel row
|
|
367
|
-
row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
|
|
368
|
-
return _makeSmart(row, _headerMap, 0);
|
|
228
|
+
// @ts-ignore
|
|
229
|
+
return rowFinder.findRow(filters, options);
|
|
369
230
|
}),
|
|
370
231
|
getRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (options === null || options === void 0 ? void 0 : options.filter) {
|
|
375
|
-
rowLocators = filterEngine.applyFilters(rowLocators, options.filter, _headerMap, options.exact || false, rootLocator.page());
|
|
376
|
-
}
|
|
377
|
-
const allRowLocs = yield rowLocators.all();
|
|
378
|
-
const smartRows = [];
|
|
379
|
-
const isRowLoading = (_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isRowLoading;
|
|
380
|
-
for (let i = 0; i < allRowLocs.length; i++) {
|
|
381
|
-
const smartRow = _makeSmart(allRowLocs[i], _headerMap, i);
|
|
382
|
-
if (isRowLoading) {
|
|
383
|
-
const loading = yield isRowLoading(smartRow);
|
|
384
|
-
if (loading)
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
smartRows.push(smartRow);
|
|
388
|
-
}
|
|
389
|
-
return (0, smartRowArray_1.createSmartRowArray)(smartRows);
|
|
232
|
+
console.warn('DEPRECATED: table.getRows() is deprecated and will be removed in a future version. Use table.findRows() instead.');
|
|
233
|
+
// @ts-ignore
|
|
234
|
+
return rowFinder.findRows((options === null || options === void 0 ? void 0 : options.filter) || {}, Object.assign(Object.assign({}, options), { maxPages: 1 }));
|
|
390
235
|
}),
|
|
391
236
|
findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
392
|
-
|
|
393
|
-
yield _ensureInitialized();
|
|
394
|
-
const allRows = [];
|
|
395
|
-
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;
|
|
396
|
-
let pageCount = 0;
|
|
397
|
-
// Collect rows from current page
|
|
398
|
-
let rowLocators = resolve(config.rowSelector, rootLocator);
|
|
399
|
-
rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false, rootLocator.page());
|
|
400
|
-
let currentRows = yield rowLocators.all();
|
|
401
|
-
const isRowLoading = (_d = config.strategies.loading) === null || _d === void 0 ? void 0 : _d.isRowLoading;
|
|
402
|
-
for (let i = 0; i < currentRows.length; i++) {
|
|
403
|
-
const smartRow = _makeSmart(currentRows[i], _headerMap, i);
|
|
404
|
-
if (isRowLoading && (yield isRowLoading(smartRow)))
|
|
405
|
-
continue;
|
|
406
|
-
allRows.push(smartRow);
|
|
407
|
-
}
|
|
408
|
-
// Paginate and collect more rows
|
|
409
|
-
while (pageCount < effectiveMaxPages && config.strategies.pagination) {
|
|
410
|
-
const paginationResult = yield config.strategies.pagination({
|
|
411
|
-
root: rootLocator,
|
|
412
|
-
config,
|
|
413
|
-
resolve,
|
|
414
|
-
page: rootLocator.page()
|
|
415
|
-
});
|
|
416
|
-
const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
|
|
417
|
-
if (!didPaginate)
|
|
418
|
-
break;
|
|
419
|
-
pageCount++;
|
|
420
|
-
_hasPaginated = true;
|
|
421
|
-
// Collect rows from new page
|
|
422
|
-
rowLocators = resolve(config.rowSelector, rootLocator);
|
|
423
|
-
rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_e = options === null || options === void 0 ? void 0 : options.exact) !== null && _e !== void 0 ? _e : false, rootLocator.page());
|
|
424
|
-
const newRows = yield rowLocators.all();
|
|
425
|
-
for (let i = 0; i < newRows.length; i++) {
|
|
426
|
-
const smartRow = _makeSmart(newRows[i], _headerMap, i);
|
|
427
|
-
if (isRowLoading && (yield isRowLoading(smartRow)))
|
|
428
|
-
continue;
|
|
429
|
-
allRows.push(smartRow);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
if (options === null || options === void 0 ? void 0 : options.asJSON) {
|
|
433
|
-
return Promise.all(allRows.map(r => r.toJSON()));
|
|
434
|
-
}
|
|
435
|
-
return allRows;
|
|
237
|
+
return rowFinder.findRows(filters, options);
|
|
436
238
|
}),
|
|
437
239
|
isInitialized: () => {
|
|
438
|
-
return
|
|
240
|
+
return tableMapper.isInitialized();
|
|
439
241
|
},
|
|
440
242
|
sorting: {
|
|
441
243
|
apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -463,6 +265,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
463
265
|
throw new Error('No pagination strategy provided.');
|
|
464
266
|
yield result.reset();
|
|
465
267
|
yield result.init();
|
|
268
|
+
const map = tableMapper.getMapSync();
|
|
466
269
|
const restrictedTable = {
|
|
467
270
|
init: result.init,
|
|
468
271
|
getHeaders: result.getHeaders,
|
|
@@ -497,7 +300,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
497
300
|
const smartRowsArray = [];
|
|
498
301
|
const isRowLoading = (_f = config.strategies.loading) === null || _f === void 0 ? void 0 : _f.isRowLoading;
|
|
499
302
|
for (let i = 0; i < rowLocators.length; i++) {
|
|
500
|
-
const smartRow = _makeSmart(rowLocators[i],
|
|
303
|
+
const smartRow = _makeSmart(rowLocators[i], map, i);
|
|
501
304
|
if (isRowLoading && (yield isRowLoading(smartRow)))
|
|
502
305
|
continue;
|
|
503
306
|
smartRowsArray.push(smartRow);
|
|
@@ -533,7 +336,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
533
336
|
let isLast = getIsLast({ index: callbackIndex, paginationResult });
|
|
534
337
|
const isLastDueToMax = index === effectiveMaxIterations - 1;
|
|
535
338
|
if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
|
|
536
|
-
yield options.beforeFirst({ index: callbackIndex, rows: callbackRows, allData });
|
|
339
|
+
yield options.beforeFirst({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows), allData });
|
|
537
340
|
}
|
|
538
341
|
const batchInfo = isBatching ? {
|
|
539
342
|
startIndex: batchStartIndex,
|
|
@@ -565,7 +368,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
565
368
|
finalIsLast = getIsLast({ index: callbackIndex, paginationResult }) || !paginationResult;
|
|
566
369
|
}
|
|
567
370
|
if (finalIsLast && (options === null || options === void 0 ? void 0 : options.afterLast)) {
|
|
568
|
-
yield options.afterLast({ index: callbackIndex, rows: callbackRows, allData });
|
|
371
|
+
yield options.afterLast({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows), allData });
|
|
569
372
|
}
|
|
570
373
|
if (finalIsLast || !paginationResult) {
|
|
571
374
|
log(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
|
|
@@ -589,7 +392,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
589
392
|
const isFirst = getIsFirst({ index: callbackIndex });
|
|
590
393
|
const isLast = true;
|
|
591
394
|
if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
|
|
592
|
-
yield options.beforeFirst({ index: callbackIndex, rows: batchRows, allData });
|
|
395
|
+
yield options.beforeFirst({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(batchRows), allData });
|
|
593
396
|
}
|
|
594
397
|
const batchInfo = {
|
|
595
398
|
startIndex: batchStartIndex,
|
|
@@ -612,7 +415,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
612
415
|
allData.push(returnValue);
|
|
613
416
|
}
|
|
614
417
|
if (options === null || options === void 0 ? void 0 : options.afterLast) {
|
|
615
|
-
yield options.afterLast({ index: callbackIndex, rows: batchRows, allData });
|
|
418
|
+
yield options.afterLast({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(batchRows), allData });
|
|
616
419
|
}
|
|
617
420
|
log(`Pagination failed mid-batch (index: ${index})`);
|
|
618
421
|
break;
|
|
@@ -18,11 +18,19 @@ function levenshteinDistance(a, b) {
|
|
|
18
18
|
}
|
|
19
19
|
for (let i = 1; i <= b.length; i++) {
|
|
20
20
|
for (let j = 1; j <= a.length; j++) {
|
|
21
|
-
|
|
21
|
+
const charB = b.charAt(i - 1);
|
|
22
|
+
const charA = a.charAt(j - 1);
|
|
23
|
+
if (charB === charA) {
|
|
24
|
+
// Exact match
|
|
22
25
|
matrix[i][j] = matrix[i - 1][j - 1];
|
|
23
26
|
}
|
|
24
27
|
else {
|
|
25
|
-
|
|
28
|
+
let cost = 1;
|
|
29
|
+
// If characters match ignoring case, cost is only 0.1 (almost identical)
|
|
30
|
+
if (charB.toLowerCase() === charA.toLowerCase()) {
|
|
31
|
+
cost = 0.1;
|
|
32
|
+
}
|
|
33
|
+
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + cost, // substitution
|
|
26
34
|
matrix[i][j - 1] + 1, // insertion
|
|
27
35
|
matrix[i - 1][j] + 1 // deletion
|
|
28
36
|
);
|
|
@@ -36,7 +44,8 @@ function levenshteinDistance(a, b) {
|
|
|
36
44
|
* 1 = identical, 0 = completely different
|
|
37
45
|
*/
|
|
38
46
|
function stringSimilarity(a, b) {
|
|
39
|
-
|
|
47
|
+
// We do NOT modify case here anymore, because levenshteinDistance now handles case weighting
|
|
48
|
+
const distance = levenshteinDistance(a, b);
|
|
40
49
|
const maxLen = Math.max(a.length, b.length);
|
|
41
50
|
return maxLen === 0 ? 1 : 1 - (distance / maxLen);
|
|
42
51
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Smart, column-aware table interactions for Playwright",
|
|
5
5
|
"author": "Cedrick Catalan",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"docs:build": "vitepress build docs",
|
|
23
23
|
"build": "npm run generate-types && npm run generate-docs && npm run generate-all-api-docs && npm run update-all-api-signatures && tsc",
|
|
24
24
|
"prepublishOnly": "npm run build",
|
|
25
|
-
"test": "npx playwright test",
|
|
25
|
+
"test": "npm run test:unit && npx playwright test",
|
|
26
|
+
"test:unit": "vitest run --reporter=verbose --reporter=html",
|
|
27
|
+
"test:unit:ui": "vitest --ui",
|
|
28
|
+
"test:e2e": "npx playwright test",
|
|
26
29
|
"test:compatibility": "npx playwright test compatibility",
|
|
27
30
|
"prepare": "husky install"
|
|
28
31
|
},
|
|
@@ -38,9 +41,11 @@
|
|
|
38
41
|
"devDependencies": {
|
|
39
42
|
"@playwright/test": "^1.49.1",
|
|
40
43
|
"@types/node": "^22.10.5",
|
|
44
|
+
"@vitest/ui": "^4.0.18",
|
|
45
|
+
"happy-dom": "^20.6.1",
|
|
41
46
|
"husky": "^9.1.7",
|
|
42
47
|
"typescript": "^5.7.2",
|
|
43
|
-
"vitepress": "^1.6.4"
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
"vitepress": "^1.6.4",
|
|
49
|
+
"vitest": "^4.0.18"
|
|
50
|
+
}
|
|
46
51
|
}
|