@rickcedwhat/playwright-smart-table 5.2.0 ā 5.4.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 +9 -2
- package/dist/typeContext.d.ts +1 -1
- package/dist/typeContext.js +27 -5
- package/dist/types.d.ts +27 -6
- package/dist/useTable.js +21 -16
- package/dist/utils/smartRowArray.d.ts +14 -0
- package/dist/utils/smartRowArray.js +22 -0
- package/package.json +19 -17
package/README.md
CHANGED
|
@@ -108,7 +108,9 @@ Enable debug logging to see exactly what the library is doing:
|
|
|
108
108
|
// Example from: https://datatables.net/examples/data_sources/dom
|
|
109
109
|
const table = useTable(page.locator('#example'), {
|
|
110
110
|
headerSelector: 'thead th',
|
|
111
|
-
debug:
|
|
111
|
+
debug: {
|
|
112
|
+
logLevel: 'verbose' // Enables verbose logging of internal operations
|
|
113
|
+
}
|
|
112
114
|
});
|
|
113
115
|
await table.init();
|
|
114
116
|
|
|
@@ -524,7 +526,8 @@ const tokyoUsers = await table.getRows({
|
|
|
524
526
|
expect(tokyoUsers.length).toBeGreaterThan(0);
|
|
525
527
|
|
|
526
528
|
// 3. Dump data to JSON
|
|
527
|
-
const
|
|
529
|
+
const rows = await table.getRows();
|
|
530
|
+
const data = await rows.toJSON();
|
|
528
531
|
console.log(data); // [{ Name: "Airi Satou", ... }, ...]
|
|
529
532
|
expect(data.length).toBeGreaterThan(0);
|
|
530
533
|
expect(data[0]).toHaveProperty('Name');
|
|
@@ -761,6 +764,10 @@ export interface PromptOptions {
|
|
|
761
764
|
* - 'console': Standard console logs (Default).
|
|
762
765
|
*/
|
|
763
766
|
output?: 'console' | 'error';
|
|
767
|
+
/**
|
|
768
|
+
* Include TypeScript type definitions in the prompt
|
|
769
|
+
* @default true
|
|
770
|
+
*/
|
|
764
771
|
includeTypes?: boolean;
|
|
765
772
|
}
|
|
766
773
|
```
|
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 = "\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\n/**\n * Debug configuration for development and troubleshooting\n */\nexport type DebugConfig = {\n /**\n * Slow down operations for debugging\n * - number: Apply same delay to all operations (ms)\n * - object: Granular delays per operation type\n */\n slow?: number | {\n pagination?: number;\n getCell?: number;\n findRow?: number;\n default?: number;\n };\n /**\n * Log level for debug output\n * - 'verbose': All logs (verbose, info, error)\n * - 'info': Info and error logs only\n * - 'error': Error logs only\n * - 'none': No logs\n */\n logLevel?: 'verbose' | 'info' | 'error' | '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 /** Debug options for development and troubleshooting */\n debug?: DebugConfig;\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?: TableConfig['debug'];\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 batchInfo?: {\n startIndex: number;\n endIndex: number;\n size: number;\n };\n }) => T | Promise<T>,\n options?: {\n pagination?: PaginationStrategy;\n dedupeStrategy?: DedupeStrategy;\n maxIterations?: number;\n batchSize?: number;\n getIsFirst?: (context: { index: number }) => boolean;\n getIsLast?: (context: { index: number, paginationResult: boolean }) => boolean;\n beforeFirst?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n afterLast?: (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";
|
|
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\n/**\n * Debug configuration for development and troubleshooting\n */\nexport type DebugConfig = {\n /**\n * Slow down operations for debugging\n * - number: Apply same delay to all operations (ms)\n * - object: Granular delays per operation type\n */\n slow?: number | {\n pagination?: number;\n getCell?: number;\n findRow?: number;\n default?: number;\n };\n /**\n * Log level for debug output\n * - 'verbose': All logs (verbose, info, error)\n * - 'info': Info and error logs only\n * - 'error': Error logs only\n * - 'none': No logs\n */\n logLevel?: 'verbose' | 'info' | 'error' | '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 /** Debug options for development and troubleshooting */\n debug?: DebugConfig;\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?: TableConfig['debug'];\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\n/**\n * Options for generateConfigPrompt\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 /**\n * Include TypeScript type definitions in the prompt\n * @default true\n */\n includeTypes?: boolean;\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 * Gets all rows on the current page only (does not paginate).\n * Auto-initializes the table if not already initialized.\n * Returns a SmartRowArray which extends Array with a toJSON() helper method.\n * @param options - Filter options\n */\n getRows: (options?: { filter?: Record<string, any>, exact?: boolean }) => Promise<SmartRowArray>;\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 batchInfo?: {\n startIndex: number;\n endIndex: number;\n size: number;\n };\n }) => T | Promise<T>,\n options?: {\n pagination?: PaginationStrategy;\n dedupeStrategy?: DedupeStrategy;\n maxIterations?: number;\n batchSize?: number;\n getIsFirst?: (context: { index: number }) => boolean;\n getIsLast?: (context: { index: number, paginationResult: boolean }) => boolean;\n beforeFirst?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n afterLast?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;\n }\n ) => Promise<T[]>;\n\n /**\n * Generate an AI-friendly configuration prompt for debugging.\n * Outputs table HTML and TypeScript definitions to help AI assistants generate config.\n */\n generateConfigPrompt: (options?: PromptOptions) => Promise<void>;\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";
|
package/dist/typeContext.js
CHANGED
|
@@ -275,6 +275,23 @@ export interface FillOptions {
|
|
|
275
275
|
inputMappers?: Record<string, (cell: Locator) => Locator>;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Options for generateConfigPrompt
|
|
280
|
+
*/
|
|
281
|
+
export interface PromptOptions {
|
|
282
|
+
/**
|
|
283
|
+
* Output Strategy:
|
|
284
|
+
* - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).
|
|
285
|
+
* - 'console': Standard console logs (Default).
|
|
286
|
+
*/
|
|
287
|
+
output?: 'console' | 'error';
|
|
288
|
+
/**
|
|
289
|
+
* Include TypeScript type definitions in the prompt
|
|
290
|
+
* @default true
|
|
291
|
+
*/
|
|
292
|
+
includeTypes?: boolean;
|
|
293
|
+
}
|
|
294
|
+
|
|
278
295
|
export interface TableResult<T = any> {
|
|
279
296
|
/**
|
|
280
297
|
* Initializes the table by resolving headers. Must be called before using sync methods.
|
|
@@ -339,13 +356,12 @@ export interface TableResult<T = any> {
|
|
|
339
356
|
scrollToColumn: (columnName: string) => Promise<void>;
|
|
340
357
|
|
|
341
358
|
/**
|
|
342
|
-
*
|
|
359
|
+
* Gets all rows on the current page only (does not paginate).
|
|
343
360
|
* Auto-initializes the table if not already initialized.
|
|
344
|
-
*
|
|
361
|
+
* Returns a SmartRowArray which extends Array with a toJSON() helper method.
|
|
362
|
+
* @param options - Filter options
|
|
345
363
|
*/
|
|
346
|
-
getRows:
|
|
347
|
-
options?: { filter?: Record<string, any>, exact?: boolean } & R
|
|
348
|
-
) => Promise<R['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;
|
|
364
|
+
getRows: (options?: { filter?: Record<string, any>, exact?: boolean }) => Promise<SmartRowArray>;
|
|
349
365
|
|
|
350
366
|
/**
|
|
351
367
|
* Resets the table state (clears cache, flags) and invokes the onReset strategy.
|
|
@@ -410,6 +426,12 @@ export interface TableResult<T = any> {
|
|
|
410
426
|
afterLast?: (context: { index: number, rows: SmartRow[], allData: any[] }) => void | Promise<void>;
|
|
411
427
|
}
|
|
412
428
|
) => Promise<T[]>;
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Generate an AI-friendly configuration prompt for debugging.
|
|
432
|
+
* Outputs table HTML and TypeScript definitions to help AI assistants generate config.
|
|
433
|
+
*/
|
|
434
|
+
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
|
413
435
|
}
|
|
414
436
|
|
|
415
437
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Locator, Page } from '@playwright/test';
|
|
2
|
+
import type { SmartRowArray } from './utils/smartRowArray';
|
|
2
3
|
/**
|
|
3
4
|
* Flexible selector type - can be a CSS string, function returning a Locator, or Locator itself.
|
|
4
5
|
* @example
|
|
@@ -259,6 +260,22 @@ export interface FillOptions {
|
|
|
259
260
|
*/
|
|
260
261
|
inputMappers?: Record<string, (cell: Locator) => Locator>;
|
|
261
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Options for generateConfigPrompt
|
|
265
|
+
*/
|
|
266
|
+
export interface PromptOptions {
|
|
267
|
+
/**
|
|
268
|
+
* Output Strategy:
|
|
269
|
+
* - 'error': Throws an error with the prompt (useful for platforms that capture error output cleanly).
|
|
270
|
+
* - 'console': Standard console logs (Default).
|
|
271
|
+
*/
|
|
272
|
+
output?: 'console' | 'error';
|
|
273
|
+
/**
|
|
274
|
+
* Include TypeScript type definitions in the prompt
|
|
275
|
+
* @default true
|
|
276
|
+
*/
|
|
277
|
+
includeTypes?: boolean;
|
|
278
|
+
}
|
|
262
279
|
export interface TableResult<T = any> {
|
|
263
280
|
/**
|
|
264
281
|
* Initializes the table by resolving headers. Must be called before using sync methods.
|
|
@@ -317,16 +334,15 @@ export interface TableResult<T = any> {
|
|
|
317
334
|
*/
|
|
318
335
|
scrollToColumn: (columnName: string) => Promise<void>;
|
|
319
336
|
/**
|
|
320
|
-
*
|
|
337
|
+
* Gets all rows on the current page only (does not paginate).
|
|
321
338
|
* Auto-initializes the table if not already initialized.
|
|
322
|
-
*
|
|
339
|
+
* Returns a SmartRowArray which extends Array with a toJSON() helper method.
|
|
340
|
+
* @param options - Filter options
|
|
323
341
|
*/
|
|
324
|
-
getRows:
|
|
325
|
-
asJSON?: boolean;
|
|
326
|
-
}>(options?: {
|
|
342
|
+
getRows: (options?: {
|
|
327
343
|
filter?: Record<string, any>;
|
|
328
344
|
exact?: boolean;
|
|
329
|
-
}
|
|
345
|
+
}) => Promise<SmartRowArray>;
|
|
330
346
|
/**
|
|
331
347
|
* Resets the table state (clears cache, flags) and invokes the onReset strategy.
|
|
332
348
|
*/
|
|
@@ -399,6 +415,11 @@ export interface TableResult<T = any> {
|
|
|
399
415
|
allData: any[];
|
|
400
416
|
}) => void | Promise<void>;
|
|
401
417
|
}) => Promise<T[]>;
|
|
418
|
+
/**
|
|
419
|
+
* Generate an AI-friendly configuration prompt for debugging.
|
|
420
|
+
* Outputs table HTML and TypeScript definitions to help AI assistants generate config.
|
|
421
|
+
*/
|
|
422
|
+
generateConfigPrompt: (options?: PromptOptions) => Promise<void>;
|
|
402
423
|
}
|
|
403
424
|
/**
|
|
404
425
|
* Restricted table result that excludes methods that shouldn't be called during iteration.
|
package/dist/useTable.js
CHANGED
|
@@ -27,6 +27,7 @@ const strategies_1 = require("./strategies");
|
|
|
27
27
|
Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
|
|
28
28
|
const validation_1 = require("./strategies/validation");
|
|
29
29
|
const debugUtils_1 = require("./utils/debugUtils");
|
|
30
|
+
const smartRowArray_1 = require("./utils/smartRowArray");
|
|
30
31
|
/**
|
|
31
32
|
* Main hook to interact with a table.
|
|
32
33
|
*/
|
|
@@ -196,18 +197,6 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
196
197
|
return null;
|
|
197
198
|
}
|
|
198
199
|
});
|
|
199
|
-
const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
|
|
200
|
-
// ... same logic ...
|
|
201
|
-
const { output = 'console', includeTypes = true } = options;
|
|
202
|
-
let finalPrompt = content;
|
|
203
|
-
if (includeTypes)
|
|
204
|
-
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
|
|
205
|
-
if (output === 'error') {
|
|
206
|
-
console.log(`ā ļø Throwing error to display [${promptName}] cleanly...`);
|
|
207
|
-
throw new Error(finalPrompt);
|
|
208
|
-
}
|
|
209
|
-
console.log(finalPrompt);
|
|
210
|
-
});
|
|
211
200
|
const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
212
201
|
return loc.evaluate((el) => {
|
|
213
202
|
const clone = el.cloneNode(true);
|
|
@@ -230,6 +219,18 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
230
219
|
return clone.outerHTML;
|
|
231
220
|
});
|
|
232
221
|
});
|
|
222
|
+
const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
|
|
223
|
+
const { output = 'console', includeTypes = true } = options;
|
|
224
|
+
let finalPrompt = content;
|
|
225
|
+
if (includeTypes) {
|
|
226
|
+
finalPrompt += `\n\nš Useful TypeScript Definitions š\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
|
|
227
|
+
}
|
|
228
|
+
if (output === 'error') {
|
|
229
|
+
console.log(`ā ļø Throwing error to display [${promptName}] cleanly...`);
|
|
230
|
+
throw new Error(finalPrompt);
|
|
231
|
+
}
|
|
232
|
+
console.log(finalPrompt);
|
|
233
|
+
});
|
|
233
234
|
const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
234
235
|
if (!_isInitialized) {
|
|
235
236
|
yield _getMap();
|
|
@@ -362,10 +363,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
362
363
|
}
|
|
363
364
|
const rows = yield rowLocators.all();
|
|
364
365
|
const smartRows = rows.map((loc, i) => _makeSmart(loc, _headerMap, i));
|
|
365
|
-
|
|
366
|
-
return Promise.all(smartRows.map(r => r.toJSON()));
|
|
367
|
-
}
|
|
368
|
-
return smartRows;
|
|
366
|
+
return (0, smartRowArray_1.createSmartRowArray)(smartRows);
|
|
369
367
|
}),
|
|
370
368
|
findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
371
369
|
var _a, _b, _c, _d;
|
|
@@ -445,6 +443,7 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
445
443
|
sorting: result.sorting,
|
|
446
444
|
scrollToColumn: result.scrollToColumn,
|
|
447
445
|
revalidate: result.revalidate,
|
|
446
|
+
generateConfigPrompt: result.generateConfigPrompt,
|
|
448
447
|
};
|
|
449
448
|
const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
|
|
450
449
|
const getIsLast = (_c = options === null || options === void 0 ? void 0 : options.getIsLast) !== null && _c !== void 0 ? _c : (() => false);
|
|
@@ -571,6 +570,12 @@ const useTable = (rootLocator, configOptions = {}) => {
|
|
|
571
570
|
log(`iterateThroughTable completed after ${index + 1} iterations, collected ${allData.length} items`);
|
|
572
571
|
return allData;
|
|
573
572
|
}),
|
|
573
|
+
generateConfigPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
574
|
+
const html = yield _getCleanHtml(rootLocator);
|
|
575
|
+
const separator = "=".repeat(50);
|
|
576
|
+
const content = `\n${separator}\nš¤ COPY INTO GEMINI/ChatGPT š¤\n${separator}\nI am using 'playwright-smart-table'.\nTarget Table Locator: ${rootLocator.toString()}\nGenerate config for:\n\`\`\`html\n${html.substring(0, 10000)} ...\n\`\`\`\n${separator}\n`;
|
|
577
|
+
yield _handlePrompt('Smart Table Config', content, options);
|
|
578
|
+
}),
|
|
574
579
|
};
|
|
575
580
|
finalTable = result;
|
|
576
581
|
return result;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SmartRow } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Extended array type with a toJSON helper method
|
|
4
|
+
*/
|
|
5
|
+
export interface SmartRowArray<T = any> extends Array<SmartRow<T>> {
|
|
6
|
+
/**
|
|
7
|
+
* Converts all rows to JSON objects
|
|
8
|
+
*/
|
|
9
|
+
toJSON(): Promise<T[]>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Wraps a SmartRow array with a convenient toJSON() method
|
|
13
|
+
*/
|
|
14
|
+
export declare function createSmartRowArray<T>(rows: SmartRow<T>[]): SmartRowArray<T>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createSmartRowArray = createSmartRowArray;
|
|
13
|
+
/**
|
|
14
|
+
* Wraps a SmartRow array with a convenient toJSON() method
|
|
15
|
+
*/
|
|
16
|
+
function createSmartRowArray(rows) {
|
|
17
|
+
const arr = rows;
|
|
18
|
+
arr.toJSON = () => __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
return Promise.all(rows.map(r => r.toJSON()));
|
|
20
|
+
});
|
|
21
|
+
return arr;
|
|
22
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rickcedwhat/playwright-smart-table",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.4.0",
|
|
4
|
+
"description": "Smart, column-aware table interactions for Playwright",
|
|
5
|
+
"author": "Cedrick Catalan",
|
|
6
|
+
"license": "MIT",
|
|
5
7
|
"repository": {
|
|
6
8
|
"type": "git",
|
|
7
|
-
"url": "
|
|
9
|
+
"url": "https://github.com/rickcedwhat/playwright-smart-table.git"
|
|
8
10
|
},
|
|
9
11
|
"main": "dist/index.js",
|
|
10
12
|
"types": "dist/index.d.ts",
|
|
@@ -14,7 +16,11 @@
|
|
|
14
16
|
"scripts": {
|
|
15
17
|
"generate-types": "node scripts/embed-types.mjs",
|
|
16
18
|
"generate-docs": "node scripts/generate-readme.mjs",
|
|
17
|
-
"
|
|
19
|
+
"generate-all-api-docs": "node scripts/generate-all-api-docs.mjs",
|
|
20
|
+
"update-all-api-signatures": "node scripts/update-all-api-signatures.mjs",
|
|
21
|
+
"docs:dev": "vitepress dev docs",
|
|
22
|
+
"docs:build": "vitepress build docs",
|
|
23
|
+
"build": "npm run generate-types && npm run generate-docs && npm run generate-all-api-docs && npm run update-all-api-signatures && tsc",
|
|
18
24
|
"prepublishOnly": "npm run build",
|
|
19
25
|
"test": "npx playwright test",
|
|
20
26
|
"test:compatibility": "npx playwright test compatibility",
|
|
@@ -26,21 +32,17 @@
|
|
|
26
32
|
"table",
|
|
27
33
|
"automation"
|
|
28
34
|
],
|
|
29
|
-
"author": "",
|
|
30
|
-
"license": "ISC",
|
|
31
35
|
"peerDependencies": {
|
|
32
|
-
"@playwright/test": "
|
|
33
|
-
},
|
|
34
|
-
"peerDependenciesMeta": {
|
|
35
|
-
"@playwright/test": {
|
|
36
|
-
"optional": true
|
|
37
|
-
}
|
|
36
|
+
"@playwright/test": "^1.40.0"
|
|
38
37
|
},
|
|
39
38
|
"devDependencies": {
|
|
40
|
-
"@playwright/test": "^1.
|
|
41
|
-
"@types/node": "^
|
|
42
|
-
"
|
|
43
|
-
"typescript": "^5.
|
|
44
|
-
"
|
|
39
|
+
"@playwright/test": "^1.49.1",
|
|
40
|
+
"@types/node": "^22.10.5",
|
|
41
|
+
"husky": "^9.1.7",
|
|
42
|
+
"typescript": "^5.7.2",
|
|
43
|
+
"vitepress": "^1.6.4"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"string-similarity": "^4.0.4"
|
|
45
47
|
}
|
|
46
48
|
}
|