@rickcedwhat/playwright-smart-table 3.0.0 → 3.1.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 CHANGED
@@ -91,8 +91,8 @@ await table.init();
91
91
  // ✅ Verify Colleen is NOT visible initially
92
92
  await expect(page.getByText("Colleen Hurst")).not.toBeVisible();
93
93
 
94
- // Use getByRowAcrossPages for pagination
95
- await expect(await table.getByRowAcrossPages({ Name: "Colleen Hurst" })).toBeVisible();
94
+ // Use searchForRow for pagination
95
+ await expect(await table.searchForRow({ Name: "Colleen Hurst" })).toBeVisible();
96
96
  // NOTE: We're now on the page where Colleen Hurst exists (typically Page 2)
97
97
  ```
98
98
  <!-- /embed: pagination -->
@@ -126,7 +126,7 @@ If your tests navigate deep into a paginated table, use `.reset()` to return to
126
126
  // Example from: https://datatables.net/examples/data_sources/dom
127
127
  // Navigate deep into the table by searching for a row on a later page
128
128
  try {
129
- await table.getByRowAcrossPages({ Name: 'Angelica Ramos' });
129
+ await table.searchForRow({ Name: 'Angelica Ramos' });
130
130
  } catch (e) {}
131
131
 
132
132
  // Reset internal state (and potentially UI) to initial page
@@ -256,7 +256,7 @@ const currentPageRow = table.getByRow({ "Last name": "Melisandre" });
256
256
  await expect(currentPageRow).not.toBeVisible();
257
257
 
258
258
  // Then find it across pages
259
- const row = await table.getByRowAcrossPages({ "Last name": "Melisandre" });
259
+ const row = await table.searchForRow({ "Last name": "Melisandre" });
260
260
  const actionsCell = row.getCell('Actions');
261
261
  await actionsCell.getByLabel("Select row").click();
262
262
  ```
@@ -328,8 +328,9 @@ await expect(table.getByRow({ Name: "Ghost User" })).not.toBeVisible();
328
328
  Get row data as JSON:
329
329
  <!-- embed: get-by-row-json -->
