@rickcedwhat/playwright-smart-table 2.0.7 ā 2.0.9
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/dist/strategies/index.d.ts +2 -17
- package/dist/strategies/index.js +46 -76
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +1 -3
- package/dist/types.d.ts +2 -4
- package/dist/useTable.d.ts +1 -1
- package/dist/useTable.js +1 -19
- package/package.json +1 -1
- package/dist/presets.d.ts +0 -18
- package/dist/presets.js +0 -30
|
@@ -2,29 +2,14 @@ import { PaginationStrategy, Selector } from '../types';
|
|
|
2
2
|
export declare const TableStrategies: {
|
|
3
3
|
/**
|
|
4
4
|
* Strategy: Clicks a "Next" button and waits for the first row of data to change.
|
|
5
|
-
* Best for: Standard pagination (Page 1 > Page 2 > Page 3)
|
|
6
|
-
* * @param nextButtonSelector - Selector for the next button (e.g. 'button.next' or getByRole('button', {name: 'Next'}))
|
|
7
|
-
* @param timeout - How long to wait for the table to refresh (default: 5000ms)
|
|
8
5
|
*/
|
|
9
6
|
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
10
7
|
/**
|
|
11
8
|
* Strategy: Clicks a "Load More" button and waits for the row count to increase.
|
|
12
|
-
* Best for: Lists where "Load More" appends data to the bottom.
|
|
13
|
-
* * @param buttonSelector - Selector for the load more button
|
|
14
|
-
* @param timeout - Wait timeout (default: 5000ms)
|
|
15
9
|
*/
|
|
16
10
|
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
17
11
|
/**
|
|
18
|
-
* Strategy: Scrolls
|
|
19
|
-
* Best for: Infinite Scroll grids (Ag-Grid, Virtual Lists)
|
|
20
|
-
* * @param options.timeout - Wait timeout (default: 5000ms)
|
|
21
|
-
* @param options.scrollerSelector - (Optional) Selector for the scrollable container.
|
|
22
|
-
* If omitted, tries to scroll the table root.
|
|
23
|
-
* @param options.interval - (Optional) Polling interval in ms (default: 1000ms)
|
|
12
|
+
* Strategy: Scrolls to the bottom and waits for more rows to appear.
|
|
24
13
|
*/
|
|
25
|
-
infiniteScroll: (
|
|
26
|
-
timeout?: number;
|
|
27
|
-
scrollerSelector?: Selector;
|
|
28
|
-
interval?: number;
|
|
29
|
-
}) => PaginationStrategy;
|
|
14
|
+
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
30
15
|
};
|
package/dist/strategies/index.js
CHANGED
|
@@ -10,118 +10,88 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.TableStrategies = void 0;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Internal helper to wait for a condition to be met.
|
|
15
|
+
* Replaces the dependency on 'expect(...).toPass()' to ensure compatibility
|
|
16
|
+
* with environments like QA Wolf where 'expect' is not globally available.
|
|
17
|
+
*/
|
|
18
|
+
const waitForCondition = (predicate, timeout, page // Context page for pauses
|
|
19
|
+
) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
while (Date.now() - startTime < timeout) {
|
|
22
|
+
if (yield predicate()) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
// Wait 100ms before next check (Standard Polling)
|
|
26
|
+
yield page.waitForTimeout(100).catch(() => new Promise(r => setTimeout(r, 100)));
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
});
|
|
15
30
|
exports.TableStrategies = {
|
|
16
31
|
/**
|
|
17
32
|
* Strategy: Clicks a "Next" button and waits for the first row of data to change.
|
|
18
|
-
* Best for: Standard pagination (Page 1 > Page 2 > Page 3)
|
|
19
|
-
* * @param nextButtonSelector - Selector for the next button (e.g. 'button.next' or getByRole('button', {name: 'Next'}))
|
|
20
|
-
* @param timeout - How long to wait for the table to refresh (default: 5000ms)
|
|
21
33
|
*/
|
|
22
34
|
clickNext: (nextButtonSelector, timeout = 5000) => {
|
|
23
|
-
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve }) {
|
|
24
|
-
// 1. Find the button using the table's helper
|
|
35
|
+
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
|
|
25
36
|
const nextBtn = resolve(nextButtonSelector, root).first();
|
|
26
|
-
//
|
|
37
|
+
// Check if button exists/enabled before clicking
|
|
27
38
|
if (!(yield nextBtn.isVisible()) || !(yield nextBtn.isEnabled())) {
|
|
28
39
|
return false;
|
|
29
40
|
}
|
|
30
|
-
//
|
|
31
|
-
// We use the table's OWN row selector to ensure we are looking at real data
|
|
41
|
+
// 1. Snapshot current state
|
|
32
42
|
const firstRow = resolve(config.rowSelector, root).first();
|
|
33
|
-
const oldText = yield firstRow.innerText().catch(() => "");
|
|
34
|
-
//
|
|
43
|
+
const oldText = yield firstRow.innerText().catch(() => "");
|
|
44
|
+
// 2. Click
|
|
35
45
|
yield nextBtn.click();
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
yield
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
catch (e) {
|
|
42
|
-
return false; // Failed: Timed out (probably end of data or broken button)
|
|
43
|
-
}
|
|
46
|
+
// 3. Smart Wait (Polling) - No 'expect' needed
|
|
47
|
+
return yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
48
|
+
const newText = yield firstRow.innerText().catch(() => "");
|
|
49
|
+
return newText !== oldText;
|
|
50
|
+
}), timeout, page);
|
|
44
51
|
});
|
|
45
52
|
},
|
|
46
53
|
/**
|
|
47
54
|
* Strategy: Clicks a "Load More" button and waits for the row count to increase.
|
|
48
|
-
* Best for: Lists where "Load More" appends data to the bottom.
|
|
49
|
-
* * @param buttonSelector - Selector for the load more button
|
|
50
|
-
* @param timeout - Wait timeout (default: 5000ms)
|
|
51
55
|
*/
|
|
52
56
|
clickLoadMore: (buttonSelector, timeout = 5000) => {
|
|
53
|
-
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve }) {
|
|
57
|
+
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
|
|
54
58
|
const loadMoreBtn = resolve(buttonSelector, root).first();
|
|
55
59
|
if (!(yield loadMoreBtn.isVisible()) || !(yield loadMoreBtn.isEnabled())) {
|
|
56
60
|
return false;
|
|
57
61
|
}
|
|
58
|
-
// 1. Snapshot
|
|
62
|
+
// 1. Snapshot count
|
|
59
63
|
const rows = resolve(config.rowSelector, root);
|
|
60
64
|
const oldCount = yield rows.count();
|
|
61
65
|
// 2. Click
|
|
62
66
|
yield loadMoreBtn.click();
|
|
63
|
-
// 3. Smart Wait
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})).toPass({ timeout });
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
67
|
+
// 3. Smart Wait (Polling)
|
|
68
|
+
return yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
69
|
+
const newCount = yield rows.count();
|
|
70
|
+
return newCount > oldCount;
|
|
71
|
+
}), timeout, page);
|
|
74
72
|
});
|
|
75
73
|
},
|
|
76
74
|
/**
|
|
77
|
-
* Strategy: Scrolls
|
|
78
|
-
* Best for: Infinite Scroll grids (Ag-Grid, Virtual Lists)
|
|
79
|
-
* * @param options.timeout - Wait timeout (default: 5000ms)
|
|
80
|
-
* @param options.scrollerSelector - (Optional) Selector for the scrollable container.
|
|
81
|
-
* If omitted, tries to scroll the table root.
|
|
82
|
-
* @param options.interval - (Optional) Polling interval in ms (default: 1000ms)
|
|
75
|
+
* Strategy: Scrolls to the bottom and waits for more rows to appear.
|
|
83
76
|
*/
|
|
84
|
-
infiniteScroll: (
|
|
85
|
-
const { timeout = 5000, scrollerSelector, interval = 1000 } = options || {};
|
|
77
|
+
infiniteScroll: (timeout = 5000) => {
|
|
86
78
|
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
|
|
87
79
|
const rows = resolve(config.rowSelector, root);
|
|
88
80
|
const oldCount = yield rows.count();
|
|
89
81
|
if (oldCount === 0)
|
|
90
82
|
return false;
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
//
|
|
83
|
+
// 1. Trigger Scroll
|
|
84
|
+
yield rows.last().scrollIntoViewIfNeeded();
|
|
85
|
+
// Optional: Keyboard press for robust grid handling
|
|
94
86
|
try {
|
|
95
|
-
yield
|
|
96
|
-
// 1. Determine target container
|
|
97
|
-
// If user provided a specific scroller (e.g. the div WRAPPING the table), use it.
|
|
98
|
-
// Otherwise, default to the root locator.
|
|
99
|
-
const scroller = scrollerSelector
|
|
100
|
-
? resolve(scrollerSelector, root)
|
|
101
|
-
: root;
|
|
102
|
-
// 2. Perform the Scroll
|
|
103
|
-
// Method A: DOM Manipulation (Fastest/Most Reliable for containers)
|
|
104
|
-
// We set scrollTop to a huge number to force it to the bottom
|
|
105
|
-
yield scroller.evaluate((el) => {
|
|
106
|
-
el.scrollTop = el.scrollHeight;
|
|
107
|
-
}).catch(() => { }); // Ignore if element doesn't support scrollTop (e.g. it's a window wrapper)
|
|
108
|
-
// Method B: Playwright Native (Fallback)
|
|
109
|
-
// Scroll the last row into view (good for Window scroll)
|
|
110
|
-
yield rows.last().scrollIntoViewIfNeeded().catch(() => { });
|
|
111
|
-
// Method C: Keyboard (Desperation)
|
|
112
|
-
yield page.keyboard.press('End');
|
|
113
|
-
// 3. Return count for assertion
|
|
114
|
-
return rows.count();
|
|
115
|
-
}), {
|
|
116
|
-
timeout,
|
|
117
|
-
// ā
FIX: Use user-provided interval (default 1000ms)
|
|
118
|
-
intervals: [interval]
|
|
119
|
-
}).toBeGreaterThan(oldCount);
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
catch (e) {
|
|
123
|
-
return false;
|
|
87
|
+
yield page.keyboard.press('End');
|
|
124
88
|
}
|
|
89
|
+
catch (e) { }
|
|
90
|
+
// 2. Smart Wait (Polling)
|
|
91
|
+
return yield waitForCondition(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
|
+
const newCount = yield rows.count();
|
|
93
|
+
return newCount > oldCount;
|
|
94
|
+
}), timeout, page);
|
|
125
95
|
});
|
|
126
96
|
}
|
|
127
97
|
};
|
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\nexport type SmartRow = Locator & {\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n};\n\nexport interface TableContext {\n root: Locator;\n config: Required<TableConfig>;\n page: Page;\n resolve: (selector: Selector, parent: Locator | Page) => Locator;\n}\n\nexport type PaginationStrategy = (context: TableContext) => Promise<boolean>;\n\nexport interface PromptOptions {\n /**\n * Output Strategy:\n * - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).\n * - 'console': Standard console logs (Default).\n
|
|
6
|
+
export declare const TYPE_CONTEXT = "\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\nexport type SmartRow = Locator & {\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n};\n\nexport interface TableContext {\n root: Locator;\n config: Required<TableConfig>;\n page: Page;\n resolve: (selector: Selector, parent: Locator | Page) => Locator;\n}\n\nexport type PaginationStrategy = (context: TableContext) => Promise<boolean>;\n\nexport interface PromptOptions {\n /**\n * Output Strategy:\n * - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).\n * - 'console': Standard console logs (Default).\n */\n output?: 'console' | 'error';\n includeTypes?: boolean;\n}\n\nexport interface TableConfig {\n rowSelector?: Selector;\n headerSelector?: Selector;\n cellSelector?: Selector;\n pagination?: PaginationStrategy;\n maxPages?: number;\n headerTransformer?: (text: string, index: number) => string;\n autoScroll?: boolean;\n}\n\nexport interface TableResult {\n getHeaders: () => Promise<string[]>;\n getHeaderCell: (columnName: string) => Promise<Locator>;\n\n getByRow: <T extends { asJSON?: boolean }>(\n filters: Record<string, string | RegExp | number>, \n options?: { exact?: boolean, maxPages?: number } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;\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";
|
package/dist/typeContext.js
CHANGED
|
@@ -28,11 +28,9 @@ export interface PromptOptions {
|
|
|
28
28
|
* Output Strategy:
|
|
29
29
|
* - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).
|
|
30
30
|
* - 'console': Standard console logs (Default).
|
|
31
|
-
* - 'report': Attaches to Playwright Report (Requires testInfo).
|
|
32
31
|
*/
|
|
33
|
-
output?: 'console' | '
|
|
32
|
+
output?: 'console' | 'error';
|
|
34
33
|
includeTypes?: boolean;
|
|
35
|
-
testInfo?: TestInfo;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
export interface TableConfig {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Locator, Page
|
|
1
|
+
import type { Locator, Page } from '@playwright/test';
|
|
2
2
|
export type Selector = string | ((root: Locator | Page) => Locator);
|
|
3
3
|
export type SmartRow = Locator & {
|
|
4
4
|
getCell(column: string): Locator;
|
|
@@ -16,11 +16,9 @@ export interface PromptOptions {
|
|
|
16
16
|
* Output Strategy:
|
|
17
17
|
* - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).
|
|
18
18
|
* - 'console': Standard console logs (Default).
|
|
19
|
-
* - 'report': Attaches to Playwright Report (Requires testInfo).
|
|
20
19
|
*/
|
|
21
|
-
output?: 'console' | '
|
|
20
|
+
output?: 'console' | 'error';
|
|
22
21
|
includeTypes?: boolean;
|
|
23
|
-
testInfo?: TestInfo;
|
|
24
22
|
}
|
|
25
23
|
export interface TableConfig {
|
|
26
24
|
rowSelector?: Selector;
|
package/dist/useTable.d.ts
CHANGED
package/dist/useTable.js
CHANGED
|
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.useTable = void 0;
|
|
13
|
-
const test_1 = require("@playwright/test");
|
|
14
13
|
const typeContext_1 = require("./typeContext");
|
|
15
14
|
const useTable = (rootLocator, configOptions = {}) => {
|
|
16
15
|
const config = Object.assign({ rowSelector: "tbody tr", headerSelector: "th", cellSelector: "td", pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }), maxPages: 1, headerTransformer: (text) => text, autoScroll: true }, configOptions);
|
|
@@ -116,7 +115,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
116
115
|
}
|
|
117
116
|
});
|
|
118
117
|
const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
|
|
119
|
-
const { output = 'console', includeTypes = true
|
|
118
|
+
const { output = 'console', includeTypes = true } = options;
|
|
120
119
|
let finalPrompt = content;
|
|
121
120
|
if (includeTypes) {
|
|
122
121
|
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
|
|
@@ -125,23 +124,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
125
124
|
console.log(`ā ļø Throwing error to display [${promptName}] cleanly...`);
|
|
126
125
|
throw new Error(finalPrompt);
|
|
127
126
|
}
|
|
128
|
-
if (output === 'report') {
|
|
129
|
-
let activeInfo = testInfo;
|
|
130
|
-
if (!activeInfo) {
|
|
131
|
-
try {
|
|
132
|
-
activeInfo = test_1.test.info();
|
|
133
|
-
}
|
|
134
|
-
catch (e) { }
|
|
135
|
-
}
|
|
136
|
-
if (activeInfo) {
|
|
137
|
-
yield activeInfo.attach(promptName, { body: finalPrompt, contentType: 'text/markdown' });
|
|
138
|
-
console.log(`ā
[${promptName}] Attached to Playwright Report.`);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
console.warn(`ā ļø [${promptName}] Cannot attach to report (Context unavailable). Logging to console instead.`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
127
|
console.log(finalPrompt);
|
|
146
128
|
});
|
|
147
129
|
return {
|
package/package.json
CHANGED
package/dist/presets.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Locator } from '@playwright/test';
|
|
2
|
-
import { TableConfig } from './types';
|
|
3
|
-
/**
|
|
4
|
-
* Preset for Key-Value Forms.
|
|
5
|
-
* * Default Structure:
|
|
6
|
-
* - Row: div.form-group
|
|
7
|
-
* - Cell: Direct children (> *)
|
|
8
|
-
* - Columns: ['Label', 'Input']
|
|
9
|
-
*/
|
|
10
|
-
export declare const useForm: (rootLocator: Locator, options?: TableConfig) => import("./types").TableResult;
|
|
11
|
-
/**
|
|
12
|
-
* Preset for Navigation Menus.
|
|
13
|
-
* * Default Structure:
|
|
14
|
-
* - Row: li
|
|
15
|
-
* - Cell: null (The row IS the cell)
|
|
16
|
-
* - Columns: ['Item']
|
|
17
|
-
*/
|
|
18
|
-
export declare const useMenu: (menuLocator: Locator, options?: TableConfig) => import("./types").TableResult;
|
package/dist/presets.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useMenu = exports.useForm = void 0;
|
|
4
|
-
const useTable_1 = require("./useTable");
|
|
5
|
-
/**
|
|
6
|
-
* Preset for Key-Value Forms.
|
|
7
|
-
* * Default Structure:
|
|
8
|
-
* - Row: div.form-group
|
|
9
|
-
* - Cell: Direct children (> *)
|
|
10
|
-
* - Columns: ['Label', 'Input']
|
|
11
|
-
*/
|
|
12
|
-
const useForm = (rootLocator, options = {}) => {
|
|
13
|
-
return (0, useTable_1.useTable)(rootLocator, Object.assign({
|
|
14
|
-
// Defaults:
|
|
15
|
-
rowSelector: 'div.form-group', cellSelector: (row) => row.locator('> *'), headerSelector: null, columnNames: ['Label', 'Input'] }, options));
|
|
16
|
-
};
|
|
17
|
-
exports.useForm = useForm;
|
|
18
|
-
/**
|
|
19
|
-
* Preset for Navigation Menus.
|
|
20
|
-
* * Default Structure:
|
|
21
|
-
* - Row: li
|
|
22
|
-
* - Cell: null (The row IS the cell)
|
|
23
|
-
* - Columns: ['Item']
|
|
24
|
-
*/
|
|
25
|
-
const useMenu = (menuLocator, options = {}) => {
|
|
26
|
-
return (0, useTable_1.useTable)(menuLocator, Object.assign({
|
|
27
|
-
// Defaults:
|
|
28
|
-
rowSelector: 'li', cellSelector: null, headerSelector: null, columnNames: ['Item'] }, options));
|
|
29
|
-
};
|
|
30
|
-
exports.useMenu = useMenu;
|