@rickcedwhat/playwright-smart-table 3.2.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 +357 -100
- package/dist/filterEngine.d.ts +11 -0
- package/dist/filterEngine.js +38 -0
- package/dist/smartRow.d.ts +7 -0
- package/dist/smartRow.js +152 -0
- package/dist/strategies/columns.d.ts +5 -17
- package/dist/strategies/columns.js +2 -34
- package/dist/strategies/headers.d.ts +0 -16
- package/dist/strategies/headers.js +1 -113
- package/dist/strategies/index.d.ts +25 -0
- package/dist/strategies/index.js +19 -1
- package/dist/strategies/pagination.d.ts +0 -21
- package/dist/strategies/pagination.js +1 -23
- package/dist/strategies/resolution.d.ts +22 -0
- package/dist/strategies/resolution.js +30 -0
- 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 +188 -58
- package/dist/types.d.ts +177 -66
- package/dist/useTable.d.ts +5 -9
- package/dist/useTable.js +139 -268
- package/package.json +2 -2
package/dist/types.d.ts
CHANGED
|
@@ -1,18 +1,98 @@
|
|
|
1
1
|
import type { Locator, Page } from '@playwright/test';
|
|
2
|
+
/**
|
|
3
|
+
* Flexible selector type - can be a CSS string, function returning a Locator, or Locator itself.
|
|
4
|
+
* @example
|
|
5
|
+
* // String selector
|
|
6
|
+
* rowSelector: 'tbody tr'
|
|
7
|
+
*
|
|
8
|
+
* // Function selector
|
|
9
|
+
* rowSelector: (root) => root.locator('[role="row"]')
|
|
10
|
+
*/
|
|
2
11
|
export type Selector = string | ((root: Locator | Page) => Locator);
|
|
3
|
-
|
|
4
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Function to get a cell locator given row, column info.
|
|
14
|
+
* Replaces the old cellResolver.
|
|
15
|
+
*/
|
|
16
|
+
export type GetCellLocatorFn = (args: {
|
|
17
|
+
row: Locator;
|
|
18
|
+
columnName: string;
|
|
19
|
+
columnIndex: number;
|
|
20
|
+
rowIndex?: number;
|
|
21
|
+
page: Page;
|
|
22
|
+
}) => Locator;
|
|
23
|
+
/**
|
|
24
|
+
* Function to get the currently active/focused cell.
|
|
25
|
+
* Returns null if no cell is active.
|
|
26
|
+
*/
|
|
27
|
+
export type GetActiveCellFn = (args: TableContext) => Promise<{
|
|
28
|
+
rowIndex: number;
|
|
29
|
+
columnIndex: number;
|
|
30
|
+
columnName?: string;
|
|
31
|
+
locator: Locator;
|
|
32
|
+
} | null>;
|
|
33
|
+
/**
|
|
34
|
+
* SmartRow - A Playwright Locator with table-aware methods.
|
|
35
|
+
*
|
|
36
|
+
* Extends all standard Locator methods (click, isVisible, etc.) with table-specific functionality.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const row = table.getRow({ Name: 'John Doe' });
|
|
40
|
+
* await row.click(); // Standard Locator method
|
|
41
|
+
* const email = row.getCell('Email'); // Table-aware method
|
|
42
|
+
* const data = await row.toJSON(); // Extract all row data
|
|
43
|
+
* await row.smartFill({ Name: 'Jane', Status: 'Active' }); // Fill form fields
|
|
44
|
+
*/
|
|
45
|
+
export type SmartRow<T = any> = Locator & {
|
|
46
|
+
/** Optional row index (0-based) if known */
|
|
5
47
|
rowIndex?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Get a cell locator by column name.
|
|
50
|
+
* @param column - Column name (case-sensitive)
|
|
51
|
+
* @returns Locator for the cell
|
|
52
|
+
* @example
|
|
53
|
+
* const emailCell = row.getCell('Email');
|
|
54
|
+
* await expect(emailCell).toHaveText('john@example.com');
|
|
55
|
+
*/
|
|
6
56
|
getCell(column: string): Locator;
|
|
7
|
-
toJSON(): Promise<Record<string, string>>;
|
|
8
57
|
/**
|
|
9
|
-
*
|
|
58
|
+
* Extract all cell data as a key-value object.
|
|
59
|
+
* @param options - Optional configuration
|
|
60
|
+
* @param options.columns - Specific columns to extract (extracts all if not specified)
|
|
61
|
+
* @returns Promise resolving to row data
|
|
62
|
+
* @example
|
|
63
|
+
* const data = await row.toJSON();
|
|
64
|
+
* // { Name: 'John', Email: 'john@example.com', ... }
|
|
65
|
+
*
|
|
66
|
+
* const partial = await row.toJSON({ columns: ['Name', 'Email'] });
|
|
67
|
+
* // { Name: 'John', Email: 'john@example.com' }
|
|
68
|
+
*/
|
|
69
|
+
toJSON(options?: {
|
|
70
|
+
columns?: string[];
|
|
71
|
+
}): Promise<T>;
|
|
72
|
+
/**
|
|
73
|
+
* Scrolls/paginates to bring this row into view.
|
|
74
|
+
* Only works if rowIndex is known (e.g., from getRowByIndex).
|
|
75
|
+
* @throws Error if rowIndex is unknown
|
|
10
76
|
*/
|
|
11
|
-
|
|
77
|
+
bringIntoView(): Promise<void>;
|
|
12
78
|
/**
|
|
13
|
-
*
|
|
79
|
+
* Intelligently fills form fields in the row.
|
|
80
|
+
* Automatically detects input types (text, select, checkbox, contenteditable).
|
|
81
|
+
*
|
|
82
|
+
* @param data - Column-value pairs to fill
|
|
83
|
+
* @param options - Optional configuration
|
|
84
|
+
* @param options.inputMappers - Custom input selectors per column
|
|
85
|
+
* @example
|
|
86
|
+
* // Auto-detection
|
|
87
|
+
* await row.smartFill({ Name: 'John', Status: 'Active', Subscribe: true });
|
|
88
|
+
*
|
|
89
|
+
* // Custom input mappers
|
|
90
|
+
* await row.smartFill(
|
|
91
|
+
* { Name: 'John' },
|
|
92
|
+
* { inputMappers: { Name: (cell) => cell.locator('.custom-input') } }
|
|
93
|
+
* );
|
|
14
94
|
*/
|
|
15
|
-
smartFill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;
|
|
95
|
+
smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
|
|
16
96
|
};
|
|
17
97
|
export type StrategyContext = TableContext & {
|
|
18
98
|
rowLocator?: Locator;
|
|
@@ -66,9 +146,46 @@ export type FillStrategy = (options: {
|
|
|
66
146
|
fillOptions?: FillOptions;
|
|
67
147
|
}) => Promise<void>;
|
|
68
148
|
export type { HeaderStrategy } from './strategies/headers';
|
|
69
|
-
export type {
|
|
149
|
+
export type { CellNavigationStrategy } from './strategies/columns';
|
|
70
150
|
import { HeaderStrategy } from './strategies/headers';
|
|
71
|
-
import {
|
|
151
|
+
import { CellNavigationStrategy } from './strategies/columns';
|
|
152
|
+
/**
|
|
153
|
+
* Strategy to resolve column names (string or regex) to their index.
|
|
154
|
+
*/
|
|
155
|
+
export type { ColumnResolutionStrategy } from './strategies/resolution';
|
|
156
|
+
/**
|
|
157
|
+
* Strategy to filter rows based on criteria.
|
|
158
|
+
*/
|
|
159
|
+
export interface FilterStrategy {
|
|
160
|
+
apply(options: {
|
|
161
|
+
rows: Locator;
|
|
162
|
+
filter: {
|
|
163
|
+
column: string;
|
|
164
|
+
value: string | RegExp | number;
|
|
165
|
+
};
|
|
166
|
+
colIndex: number;
|
|
167
|
+
tableContext: TableContext;
|
|
168
|
+
}): Locator;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Organized container for all table interaction strategies.
|
|
172
|
+
*/
|
|
173
|
+
export interface TableStrategies {
|
|
174
|
+
/** Strategy for discovering/scanning headers */
|
|
175
|
+
header?: HeaderStrategy;
|
|
176
|
+
/** Strategy for navigating to specific cells (row + column) */
|
|
177
|
+
cellNavigation?: CellNavigationStrategy;
|
|
178
|
+
/** Strategy for filling form inputs */
|
|
179
|
+
fill?: FillStrategy;
|
|
180
|
+
/** Strategy for paginating through data */
|
|
181
|
+
pagination?: PaginationStrategy;
|
|
182
|
+
/** Strategy for sorting columns */
|
|
183
|
+
sorting?: SortingStrategy;
|
|
184
|
+
/** Function to get a cell locator */
|
|
185
|
+
getCellLocator?: GetCellLocatorFn;
|
|
186
|
+
/** Function to get the currently active/focused cell */
|
|
187
|
+
getActiveCell?: GetActiveCellFn;
|
|
188
|
+
}
|
|
72
189
|
/**
|
|
73
190
|
* Configuration options for useTable.
|
|
74
191
|
*/
|
|
@@ -79,21 +196,9 @@ export interface TableConfig {
|
|
|
79
196
|
rowSelector?: string;
|
|
80
197
|
/** Selector for the cells within a row */
|
|
81
198
|
cellSelector?: string;
|
|
82
|
-
/** Strategy for filling forms within the table */
|
|
83
|
-
fillStrategy?: FillStrategy;
|
|
84
|
-
/** Strategy for discovering headers */
|
|
85
|
-
headerStrategy?: HeaderStrategy;
|
|
86
|
-
/** Strategy for navigating to columns */
|
|
87
|
-
columnStrategy?: ColumnStrategy;
|
|
88
199
|
/** Number of pages to scan for verification */
|
|
89
200
|
maxPages?: number;
|
|
90
|
-
/**
|
|
91
|
-
pagination?: PaginationStrategy;
|
|
92
|
-
/** Sorting Strategy */
|
|
93
|
-
sorting?: SortingStrategy;
|
|
94
|
-
/**
|
|
95
|
-
* Hook to rename columns dynamically.
|
|
96
|
-
*/
|
|
201
|
+
/** Hook to rename columns dynamically */
|
|
97
202
|
headerTransformer?: (args: {
|
|
98
203
|
text: string;
|
|
99
204
|
index: number;
|
|
@@ -105,27 +210,14 @@ export interface TableConfig {
|
|
|
105
210
|
debug?: boolean;
|
|
106
211
|
/** Reset hook */
|
|
107
212
|
onReset?: (context: TableContext) => Promise<void>;
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
* Overrides cellSelector logic if provided.
|
|
111
|
-
* Useful for virtualized tables where nth() index doesn't match DOM index.
|
|
112
|
-
*/
|
|
113
|
-
cellResolver?: (args: {
|
|
114
|
-
row: Locator;
|
|
115
|
-
columnName: string;
|
|
116
|
-
columnIndex: number;
|
|
117
|
-
rowIndex?: number;
|
|
118
|
-
}) => Locator;
|
|
213
|
+
/** All interaction strategies */
|
|
214
|
+
strategies?: TableStrategies;
|
|
119
215
|
}
|
|
120
216
|
export interface FinalTableConfig extends TableConfig {
|
|
121
217
|
headerSelector: string;
|
|
122
218
|
rowSelector: string;
|
|
123
219
|
cellSelector: string;
|
|
124
|
-
fillStrategy: FillStrategy;
|
|
125
|
-
headerStrategy: HeaderStrategy;
|
|
126
|
-
columnStrategy: ColumnStrategy;
|
|
127
220
|
maxPages: number;
|
|
128
|
-
pagination: PaginationStrategy;
|
|
129
221
|
autoScroll: boolean;
|
|
130
222
|
debug: boolean;
|
|
131
223
|
headerTransformer: (args: {
|
|
@@ -134,12 +226,7 @@ export interface FinalTableConfig extends TableConfig {
|
|
|
134
226
|
locator: Locator;
|
|
135
227
|
}) => string | Promise<string>;
|
|
136
228
|
onReset: (context: TableContext) => Promise<void>;
|
|
137
|
-
|
|
138
|
-
row: Locator;
|
|
139
|
-
columnName: string;
|
|
140
|
-
columnIndex: number;
|
|
141
|
-
rowIndex?: number;
|
|
142
|
-
}) => Locator;
|
|
229
|
+
strategies: TableStrategies;
|
|
143
230
|
}
|
|
144
231
|
export interface FillOptions {
|
|
145
232
|
/**
|
|
@@ -148,7 +235,7 @@ export interface FillOptions {
|
|
|
148
235
|
*/
|
|
149
236
|
inputMappers?: Record<string, (cell: Locator) => Locator>;
|
|
150
237
|
}
|
|
151
|
-
export interface TableResult {
|
|
238
|
+
export interface TableResult<T = any> {
|
|
152
239
|
/**
|
|
153
240
|
* Initializes the table by resolving headers. Must be called before using sync methods.
|
|
154
241
|
* @param options Optional timeout for header resolution (default: 3000ms)
|
|
@@ -156,51 +243,75 @@ export interface TableResult {
|
|
|
156
243
|
init(options?: {
|
|
157
244
|
timeout?: number;
|
|
158
245
|
}): Promise<TableResult>;
|
|
246
|
+
/**
|
|
247
|
+
* SYNC: Checks if the table has been initialized.
|
|
248
|
+
* @returns true if init() has been called and completed, false otherwise
|
|
249
|
+
*/
|
|
250
|
+
isInitialized(): boolean;
|
|
159
251
|
getHeaders: () => Promise<string[]>;
|
|
160
252
|
getHeaderCell: (columnName: string) => Promise<Locator>;
|
|
161
253
|
/**
|
|
162
|
-
* Finds a row on the current page only. Returns immediately (sync).
|
|
254
|
+
* Finds a row by filters on the current page only. Returns immediately (sync).
|
|
163
255
|
* Throws error if table is not initialized.
|
|
164
256
|
*/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
exact?: boolean;
|
|
169
|
-
}): SmartRow;
|
|
170
|
-
};
|
|
257
|
+
getRow: (filters: Record<string, string | RegExp | number>, options?: {
|
|
258
|
+
exact?: boolean;
|
|
259
|
+
}) => SmartRow;
|
|
171
260
|
/**
|
|
172
|
-
*
|
|
173
|
-
*
|
|
261
|
+
* Gets a row by 1-based index on the current page.
|
|
262
|
+
* Throws error if table is not initialized.
|
|
263
|
+
* @param index 1-based row index
|
|
264
|
+
* @param options Optional settings including bringIntoView
|
|
265
|
+
*/
|
|
266
|
+
getRowByIndex: (index: number, options?: {
|
|
267
|
+
bringIntoView?: boolean;
|
|
268
|
+
}) => SmartRow;
|
|
269
|
+
/**
|
|
270
|
+
* ASYNC: Searches for a single row across pages using pagination.
|
|
271
|
+
* Auto-initializes the table if not already initialized.
|
|
272
|
+
* @param filters - The filter criteria to match
|
|
273
|
+
* @param options - Search options including exact match and max pages
|
|
174
274
|
*/
|
|
175
|
-
|
|
275
|
+
findRow: (filters: Record<string, string | RegExp | number>, options?: {
|
|
176
276
|
exact?: boolean;
|
|
177
277
|
maxPages?: number;
|
|
178
278
|
}) => Promise<SmartRow>;
|
|
179
279
|
/**
|
|
180
|
-
*
|
|
280
|
+
* ASYNC: Searches for all matching rows across pages using pagination.
|
|
281
|
+
* Auto-initializes the table if not already initialized.
|
|
282
|
+
* @param filters - The filter criteria to match
|
|
283
|
+
* @param options - Search options including exact match, max pages, and asJSON
|
|
181
284
|
*/
|
|
182
|
-
|
|
183
|
-
getAllCurrentRows: <T extends {
|
|
285
|
+
findRows: <R extends {
|
|
184
286
|
asJSON?: boolean;
|
|
185
|
-
}>(options?: {
|
|
186
|
-
filter?: Record<string, any>;
|
|
287
|
+
}>(filters: Record<string, string | RegExp | number>, options?: {
|
|
187
288
|
exact?: boolean;
|
|
188
|
-
|
|
289
|
+
maxPages?: number;
|
|
290
|
+
} & R) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
291
|
+
/**
|
|
292
|
+
* Navigates to a specific column using the configured CellNavigationStrategy.
|
|
293
|
+
*/
|
|
294
|
+
scrollToColumn: (columnName: string) => Promise<void>;
|
|
189
295
|
/**
|
|
190
|
-
*
|
|
296
|
+
* ASYNC: Gets all rows on the current page only (does not paginate).
|
|
297
|
+
* Auto-initializes the table if not already initialized.
|
|
298
|
+
* @param options - Filter and formatting options
|
|
191
299
|
*/
|
|
192
|
-
|
|
300
|
+
getRows: <R extends {
|
|
193
301
|
asJSON?: boolean;
|
|
194
302
|
}>(options?: {
|
|
195
303
|
filter?: Record<string, any>;
|
|
196
304
|
exact?: boolean;
|
|
197
|
-
} &
|
|
198
|
-
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
|
199
|
-
generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
|
|
305
|
+
} & R) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
200
306
|
/**
|
|
201
307
|
* Resets the table state (clears cache, flags) and invokes the onReset strategy.
|
|
202
308
|
*/
|
|
203
309
|
reset: () => Promise<void>;
|
|
310
|
+
/**
|
|
311
|
+
* Revalidates the table's structure (headers, columns) without resetting pagination or state.
|
|
312
|
+
* Useful when columns change visibility or order dynamically.
|
|
313
|
+
*/
|
|
314
|
+
revalidate: () => Promise<void>;
|
|
204
315
|
/**
|
|
205
316
|
* Scans a specific column across all pages and returns the values.
|
|
206
317
|
*/
|
|
@@ -262,4 +373,4 @@ export interface TableResult {
|
|
|
262
373
|
/**
|
|
263
374
|
* Restricted table result that excludes methods that shouldn't be called during iteration.
|
|
264
375
|
*/
|
|
265
|
-
export type RestrictedTableResult = Omit<TableResult
|
|
376
|
+
export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
|
package/dist/useTable.d.ts
CHANGED
|
@@ -2,22 +2,18 @@ import type { Locator } from '@playwright/test';
|
|
|
2
2
|
import { TableConfig, Selector, TableResult, PaginationStrategy } from './types';
|
|
3
3
|
import { FillStrategies } from './strategies/fill';
|
|
4
4
|
import { HeaderStrategies } from './strategies/headers';
|
|
5
|
-
import {
|
|
5
|
+
import { CellNavigationStrategies } from './strategies/columns';
|
|
6
|
+
import { ResolutionStrategies } from './strategies/resolution';
|
|
7
|
+
import { Strategies } from './strategies';
|
|
6
8
|
/**
|
|
7
9
|
* Main hook to interact with a table.
|
|
8
10
|
*/
|
|
9
|
-
export declare const useTable: (rootLocator: Locator, configOptions?: TableConfig) => TableResult
|
|
11
|
+
export declare const useTable: <T = any>(rootLocator: Locator, configOptions?: TableConfig) => TableResult<T>;
|
|
10
12
|
export declare const PaginationStrategies: {
|
|
11
13
|
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
12
|
-
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
13
|
-
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
14
|
-
};
|
|
15
|
-
export declare const TableStrategies: {
|
|
16
|
-
clickNext: (nextButtonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
17
|
-
clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
|
|
18
14
|
infiniteScroll: (timeout?: number) => PaginationStrategy;
|
|
19
15
|
};
|
|
20
16
|
export declare const SortingStrategies: {
|
|
21
17
|
AriaSort: () => import("./types").SortingStrategy;
|
|
22
18
|
};
|
|
23
|
-
export { FillStrategies, HeaderStrategies,
|
|
19
|
+
export { FillStrategies, HeaderStrategies, CellNavigationStrategies, ResolutionStrategies, Strategies };
|