@rickcedwhat/playwright-smart-table 2.0.6 → 2.0.8
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 +1 -23
- package/dist/strategies/index.js +42 -59
- 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
|
@@ -1,28 +1,6 @@
|
|
|
1
1
|
import { PaginationStrategy, Selector } from '../types';
|
|
2
2
|
export declare const TableStrategies: {
|
|
3
|
-
/**
|
|
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
|
-
*/
|
|
9
3
|
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
10
|
-
/**
|
|
11
|
-
* 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
|
-
*/
|
|
16
4
|
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
17
|
-
|
|
18
|
-
* Strategy: Scrolls a specific container (or the window) to the bottom.
|
|
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
|
-
*/
|
|
24
|
-
infiniteScroll: (options?: {
|
|
25
|
-
timeout?: number;
|
|
26
|
-
scrollerSelector?: Selector;
|
|
27
|
-
}) => PaginationStrategy;
|
|
5
|
+
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
28
6
|
};
|
package/dist/strategies/index.js
CHANGED
|
@@ -10,61 +10,66 @@ 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
|
+
* Helper to get 'expect' safely in ANY environment.
|
|
15
|
+
* 1. Tries global scope (QA Wolf / Cloud Runners).
|
|
16
|
+
* 2. Tries local require (Standard Playwright).
|
|
17
|
+
*/
|
|
18
|
+
const getExpect = () => {
|
|
19
|
+
// 1. Priority: Global (Environment injected)
|
|
20
|
+
const globalExpect = globalThis.expect;
|
|
21
|
+
if (globalExpect)
|
|
22
|
+
return globalExpect;
|
|
23
|
+
// 2. Fallback: Module Import (Local development)
|
|
24
|
+
// We use a try-catch with require to safely attempt loading the module
|
|
25
|
+
// without crashing environments where the module doesn't exist.
|
|
26
|
+
try {
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
28
|
+
const { expect } = require('@playwright/test');
|
|
29
|
+
if (expect)
|
|
30
|
+
return expect;
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
// Module not found or require not available.
|
|
34
|
+
}
|
|
35
|
+
// 3. Fatal Error
|
|
36
|
+
throw new Error("@rickcedwhat/playwright-smart-table: 'expect' not found. Ensure you are running in a Playwright test.");
|
|
37
|
+
};
|
|
15
38
|
exports.TableStrategies = {
|
|
16
|
-
/**
|
|
17
|
-
* 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
|
-
*/
|
|
22
39
|
clickNext: (nextButtonSelector, timeout = 5000) => {
|
|
23
40
|
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve }) {
|
|
24
|
-
//
|
|
41
|
+
// ✅ LAZY LOAD: Safe for both Local & Cloud
|
|
42
|
+
const expect = getExpect();
|
|
25
43
|
const nextBtn = resolve(nextButtonSelector, root).first();
|
|
26
|
-
// If button isn't there or disabled, we are at the end
|
|
27
44
|
if (!(yield nextBtn.isVisible()) || !(yield nextBtn.isEnabled())) {
|
|
28
45
|
return false;
|
|
29
46
|
}
|
|
30
|
-
// 2. Snapshot the current state (text of the first row)
|
|
31
|
-
// We use the table's OWN row selector to ensure we are looking at real data
|
|
32
47
|
const firstRow = resolve(config.rowSelector, root).first();
|
|
33
|
-
const oldText = yield firstRow.innerText().catch(() => "");
|
|
34
|
-
// 3. Click the button
|
|
48
|
+
const oldText = yield firstRow.innerText().catch(() => "");
|
|
35
49
|
yield nextBtn.click();
|
|
36
|
-
// 4. Smart Wait: Wait for the first row to have DIFFERENT text
|
|
37
50
|
try {
|
|
38
|
-
yield
|
|
39
|
-
return true;
|
|
51
|
+
yield expect(firstRow).not.toHaveText(oldText, { timeout });
|
|
52
|
+
return true;
|
|
40
53
|
}
|
|
41
54
|
catch (e) {
|
|
42
|
-
return false;
|
|
55
|
+
return false;
|
|
43
56
|
}
|
|
44
57
|
});
|
|
45
58
|
},
|
|
46
|
-
/**
|
|
47
|
-
* 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
|
-
*/
|
|
52
59
|
clickLoadMore: (buttonSelector, timeout = 5000) => {
|
|
53
60
|
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve }) {
|
|
61
|
+
const expect = getExpect(); // ✅ LAZY LOAD
|
|
54
62
|
const loadMoreBtn = resolve(buttonSelector, root).first();
|
|
55
63
|
if (!(yield loadMoreBtn.isVisible()) || !(yield loadMoreBtn.isEnabled())) {
|
|
56
64
|
return false;
|
|
57
65
|
}
|
|
58
|
-
// 1. Snapshot: Count current rows
|
|
59
66
|
const rows = resolve(config.rowSelector, root);
|
|
60
67
|
const oldCount = yield rows.count();
|
|
61
|
-
// 2. Click
|
|
62
68
|
yield loadMoreBtn.click();
|
|
63
|
-
// 3. Smart Wait: Wait for row count to be greater than before
|
|
64
69
|
try {
|
|
65
|
-
yield
|
|
70
|
+
yield expect(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
71
|
const newCount = yield rows.count();
|
|
67
|
-
|
|
72
|
+
expect(newCount).toBeGreaterThan(oldCount);
|
|
68
73
|
})).toPass({ timeout });
|
|
69
74
|
return true;
|
|
70
75
|
}
|
|
@@ -73,42 +78,20 @@ exports.TableStrategies = {
|
|
|
73
78
|
}
|
|
74
79
|
});
|
|
75
80
|
},
|
|
76
|
-
|
|
77
|
-
* Strategy: Scrolls a specific container (or the window) to the bottom.
|
|
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
|
-
*/
|
|
83
|
-
infiniteScroll: (options) => {
|
|
84
|
-
const { timeout = 5000, scrollerSelector } = options || {};
|
|
81
|
+
infiniteScroll: (timeout = 5000) => {
|
|
85
82
|
return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
|
|
83
|
+
const expect = getExpect(); // ✅ LAZY LOAD
|
|
86
84
|
const rows = resolve(config.rowSelector, root);
|
|
87
85
|
const oldCount = yield rows.count();
|
|
88
86
|
if (oldCount === 0)
|
|
89
87
|
return false;
|
|
88
|
+
yield rows.last().scrollIntoViewIfNeeded();
|
|
89
|
+
yield page.keyboard.press('End');
|
|
90
90
|
try {
|
|
91
|
-
yield
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const scroller = scrollerSelector
|
|
96
|
-
? resolve(scrollerSelector, root)
|
|
97
|
-
: root;
|
|
98
|
-
// 2. Perform the Scroll
|
|
99
|
-
// Method A: DOM Manipulation (Fastest/Most Reliable for containers)
|
|
100
|
-
// We set scrollTop to a huge number to force it to the bottom
|
|
101
|
-
yield scroller.evaluate((el) => {
|
|
102
|
-
el.scrollTop = el.scrollHeight;
|
|
103
|
-
}).catch(() => { }); // Ignore if element doesn't support scrollTop (e.g. it's a window wrapper)
|
|
104
|
-
// Method B: Playwright Native (Fallback)
|
|
105
|
-
// Scroll the last row into view (good for Window scroll)
|
|
106
|
-
yield rows.last().scrollIntoViewIfNeeded().catch(() => { });
|
|
107
|
-
// Method C: Keyboard (Desperation)
|
|
108
|
-
yield page.keyboard.press('End');
|
|
109
|
-
// 3. Return count for assertion
|
|
110
|
-
return rows.count();
|
|
111
|
-
}), { timeout }).toBeGreaterThan(oldCount);
|
|
91
|
+
yield expect(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
|
+
const newCount = yield rows.count();
|
|
93
|
+
expect(newCount).toBeGreaterThan(oldCount);
|
|
94
|
+
})).toPass({ timeout });
|
|
112
95
|
return true;
|
|
113
96
|
}
|
|
114
97
|
catch (e) {
|
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;
|