330
330
  ```typescript
331
- // Get row data directly as JSON object
332
- const data = await table.getByRow({ Name: 'Airi Satou' }, { asJSON: true });
331
+ // Get row data as JSON object
332
+ const row = table.getByRow({ Name: 'Airi Satou' });
333
+ const data = await row.toJSON();
333
334
  // Returns: { Name: "Airi Satou", Position: "Accountant", Office: "Tokyo", ... }
334
335
 
335
336
  expect(data).toHaveProperty('Name', 'Airi Satou');
@@ -3,4 +3,4 @@
3
3
  * This file is generated by scripts/embed-types.js
4
4
  * It contains the raw text of types.ts to provide context for LLM prompts.
5
5
  */
6
- export declare const TYPE_CONTEXT = "\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\nexport type SmartRow = Locator & {\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n /**\n * Fills the row with data. Automatically detects input types (text input, select, checkbox, etc.).\n */\n smartFill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;\n};\n\nexport type StrategyContext = TableContext;\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 interface TableConfig {\n rowSelector?: Selector;\n headerSelector?: Selector;\n cellSelector?: Selector;\n pagination?: PaginationStrategy;\n sorting?: SortingStrategy;\n maxPages?: number;\n /**\n * Hook to rename columns dynamically.\n * * @param args.text - The default innerText of the header.\n * @param args.index - The column index.\n * @param args.locator - The specific header cell locator.\n */\n headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n autoScroll?: boolean;\n /**\n * Enable debug mode to log internal state to console.\n */\n debug?: boolean;\n /**\n * Strategy to reset the table to the initial page.\n * Called when table.reset() is invoked.\n */\n onReset?: (context: TableContext) => Promise<void>;\n}\n\n/**\n * Represents the final, resolved table configuration after default values have been applied.\n * All optional properties from TableConfig are now required, except for `sorting`.\n */\nexport type FinalTableConfig = Required<Omit<TableConfig, 'sorting'>> & {\n sorting?: SortingStrategy;\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 * Columns not specified here will use auto-detection.\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: <T extends { asJSON?: boolean }>(\n filters: Record<string, string | RegExp | number>, \n options?: { exact?: boolean } & T\n ) => T['asJSON'] extends true ? Promise<Record<string, string>> : SmartRow;\n\n /**\n * Finds a row across multiple pages using pagination. Auto-initializes if needed.\n */\n getByRowAcrossPages: <T extends { asJSON?: boolean }>(\n filters: Record<string, string | RegExp | number>, \n options?: { exact?: boolean, maxPages?: number } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;\n\n getAllRows: <T extends { asJSON?: boolean }>(\n options?: { filter?: Record<string, any>, exact?: boolean } & T\n ) => Promise<T['asJSON'] extends true ? Record<string, string>[] : SmartRow[]>;\n\n generateConfigPrompt: (options?: PromptOptions) => Promise<void>;\n generateStrategyPrompt: (options?: PromptOptions) => Promise<void>;\n\n /**\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, 'getByRowAcrossPages' | 'iterateThroughTable' | 'reset'>;\n";
6
+ export declare const TYPE_CONTEXT = "\nexport type Selector = string | ((root: Locator | Page) => Locator);\n\nexport type SmartRow = Locator & {\n getCell(column: string): Locator;\n toJSON(): Promise<Record<string, string>>;\n /**\n * Fills the row with data. Automatically detects input types (text input, select, checkbox, etc.).\n */\n smartFill: (data: Record<string, any>, options?: FillOptions) => Promise<void>;\n};\n\nexport type StrategyContext = TableContext;\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 interface TableConfig {\n rowSelector?: Selector;\n headerSelector?: Selector;\n cellSelector?: Selector;\n pagination?: PaginationStrategy;\n sorting?: SortingStrategy;\n maxPages?: number;\n /**\n * Hook to rename columns dynamically.\n * * @param args.text - The default innerText of the header.\n * @param args.index - The column index.\n * @param args.locator - The specific header cell locator.\n */\n headerTransformer?: (args: { text: string, index: number, locator: Locator }) => string | Promise<string>;\n autoScroll?: boolean;\n /**\n * Enable debug mode to log internal state to console.\n */\n debug?: boolean;\n /**\n * Strategy to reset the table to the initial page.\n * Called when table.reset() is invoked.\n */\n onReset?: (context: TableContext) => Promise<void>;\n}\n\n/**\n * Represents the final, resolved table configuration after default values have been applied.\n * All optional properties from TableConfig are now required, except for `sorting`.\n */\nexport type FinalTableConfig = Required<Omit<TableConfig, 'sorting'>> & {\n sorting?: SortingStrategy;\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 * Columns not specified here will use auto-detection.\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 filters: Record<string, string | RegExp | number>, \n options?: { exact?: boolean }\n ) => SmartRow;\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 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'>;\n";
@@ -120,18 +120,19 @@ export interface TableResult {
120
120
  * Finds a row on the current page only. Returns immediately (sync).
121
121
  * Throws error if table is not initialized.
122
122
  */
123
- getByRow: <T extends { asJSON?: boolean }>(
123
+ getByRow: (
124
124
  filters: Record<string, string | RegExp | number>,
125
- options?: { exact?: boolean } & T
126
- ) => T['asJSON'] extends true ? Promise<Record<string, string>> : SmartRow;
125
+ options?: { exact?: boolean }
126
+ ) => SmartRow;
127
127
 
128
128
  /**
129
- * Finds a row across multiple pages using pagination. Auto-initializes if needed.
129
+ * Searches for a row across all available data using the configured strategy (pagination, scroll, etc.).
130
+ * Auto-initializes if needed.
130
131
  */
131
- getByRowAcrossPages: <T extends { asJSON?: boolean }>(
132
+ searchForRow: (
132
133
  filters: Record<string, string | RegExp | number>,
133
- options?: { exact?: boolean, maxPages?: number } & T
134
- ) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
134
+ options?: { exact?: boolean, maxPages?: number }
135
+ ) => Promise<SmartRow>;
135
136
 
136
137
  getAllRows: <T extends { asJSON?: boolean }>(
137
138
  options?: { filter?: Record<string, any>, exact?: boolean } & T
@@ -196,5 +197,5 @@ export interface TableResult {
196
197
  /**
197
198
  * Restricted table result that excludes methods that shouldn't be called during iteration.
198
199
  */
199
- export type RestrictedTableResult = Omit<TableResult, 'getByRowAcrossPages' | 'iterateThroughTable' | 'reset'>;
200
+ export type RestrictedTableResult = Omit<TableResult, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
200
201
  `;
