@rickcedwhat/playwright-smart-table 2.0.1 ā 2.0.5
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 +89 -60
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +7 -43
- package/dist/types.d.ts +8 -38
- package/dist/useTable.js +45 -45
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ npm install @rickcedwhat/playwright-smart-table
|
|
|
11
11
|
|
|
12
12
|
Requires @playwright/test as a peer dependency.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
ā” Quick Start
|
|
15
15
|
|
|
16
16
|
1. The Standard HTML Table
|
|
17
17
|
|
|
@@ -23,17 +23,17 @@ import { useTable } from '@rickcedwhat/playwright-smart-table';
|
|
|
23
23
|
test('Verify User Email', async ({ page }) => {
|
|
24
24
|
const table = useTable(page.locator('#users-table'));
|
|
25
25
|
|
|
26
|
-
// šŖ
|
|
26
|
+
// šŖ Finds the row with Name="Alice", then gets the Email cell.
|
|
27
27
|
// If Alice is on Page 2, it handles pagination automatically.
|
|
28
|
-
await
|
|
29
|
-
|
|
30
|
-
).toHaveText('alice@example.com');
|
|
28
|
+
const row = await table.getByRow({ Name: 'Alice' });
|
|
29
|
+
|
|
30
|
+
await expect(row.getCell('Email')).toHaveText('alice@example.com');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
2. Complex Grids (Material UI / AG-Grid / Divs)
|
|
35
35
|
|
|
36
|
-
For modern React grids
|
|
36
|
+
For modern React grids, simply override the selectors and define a pagination strategy.
|
|
37
37
|
|
|
38
38
|
import { useTable, TableStrategies } from '@rickcedwhat/playwright-smart-table';
|
|
39
39
|
|
|
@@ -43,110 +43,139 @@ const table = useTable(page.locator('.MuiDataGrid-root'), {
|
|
|
43
43
|
cellSelector: '.MuiDataGrid-cell',
|
|
44
44
|
// Strategy: Tell it how to find the next page
|
|
45
45
|
pagination: TableStrategies.clickNext(
|
|
46
|
-
|
|
46
|
+
// Use 'page' to find buttons outside the table container
|
|
47
|
+
(root) => root.page().getByRole('button', { name: 'Go to next page' })
|
|
47
48
|
)
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
š§ SmartRow Pattern
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
The core power of this library is the SmartRow.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
Unlike a standard Playwright Locator, a SmartRow is aware of its context within the table's schema. It extends the standard Locator API, so you can chain standard Playwright methods (.click(), .isVisible()) directly off it.
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
getCell(columnName)
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
Instead of writing brittle nth-child selectors, ask for the column by name.
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
// ā
Good: Resilient to column reordering
|
|
63
|
+
await row.getCell('Email').click();
|
|
62
64
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
+
// ā Bad: Brittle
|
|
66
|
+
await row.locator('td').nth(2).click();
|
|
65
67
|
|
|
66
|
-
// Locator Function (More Robust)
|
|
67
|
-
pagination: TableStrategies.clickNext((root) => root.getByRole('button', { name: 'Next' }))
|
|
68
68
|
|
|
69
|
+
toJSON()
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
Extracts the entire row's data into a clean key-value object.
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
const data = await row.toJSON();
|
|
74
|
+
console.log(data);
|
|
75
|
+
// { Name: "Alice", Role: "Admin", Status: "Active" }
|
|
73
76
|
|
|
74
|
-
Behavior: Aggressively scrolls the container/window to the bottom -> Waits for row count to increase.
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
š API Reference
|
|
77
79
|
|
|
80
|
+
getByRow(filters, options?)
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
Strict Retrieval. Finds a single specific row.
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
Throws Error if >1 rows match (ambiguous query).
|
|
82
85
|
|
|
83
|
-
|
|
86
|
+
Returns Sentinel if 0 rows match (allows not.toBeVisible() assertions).
|
|
84
87
|
|
|
85
|
-
|
|
88
|
+
Auto-Paginates if the row isn't found on the current page.
|
|
86
89
|
|
|
90
|
+
// Find a row where Name is "Alice" AND Role is "Admin"
|
|
91
|
+
const row = await table.getByRow({ Name: "Alice", Role: "Admin" });
|
|
92
|
+
await expect(row).toBeVisible();
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
// Assert it does NOT exist
|
|
95
|
+
await expect(await table.getByRow({ Name: "Ghost" })).not.toBeVisible();
|
|
89
96
|
|
|
90
|
-
getByRow(filters, options?)
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
getAllRows(options?)
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
Inclusive Retrieval. Gets a collection of rows.
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
Returns: Array of SmartRow objects.
|
|
97
103
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
Best for: Checking existence ("at least one") or validating sort order.
|
|
105
|
+
|
|
106
|
+
// 1. Get ALL rows on the current page
|
|
107
|
+
const allRows = await table.getAllRows();
|
|
108
|
+
|
|
109
|
+
// 2. Get subset of rows (Filtering)
|
|
110
|
+
const activeUsers = await table.getAllRows({
|
|
111
|
+
filter: { Status: 'Active' }
|
|
112
|
+
});
|
|
113
|
+
expect(activeUsers.length).toBeGreaterThan(0); // "At least one active user"
|
|
101
114
|
|
|
115
|
+
// 3. Dump data to JSON
|
|
116
|
+
const data = await table.getAllRows({ asJSON: true });
|
|
117
|
+
console.log(data); // [{ Name: "Alice", Status: "Active" }, ...]
|
|
102
118
|
|
|
103
|
-
getByCell(filters, targetColumn)
|
|
104
119
|
|
|
105
|
-
|
|
120
|
+
š§© Pagination Strategies
|
|
106
121
|
|
|
107
|
-
|
|
122
|
+
This library uses the Strategy Pattern to handle navigation. You can use the built-in strategies or write your own.
|
|
108
123
|
|
|
109
|
-
|
|
110
|
-
await table.getByCell({ Name: "Alice" }, "Actions").getByRole('button').click();
|
|
124
|
+
Built-in Strategies
|
|
111
125
|
|
|
126
|
+
clickNext(selector)
|
|
127
|
+
Best for standard tables (Datatables, lists). Clicks a button and waits for data to change.
|
|
112
128
|
|
|
113
|
-
|
|
129
|
+
pagination: TableStrategies.clickNext((root) =>
|
|
130
|
+
root.page().getByRole('button', { name: 'Next' })
|
|
131
|
+
)
|
|
114
132
|
|
|
115
|
-
Returns a POJO (Plain Old JavaScript Object) of the row data. Useful for debugging or strict data assertions.
|
|
116
133
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
infiniteScroll()
|
|
135
|
+
Best for Virtualized Grids (AG-Grid, HTMX). Aggressively scrolls to trigger data loading.
|
|
136
|
+
|
|
137
|
+
pagination: TableStrategies.infiniteScroll()
|
|
120
138
|
|
|
121
139
|
|
|
122
|
-
|
|
140
|
+
clickLoadMore(selector)
|
|
141
|
+
Best for "Load More" buttons. Clicks and waits for row count to increase.
|
|
123
142
|
|
|
124
|
-
|
|
143
|
+
Writing Custom Strategies
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
expect(allRows[0].Name).toBe("Alice"); // Verify sort order
|
|
145
|
+
A Strategy is just a function that receives the table context and returns a Promise<boolean> (true if navigation happened, false if we reached the end).
|
|
128
146
|
|
|
147
|
+
import { PaginationStrategy } from '@rickcedwhat/playwright-smart-table';
|
|
129
148
|
|
|
130
|
-
|
|
149
|
+
const myCustomStrategy: PaginationStrategy = async ({ root, page, config }) => {
|
|
150
|
+
// 1. Check if we can navigate
|
|
151
|
+
const nextBtn = page.getByTestId('custom-next-arrow');
|
|
152
|
+
if (!await nextBtn.isVisible()) return false;
|
|
131
153
|
|
|
132
|
-
|
|
154
|
+
// 2. Perform Navigation
|
|
155
|
+
await nextBtn.click();
|
|
133
156
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
await
|
|
157
|
+
// 3. Smart Wait (Crucial!)
|
|
158
|
+
// Wait for a loading spinner to disappear, or data to change
|
|
159
|
+
await expect(page.locator('.spinner')).not.toBeVisible();
|
|
160
|
+
|
|
161
|
+
return true; // We successfully moved to the next page
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
š ļø Developer Tools
|
|
137
166
|
|
|
138
|
-
|
|
139
|
-
await table.generateStrategyPrompt();
|
|
167
|
+
Don't waste time writing selectors manually. Use the generator tools to create your config.
|
|
140
168
|
|
|
169
|
+
generateConfigPrompt(options?)
|
|
141
170
|
|
|
142
|
-
|
|
171
|
+
Prints a prompt you can paste into ChatGPT/Gemini to generate the TableConfig for your specific HTML.
|
|
143
172
|
|
|
144
|
-
|
|
173
|
+
// Options: 'console' (default), 'report' (Playwright HTML Report), 'file'
|
|
174
|
+
await table.generateConfigPrompt({ output: 'report' });
|
|
145
175
|
|
|
146
|
-
1.x.x: No breaking changes to the useTable signature.
|
|
147
176
|
|
|
148
|
-
|
|
177
|
+
generateStrategyPrompt(options?)
|
|
149
178
|
|
|
150
|
-
|
|
179
|
+
Prints a prompt to help you write a custom Pagination Strategy.
|
|
151
180
|
|
|
152
|
-
|
|
181
|
+
await table.generateStrategyPrompt({ output: 'console' });
|
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 = "\
|
|
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 * - 'report': Attaches to Playwright Report (Requires testInfo).\n */\n output?: 'console' | 'report' | 'error';\n includeTypes?: boolean;\n testInfo?: TestInfo;\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
|
@@ -7,8 +7,6 @@ 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
|
-
// src/types.ts
|
|
11
|
-
|
|
12
10
|
export type Selector = string | ((root: Locator | Page) => Locator);
|
|
13
11
|
|
|
14
12
|
export type SmartRow = Locator & {
|
|
@@ -23,48 +21,27 @@ export interface TableContext {
|
|
|
23
21
|
resolve: (selector: Selector, parent: Locator | Page) => Locator;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
/**
|
|
27
|
-
* A function that handles pagination logic.
|
|
28
|
-
* Returns true if more data was loaded (navigation occurred), false if end of data.
|
|
29
|
-
*/
|
|
30
24
|
export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
|
|
31
25
|
|
|
32
26
|
export interface PromptOptions {
|
|
33
|
-
/** * Where to output the prompt.
|
|
34
|
-
* - 'console': Logs to stdout (default).
|
|
35
|
-
* - 'report': Attaches to the Playwright HTML report (best for CI/VMs).
|
|
36
|
-
* - 'file': Writes to a local file (e.g. 'smart-table-prompt.md').
|
|
37
|
-
*/
|
|
38
|
-
output?: 'console' | 'report' | 'file';
|
|
39
|
-
|
|
40
27
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
28
|
+
* Output Strategy:
|
|
29
|
+
* - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).
|
|
30
|
+
* - 'console': Standard console logs (Default).
|
|
31
|
+
* - 'report': Attaches to Playwright Report (Requires testInfo).
|
|
44
32
|
*/
|
|
33
|
+
output?: 'console' | 'report' | 'error';
|
|
45
34
|
includeTypes?: boolean;
|
|
35
|
+
testInfo?: TestInfo;
|
|
46
36
|
}
|
|
47
37
|
|
|
48
38
|
export interface TableConfig {
|
|
49
39
|
rowSelector?: Selector;
|
|
50
40
|
headerSelector?: Selector;
|
|
51
41
|
cellSelector?: Selector;
|
|
52
|
-
/**
|
|
53
|
-
* Strategy for handling pagination.
|
|
54
|
-
* Use presets from TableStrategies or write your own.
|
|
55
|
-
*/
|
|
56
42
|
pagination?: PaginationStrategy;
|
|
57
43
|
maxPages?: number;
|
|
58
|
-
/**
|
|
59
|
-
* Optional hook to rename columns dynamically.
|
|
60
|
-
* Useful for naming empty columns (like '__col_0') to something semantic like 'Actions'.
|
|
61
|
-
*/
|
|
62
44
|
headerTransformer?: (text: string, index: number) => string;
|
|
63
|
-
/**
|
|
64
|
-
* Automatically scroll the table into view on first interaction.
|
|
65
|
-
* Helps ensure the table is visible in traces/videos.
|
|
66
|
-
* Default: true
|
|
67
|
-
*/
|
|
68
45
|
autoScroll?: boolean;
|
|
69
46
|
}
|
|
70
47
|
|
|
@@ -72,26 +49,13 @@ export interface TableResult {
|
|
|
72
49
|
getHeaders: () => Promise<string[]>;
|
|
73
50
|
getHeaderCell: (columnName: string) => Promise<Locator>;
|
|
74
51
|
|
|
75
|
-
/** * Find a specific row by its content.
|
|
76
|
-
* Default: Returns SmartRow (Locator).
|
|
77
|
-
* Option { asJSON: true }: Returns Record<string, string> (Data).
|
|
78
|
-
*/
|
|
79
52
|
getByRow: <T extends { asJSON?: boolean }>(
|
|
80
53
|
filters: Record<string, string | RegExp | number>,
|
|
81
54
|
options?: { exact?: boolean, maxPages?: number } & T
|
|
82
55
|
) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
|
|
83
56
|
|
|
84
|
-
/** * Get all rows on the current page.
|
|
85
|
-
* Can be filtered by column values.
|
|
86
|
-
* * @param options.asJSON - Returns data objects instead of SmartRows
|
|
87
|
-
* @param options.filter - Key-Value pairs to filter rows (e.g. { Status: 'Active' })
|
|
88
|
-
* @param options.exact - Exact match for filters (default: false)
|
|
89
|
-
*/
|
|
90
57
|
getAllRows: <T extends { asJSON?: boolean }>(
|
|
91
|
-
options?: {
|
|
92
|
-
filter?: Record<string, string | RegExp | number>;
|
|
93
|
-
exact?: boolean
|
|
94
|
-
} & T
|
|
58
|
+
options?: { filter?: Record<string, any>, exact?: boolean } & T
|
|
95
59
|
) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
96
60
|
|
|
97
61
|
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Locator, Page } from '@playwright/test';
|
|
1
|
+
import { Locator, Page, TestInfo } 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;
|
|
@@ -10,70 +10,40 @@ export interface TableContext {
|
|
|
10
10
|
page: Page;
|
|
11
11
|
resolve: (selector: Selector, parent: Locator | Page) => Locator;
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* A function that handles pagination logic.
|
|
15
|
-
* Returns true if more data was loaded (navigation occurred), false if end of data.
|
|
16
|
-
*/
|
|
17
13
|
export type PaginationStrategy = (context: TableContext) => Promise<boolean>;
|
|
18
14
|
export interface PromptOptions {
|
|
19
|
-
/** * Where to output the prompt.
|
|
20
|
-
* - 'console': Logs to stdout (default).
|
|
21
|
-
* - 'report': Attaches to the Playwright HTML report (best for CI/VMs).
|
|
22
|
-
* - 'file': Writes to a local file (e.g. 'smart-table-prompt.md').
|
|
23
|
-
*/
|
|
24
|
-
output?: 'console' | 'report' | 'file';
|
|
25
15
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
16
|
+
* Output Strategy:
|
|
17
|
+
* - 'error': Throws an error with the prompt (Best for Cloud/QA Wolf to get clean text).
|
|
18
|
+
* - 'console': Standard console logs (Default).
|
|
19
|
+
* - 'report': Attaches to Playwright Report (Requires testInfo).
|
|
29
20
|
*/
|
|
21
|
+
output?: 'console' | 'report' | 'error';
|
|
30
22
|
includeTypes?: boolean;
|
|
23
|
+
testInfo?: TestInfo;
|
|
31
24
|
}
|
|
32
25
|
export interface TableConfig {
|
|
33
26
|
rowSelector?: Selector;
|
|
34
27
|
headerSelector?: Selector;
|
|
35
28
|
cellSelector?: Selector;
|
|
36
|
-
/**
|
|
37
|
-
* Strategy for handling pagination.
|
|
38
|
-
* Use presets from TableStrategies or write your own.
|
|
39
|
-
*/
|
|
40
29
|
pagination?: PaginationStrategy;
|
|
41
30
|
maxPages?: number;
|
|
42
|
-
/**
|
|
43
|
-
* Optional hook to rename columns dynamically.
|
|
44
|
-
* Useful for naming empty columns (like '__col_0') to something semantic like 'Actions'.
|
|
45
|
-
*/
|
|
46
31
|
headerTransformer?: (text: string, index: number) => string;
|
|
47
|
-
/**
|
|
48
|
-
* Automatically scroll the table into view on first interaction.
|
|
49
|
-
* Helps ensure the table is visible in traces/videos.
|
|
50
|
-
* Default: true
|
|
51
|
-
*/
|
|
52
32
|
autoScroll?: boolean;
|
|
53
33
|
}
|
|
54
34
|
export interface TableResult {
|
|
55
35
|
getHeaders: () => Promise<string[]>;
|
|
56
36
|
getHeaderCell: (columnName: string) => Promise<Locator>;
|
|
57
|
-
/** * Find a specific row by its content.
|
|
58
|
-
* Default: Returns SmartRow (Locator).
|
|
59
|
-
* Option { asJSON: true }: Returns Record<string, string> (Data).
|
|
60
|
-
*/
|
|
61
37
|
getByRow: <T extends {
|
|
62
38
|
asJSON?: boolean;
|
|
63
39
|
}>(filters: Record<string, string | RegExp | number>, options?: {
|
|
64
40
|
exact?: boolean;
|
|
65
41
|
maxPages?: number;
|
|
66
42
|
} & T) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
|
|
67
|
-
/** * Get all rows on the current page.
|
|
68
|
-
* Can be filtered by column values.
|
|
69
|
-
* * @param options.asJSON - Returns data objects instead of SmartRows
|
|
70
|
-
* @param options.filter - Key-Value pairs to filter rows (e.g. { Status: 'Active' })
|
|
71
|
-
* @param options.exact - Exact match for filters (default: false)
|
|
72
|
-
*/
|
|
73
43
|
getAllRows: <T extends {
|
|
74
44
|
asJSON?: boolean;
|
|
75
45
|
}>(options?: {
|
|
76
|
-
filter?: Record<string,
|
|
46
|
+
filter?: Record<string, any>;
|
|
77
47
|
exact?: boolean;
|
|
78
48
|
} & T) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
79
49
|
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
package/dist/useTable.js
CHANGED
|
@@ -13,7 +13,7 @@ exports.useTable = void 0;
|
|
|
13
13
|
const test_1 = require("@playwright/test");
|
|
14
14
|
const typeContext_1 = require("./typeContext");
|
|
15
15
|
const useTable = (rootLocator, configOptions = {}) => {
|
|
16
|
-
const config = Object.assign({ rowSelector: "tbody tr", headerSelector: "th", cellSelector: "td", pagination:
|
|
16
|
+
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);
|
|
17
17
|
const resolve = (item, parent) => {
|
|
18
18
|
if (typeof item === 'string')
|
|
19
19
|
return parent.locator(item);
|
|
@@ -25,9 +25,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
25
25
|
const _getMap = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
26
|
if (_headerMap)
|
|
27
27
|
return _headerMap;
|
|
28
|
-
// ā
New Feature: Auto-Scroll on first interaction
|
|
29
28
|
if (config.autoScroll) {
|
|
30
|
-
|
|
29
|
+
try {
|
|
30
|
+
yield rootLocator.scrollIntoViewIfNeeded({ timeout: 1000 });
|
|
31
|
+
}
|
|
32
|
+
catch (e) { }
|
|
31
33
|
}
|
|
32
34
|
const headerLoc = resolve(config.headerSelector, rootLocator);
|
|
33
35
|
try {
|
|
@@ -37,8 +39,8 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
37
39
|
const texts = yield headerLoc.allInnerTexts();
|
|
38
40
|
_headerMap = new Map(texts.map((t, i) => {
|
|
39
41
|
let text = t.trim() || `__col_${i}`;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
// Safe call: config.headerTransformer is always defined now
|
|
43
|
+
text = config.headerTransformer(text, i);
|
|
42
44
|
return [text, i];
|
|
43
45
|
}));
|
|
44
46
|
return _headerMap;
|
|
@@ -69,7 +71,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
69
71
|
});
|
|
70
72
|
return smart;
|
|
71
73
|
};
|
|
72
|
-
// ā»ļø HELPER: Centralized logic to filter a row locator
|
|
73
74
|
const _applyFilters = (baseRows, filters, map, exact) => {
|
|
74
75
|
let filtered = baseRows;
|
|
75
76
|
const page = rootLocator.page();
|
|
@@ -79,59 +80,34 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
79
80
|
throw new Error(`Column '${colName}' not found.`);
|
|
80
81
|
const filterVal = typeof value === 'number' ? String(value) : value;
|
|
81
82
|
const cellTemplate = resolve(config.cellSelector, page);
|
|
82
|
-
// Filter the TRs that contain the matching cell at the specific index
|
|
83
83
|
filtered = filtered.filter({
|
|
84
84
|
has: cellTemplate.nth(colIndex).getByText(filterVal, { exact }),
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
return filtered;
|
|
88
88
|
};
|
|
89
|
-
const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
|
|
90
|
-
const { output = 'console', includeTypes = true } = options; // Default includeTypes to true
|
|
91
|
-
let finalPrompt = content;
|
|
92
|
-
if (includeTypes) {
|
|
93
|
-
// ā
Inject the dynamic TYPE_CONTEXT
|
|
94
|
-
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
|
|
95
|
-
}
|
|
96
|
-
if (output === 'console') {
|
|
97
|
-
console.log(finalPrompt);
|
|
98
|
-
}
|
|
99
|
-
else if (output === 'report') {
|
|
100
|
-
if (test_1.test.info()) {
|
|
101
|
-
yield test_1.test.info().attach(promptName, {
|
|
102
|
-
body: finalPrompt,
|
|
103
|
-
contentType: 'text/markdown'
|
|
104
|
-
});
|
|
105
|
-
console.log(`ā
Attached '${promptName}' to Playwright Report.`);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
console.warn('ā ļø Cannot attach to report: No active test info found.');
|
|
109
|
-
console.log(finalPrompt);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
// ... (file output logic) ...
|
|
113
|
-
});
|
|
114
89
|
const _findRowLocator = (filters_1, ...args_1) => __awaiter(void 0, [filters_1, ...args_1], void 0, function* (filters, options = {}) {
|
|
115
90
|
var _a;
|
|
116
91
|
const map = yield _getMap();
|
|
117
92
|
const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
|
|
118
93
|
let currentPage = 1;
|
|
119
94
|
while (true) {
|
|
120
|
-
// 1. Get all rows
|
|
121
95
|
const allRows = resolve(config.rowSelector, rootLocator);
|
|
122
|
-
// 2. Apply filters using helper
|
|
123
96
|
const matchedRows = _applyFilters(allRows, filters, map, options.exact || false);
|
|
124
|
-
// 3. Check Count
|
|
125
97
|
const count = yield matchedRows.count();
|
|
126
98
|
if (count > 1)
|
|
127
99
|
throw new Error(`Strict Mode Violation: Found ${count} rows matching ${JSON.stringify(filters)}.`);
|
|
128
100
|
if (count === 1)
|
|
129
101
|
return matchedRows.first();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
102
|
+
if (currentPage < effectiveMaxPages) {
|
|
103
|
+
const context = {
|
|
104
|
+
root: rootLocator,
|
|
105
|
+
config: config,
|
|
106
|
+
page: rootLocator.page(),
|
|
107
|
+
resolve: resolve
|
|
108
|
+
};
|
|
109
|
+
const didLoadMore = yield config.pagination(context);
|
|
110
|
+
if (didLoadMore) {
|
|
135
111
|
currentPage++;
|
|
136
112
|
continue;
|
|
137
113
|
}
|
|
@@ -139,6 +115,35 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
139
115
|
return null;
|
|
140
116
|
}
|
|
141
117
|
});
|
|
118
|
+
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, testInfo } = options;
|
|
120
|
+
let finalPrompt = content;
|
|
121
|
+
if (includeTypes) {
|
|
122
|
+
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
|
|
123
|
+
}
|
|
124
|
+
if (output === 'error') {
|
|
125
|
+
console.log(`ā ļø Throwing error to display [${promptName}] cleanly...`);
|
|
126
|
+
throw new Error(finalPrompt);
|
|
127
|
+
}
|
|
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
|
+
console.log(finalPrompt);
|
|
146
|
+
});
|
|
142
147
|
return {
|
|
143
148
|
getHeaders: () => __awaiter(void 0, void 0, void 0, function* () { return Array.from((yield _getMap()).keys()); }),
|
|
144
149
|
getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -150,14 +155,11 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
150
155
|
}),
|
|
151
156
|
getByRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
152
157
|
let row = yield _findRowLocator(filters, options);
|
|
153
|
-
// ā
FIX: Sentinel Logic for negative assertions (expect(row).not.toBeVisible())
|
|
154
158
|
if (!row) {
|
|
155
159
|
row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
|
|
156
160
|
}
|
|
157
161
|
const smartRow = _makeSmart(row, yield _getMap());
|
|
158
162
|
if (options === null || options === void 0 ? void 0 : options.asJSON) {
|
|
159
|
-
// If row doesn't exist, toJSON() returns empty object or throws?
|
|
160
|
-
// For safety, let's let it run naturally (it will likely return empty strings)
|
|
161
163
|
return smartRow.toJSON();
|
|
162
164
|
}
|
|
163
165
|
return smartRow;
|
|
@@ -165,11 +167,9 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
165
167
|
getAllRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
166
168
|
const map = yield _getMap();
|
|
167
169
|
let rowLocators = resolve(config.rowSelector, rootLocator);
|
|
168
|
-
// ā
NEW: Apply filters if they exist
|
|
169
170
|
if (options === null || options === void 0 ? void 0 : options.filter) {
|
|
170
171
|
rowLocators = _applyFilters(rowLocators, options.filter, map, options.exact || false);
|
|
171
172
|
}
|
|
172
|
-
// Convert Locator to array of Locators
|
|
173
173
|
const rows = yield rowLocators.all();
|
|
174
174
|
const smartRows = rows.map(loc => _makeSmart(loc, map));
|
|
175
175
|
if (options === null || options === void 0 ? void 0 : options.asJSON) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "A smart table utility for Playwright with built-in pagination strategies that are fully extensible.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/rickcedwhat/playwright-smart-table.git"
|
|
8
|
+
},
|
|
5
9
|
"main": "dist/index.js",
|
|
6
10
|
"types": "dist/index.d.ts",
|
|
7
11
|
"files": [
|
|
@@ -11,7 +15,6 @@
|
|
|
11
15
|
"generate-types": "node scripts/embed-types.mjs",
|
|
12
16
|
"build": "npm run generate-types && tsc",
|
|
13
17
|
"prepublishOnly": "npm run build",
|
|
14
|
-
"release": "npm run build && npm publish --access public",
|
|
15
18
|
"test": "npx playwright test"
|
|
16
19
|
},
|
|
17
20
|
"keywords": [
|