@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.
@@ -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
+ }
@@ -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 getRequestIndex(): number | undefined; // Helper to get the row index if known\n rowIndex?: number;\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n /**\n * Fills the row with data. Automatically detects input types (text input, select, checkbox, etc.).\n */\n fill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;\n /**\n * Alias for fill() to avoid conflict with Locator.fill() \n */\n smartFill: (data: 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 { ColumnStrategy } from './strategies/columns';\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 /** Strategy for filling forms within the table */\n fillStrategy?: FillStrategy;\n /** Strategy for discovering headers */\n headerStrategy?: HeaderStrategy;\n /** Strategy for navigating to columns */\n columnStrategy?: ColumnStrategy;\n /** Number of pages to scan for verification */\n maxPages?: number;\n\n /** Pagination Strategy */\n pagination?: PaginationStrategy;\n /** Sorting Strategy */\n sorting?: SortingStrategy;\n /** \n * Hook to rename columns dynamically.\n */\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 /**\n * Custom resolver for finding a cell. \n * Overrides cellSelector logic if provided.\n * Useful for virtualized tables where nth() index doesn't match DOM index.\n */\n cellResolver?: (args: { row: Locator, columnName: string, columnIndex: number, rowIndex?: number }) => Locator;\n}\n\nexport interface FinalTableConfig extends TableConfig {\n headerSelector: string;\n rowSelector: string;\n cellSelector: string;\n fillStrategy: FillStrategy;\n headerStrategy: HeaderStrategy;\n columnStrategy: ColumnStrategy;\n maxPages: number;\n pagination: PaginationStrategy;\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 cellResolver?: (args: { row: Locator, columnName: string, columnIndex: number, rowIndex?: number }) => Locator;\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 {\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 on the current page only. Returns immediately (sync).\n * Throws error if table is not initialized.\n */\n getByRow: {\n (index: number): SmartRow;\n (\n filters: Record<string, string | RegExp | number>,\n options?: { exact?: boolean }\n ): SmartRow;\n };\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 * Manually scrolls to a column using the configured ColumnStrategy.\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 * 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 = Omit<TableResult, '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";
@@ -7,21 +7,106 @@ 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
- export type SmartRow = Locator & {
13
- getRequestIndex(): number | undefined; // Helper to get the row index if known
21
+ /**
22
+ * Function to get a cell locator given row, column info.
23
+ * Replaces the old cellResolver.
24
+ */
25
+ export type GetCellLocatorFn = (args: {
26
+ row: Locator;
27
+ columnName: string;
28
+ columnIndex: number;
14
29
  rowIndex?: number;
30
+ page: Page;
31
+ }) => Locator;
32
+
33
+ /**
34
+ * Function to get the currently active/focused cell.
35
+ * Returns null if no cell is active.
36
+ */
37
+ export type GetActiveCellFn = (args: TableContext) => Promise<{
38
+ rowIndex: number;
39
+ columnIndex: number;
40
+ columnName?: string;
41
+ locator: Locator;
42
+ } | null>;
43
+
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
+ */
57
+ export type SmartRow<T = any> = Locator & {
58
+ /** Optional row index (0-based) if known */
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
+ */
15
69
  getCell(column: string): Locator;
16
- toJSON(): Promise<Record<string, string>>;
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
+ */
83
+ toJSON(options?: { columns?: string[] }): Promise<T>;
84
+
17
85
  /**
18
- * Fills the row with data. Automatically detects input types (text input, select, checkbox, etc.).
86
+ * Scrolls/paginates to bring this row into view.
87
+ * Only works if rowIndex is known (e.g., from getRowByIndex).
88
+ * @throws Error if rowIndex is unknown
19
89
  */
20
- fill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;
90
+ bringIntoView(): Promise<void>;
91
+
21
92
  /**
22
- * Alias for fill() to avoid conflict with Locator.fill()
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
+ * );
23
108
  */
24
- smartFill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;
109
+ smartFill: (data: Partial<T> | Record<string, any>, options?: FillOptions) => Promise<void>;
25
110
  };
26
111
 
27
112
  export type StrategyContext = TableContext & { rowLocator?: Locator; rowIndex?: number };
@@ -81,7 +166,44 @@ export type FillStrategy = (options: {
81
166
  }) => Promise<void>;
82
167
 
83
168
  export type { HeaderStrategy } from './strategies/headers';
84
- export type { ColumnStrategy } from './strategies/columns';
169
+ export type { CellNavigationStrategy } from './strategies/columns';
170
+
171
+ /**
172
+ * Strategy to resolve column names (string or regex) to their index.
173
+ */
174
+ export type { ColumnResolutionStrategy } from './strategies/resolution';
175
+
176
+ /**
177
+ * Strategy to filter rows based on criteria.
178
+ */
179
+ export interface FilterStrategy {
180
+ apply(options: {
181
+ rows: Locator;
182
+ filter: { column: string, value: string | RegExp | number };
183
+ colIndex: number;
184
+ tableContext: TableContext;
185
+ }): Locator;
186
+ }
187
+
188
+ /**
189
+ * Organized container for all table interaction strategies.
190
+ */
191
+ export interface TableStrategies {
192
+ /** Strategy for discovering/scanning headers */
193
+ header?: HeaderStrategy;
194
+ /** Strategy for navigating to specific cells (row + column) */
195
+ cellNavigation?: CellNavigationStrategy;
196
+ /** Strategy for filling form inputs */
197
+ fill?: FillStrategy;
198
+ /** Strategy for paginating through data */
199
+ pagination?: PaginationStrategy;
200
+ /** Strategy for sorting columns */
201
+ sorting?: SortingStrategy;
202
+ /** Function to get a cell locator */
203
+ getCellLocator?: GetCellLocatorFn;
204
+ /** Function to get the currently active/focused cell */
205
+ getActiveCell?: GetActiveCellFn;
206
+ }
85
207
 
86
208
  /**
87
209
  * Configuration options for useTable.
@@ -93,22 +215,9 @@ export interface TableConfig {
93
215
  rowSelector?: string;
94
216
  /** Selector for the cells within a row */
95
217
  cellSelector?: string;
96
- /** Strategy for filling forms within the table */
97
- fillStrategy?: FillStrategy;
98
- /** Strategy for discovering headers */
99
- headerStrategy?: HeaderStrategy;
100
- /** Strategy for navigating to columns */
101
- columnStrategy?: ColumnStrategy;
102
218
  /** Number of pages to scan for verification */
103
219
  maxPages?: number;
104
-
105
- /** Pagination Strategy */
106
- pagination?: PaginationStrategy;
107
- /** Sorting Strategy */
108
- sorting?: SortingStrategy;
109
- /**
110
- * Hook to rename columns dynamically.
111
- */
220
+ /** Hook to rename columns dynamically */
112
221
  headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;
113
222
  /** Automatically scroll to table on init */
114
223
  autoScroll?: boolean;
@@ -116,30 +225,23 @@ export interface TableConfig {
116
225
  debug?: boolean;
117
226
  /** Reset hook */
118
227
  onReset?: (context: TableContext) => Promise<void>;
119
- /**
120
- * Custom resolver for finding a cell.
121
- * Overrides cellSelector logic if provided.
122
- * Useful for virtualized tables where nth() index doesn't match DOM index.
123
- */
124
- cellResolver?: (args: { row: Locator, columnName: string, columnIndex: number, rowIndex?: number }) => Locator;
228
+ /** All interaction strategies */
229
+ strategies?: TableStrategies;
125
230
  }
126
231
 
127
232
  export interface FinalTableConfig extends TableConfig {
128
233
  headerSelector: string;
129
234
  rowSelector: string;
130
235
  cellSelector: string;
131
- fillStrategy: FillStrategy;
132
- headerStrategy: HeaderStrategy;
133
- columnStrategy: ColumnStrategy;
134
236
  maxPages: number;
135
- pagination: PaginationStrategy;
136
237
  autoScroll: boolean;
137
238
  debug: boolean;
138
239
  headerTransformer: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;
139
240
  onReset: (context: TableContext) => Promise<void>;
140
- cellResolver?: (args: { row: Locator, columnName: string, columnIndex: number, rowIndex?: number }) => Locator;
241
+ strategies: TableStrategies;
141
242
  }
142
243
 
244
+
143
245
  export interface FillOptions {
144
246
  /**
145
247
  * Custom input mappers for specific columns.
@@ -148,61 +250,89 @@ export interface FillOptions {
148
250
  inputMappers?: Record<string, (cell: Locator) => Locator>;
149
251
  }
150
252
 
151
- export interface TableResult {
253
+ export interface TableResult<T = any> {
152
254
  /**
153
255
  * Initializes the table by resolving headers. Must be called before using sync methods.
154
256
  * @param options Optional timeout for header resolution (default: 3000ms)
155
257
  */
156
258
  init(options?: { timeout?: number }): Promise<TableResult>;
157
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
+
158
266
  getHeaders: () => Promise<string[]>;
159
267
  getHeaderCell: (columnName: string) => Promise<Locator>;
160
268
 
161
269
  /**
162
- * Finds a row on the current page only. Returns immediately (sync).
270
+ * Finds a row by filters on the current page only. Returns immediately (sync).
163
271
  * Throws error if table is not initialized.
164
272
  */
165
- getByRow: {
166
- (index: number): SmartRow;
167
- (
168
- filters: Record<string, string | RegExp | number>,
169
- options?: { exact?: boolean }
170
- ): SmartRow;
171
- };
273
+ getRow: (
274
+ filters: Record<string, string | RegExp | number>,
275
+ options?: { exact?: boolean }
276
+ ) => SmartRow;
172
277
 
173
278
  /**
174
- * Searches for a row across all available data using the configured strategy (pagination, scroll, etc.).
175
- * Auto-initializes if needed.
279
+ * Gets a row by 1-based index on the current page.
280
+ * Throws error if table is not initialized.
281
+ * @param index 1-based row index
282
+ * @param options Optional settings including bringIntoView
176
283
  */
177
- searchForRow: (
284
+ getRowByIndex: (
285
+ index: number,
286
+ options?: { bringIntoView?: boolean }
287
+ ) => SmartRow;
288
+
289
+ /**
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
294
+ */
295
+ findRow: (
178
296
  filters: Record<string, string | RegExp | number>,
179
297
  options?: { exact?: boolean, maxPages?: number }
180
298
  ) => Promise<SmartRow>;
181
299
 
182
300
  /**
183
- * Manually scrolls to a column using the configured ColumnStrategy.
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
184
305
  */
185
- scrollToColumn: (columnName: string) => Promise<void>;
186
-
187
- getAllCurrentRows: <T extends { asJSON?: boolean }>(
188
- options?: { filter?: Record<string, any>, exact?: boolean } & T
189
- ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
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[]>;
190
310
 
191
311
  /**
192
- * @deprecated Use getAllCurrentRows instead. This method will be removed in a future major version.
312
+ * Navigates to a specific column using the configured CellNavigationStrategy.
193
313
  */
194
- getAllRows: <T extends { asJSON?: boolean }>(
195
- options?: { filter?: Record<string, any>, exact?: boolean } & T
196
- ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
314
+ scrollToColumn: (columnName: string) => Promise<void>;
197
315
 
198
- generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
199
- generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;
316
+ /**
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
320
+ */
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[]>;
200
324
 
201
325
  /**
202
326
  * Resets the table state (clears cache, flags) and invokes the onReset strategy.
203
327
  */
204
328
  reset: () => Promise<void>;
205
329
 
330
+ /**
331
+ * Revalidates the table's structure (headers, columns) without resetting pagination or state.
332
+ * Useful when columns change visibility or order dynamically.
333
+ */
334
+ revalidate: () => Promise<void>;
335
+
206
336
  /**
207
337
  * Scans a specific column across all pages and returns the values.
208
338
  */
@@ -254,5 +384,5 @@ export interface TableResult {
254
384
  /**
255
385
  * Restricted table result that excludes methods that shouldn't be called during iteration.
256
386
  */
257
- export type RestrictedTableResult = Omit<TableResult, 'searchForRow' | 'iterateThroughTable' | 'reset' | 'getAllRows'>;
387
+ export type RestrictedTableResult<T = any> = Omit<TableResult<T>, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
258
388
  `;