package/dist/types.d.ts CHANGED
@@ -104,20 +104,17 @@ export interface TableResult {
104
104
  * Finds a row on the current page only. Returns immediately (sync).
105
105
  * Throws error if table is not initialized.
106
106
  */
107
- getByRow: <T extends {
108
- asJSON?: boolean;
109
- }>(filters: Record<string, string | RegExp | number>, options?: {
107
+ getByRow: (filters: Record<string, string | RegExp | number>, options?: {
110
108
  exact?: boolean;
111
- } & T) => T['asJSON'] extends true ? Promise<Record<string, string>> : SmartRow;
109
+ }) => SmartRow;
112
110
  /**
113
- * Finds a row across multiple pages using pagination. Auto-initializes if needed.
111
+ * Searches for a row across all available data using the configured strategy (pagination, scroll, etc.).
112
+ * Auto-initializes if needed.
114
113
  */
115
- getByRowAcrossPages: <T extends {
116
- asJSON?: boolean;
117
- }>(filters: Record<string, string | RegExp | number>, options?: {
114
+ searchForRow: (filters: Record<string, string | RegExp | number>, options?: {
118
115
  exact?: boolean;
119
116
  maxPages?: number;
120
- } & T) => Promise<T['asJSON'] extends true ? Record<string, string> : SmartRow>;
117
+ }) => Promise<SmartRow>;
121
118
  getAllRows: <T extends {
122
119
  asJSON?: boolean;
123
120
  }>(options?: {
@@ -191,4 +188,4 @@ export interface TableResult {
191
188
  /**
192
189
  * Restricted table result that excludes methods that shouldn't be called during iteration.
193
190
  */
194
- export type RestrictedTableResult = Omit<TableResult, 'getByRowAcrossPages' | 'iterateThroughTable' | 'reset'>;
191
+ export type RestrictedTableResult = Omit<TableResult, 'searchForRow' | 'iterateThroughTable' | 'reset'>;
package/dist/useTable.js CHANGED
@@ -420,13 +420,9 @@ const useTable = (rootLocator, configOptions = {}) => {
420
420
  const matchedRows = _applyFilters(allRows, filters, _headerMap, (options === null || options === void 0 ? void 0 : options.exact) || false);
421
421
  // Return first match (or sentinel) - lazy, doesn't check existence
422
422
  const rowLocator = matchedRows.first();
423
- const smartRow = _makeSmart(rowLocator, _headerMap);
424
- if (options === null || options === void 0 ? void 0 : options.asJSON) {
425
- return smartRow.toJSON();
426
- }
427
- return smartRow;
423
+ return _makeSmart(rowLocator, _headerMap);
428
424
  },
429
- getByRowAcrossPages: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
425
+ searchForRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
430
426
  // Auto-init if needed (async methods can auto-init)
431
427
  yield _ensureInitialized();
432
428
  // Full pagination logic (existing _findRowLocator logic)
@@ -434,11 +430,7 @@ const useTable = (rootLocator, configOptions = {}) => {
434
430
  if (!row) {
435
431
  row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
436
432
  }
437
- const smartRow = _makeSmart(row, _headerMap);
438
- if (options === null || options === void 0 ? void 0 : options.asJSON) {
439
- return smartRow.toJSON();
440
- }
441
- return smartRow;
433
+ return _makeSmart(row, _headerMap);
442
434
  }),
443
435
  getAllRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
444
436
  // Auto-init if needed (async methods can auto-init)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "A smart table utility for Playwright with built-in pagination strategies that are fully extensible.",
5
5
  "repository": {
6
6
  "type": "git",