@rickcedwhat/playwright-smart-table 2.0.1 → 2.0.5

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