@rickcedwhat/playwright-smart-table 4.0.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 +286 -102
- package/dist/smartRow.js +2 -5
- package/dist/strategies/columns.d.ts +0 -34
- package/dist/strategies/columns.js +1 -34
- package/dist/strategies/headers.d.ts +0 -16
- package/dist/strategies/headers.js +1 -113
- package/dist/strategies/index.d.ts +0 -28
- package/dist/strategies/index.js +0 -3
- package/dist/strategies/pagination.d.ts +0 -21
- package/dist/strategies/pagination.js +1 -23
- 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 +94 -24
- package/dist/types.d.ts +89 -24
- package/dist/useTable.d.ts +2 -9
- package/dist/useTable.js +54 -32
- package/package.json +2 -2
|
@@ -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.CellNavigationStrategies = void 0;
|
|
13
13
|
exports.CellNavigationStrategies = {
|
|
14
14
|
/**
|
|
15
15
|
* Default strategy: Assumes column is accessible or standard scrolling works.
|
|
@@ -17,38 +17,5 @@ exports.CellNavigationStrategies = {
|
|
|
17
17
|
*/
|
|
18
18
|
default: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
19
19
|
// No-op
|
|
20
|
-
}),
|
|
21
|
-
/**
|
|
22
|
-
* Strategy that clicks into the table to establish focus and then uses the Right Arrow key
|
|
23
|
-
* to navigate to the target CELL (navigates down to the row, then right to the column).
|
|
24
|
-
*
|
|
25
|
-
* Useful for canvas-based grids like Glide where DOM scrolling might not be enough for interaction
|
|
26
|
-
* or where keyboard navigation is the primary way to move focus.
|
|
27
|
-
*/
|
|
28
|
-
keyboard: (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
-
const { root, page, index, rowLocator, rowIndex } = context;
|
|
30
|
-
if (typeof rowIndex !== 'number') {
|
|
31
|
-
throw new Error('Row index is required for keyboard navigation');
|
|
32
|
-
}
|
|
33
|
-
yield root.focus();
|
|
34
|
-
yield page.waitForTimeout(100);
|
|
35
|
-
// Robust Navigation:
|
|
36
|
-
// 1. Jump to Top-Left (Reset) - Sequence for Cross-OS (Mac/Windows)
|
|
37
|
-
yield page.keyboard.press('Control+Home');
|
|
38
|
-
yield page.keyboard.press('Meta+ArrowUp'); // Mac Go-To-Top
|
|
39
|
-
yield page.keyboard.press('Home'); // Ensure start of row
|
|
40
|
-
yield page.waitForTimeout(150);
|
|
41
|
-
// 2. Move Down to Target Row
|
|
42
|
-
for (let i = 0; i < rowIndex; i++) {
|
|
43
|
-
yield page.keyboard.press('ArrowDown');
|
|
44
|
-
}
|
|
45
|
-
// 3. Move Right to Target Column
|
|
46
|
-
for (let i = 0; i < index; i++) {
|
|
47
|
-
yield page.keyboard.press('ArrowRight');
|
|
48
|
-
}
|
|
49
|
-
yield page.waitForTimeout(50);
|
|
50
20
|
})
|
|
51
21
|
};
|
|
52
|
-
// Backwards compatibility - deprecated
|
|
53
|
-
/** @deprecated Use CellNavigationStrategies instead */
|
|
54
|
-
exports.ColumnStrategies = exports.CellNavigationStrategies;
|
|
@@ -10,20 +10,4 @@ export declare const HeaderStrategies: {
|
|
|
10
10
|
* This is fast but won't find virtualized columns off-screen.
|
|
11
11
|
*/
|
|
12
12
|
visible: ({ config, resolve, root }: StrategyContext) => Promise<string[]>;
|
|
13
|
-
/**
|
|
14
|
-
* Scans for headers by finding a scrollable container and setting scrollLeft.
|
|
15
|
-
*/
|
|
16
|
-
scrollRight: (context: StrategyContext, options?: {
|
|
17
|
-
limit?: number;
|
|
18
|
-
selector?: string;
|
|
19
|
-
scrollAmount?: number;
|
|
20
|
-
}) => Promise<string[]>;
|
|
21
|
-
/**
|
|
22
|
-
* Strategy that clicks into the table to establish focus and then uses the Right Arrow key
|
|
23
|
-
* to navigate cell-by-cell, collecting headers found along the way.
|
|
24
|
-
*/
|
|
25
|
-
keyboard: (context: StrategyContext, options?: {
|
|
26
|
-
limit?: number;
|
|
27
|
-
maxSilentClicks?: number;
|
|
28
|
-
}) => Promise<string[]>;
|
|
29
13
|
};
|
|
@@ -26,117 +26,5 @@ exports.HeaderStrategies = {
|
|
|
26
26
|
}
|
|
27
27
|
const texts = yield headerLoc.allInnerTexts();
|
|
28
28
|
return texts.map(t => t.trim());
|
|
29
|
-
})
|
|
30
|
-
/**
|
|
31
|
-
* Scans for headers by finding a scrollable container and setting scrollLeft.
|
|
32
|
-
*/
|
|
33
|
-
scrollRight: (context, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
34
|
-
var _a, _b;
|
|
35
|
-
const { resolve, config, root, page } = context;
|
|
36
|
-
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 20;
|
|
37
|
-
const scrollAmount = (_b = options === null || options === void 0 ? void 0 : options.scrollAmount) !== null && _b !== void 0 ? _b : 300;
|
|
38
|
-
const collectedHeaders = new Set();
|
|
39
|
-
const getVisible = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
-
const headerLoc = resolve(config.headerSelector, root);
|
|
41
|
-
const texts = yield headerLoc.allInnerTexts();
|
|
42
|
-
return texts.map(t => t.trim());
|
|
43
|
-
});
|
|
44
|
-
// Initial capture
|
|
45
|
-
let currentHeaders = yield getVisible();
|
|
46
|
-
currentHeaders.forEach(h => collectedHeaders.add(h));
|
|
47
|
-
// Find scroller using JS for better iframe/shadow support
|
|
48
|
-
const scrollerHandle = yield root.evaluateHandle((el, selector) => {
|
|
49
|
-
if (selector && el.matches(selector))
|
|
50
|
-
return el;
|
|
51
|
-
const effectiveSelector = selector || '.dvn-scroller';
|
|
52
|
-
const ancestor = el.closest(effectiveSelector);
|
|
53
|
-
if (ancestor)
|
|
54
|
-
return ancestor;
|
|
55
|
-
return document.querySelector(effectiveSelector);
|
|
56
|
-
}, options === null || options === void 0 ? void 0 : options.selector);
|
|
57
|
-
const isScrollerFound = yield scrollerHandle.evaluate(el => !!el);
|
|
58
|
-
if (isScrollerFound) {
|
|
59
|
-
yield scrollerHandle.evaluate(el => el.scrollLeft = 0);
|
|
60
|
-
yield page.waitForTimeout(200);
|
|
61
|
-
for (let i = 0; i < limit; i++) {
|
|
62
|
-
const sizeBefore = collectedHeaders.size;
|
|
63
|
-
yield scrollerHandle.evaluate((el, amount) => el.scrollLeft += amount, scrollAmount);
|
|
64
|
-
yield page.waitForTimeout(300);
|
|
65
|
-
const newHeaders = yield getVisible();
|
|
66
|
-
newHeaders.forEach(h => collectedHeaders.add(h));
|
|
67
|
-
if (collectedHeaders.size === sizeBefore) {
|
|
68
|
-
yield scrollerHandle.evaluate((el, amount) => el.scrollLeft += amount, scrollAmount);
|
|
69
|
-
yield page.waitForTimeout(300);
|
|
70
|
-
const retryHeaders = yield getVisible();
|
|
71
|
-
retryHeaders.forEach(h => collectedHeaders.add(h));
|
|
72
|
-
if (collectedHeaders.size === sizeBefore)
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
console.warn("HeaderStrategies.scrollRight: Could not find scroller. Returning visible headers.");
|
|
79
|
-
}
|
|
80
|
-
// Scroll back to start
|
|
81
|
-
yield scrollerHandle.evaluate(el => el.scrollLeft = 0);
|
|
82
|
-
yield page.waitForTimeout(200);
|
|
83
|
-
return Array.from(collectedHeaders);
|
|
84
|
-
}),
|
|
85
|
-
/**
|
|
86
|
-
* Strategy that clicks into the table to establish focus and then uses the Right Arrow key
|
|
87
|
-
* to navigate cell-by-cell, collecting headers found along the way.
|
|
88
|
-
*/
|
|
89
|
-
keyboard: (context, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
90
|
-
var _a, _b;
|
|
91
|
-
const { resolve, config, root, page } = context;
|
|
92
|
-
const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 100;
|
|
93
|
-
const maxSilentClicks = (_b = options === null || options === void 0 ? void 0 : options.maxSilentClicks) !== null && _b !== void 0 ? _b : 10;
|
|
94
|
-
const collectedHeaders = new Set();
|
|
95
|
-
const getVisible = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
96
|
-
const headerLoc = resolve(config.headerSelector, root);
|
|
97
|
-
const texts = yield headerLoc.allInnerTexts();
|
|
98
|
-
return texts.map(t => t.trim());
|
|
99
|
-
});
|
|
100
|
-
// 1. Initial capture
|
|
101
|
-
let currentHeaders = yield getVisible();
|
|
102
|
-
currentHeaders.forEach(h => collectedHeaders.add(h));
|
|
103
|
-
// 2. Click to focus
|
|
104
|
-
// Try to find the canvas sibling specifically for Glide, otherwise click root
|
|
105
|
-
const canvas = root.locator('xpath=ancestor::div').locator('canvas').first();
|
|
106
|
-
if ((yield canvas.count()) > 0) {
|
|
107
|
-
// Click lower in the canvas to hit a data cell instead of header
|
|
108
|
-
// Adjusted to y=60 to target Row 1
|
|
109
|
-
yield canvas.click({ position: { x: 50, y: 60 }, force: true }).catch(() => { });
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
yield root.click({ position: { x: 10, y: 10 }, force: true }).catch(() => { });
|
|
113
|
-
}
|
|
114
|
-
// Reset to home
|
|
115
|
-
yield page.keyboard.press('Control+Home');
|
|
116
|
-
yield page.keyboard.press('Home');
|
|
117
|
-
// Wait for potential scroll/focus reset
|
|
118
|
-
yield page.evaluate(() => new Promise(requestAnimationFrame));
|
|
119
|
-
currentHeaders = yield getVisible();
|
|
120
|
-
currentHeaders.forEach(h => collectedHeaders.add(h));
|
|
121
|
-
// 3. Navigate right loop
|
|
122
|
-
let silentCounter = 0;
|
|
123
|
-
for (let i = 0; i < limit; i++) {
|
|
124
|
-
const sizeBefore = collectedHeaders.size;
|
|
125
|
-
yield page.keyboard.press('ArrowRight');
|
|
126
|
-
// Small breathing room for key press to register
|
|
127
|
-
yield page.evaluate(() => new Promise(requestAnimationFrame));
|
|
128
|
-
const newHeaders = yield getVisible();
|
|
129
|
-
newHeaders.forEach(h => collectedHeaders.add(h));
|
|
130
|
-
if (collectedHeaders.size === sizeBefore) {
|
|
131
|
-
silentCounter++;
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
silentCounter = 0;
|
|
135
|
-
}
|
|
136
|
-
if (silentCounter >= maxSilentClicks) {
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return Array.from(collectedHeaders);
|
|
141
|
-
}),
|
|
29
|
+
})
|
|
142
30
|
};
|
|
@@ -7,39 +7,16 @@ export * from './resolution';
|
|
|
7
7
|
export declare const Strategies: {
|
|
8
8
|
Pagination: {
|
|
9
9
|
clickNext: (nextButtonSelector: import("..").Selector, timeout?: number) => import("..").PaginationStrategy;
|
|
10
|
-
clickLoadMore: (buttonSelector: import("..").Selector, timeout?: number) => import("..").PaginationStrategy;
|
|
11
10
|
infiniteScroll: (timeout?: number) => import("..").PaginationStrategy;
|
|
12
11
|
};
|
|
13
12
|
Sorting: {
|
|
14
13
|
AriaSort: () => import("..").SortingStrategy;
|
|
15
14
|
};
|
|
16
|
-
Column: {
|
|
17
|
-
default: () => Promise<void>;
|
|
18
|
-
keyboard: (context: import("..").StrategyContext & {
|
|
19
|
-
column: string;
|
|
20
|
-
index: number;
|
|
21
|
-
rowIndex?: number;
|
|
22
|
-
}) => Promise<void>;
|
|
23
|
-
};
|
|
24
15
|
CellNavigation: {
|
|
25
16
|
default: () => Promise<void>;
|
|
26
|
-
keyboard: (context: import("..").StrategyContext & {
|
|
27
|
-
column: string;
|
|
28
|
-
index: number;
|
|
29
|
-
rowIndex?: number;
|
|
30
|
-
}) => Promise<void>;
|
|
31
17
|
};
|
|
32
18
|
Header: {
|
|
33
19
|
visible: ({ config, resolve, root }: import("..").StrategyContext) => Promise<string[]>;
|
|
34
|
-
scrollRight: (context: import("..").StrategyContext, options?: {
|
|
35
|
-
limit?: number;
|
|
36
|
-
selector?: string;
|
|
37
|
-
scrollAmount?: number;
|
|
38
|
-
}) => Promise<string[]>;
|
|
39
|
-
keyboard: (context: import("..").StrategyContext, options?: {
|
|
40
|
-
limit?: number;
|
|
41
|
-
maxSilentClicks?: number;
|
|
42
|
-
}) => Promise<string[]>;
|
|
43
20
|
};
|
|
44
21
|
Fill: {
|
|
45
22
|
default: ({ row, columnName, value, fillOptions }: Parameters<import("..").FillStrategy>[0]) => Promise<void>;
|
|
@@ -47,9 +24,4 @@ export declare const Strategies: {
|
|
|
47
24
|
Resolution: {
|
|
48
25
|
default: import("./resolution").ColumnResolutionStrategy;
|
|
49
26
|
};
|
|
50
|
-
DeprecatedPagination: {
|
|
51
|
-
clickNext: (nextButtonSelector: import("..").Selector, timeout?: number) => import("..").PaginationStrategy;
|
|
52
|
-
clickLoadMore: (buttonSelector: import("..").Selector, timeout?: number) => import("..").PaginationStrategy;
|
|
53
|
-
infiniteScroll: (timeout?: number) => import("..").PaginationStrategy;
|
|
54
|
-
};
|
|
55
27
|
};
|
package/dist/strategies/index.js
CHANGED
|
@@ -30,11 +30,8 @@ __exportStar(require("./resolution"), exports);
|
|
|
30
30
|
exports.Strategies = {
|
|
31
31
|
Pagination: pagination_1.PaginationStrategies,
|
|
32
32
|
Sorting: sorting_1.SortingStrategies,
|
|
33
|
-
Column: columns_1.ColumnStrategies,
|
|
34
33
|
CellNavigation: columns_1.CellNavigationStrategies,
|
|
35
34
|
Header: headers_1.HeaderStrategies,
|
|
36
35
|
Fill: fill_1.FillStrategies,
|
|
37
36
|
Resolution: resolution_1.ResolutionStrategies,
|
|
38
|
-
// Alias for backward compatibility if needed, though we are encouraging the new structure
|
|
39
|
-
DeprecatedPagination: pagination_1.DeprecatedPaginationStrategies
|
|
40
37
|
};
|
|
@@ -4,27 +4,6 @@ export declare const PaginationStrategies: {
|
|
|
4
4
|
* Strategy: Clicks a "Next" button and waits for the first row of data to change.
|
|
5
5
|
*/
|
|
6
6
|
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
7
|
-
/**
|
|
8
|
-
* Strategy: Clicks a "Load More" button and waits for the row count to increase.
|
|
9
|
-
*/
|
|
10
|
-
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
11
|
-
/**
|
|
12
|
-
* Strategy: Scrolls to the bottom and waits for more rows to appear.
|
|
13
|
-
*/
|
|
14
|
-
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* @deprecated Use `PaginationStrategies` instead. This alias will be removed in a future major version.
|
|
18
|
-
*/
|
|
19
|
-
export declare const DeprecatedPaginationStrategies: {
|
|
20
|
-
/**
|
|
21
|
-
* Strategy: Clicks a "Next" button and waits for the first row of data to change.
|
|
22
|
-
*/
|
|
23
|
-
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
24
|
-
/**
|
|
25
|
-
* Strategy: Clicks a "Load More" button and waits for the row count to increase.
|
|
26
|
-
*/
|
|
27
|
-
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
28
7
|
/**
|
|
29
8
|
* Strategy: Scrolls to the bottom and waits for more rows to appear.
|
|
30
9
|
*/
|
|
@@ -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.PaginationStrategies = void 0;
|
|
13
13
|
const utils_1 = require("../utils");
|
|
14
14
|
exports.PaginationStrategies = {
|
|
15
15
|
/**
|
|
@@ -31,24 +31,6 @@ exports.PaginationStrategies = {
|
|
|
31
31
|
return success;
|
|
32
32
|
});
|
|
33
33
|
},
|
|
34
|
-
/**
|
|
35
|
-
* Strategy: Clicks a "Load More" button and waits for the row count to increase.
|
|
36
|
-
*/
|
|
37
|
-
clickLoadMore: (buttonSelector, timeout = 5000) => {
|
|
38
|
-
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
|
|
39
|
-
const loadMoreBtn = resolve(buttonSelector, root).first();
|
|
40
|
-
if (!(yield loadMoreBtn.isVisible()) || !(yield loadMoreBtn.isEnabled())) {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
const rows = resolve(config.rowSelector, root);
|
|
44
|
-
const oldCount = yield rows.count();
|
|
45
|
-
yield loadMoreBtn.click();
|
|
46
|
-
return yield (0, utils_1.waitForCondition)(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
|
-
const newCount = yield rows.count();
|
|
48
|
-
return newCount > oldCount;
|
|
49
|
-
}), timeout, page);
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
34
|
/**
|
|
53
35
|
* Strategy: Scrolls to the bottom and waits for more rows to appear.
|
|
54
36
|
*/
|
|
@@ -66,7 +48,3 @@ exports.PaginationStrategies = {
|
|
|
66
48
|
});
|
|
67
49
|
}
|
|
68
50
|
};
|
|
69
|
-
/**
|
|
70
|
-
* @deprecated Use `PaginationStrategies` instead. This alias will be removed in a future major version.
|
|
71
|
-
*/
|
|
72
|
-
exports.DeprecatedPaginationStrategies = exports.PaginationStrategies;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PaginationStrategy, SortingStrategy, FillStrategy } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that a pagination strategy returns a boolean.
|
|
4
|
+
* @param result - The result from a pagination strategy
|
|
5
|
+
* @param strategyName - Name of the strategy for error messages
|
|
6
|
+
*/
|
|
7
|
+
export declare function validatePaginationResult(result: any, strategyName?: string): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Validates that a pagination strategy is properly configured.
|
|
10
|
+
* @param strategy - The pagination strategy to validate
|
|
11
|
+
*/
|
|
12
|
+
export declare function validatePaginationStrategy(strategy: any): strategy is PaginationStrategy;
|
|
13
|
+
/**
|
|
14
|
+
* Validates that a sorting strategy has the required methods.
|
|
15
|
+
* @param strategy - The sorting strategy to validate
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateSortingStrategy(strategy: any): strategy is SortingStrategy;
|
|
18
|
+
/**
|
|
19
|
+
* Validates that a fill strategy is properly configured.
|
|
20
|
+
* @param strategy - The fill strategy to validate
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateFillStrategy(strategy: any): strategy is FillStrategy;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validatePaginationResult = validatePaginationResult;
|
|
4
|
+
exports.validatePaginationStrategy = validatePaginationStrategy;
|
|
5
|
+
exports.validateSortingStrategy = validateSortingStrategy;
|
|
6
|
+
exports.validateFillStrategy = validateFillStrategy;
|
|
7
|
+
/**
|
|
8
|
+
* Validates that a pagination strategy returns a boolean.
|
|
9
|
+
* @param result - The result from a pagination strategy
|
|
10
|
+
* @param strategyName - Name of the strategy for error messages
|
|
11
|
+
*/
|
|
12
|
+
function validatePaginationResult(result, strategyName = 'Custom Pagination Strategy') {
|
|
13
|
+
if (typeof result !== 'boolean') {
|
|
14
|
+
throw new Error(`[${strategyName}] Pagination strategy must return a boolean (true if paginated, false if no more pages). ` +
|
|
15
|
+
`Received: ${typeof result} (${JSON.stringify(result)})`);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Validates that a pagination strategy is properly configured.
|
|
21
|
+
* @param strategy - The pagination strategy to validate
|
|
22
|
+
*/
|
|
23
|
+
function validatePaginationStrategy(strategy) {
|
|
24
|
+
if (typeof strategy !== 'function') {
|
|
25
|
+
throw new Error(`Pagination strategy must be a function. Received: ${typeof strategy}`);
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validates that a sorting strategy has the required methods.
|
|
31
|
+
* @param strategy - The sorting strategy to validate
|
|
32
|
+
*/
|
|
33
|
+
function validateSortingStrategy(strategy) {
|
|
34
|
+
if (!strategy || typeof strategy !== 'object') {
|
|
35
|
+
throw new Error(`Sorting strategy must be an object with 'doSort' and 'getSortState' methods. Received: ${typeof strategy}`);
|
|
36
|
+
}
|
|
37
|
+
if (typeof strategy.doSort !== 'function') {
|
|
38
|
+
throw new Error(`Sorting strategy must have a 'doSort' method. Missing or not a function.`);
|
|
39
|
+
}
|
|
40
|
+
if (typeof strategy.getSortState !== 'function') {
|
|
41
|
+
throw new Error(`Sorting strategy must have a 'getSortState' method. Missing or not a function.`);
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Validates that a fill strategy is properly configured.
|
|
47
|
+
* @param strategy - The fill strategy to validate
|
|
48
|
+
*/
|
|
49
|
+
function validateFillStrategy(strategy) {
|
|
50
|
+
if (typeof strategy !== 'function') {
|
|
51
|
+
throw new Error(`Fill strategy must be a function. Received: ${typeof strategy}`);
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
package/dist/typeContext.d.ts
CHANGED
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* This file is generated by scripts/embed-types.js
|
|
4
4
|
* It contains the raw text of types.ts to provide context for LLM prompts.
|
|
5
5
|
*/
|
|
6
|
-
export declare const TYPE_CONTEXT = "\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\n/**\n * Function to get a cell locator given row, column info.\n * Replaces the old cellResolver.\n */\nexport type GetCellLocatorFn = (args: {\n row: Locator;\n columnName: string;\n columnIndex: number;\n rowIndex?: number;\n page: Page;\n}) => Locator;\n\n/**\n * Function to get the currently active/focused cell.\n * Returns null if no cell is active.\n */\nexport type GetActiveCellFn = (args: TableContext) => Promise<{\n rowIndex: number;\n columnIndex: number;\n columnName?: string;\n locator: Locator;\n} | null>;\n\n\nexport type SmartRow<T = any> = Locator & {\n getRequestIndex(): number | undefined;\n rowIndex?: number;\n getCell(column: string): Locator;\n toJSON(options?: { columns?: string[] }): Promise<T>;\n /**\n * Scrolls/paginates to bring this row into view.\n * Only works if rowIndex is known.\n */\n bringIntoView(): Promise<void>;\n /**\n * Fills the row with data. Automatically detects input types.\n */\n fill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;\n /**\n * Alias for fill() to avoid conflict with Locator.fill()\n */\n smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;\n};\n\nexport type StrategyContext = TableContext & { rowLocator?: Locator; rowIndex?: number };\n\n/**\n * Defines the contract for a sorting strategy.\n */\nexport interface SortingStrategy {\n /**\n * Performs the sort action on a column.\n */\n doSort(options: {\n columnName: string;\n direction: 'asc' | 'desc';\n context: StrategyContext;\n }): Promise<void>;\n\n /**\n * Retrieves the current sort state of a column.\n */\n getSortState(options: {\n columnName: string;\n context: StrategyContext;\n }): Promise<'asc' | 'desc' | 'none'>;\n}\n\nexport interface TableContext {\n root: Locator;\n config: FinalTableConfig;\n page: Page;\n resolve: (selector: Selector, parent: Locator | Page) => Locator;\n}\n\nexport type PaginationStrategy = (context: TableContext) => Promise<boolean>;\n\nexport type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;\n\nexport interface PromptOptions {\n /**\n * Output Strategy:\n * - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).\n * - 'console': Standard console logs (Default).\n */\n output?: 'console' | 'error';\n includeTypes?: boolean;\n}\n\nexport type FillStrategy = (options: {\n row: SmartRow;\n columnName: string;\n value: any;\n index: number;\n page: Page;\n rootLocator: Locator;\n table: TableResult; // The parent table instance\n fillOptions?: FillOptions;\n}) => Promise<void>;\n\nexport type { HeaderStrategy } from './strategies/headers';\nexport type { CellNavigationStrategy } from './strategies/columns';\n\n/**\n * Strategy to resolve column names (string or regex) to their index.\n */\nexport type { ColumnResolutionStrategy } from './strategies/resolution';\n\n/**\n * Strategy to filter rows based on criteria.\n */\nexport interface FilterStrategy {\n apply(options: {\n rows: Locator;\n filter: { column: string, value: string | RegExp | number };\n colIndex: number;\n tableContext: TableContext;\n }): Locator;\n}\n\n/**\n * Organized container for all table interaction strategies.\n */\nexport interface TableStrategies {\n /** Strategy for discovering/scanning headers */\n header?: HeaderStrategy;\n /** Strategy for navigating to specific cells (row + column) */\n cellNavigation?: CellNavigationStrategy;\n /** Strategy for filling form inputs */\n fill?: FillStrategy;\n /** Strategy for paginating through data */\n pagination?: PaginationStrategy;\n /** Strategy for sorting columns */\n sorting?: SortingStrategy;\n /** Function to get a cell locator */\n getCellLocator?: GetCellLocatorFn;\n /** Function to get the currently active/focused cell */\n getActiveCell?: GetActiveCellFn;\n}\n\n/**\n * Configuration options for useTable.\n */\nexport interface TableConfig {\n /** Selector for the table headers */\n headerSelector?: string;\n /** Selector for the table rows */\n rowSelector?: string;\n /** Selector for the cells within a row */\n cellSelector?: string;\n /** Number of pages to scan for verification */\n maxPages?: number;\n /** Hook to rename columns dynamically */\n headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n /** Automatically scroll to table on init */\n autoScroll?: boolean;\n /** Enable debug logs */\n debug?: boolean;\n /** Reset hook */\n onReset?: (context: TableContext) => Promise<void>;\n /** All interaction strategies */\n strategies?: TableStrategies;\n}\n\nexport interface FinalTableConfig extends TableConfig {\n headerSelector: string;\n rowSelector: string;\n cellSelector: string;\n maxPages: number;\n autoScroll: boolean;\n debug: boolean;\n headerTransformer: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n onReset: (context: TableContext) => Promise<void>;\n strategies: TableStrategies;\n}\n\n\nexport interface FillOptions {\n /**\n * Custom input mappers for specific columns.\n * Maps column names to functions that return the input locator for that cell.\n */\n inputMappers?: Record<string, (cell: Locator) => Locator>;\n}\n\nexport interface TableResult<T = any> {\n /**\n * Initializes the table by resolving headers. Must be called before using sync methods.\n * @param options Optional timeout for header resolution (default: 3000ms)\n */\n init(options?: { timeout?: number }): Promise<TableResult>;\n\n getHeaders: () => Promise<string[]>;\n getHeaderCell: (columnName: string) => Promise<Locator>;\n\n /**\n * Finds a row by filters on the current page only. Returns immediately (sync).\n * Throws error if table is not initialized.\n */\n getByRow: (\n filters: Record<string, string | RegExp | number>,\n options?: { exact?: boolean }\n ) => SmartRow;\n\n /**\n * Gets a row by 1-based index on the current page.\n * Throws error if table is not initialized.\n * @param index 1-based row index\n * @param options Optional settings including bringIntoView\n */\n getByRowIndex: (\n index: number,\n options?: { bringIntoView?: boolean }\n ) => SmartRow;\n\n /**\n * Searches for a row across all available data using the configured strategy (pagination, scroll, etc.).\n * Auto-initializes if needed.\n */\n searchForRow: (\n filters: Record<string, string | RegExp | number>,\n options?: { exact?: boolean, maxPages?: number }\n ) => Promise<SmartRow>;\n\n /**\n * Navigates to a specific column using the configured CellNavigationStrategy.\n */\n scrollToColumn: (columnName: string) => Promise<void>;\n\n getAllCurrentRows: <T extends { asJSON?: boolean }>(\n options?: { filter?: Record<string, any>, exact?: boolean } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n /**\n * @deprecated Use getAllCurrentRows instead. This method will be removed in a future major version.\n */\n getAllRows: <T extends { asJSON?: boolean }>(\n options?: { filter?: Record<string, any>, exact?: boolean } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n generateConfigPrompt: (options?: PromptOptions) => Promise<void>;\n generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;\n\n /**\n * Resets the table state (clears cache, flags) and invokes the onReset strategy.\n */\n reset: () => Promise<void>;\n\n /**\n * Revalidates the table's structure (headers, columns) without resetting pagination or state.\n * Useful when columns change visibility or order dynamically.\n */\n revalidate: () => Promise<void>;\n\n /**\n * Scans a specific column across all pages and returns the values.\n */\n getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;\n\n /**\n * Provides access to sorting actions and assertions.\n */\n sorting: {\n /**\n * Applies the configured sorting strategy to the specified column.\n * @param columnName The name of the column to sort.\n * @param direction The direction to sort ('asc' or 'desc').\n */\n apply(columnName: string, direction: 'asc' | 'desc'): Promise<void>;\n /**\n * Gets the current sort state of a column using the configured sorting strategy.\n * @param columnName The name of the column to check.\n * @returns A promise that resolves to 'asc', 'desc', or 'none'.\n */\n getState(columnName: string): Promise<'asc' | 'desc' | 'none'>;\n };\n\n /**\n * Iterates through paginated table data, calling the callback for each iteration.\n * Callback return values are automatically appended to allData, which is returned.\n */\n iterateThroughTable: <T = any>(\n callback: (context: {\n index: number;\n isFirst: boolean;\n isLast: boolean;\n rows: SmartRow[];\n allData: T[];\n table: RestrictedTableResult;\n }) => T | Promise<T>,\n options?: {\n pagination?: PaginationStrategy;\n dedupeStrategy?: DedupeStrategy;\n maxIterations?: number;\n getIsFirst?: (context: { index: number }) => boolean;\n getIsLast?: (context: { index: number, paginationResult: boolean }) => boolean;\n onFirst?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n onLast?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n }\n ) => Promise<T[]>;\n}\n\n/**\n * Restricted table result that excludes methods that shouldn't be called during iteration.\n */\nexport type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset' | 'getAllRows'>;\n";
|
|
6
|
+
export declare const TYPE_CONTEXT = "\n/**\n * Flexible selector type - can be a CSS string, function returning a Locator, or Locator itself.\n * @example\n * // String selector\n * rowSelector: 'tbody tr'\n * \n * // Function selector\n * rowSelector: (root) => root.locator('[role=\"row\"]')\n */\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\n/**\n * Function to get a cell locator given row, column info.\n * Replaces the old cellResolver.\n */\nexport type GetCellLocatorFn = (args: {\n row: Locator;\n columnName: string;\n columnIndex: number;\n rowIndex?: number;\n page: Page;\n}) => Locator;\n\n/**\n * Function to get the currently active/focused cell.\n * Returns null if no cell is active.\n */\nexport type GetActiveCellFn = (args: TableContext) => Promise<{\n rowIndex: number;\n columnIndex: number;\n columnName?: string;\n locator: Locator;\n} | null>;\n\n\n/**\n * SmartRow - A Playwright Locator with table-aware methods.\n * \n * Extends all standard Locator methods (click, isVisible, etc.) with table-specific functionality.\n * \n * @example\n * const row = table.getRow({ Name: 'John Doe' });\n * await row.click(); // Standard Locator method\n * const email = row.getCell('Email'); // Table-aware method\n * const data = await row.toJSON(); // Extract all row data\n * await row.smartFill({ Name: 'Jane', Status: 'Active' }); // Fill form fields\n */\nexport type SmartRow<T = any> = Locator & {\n /** Optional row index (0-based) if known */\n rowIndex?: number;\n\n /**\n * Get a cell locator by column name.\n * @param column - Column name (case-sensitive)\n * @returns Locator for the cell\n * @example\n * const emailCell = row.getCell('Email');\n * await expect(emailCell).toHaveText('john@example.com');\n */\n getCell(column: string): Locator;\n\n /**\n * Extract all cell data as a key-value object.\n * @param options - Optional configuration\n * @param options.columns - Specific columns to extract (extracts all if not specified)\n * @returns Promise resolving to row data\n * @example\n * const data = await row.toJSON();\n * // { Name: 'John', Email: 'john@example.com', ... }\n * \n * const partial = await row.toJSON({ columns: ['Name', 'Email'] });\n * // { Name: 'John', Email: 'john@example.com' }\n */\n toJSON(options?: { columns?: string[] }): Promise<T>;\n\n /**\n * Scrolls/paginates to bring this row into view.\n * Only works if rowIndex is known (e.g., from getRowByIndex).\n * @throws Error if rowIndex is unknown\n */\n bringIntoView(): Promise<void>;\n\n /**\n * Intelligently fills form fields in the row.\n * Automatically detects input types (text, select, checkbox, contenteditable).\n * \n * @param data - Column-value pairs to fill\n * @param options - Optional configuration\n * @param options.inputMappers - Custom input selectors per column\n * @example\n * // Auto-detection\n * await row.smartFill({ Name: 'John', Status: 'Active', Subscribe: true });\n * \n * // Custom input mappers\n * await row.smartFill(\n * { Name: 'John' },\n * { inputMappers: { Name: (cell) => cell.locator('.custom-input') } }\n * );\n */\n smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;\n};\n\nexport type StrategyContext = TableContext & { rowLocator?: Locator; rowIndex?: number };\n\n/**\n * Defines the contract for a sorting strategy.\n */\nexport interface SortingStrategy {\n /**\n * Performs the sort action on a column.\n */\n doSort(options: {\n columnName: string;\n direction: 'asc' | 'desc';\n context: StrategyContext;\n }): Promise<void>;\n\n /**\n * Retrieves the current sort state of a column.\n */\n getSortState(options: {\n columnName: string;\n context: StrategyContext;\n }): Promise<'asc' | 'desc' | 'none'>;\n}\n\nexport interface TableContext {\n root: Locator;\n config: FinalTableConfig;\n page: Page;\n resolve: (selector: Selector, parent: Locator | Page) => Locator;\n}\n\nexport type PaginationStrategy = (context: TableContext) => Promise<boolean>;\n\nexport type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;\n\nexport interface PromptOptions {\n /**\n * Output Strategy:\n * - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).\n * - 'console': Standard console logs (Default).\n */\n output?: 'console' | 'error';\n includeTypes?: boolean;\n}\n\nexport type FillStrategy = (options: {\n row: SmartRow;\n columnName: string;\n value: any;\n index: number;\n page: Page;\n rootLocator: Locator;\n table: TableResult; // The parent table instance\n fillOptions?: FillOptions;\n}) => Promise<void>;\n\nexport type { HeaderStrategy } from './strategies/headers';\nexport type { CellNavigationStrategy } from './strategies/columns';\n\n/**\n * Strategy to resolve column names (string or regex) to their index.\n */\nexport type { ColumnResolutionStrategy } from './strategies/resolution';\n\n/**\n * Strategy to filter rows based on criteria.\n */\nexport interface FilterStrategy {\n apply(options: {\n rows: Locator;\n filter: { column: string, value: string | RegExp | number };\n colIndex: number;\n tableContext: TableContext;\n }): Locator;\n}\n\n/**\n * Organized container for all table interaction strategies.\n */\nexport interface TableStrategies {\n /** Strategy for discovering/scanning headers */\n header?: HeaderStrategy;\n /** Strategy for navigating to specific cells (row + column) */\n cellNavigation?: CellNavigationStrategy;\n /** Strategy for filling form inputs */\n fill?: FillStrategy;\n /** Strategy for paginating through data */\n pagination?: PaginationStrategy;\n /** Strategy for sorting columns */\n sorting?: SortingStrategy;\n /** Function to get a cell locator */\n getCellLocator?: GetCellLocatorFn;\n /** Function to get the currently active/focused cell */\n getActiveCell?: GetActiveCellFn;\n}\n\n/**\n * Configuration options for useTable.\n */\nexport interface TableConfig {\n /** Selector for the table headers */\n headerSelector?: string;\n /** Selector for the table rows */\n rowSelector?: string;\n /** Selector for the cells within a row */\n cellSelector?: string;\n /** Number of pages to scan for verification */\n maxPages?: number;\n /** Hook to rename columns dynamically */\n headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n /** Automatically scroll to table on init */\n autoScroll?: boolean;\n /** Enable debug logs */\n debug?: boolean;\n /** Reset hook */\n onReset?: (context: TableContext) => Promise<void>;\n /** All interaction strategies */\n strategies?: TableStrategies;\n}\n\nexport interface FinalTableConfig extends TableConfig {\n headerSelector: string;\n rowSelector: string;\n cellSelector: string;\n maxPages: number;\n autoScroll: boolean;\n debug: boolean;\n headerTransformer: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n onReset: (context: TableContext) => Promise<void>;\n strategies: TableStrategies;\n}\n\n\nexport interface FillOptions {\n /**\n * Custom input mappers for specific columns.\n * Maps column names to functions that return the input locator for that cell.\n */\n inputMappers?: Record<string, (cell: Locator) => Locator>;\n}\n\nexport interface TableResult<T = any> {\n /**\n * Initializes the table by resolving headers. Must be called before using sync methods.\n * @param options Optional timeout for header resolution (default: 3000ms)\n */\n init(options?: { timeout?: number }): Promise<TableResult>;\n\n /**\n * SYNC: Checks if the table has been initialized.\n * @returns true if init() has been called and completed, false otherwise\n */\n isInitialized(): boolean;\n\n getHeaders: () => Promise<string[]>;\n getHeaderCell: (columnName: string) => Promise<Locator>;\n\n /**\n * Finds a row by filters on the current page only. Returns immediately (sync).\n * Throws error if table is not initialized.\n */\n getRow: (\n filters: Record<string, string | RegExp | number>,\n options?: { exact?: boolean }\n ) => SmartRow;\n\n /**\n * Gets a row by 1-based index on the current page.\n * Throws error if table is not initialized.\n * @param index 1-based row index\n * @param options Optional settings including bringIntoView\n */\n getRowByIndex: (\n index: number,\n options?: { bringIntoView?: boolean }\n ) => SmartRow;\n\n /**\n * ASYNC: Searches for a single row across pages using pagination.\n * Auto-initializes the table if not already initialized.\n * @param filters - The filter criteria to match\n * @param options - Search options including exact match and max pages\n */\n findRow: (\n filters: Record<string, string | RegExp | number>,\n options?: { exact?: boolean, maxPages?: number }\n ) => Promise<SmartRow>;\n\n /**\n * ASYNC: Searches for all matching rows across pages using pagination.\n * Auto-initializes the table if not already initialized.\n * @param filters - The filter criteria to match\n * @param options - Search options including exact match, max pages, and asJSON\n */\n findRows: <R extends { asJSON?: boolean }>(\n filters: Record<string, string | RegExp | number>,\n options?: { exact?: boolean, maxPages?: number } & R\n ) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n /**\n * Navigates to a specific column using the configured CellNavigationStrategy.\n */\n scrollToColumn: (columnName: string) => Promise<void>;\n\n /**\n * ASYNC: Gets all rows on the current page only (does not paginate).\n * Auto-initializes the table if not already initialized.\n * @param options - Filter and formatting options\n */\n getRows: <R extends { asJSON?: boolean }>(\n options?: { filter?: Record<string, any>, exact?: boolean } & R\n ) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n /**\n * Resets the table state (clears cache, flags) and invokes the onReset strategy.\n */\n reset: () => Promise<void>;\n\n /**\n * Revalidates the table's structure (headers, columns) without resetting pagination or state.\n * Useful when columns change visibility or order dynamically.\n */\n revalidate: () => Promise<void>;\n\n /**\n * Scans a specific column across all pages and returns the values.\n */\n getColumnValues: <V = string>(column: string, options?: { mapper?: (cell: Locator) => Promise<V> | V, maxPages?: number }) => Promise<V[]>;\n\n /**\n * Provides access to sorting actions and assertions.\n */\n sorting: {\n /**\n * Applies the configured sorting strategy to the specified column.\n * @param columnName The name of the column to sort.\n * @param direction The direction to sort ('asc' or 'desc').\n */\n apply(columnName: string, direction: 'asc' | 'desc'): Promise<void>;\n /**\n * Gets the current sort state of a column using the configured sorting strategy.\n * @param columnName The name of the column to check.\n * @returns A promise that resolves to 'asc', 'desc', or 'none'.\n */\n getState(columnName: string): Promise<'asc' | 'desc' | 'none'>;\n };\n\n /**\n * Iterates through paginated table data, calling the callback for each iteration.\n * Callback return values are automatically appended to allData, which is returned.\n */\n iterateThroughTable: <T = any>(\n callback: (context: {\n index: number;\n isFirst: boolean;\n isLast: boolean;\n rows: SmartRow[];\n allData: T[];\n table: RestrictedTableResult;\n }) => T | Promise<T>,\n options?: {\n pagination?: PaginationStrategy;\n dedupeStrategy?: DedupeStrategy;\n maxIterations?: number;\n getIsFirst?: (context: { index: number }) => boolean;\n getIsLast?: (context: { index: number, paginationResult: boolean }) => boolean;\n onFirst?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n onLast?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n }\n ) => Promise<T[]>;\n}\n\n/**\n * Restricted table result that excludes methods that shouldn't be called during iteration.\n */\nexport type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;\n";
|
package/dist/typeContext.js
CHANGED
|
@@ -7,6 +7,15 @@ exports.TYPE_CONTEXT = void 0;
|
|
|
7
7
|
* It contains the raw text of types.ts to provide context for LLM prompts.
|
|
8
8
|
*/
|
|
9
9
|
exports.TYPE_CONTEXT = `
|
|
10
|
+
/**
|
|
11
|
+
* Flexible selector type - can be a CSS string, function returning a Locator, or Locator itself.
|
|
12
|
+
* @example
|
|
13
|
+
* // String selector
|
|
14
|
+
* rowSelector: 'tbody tr'
|
|
15
|
+
*
|
|
16
|
+
* // Function selector
|
|
17
|
+
* rowSelector: (root) => root.locator('[role="row"]')
|
|
18
|
+
*/
|
|
10
19
|
export type Selector = string | ((root: Locator | Page) => Locator);
|
|
11
20
|
|
|
12
21
|
/**
|
|
@@ -33,22 +42,69 @@ export type GetActiveCellFn = (args: TableContext) => Promise<{
|
|
|
33
42
|
} | null>;
|
|
34
43
|
|
|
35
44
|
|
|
45
|
+
/**
|
|
46
|
+
* SmartRow - A Playwright Locator with table-aware methods.
|
|
47
|
+
*
|
|
48
|
+
* Extends all standard Locator methods (click, isVisible, etc.) with table-specific functionality.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* const row = table.getRow({ Name: 'John Doe' });
|
|
52
|
+
* await row.click(); // Standard Locator method
|
|
53
|
+
* const email = row.getCell('Email'); // Table-aware method
|
|
54
|
+
* const data = await row.toJSON(); // Extract all row data
|
|
55
|
+
* await row.smartFill({ Name: 'Jane', Status: 'Active' }); // Fill form fields
|
|
56
|
+
*/
|
|
36
57
|
export type SmartRow<T = any> = Locator & {
|
|
37
|
-
|
|
58
|
+
/** Optional row index (0-based) if known */
|
|
38
59
|
rowIndex?: number;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get a cell locator by column name.
|
|
63
|
+
* @param column - Column name (case-sensitive)
|
|
64
|
+
* @returns Locator for the cell
|
|
65
|
+
* @example
|
|
66
|
+
* const emailCell = row.getCell('Email');
|
|
67
|
+
* await expect(emailCell).toHaveText('john@example.com');
|
|
68
|
+
*/
|
|
39
69
|
getCell(column: string): Locator;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Extract all cell data as a key-value object.
|
|
73
|
+
* @param options - Optional configuration
|
|
74
|
+
* @param options.columns - Specific columns to extract (extracts all if not specified)
|
|
75
|
+
* @returns Promise resolving to row data
|
|
76
|
+
* @example
|
|
77
|
+
* const data = await row.toJSON();
|
|
78
|
+
* // { Name: 'John', Email: 'john@example.com', ... }
|
|
79
|
+
*
|
|
80
|
+
* const partial = await row.toJSON({ columns: ['Name', 'Email'] });
|
|
81
|
+
* // { Name: 'John', Email: 'john@example.com' }
|
|
82
|
+
*/
|
|
40
83
|
toJSON(options?: { columns?: string[] }): Promise<T>;
|
|
84
|
+
|
|
41
85
|
/**
|
|
42
86
|
* Scrolls/paginates to bring this row into view.
|
|
43
|
-
* Only works if rowIndex is known.
|
|
87
|
+
* Only works if rowIndex is known (e.g., from getRowByIndex).
|
|
88
|
+
* @throws Error if rowIndex is unknown
|
|
44
89
|
*/
|
|
45
90
|
bringIntoView(): Promise<void>;
|
|
91
|
+
|
|
46
92
|
/**
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
93
|
+
* Intelligently fills form fields in the row.
|
|
94
|
+
* Automatically detects input types (text, select, checkbox, contenteditable).
|
|
95
|
+
*
|
|
96
|
+
* @param data - Column-value pairs to fill
|
|
97
|
+
* @param options - Optional configuration
|
|
98
|
+
* @param options.inputMappers - Custom input selectors per column
|
|
99
|
+
* @example
|
|
100
|
+
* // Auto-detection
|
|
101
|
+
* await row.smartFill({ Name: 'John', Status: 'Active', Subscribe: true });
|
|
102
|
+
*
|
|
103
|
+
* // Custom input mappers
|
|
104
|
+
* await row.smartFill(
|
|
105
|
+
* { Name: 'John' },
|
|
106
|
+
* { inputMappers: { Name: (cell) => cell.locator('.custom-input') } }
|
|
107
|
+
* );
|
|
52
108
|
*/
|
|
53
109
|
smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
|
|
54
110
|
};
|
|
@@ -201,6 +257,12 @@ export interface TableResult<T = any> {
|
|
|
201
257
|
*/
|
|
202
258
|
init(options?: { timeout?: number }): Promise<TableResult>;
|
|
203
259
|
|
|
260
|
+
/**
|
|
261
|
+
* SYNC: Checks if the table has been initialized.
|
|
262
|
+
* @returns true if init() has been called and completed, false otherwise
|
|
263
|
+
*/
|
|
264
|
+
isInitialized(): boolean;
|
|
265
|
+
|
|
204
266
|
getHeaders: () => Promise<string[]>;
|
|
205
267
|
getHeaderCell: (columnName: string) => Promise<Locator>;
|
|
206
268
|
|
|
@@ -208,7 +270,7 @@ export interface TableResult<T = any> {
|
|
|
208
270
|
* Finds a row by filters on the current page only. Returns immediately (sync).
|
|
209
271
|
* Throws error if table is not initialized.
|
|
210
272
|
*/
|
|
211
|
-
|
|
273
|
+
getRow: (
|
|
212
274
|
filters: Record<string, string | RegExp | number>,
|
|
213
275
|
options?: { exact?: boolean }
|
|
214
276
|
) => SmartRow;
|
|
@@ -219,38 +281,46 @@ export interface TableResult<T = any> {
|
|
|
219
281
|
* @param index 1-based row index
|
|
220
282
|
* @param options Optional settings including bringIntoView
|
|
221
283
|
*/
|
|
222
|
-
|
|
284
|
+
getRowByIndex: (
|
|
223
285
|
index: number,
|
|
224
286
|
options?: { bringIntoView?: boolean }
|
|
225
287
|
) => SmartRow;
|
|
226
288
|
|
|
227
289
|
/**
|
|
228
|
-
* Searches for a row across
|
|
229
|
-
* Auto-initializes if
|
|
290
|
+
* ASYNC: Searches for a single row across pages using pagination.
|
|
291
|
+
* Auto-initializes the table if not already initialized.
|
|
292
|
+
* @param filters - The filter criteria to match
|
|
293
|
+
* @param options - Search options including exact match and max pages
|
|
230
294
|
*/
|
|
231
|
-
|
|
295
|
+
findRow: (
|
|
232
296
|
filters: Record<string, string | RegExp | number>,
|
|
233
297
|
options?: { exact?: boolean, maxPages?: number }
|
|
234
298
|
) => Promise<SmartRow>;
|
|
235
299
|
|
|
300
|
+
/**
|
|
301
|
+
* ASYNC: Searches for all matching rows across pages using pagination.
|
|
302
|
+
* Auto-initializes the table if not already initialized.
|
|
303
|
+
* @param filters - The filter criteria to match
|
|
304
|
+
* @param options - Search options including exact match, max pages, and asJSON
|
|
305
|
+
*/
|
|
306
|
+
findRows: <R extends { asJSON?: boolean }>(
|
|
307
|
+
filters: Record<string, string | RegExp | number>,
|
|
308
|
+
options?: { exact?: boolean, maxPages?: number } & R
|
|
309
|
+
) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
310
|
+
|
|
236
311
|
/**
|
|
237
312
|
* Navigates to a specific column using the configured CellNavigationStrategy.
|
|
238
313
|
*/
|
|
239
314
|
scrollToColumn: (columnName: string) => Promise<void>;
|
|
240
315
|
|
|
241
|
-
getAllCurrentRows: <T extends { asJSON?: boolean }>(
|
|
242
|
-
options?: { filter?: Record<string, any>, exact?: boolean } & T
|
|
243
|
-
) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
244
|
-
|
|
245
316
|
/**
|
|
246
|
-
*
|
|
317
|
+
* ASYNC: Gets all rows on the current page only (does not paginate).
|
|
318
|
+
* Auto-initializes the table if not already initialized.
|
|
319
|
+
* @param options - Filter and formatting options
|
|
247
320
|
*/
|
|
248
|
-
|
|
249
|
-
options?: { filter?: Record<string, any>, exact?: boolean } &
|
|
250
|
-
) => Promise<
|
|
251
|
-
|
|
252
|
-
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
|
253
|
-
generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
|
|
321
|
+
getRows: <R extends { asJSON?: boolean }>(
|
|
322
|
+
options?: { filter?: Record<string, any>, exact?: boolean } & R
|
|
323
|
+
) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
254
324
|
|
|
255
325
|
/**
|
|
256
326
|
* Resets the table state (clears cache, flags) and invokes the onReset strategy.
|
|
@@ -314,5 +384,5 @@ export interface TableResult<T = any> {
|
|
|
314
384
|
/**
|
|
315
385
|
* Restricted table result that excludes methods that shouldn't be called during iteration.
|
|
316
386
|
*/
|
|
317
|
-
export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'
|
|
387
|
+
export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
|
|
318
388
|
`;
|