@rickcedwhat/playwright-smart-table 2.0.2 → 2.0.6

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.
@@ -15,9 +15,14 @@ export declare const TableStrategies: {
15
15
  */
16
16
  clickLoadMore: (buttonSelector: Selector, timeout?: number) => PaginationStrategy;
17
17
  /**
18
- * Strategy: Scrolls to the bottom of the table and waits for more rows to appear.
18
+ * Strategy: Scrolls a specific container (or the window) to the bottom.
19
19
  * Best for: Infinite Scroll grids (Ag-Grid, Virtual Lists)
20
- * * @param timeout - Wait timeout (default: 5000ms)
20
+ * * @param options.timeout - Wait timeout (default: 5000ms)
21
+ * @param options.scrollerSelector - (Optional) Selector for the scrollable container.
22
+ * If omitted, tries to scroll the table root.
21
23
  */
22
- infiniteScroll: (timeout?: number) => PaginationStrategy;
24
+ infiniteScroll: (options?: {
25
+ timeout?: number;
26
+ scrollerSelector?: Selector;
27
+ }) => PaginationStrategy;
23
28
  };
@@ -74,24 +74,37 @@ exports.TableStrategies = {
74
74
  });
75
75
  },
76
76
  /**
77
- * Strategy: Scrolls to the bottom of the table and waits for more rows to appear.
77
+ * Strategy: Scrolls a specific container (or the window) to the bottom.
78
78
  * Best for: Infinite Scroll grids (Ag-Grid, Virtual Lists)
79
- * * @param timeout - Wait timeout (default: 5000ms)
79
+ * * @param options.timeout - Wait timeout (default: 5000ms)
80
+ * @param options.scrollerSelector - (Optional) Selector for the scrollable container.
81
+ * If omitted, tries to scroll the table root.
80
82
  */
81
- infiniteScroll: (timeout = 5000) => {
83
+ infiniteScroll: (options) => {
84
+ const { timeout = 5000, scrollerSelector } = options || {};
82
85
  return (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, config, resolve, page }) {
83
86
  const rows = resolve(config.rowSelector, root);
84
87
  const oldCount = yield rows.count();
85
88
  if (oldCount === 0)
86
89
  return false;
87
- // Aggressive Scroll Logic:
88
- // We use expect.poll to RETRY the scroll action if the count hasn't increased.
89
- // This fixes flakiness where the first scroll might be missed by the intersection observer.
90
90
  try {
91
91
  yield test_1.expect.poll(() => __awaiter(void 0, void 0, void 0, function* () {
92
- // 1. Trigger: Scroll the last row into view
93
- yield rows.last().scrollIntoViewIfNeeded();
94
- // 2. Force: Press "End" to help with stubborn window-scrollers
92
+ // 1. Determine target container
93
+ // If user provided a specific scroller (e.g. the div WRAPPING the table), use it.
94
+ // Otherwise, default to the root locator.
95
+ const scroller = scrollerSelector
96
+ ? resolve(scrollerSelector, root)
97
+ : root;
98
+ // 2. Perform the Scroll
99
+ // Method A: DOM Manipulation (Fastest/Most Reliable for containers)
100
+ // We set scrollTop to a huge number to force it to the bottom
101
+ yield scroller.evaluate((el) => {
102
+ el.scrollTop = el.scrollHeight;
103
+ }).catch(() => { }); // Ignore if element doesn't support scrollTop (e.g. it's a window wrapper)
104
+ // Method B: Playwright Native (Fallback)
105
+ // Scroll the last row into view (good for Window scroll)
106
+ yield rows.last().scrollIntoViewIfNeeded().catch(() => { });
107
+ // Method C: Keyboard (Desperation)
95
108
  yield page.keyboard.press('End');
96
109
  // 3. Return count for assertion
97
110
  return rows.count();
@@ -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);
@@ -26,7 +26,10 @@ const useTable = (rootLocator, configOptions = {}) => {
26
26
  if (_headerMap)
27
27
  return _headerMap;
28
28
  if (config.autoScroll) {
29
- yield rootLocator.scrollIntoViewIfNeeded();
29
+ try {
30
+ yield rootLocator.scrollIntoViewIfNeeded({ timeout: 1000 });
31
+ }
32
+ catch (e) { }
30
33
  }
31
34
  const headerLoc = resolve(config.headerSelector, rootLocator);
32
35
  try {
@@ -36,8 +39,8 @@ const useTable = (rootLocator, configOptions = {}) => {
36
39
  const texts = yield headerLoc.allInnerTexts();
37
40
  _headerMap = new Map(texts.map((t, i) => {
38
41
  let text = t.trim() || `__col_${i}`;
39
- if (config.headerTransformer)
40
- text = config.headerTransformer(text, i);
42
+ // Safe call: config.headerTransformer is always defined now
43
+ text = config.headerTransformer(text, i);
41
44
  return [text, i];
42
45
  }));
43
46
  return _headerMap;
@@ -96,7 +99,7 @@ const useTable = (rootLocator, configOptions = {}) => {
96
99
  throw new Error(`Strict Mode Violation: Found ${count} rows matching ${JSON.stringify(filters)}.`);
97
100
  if (count === 1)
98
101
  return matchedRows.first();
99
- if (config.pagination && currentPage < effectiveMaxPages) {
102
+ if (currentPage < effectiveMaxPages) {
100
103
  const context = {
101
104
  root: rootLocator,
102
105
  config: config,
@@ -113,27 +116,33 @@ const useTable = (rootLocator, configOptions = {}) => {
113
116
  }
114
117
  });
115
118
  const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
116
- const { output = 'console', includeTypes = true } = options;
119
+ const { output = 'console', includeTypes = true, testInfo } = options;
117
120
  let finalPrompt = content;
118
121
  if (includeTypes) {
119
122
  finalPrompt += `\n\nšŸ‘‡ Useful TypeScript Definitions šŸ‘‡\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
120
123
  }
121
- if (output === 'console') {
122
- console.log(finalPrompt);
124
+ if (output === 'error') {
125
+ console.log(`āš ļø Throwing error to display [${promptName}] cleanly...`);
126
+ throw new Error(finalPrompt);
123
127
  }
124
- else if (output === 'report') {
125
- if (test_1.test.info()) {
126
- yield test_1.test.info().attach(promptName, {
127
- body: finalPrompt,
128
- contentType: 'text/markdown'
129
- });
130
- console.log(`āœ… Attached '${promptName}' to Playwright Report.`);
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;
131
140
  }
132
141
  else {
133
- console.warn('āš ļø Cannot attach to report: No active test info found. Logging to console instead.');
134
- console.log(finalPrompt);
142
+ console.warn(`āš ļø [${promptName}] Cannot attach to report (Context unavailable). Logging to console instead.`);
135
143
  }
136
144
  }
145
+ console.log(finalPrompt);
137
146
  });
138
147
  return {
139
148
  getHeaders: () => __awaiter(void 0, void 0, void 0, function* () { return Array.from((yield _getMap()).keys()); }),
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "2.0.2",
3
+ "version": "2.0.6",
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": [