@rickcedwhat/playwright-smart-table 5.3.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +78 -957
  2. package/dist/examples/glide-strategies/columns.d.ts +13 -0
  3. package/dist/examples/glide-strategies/columns.js +43 -0
  4. package/dist/examples/glide-strategies/headers.d.ts +9 -0
  5. package/dist/examples/glide-strategies/headers.js +68 -0
  6. package/dist/src/filterEngine.d.ts +11 -0
  7. package/dist/src/filterEngine.js +39 -0
  8. package/dist/src/index.d.ts +2 -0
  9. package/dist/src/index.js +18 -0
  10. package/dist/src/plugins.d.ts +32 -0
  11. package/dist/src/plugins.js +13 -0
  12. package/dist/src/smartRow.d.ts +7 -0
  13. package/dist/src/smartRow.js +160 -0
  14. package/dist/src/strategies/columns.d.ts +18 -0
  15. package/dist/src/strategies/columns.js +21 -0
  16. package/dist/src/strategies/dedupe.d.ts +9 -0
  17. package/dist/src/strategies/dedupe.js +27 -0
  18. package/dist/src/strategies/fill.d.ts +7 -0
  19. package/dist/src/strategies/fill.js +88 -0
  20. package/dist/src/strategies/glide.d.ts +29 -0
  21. package/dist/src/strategies/glide.js +98 -0
  22. package/dist/src/strategies/headers.d.ts +13 -0
  23. package/dist/src/strategies/headers.js +30 -0
  24. package/dist/src/strategies/index.d.ts +54 -0
  25. package/dist/src/strategies/index.js +43 -0
  26. package/dist/src/strategies/loading.d.ts +48 -0
  27. package/dist/src/strategies/loading.js +82 -0
  28. package/dist/src/strategies/pagination.d.ts +33 -0
  29. package/dist/src/strategies/pagination.js +79 -0
  30. package/dist/src/strategies/rdg.d.ts +25 -0
  31. package/dist/src/strategies/rdg.js +100 -0
  32. package/dist/src/strategies/resolution.d.ts +22 -0
  33. package/dist/src/strategies/resolution.js +30 -0
  34. package/dist/src/strategies/sorting.d.ts +12 -0
  35. package/dist/src/strategies/sorting.js +68 -0
  36. package/dist/src/strategies/stabilization.d.ts +29 -0
  37. package/dist/src/strategies/stabilization.js +91 -0
  38. package/dist/src/strategies/validation.d.ts +22 -0
  39. package/dist/src/strategies/validation.js +54 -0
  40. package/dist/src/strategies/virtualizedPagination.d.ts +32 -0
  41. package/dist/src/strategies/virtualizedPagination.js +80 -0
  42. package/dist/src/typeContext.d.ts +6 -0
  43. package/dist/src/typeContext.js +465 -0
  44. package/dist/src/types.d.ts +458 -0
  45. package/dist/src/types.js +2 -0
  46. package/dist/src/useTable.d.ts +44 -0
  47. package/dist/src/useTable.js +642 -0
  48. package/dist/src/utils/debugUtils.d.ts +17 -0
  49. package/dist/src/utils/debugUtils.js +62 -0
  50. package/dist/src/utils/smartRowArray.d.ts +14 -0
  51. package/dist/src/utils/smartRowArray.js +22 -0
  52. package/dist/src/utils/stringUtils.d.ts +22 -0
  53. package/dist/src/utils/stringUtils.js +73 -0
  54. package/dist/src/utils.d.ts +7 -0
  55. package/dist/src/utils.js +29 -0
  56. package/dist/typeContext.d.ts +1 -1
  57. package/dist/typeContext.js +27 -5
  58. package/dist/types.d.ts +27 -6
  59. package/dist/useTable.js +21 -16
  60. package/dist/utils/smartRowArray.d.ts +14 -0
  61. package/dist/utils/smartRowArray.js +22 -0
  62. package/package.json +16 -20
@@ -0,0 +1,13 @@
1
+ import { StrategyContext } from '../../src/types';
2
+ /**
3
+ * Strategy that clicks into the table to establish focus and then uses the Right Arrow key
4
+ * to navigate to the target CELL (navigates down to the row, then right to the column).
5
+ *
6
+ * Useful for canvas-based grids like Glide where DOM scrolling might not be enough for interaction
7
+ * or where keyboard navigation is the primary way to move focus.
8
+ */
9
+ export declare const keyboardCellNavigation: (context: StrategyContext & {
10
+ column: string;
11
+ index: number;
12
+ rowIndex?: number;
13
+ }) => Promise<void>;
@@ -0,0 +1,43 @@
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.keyboardCellNavigation = void 0;
13
+ /**
14
+ * Strategy that clicks into the table to establish focus and then uses the Right Arrow key
15
+ * to navigate to the target CELL (navigates down to the row, then right to the column).
16
+ *
17
+ * Useful for canvas-based grids like Glide where DOM scrolling might not be enough for interaction
18
+ * or where keyboard navigation is the primary way to move focus.
19
+ */
20
+ const keyboardCellNavigation = (context) => __awaiter(void 0, void 0, void 0, function* () {
21
+ const { root, page, index, rowIndex } = context;
22
+ if (typeof rowIndex !== 'number') {
23
+ throw new Error('Row index is required for keyboard navigation');
24
+ }
25
+ yield root.focus();
26
+ yield page.waitForTimeout(100);
27
+ // Robust Navigation:
28
+ // 1. Jump to Top-Left (Reset) - Sequence for Cross-OS (Mac/Windows)
29
+ yield page.keyboard.press('Control+Home');
30
+ yield page.keyboard.press('Meta+ArrowUp'); // Mac Go-To-Top
31
+ yield page.keyboard.press('Home'); // Ensure start of row
32
+ yield page.waitForTimeout(150);
33
+ // 2. Move Down to Target Row
34
+ for (let i = 0; i < rowIndex; i++) {
35
+ yield page.keyboard.press('ArrowDown');
36
+ }
37
+ // 3. Move Right to Target Column
38
+ for (let i = 0; i < index; i++) {
39
+ yield page.keyboard.press('ArrowRight');
40
+ }
41
+ yield page.waitForTimeout(50);
42
+ });
43
+ exports.keyboardCellNavigation = keyboardCellNavigation;
@@ -0,0 +1,9 @@
1
+ import { StrategyContext } from '../../src/types';
2
+ /**
3
+ * Scans for headers by finding a scrollable container and setting scrollLeft.
4
+ */
5
+ export declare const scrollRightHeader: (context: StrategyContext, options?: {
6
+ limit?: number;
7
+ selector?: string;
8
+ scrollAmount?: number;
9
+ }) => Promise<string[]>;
@@ -0,0 +1,68 @@
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.scrollRightHeader = void 0;
13
+ /**
14
+ * Scans for headers by finding a scrollable container and setting scrollLeft.
15
+ */
16
+ const scrollRightHeader = (context, options) => __awaiter(void 0, void 0, void 0, function* () {
17
+ var _a, _b;
18
+ const { resolve, config, root, page } = context;
19
+ const limit = (_a = options === null || options === void 0 ? void 0 : options.limit) !== null && _a !== void 0 ? _a : 20;
20
+ const scrollAmount = (_b = options === null || options === void 0 ? void 0 : options.scrollAmount) !== null && _b !== void 0 ? _b : 300;
21
+ const collectedHeaders = new Set();
22
+ const getVisible = () => __awaiter(void 0, void 0, void 0, function* () {
23
+ const headerLoc = resolve(config.headerSelector, root);
24
+ const texts = yield headerLoc.allInnerTexts();
25
+ return texts.map(t => t.trim());
26
+ });
27
+ // Initial capture
28
+ let currentHeaders = yield getVisible();
29
+ currentHeaders.forEach(h => collectedHeaders.add(h));
30
+ // Find scroller using JS for better iframe/shadow support
31
+ const scrollerHandle = yield root.evaluateHandle((el, selector) => {
32
+ if (selector && el.matches(selector))
33
+ return el;
34
+ const effectiveSelector = selector || '.dvn-scroller';
35
+ const ancestor = el.closest(effectiveSelector);
36
+ if (ancestor)
37
+ return ancestor;
38
+ return document.querySelector(effectiveSelector);
39
+ }, options === null || options === void 0 ? void 0 : options.selector);
40
+ const isScrollerFound = yield scrollerHandle.evaluate(el => !!el);
41
+ if (isScrollerFound) {
42
+ yield scrollerHandle.evaluate(el => el.scrollLeft = 0);
43
+ yield page.waitForTimeout(200);
44
+ for (let i = 0; i < limit; i++) {
45
+ const sizeBefore = collectedHeaders.size;
46
+ yield scrollerHandle.evaluate((el, amount) => el.scrollLeft += amount, scrollAmount);
47
+ yield page.waitForTimeout(300);
48
+ const newHeaders = yield getVisible();
49
+ newHeaders.forEach(h => collectedHeaders.add(h));
50
+ if (collectedHeaders.size === sizeBefore) {
51
+ yield scrollerHandle.evaluate((el, amount) => el.scrollLeft += amount, scrollAmount);
52
+ yield page.waitForTimeout(300);
53
+ const retryHeaders = yield getVisible();
54
+ retryHeaders.forEach(h => collectedHeaders.add(h));
55
+ if (collectedHeaders.size === sizeBefore)
56
+ break;
57
+ }
58
+ }
59
+ }
60
+ else {
61
+ console.warn("HeaderStrategies.scrollRight: Could not find scroller. Returning visible headers.");
62
+ }
63
+ // Scroll back to start
64
+ yield scrollerHandle.evaluate(el => el.scrollLeft = 0);
65
+ yield page.waitForTimeout(200);
66
+ return Array.from(collectedHeaders);
67
+ });
68
+ exports.scrollRightHeader = scrollRightHeader;
@@ -0,0 +1,11 @@
1
+ import { Locator, Page } from "@playwright/test";
2
+ import { FinalTableConfig } from "./types";
3
+ export declare class FilterEngine {
4
+ private config;
5
+ private resolve;
6
+ constructor(config: FinalTableConfig, resolve: (selector: any, parent: Locator | Page) => Locator);
7
+ /**
8
+ * Applies filters to a set of rows.
9
+ */
10
+ applyFilters(baseRows: Locator, filters: Record<string, string | RegExp | number>, map: Map<string, number>, exact: boolean, page: Page): Locator;
11
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FilterEngine = void 0;
4
+ const stringUtils_1 = require("./utils/stringUtils");
5
+ class FilterEngine {
6
+ constructor(config, resolve) {
7
+ this.config = config;
8
+ this.resolve = resolve;
9
+ }
10
+ /**
11
+ * Applies filters to a set of rows.
12
+ */
13
+ applyFilters(baseRows, filters, map, exact, page) {
14
+ let filtered = baseRows;
15
+ // Iterate through each filter criteria
16
+ for (const [colName, value] of Object.entries(filters)) {
17
+ // Find column index
18
+ const colIndex = map.get(colName);
19
+ // TODO: Use ColumnStrategy for better resolution error handling
20
+ if (colIndex === undefined) {
21
+ throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
22
+ }
23
+ const filterVal = typeof value === 'number' ? String(value) : value;
24
+ // Use strategy if provided (For future: configured filter strategies)
25
+ // But for now, we implement the default logic or use custom if we add it to config later
26
+ // Default Filter Logic
27
+ const cellTemplate = this.resolve(this.config.cellSelector, page);
28
+ // This logic assumes 1:1 row-to-cell mapping based on index.
29
+ // filter({ has: ... }) checks if the row *contains* the matching cell.
30
+ // But we need to be specific about WHICH cell.
31
+ // Locator filtering by `has: locator.nth(index)` works if `locator` search is relative to the row.
32
+ filtered = filtered.filter({
33
+ has: cellTemplate.nth(colIndex).getByText(filterVal, { exact }),
34
+ });
35
+ }
36
+ return filtered;
37
+ }
38
+ }
39
+ exports.FilterEngine = FilterEngine;
@@ -0,0 +1,2 @@
1
+ export * from './useTable';
2
+ export * from './types';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./useTable"), exports);
18
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,32 @@
1
+ export declare const Plugins: {
2
+ RDG: {
3
+ Strategies: {
4
+ header: (context: import("./types").TableContext) => Promise<string[]>;
5
+ getCellLocator: ({ row, columnIndex }: any) => any;
6
+ cellNavigation: ({ root, page, index }: any) => Promise<void>;
7
+ pagination: import("./types").PaginationStrategy;
8
+ };
9
+ };
10
+ Glide: {
11
+ Strategies: {
12
+ fill: import("./types").FillStrategy;
13
+ pagination: import("./types").PaginationStrategy;
14
+ header: (context: import("./types").StrategyContext, options?: {
15
+ limit?: number;
16
+ selector?: string;
17
+ scrollAmount?: number;
18
+ }) => Promise<string[]>;
19
+ cellNavigation: (context: import("./types").StrategyContext & {
20
+ column: string;
21
+ index: number;
22
+ rowIndex?: number;
23
+ }) => Promise<void>;
24
+ getCellLocator: ({ row, columnIndex }: any) => any;
25
+ getActiveCell: ({ page }: any) => Promise<{
26
+ rowIndex: number;
27
+ columnIndex: number;
28
+ locator: any;
29
+ } | null>;
30
+ };
31
+ };
32
+ };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Plugins = void 0;
4
+ const rdg_1 = require("./strategies/rdg");
5
+ const glide_1 = require("./strategies/glide");
6
+ exports.Plugins = {
7
+ RDG: {
8
+ Strategies: rdg_1.RDGStrategies
9
+ },
10
+ Glide: {
11
+ Strategies: glide_1.GlideStrategies
12
+ }
13
+ };
@@ -0,0 +1,7 @@
1
+ import type { Locator, Page } from '@playwright/test';
2
+ import { SmartRow as SmartRowType, FinalTableConfig, TableResult } from './types';
3
+ /**
4
+ * Factory to create a SmartRow by extending a Playwright Locator.
5
+ * We avoid Class/Proxy to ensure full compatibility with Playwright's expect(locator) matchers.
6
+ */
7
+ export declare const createSmartRow: <T = any>(rowLocator: Locator, map: Map<string, number>, rowIndex: number | undefined, config: FinalTableConfig, rootLocator: Locator, resolve: (item: any, parent: Locator | Page) => Locator, table: TableResult<T> | null) => SmartRowType<T>;
@@ -0,0 +1,160 @@
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.createSmartRow = void 0;
13
+ const fill_1 = require("./strategies/fill");
14
+ const stringUtils_1 = require("./utils/stringUtils");
15
+ const debugUtils_1 = require("./utils/debugUtils");
16
+ /**
17
+ * Factory to create a SmartRow by extending a Playwright Locator.
18
+ * We avoid Class/Proxy to ensure full compatibility with Playwright's expect(locator) matchers.
19
+ */
20
+ const createSmartRow = (rowLocator, map, rowIndex, config, rootLocator, resolve, table) => {
21
+ const smart = rowLocator;
22
+ // Attach State
23
+ smart.rowIndex = rowIndex;
24
+ // Attach Methods
25
+ smart.getCell = (colName) => {
26
+ const idx = map.get(colName);
27
+ if (idx === undefined) {
28
+ throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
29
+ }
30
+ if (config.strategies.getCellLocator) {
31
+ return config.strategies.getCellLocator({
32
+ row: rowLocator,
33
+ columnName: colName,
34
+ columnIndex: idx,
35
+ rowIndex: rowIndex,
36
+ page: rootLocator.page()
37
+ });
38
+ }
39
+ return resolve(config.cellSelector, rowLocator).nth(idx);
40
+ };
41
+ smart.toJSON = (options) => __awaiter(void 0, void 0, void 0, function* () {
42
+ const result = {};
43
+ const page = rootLocator.page();
44
+ for (const [col, idx] of map.entries()) {
45
+ if ((options === null || options === void 0 ? void 0 : options.columns) && !options.columns.includes(col)) {
46
+ continue;
47
+ }
48
+ // Get the cell locator
49
+ const cell = config.strategies.getCellLocator
50
+ ? config.strategies.getCellLocator({
51
+ row: rowLocator,
52
+ columnName: col,
53
+ columnIndex: idx,
54
+ rowIndex: rowIndex,
55
+ page: page
56
+ })
57
+ : resolve(config.cellSelector, rowLocator).nth(idx);
58
+ let targetCell = cell;
59
+ // Check if cell exists
60
+ const count = yield cell.count();
61
+ if (count === 0) {
62
+ // Optimization: Check if we are ALREADY at the target cell
63
+ if (config.strategies.getActiveCell) {
64
+ const active = yield config.strategies.getActiveCell({
65
+ config,
66
+ root: rootLocator,
67
+ page,
68
+ resolve
69
+ });
70
+ if (active && active.rowIndex === rowIndex && active.columnIndex === idx) {
71
+ if (config.debug)
72
+ console.log(`[SmartRow] Already at target cell (r:${active.rowIndex}, c:${active.columnIndex}), skipping navigation.`);
73
+ targetCell = active.locator;
74
+ // Skip navigation and go to reading text
75
+ const text = yield targetCell.innerText();
76
+ result[col] = (text || '').trim();
77
+ continue;
78
+ }
79
+ }
80
+ // Cell doesn't exist - navigate to it
81
+ if (config.debug) {
82
+ console.log(`[SmartRow.toJSON] Cell not found for column "${col}" (index ${idx}), navigating...`);
83
+ }
84
+ yield config.strategies.cellNavigation({
85
+ config: config,
86
+ root: rootLocator,
87
+ page: page,
88
+ resolve: resolve,
89
+ column: col,
90
+ index: idx,
91
+ rowLocator: rowLocator,
92
+ rowIndex: rowIndex
93
+ });
94
+ // Optimization: check if we can get the active cell directly
95
+ if (config.strategies.getActiveCell) {
96
+ const activeCell = yield config.strategies.getActiveCell({
97
+ config,
98
+ root: rootLocator,
99
+ page,
100
+ resolve
101
+ });
102
+ if (activeCell) {
103
+ if (config.debug) {
104
+ console.log(`[SmartRow.toJSON] switching to active cell locator (r:${activeCell.rowIndex}, c:${activeCell.columnIndex})`);
105
+ }
106
+ targetCell = activeCell.locator;
107
+ }
108
+ }
109
+ }
110
+ const text = yield targetCell.innerText();
111
+ result[col] = (text || '').trim();
112
+ }
113
+ return result;
114
+ });
115
+ smart.smartFill = (data, fillOptions) => __awaiter(void 0, void 0, void 0, function* () {
116
+ (0, debugUtils_1.logDebug)(config, 'info', 'Filling row', data);
117
+ for (const [colName, value] of Object.entries(data)) {
118
+ if (value === undefined)
119
+ continue;
120
+ const colIdx = map.get(colName);
121
+ if (colIdx === undefined) {
122
+ throw new Error((0, stringUtils_1.buildColumnNotFoundError)(colName, Array.from(map.keys())));
123
+ }
124
+ yield config.strategies.cellNavigation({
125
+ config: config,
126
+ root: rootLocator,
127
+ page: rootLocator.page(),
128
+ resolve: resolve,
129
+ column: colName,
130
+ index: colIdx,
131
+ rowLocator: rowLocator,
132
+ rowIndex: rowIndex
133
+ });
134
+ const strategy = config.strategies.fill || fill_1.FillStrategies.default;
135
+ (0, debugUtils_1.logDebug)(config, 'verbose', `Filling cell "${colName}" with value`, value);
136
+ yield strategy({
137
+ row: smart,
138
+ columnName: colName,
139
+ value,
140
+ index: rowIndex !== null && rowIndex !== void 0 ? rowIndex : -1,
141
+ page: rowLocator.page(),
142
+ rootLocator,
143
+ table: table,
144
+ fillOptions
145
+ });
146
+ // Delay after filling
147
+ yield (0, debugUtils_1.debugDelay)(config, 'getCell');
148
+ }
149
+ (0, debugUtils_1.logDebug)(config, 'info', 'Row fill complete');
150
+ });
151
+ smart.bringIntoView = () => __awaiter(void 0, void 0, void 0, function* () {
152
+ if (rowIndex === undefined) {
153
+ throw new Error('Cannot bring row into view - row index is unknown. Use getRowByIndex() instead of getRow().');
154
+ }
155
+ // Scroll row into view using Playwright's built-in method
156
+ yield rowLocator.scrollIntoViewIfNeeded();
157
+ });
158
+ return smart;
159
+ };
160
+ exports.createSmartRow = createSmartRow;
@@ -0,0 +1,18 @@
1
+ import { StrategyContext } from '../types';
2
+ /**
3
+ * Defines the contract for a cell navigation strategy.
4
+ * It is responsible for ensuring a specific CELL is visible/focused (navigates to row + column),
5
+ * typically by scrolling or using keyboard navigation.
6
+ */
7
+ export type CellNavigationStrategy = (context: StrategyContext & {
8
+ column: string;
9
+ index: number;
10
+ rowIndex?: number;
11
+ }) => Promise<void>;
12
+ export declare const CellNavigationStrategies: {
13
+ /**
14
+ * Default strategy: Assumes column is accessible or standard scrolling works.
15
+ * No specific action taken other than what Playwright's default locator handling does.
16
+ */
17
+ default: () => Promise<void>;
18
+ };
@@ -0,0 +1,21 @@
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.CellNavigationStrategies = void 0;
13
+ exports.CellNavigationStrategies = {
14
+ /**
15
+ * Default strategy: Assumes column is accessible or standard scrolling works.
16
+ * No specific action taken other than what Playwright's default locator handling does.
17
+ */
18
+ default: () => __awaiter(void 0, void 0, void 0, function* () {
19
+ // No-op
20
+ })
21
+ };
@@ -0,0 +1,9 @@
1
+ import { DedupeStrategy } from '../types';
2
+ export declare const DedupeStrategies: {
3
+ /**
4
+ * Deduplicates rows based on their vertical position (Y coordinate).
5
+ * Useful for virtualized tables where row DOM elements are reused but content changes.
6
+ * @param tolerance Pixel tolerance for position comparison (default: 2)
7
+ */
8
+ byTopPosition: (tolerance?: number) => DedupeStrategy;
9
+ };
@@ -0,0 +1,27 @@
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.DedupeStrategies = void 0;
13
+ exports.DedupeStrategies = {
14
+ /**
15
+ * Deduplicates rows based on their vertical position (Y coordinate).
16
+ * Useful for virtualized tables where row DOM elements are reused but content changes.
17
+ * @param tolerance Pixel tolerance for position comparison (default: 2)
18
+ */
19
+ byTopPosition: (tolerance = 2) => (row) => __awaiter(void 0, void 0, void 0, function* () {
20
+ const box = yield row.boundingBox();
21
+ if (!box)
22
+ return 'unknown';
23
+ // Round to nearest tolerance
24
+ const y = Math.round(box.y / tolerance) * tolerance;
25
+ return `pos_${y}`;
26
+ })
27
+ };
@@ -0,0 +1,7 @@
1
+ import type { FillStrategy } from '../types';
2
+ export declare const FillStrategies: {
3
+ /**
4
+ * Default strategy: Detects input type and fills accordingly (Text, Select, Checkbox, ContentEditable).
5
+ */
6
+ default: ({ row, columnName, value, fillOptions }: Parameters<FillStrategy>[0]) => Promise<void>;
7
+ };
@@ -0,0 +1,88 @@
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.FillStrategies = void 0;
13
+ exports.FillStrategies = {
14
+ /**
15
+ * Default strategy: Detects input type and fills accordingly (Text, Select, Checkbox, ContentEditable).
16
+ */
17
+ default: (_a) => __awaiter(void 0, [_a], void 0, function* ({ row, columnName, value, fillOptions }) {
18
+ var _b;
19
+ const cell = row.getCell(columnName);
20
+ // Use custom input mapper for this column if provided, otherwise auto-detect
21
+ let inputLocator;
22
+ if ((_b = fillOptions === null || fillOptions === void 0 ? void 0 : fillOptions.inputMappers) === null || _b === void 0 ? void 0 : _b[columnName]) {
23
+ inputLocator = fillOptions.inputMappers[columnName](cell);
24
+ }
25
+ else {
26
+ // Auto-detect input type
27
+ // Check for text input
28
+ const textInput = cell.locator('input[type="text"], input:not([type]), textarea').first();
29
+ const textInputCount = yield textInput.count().catch(() => 0);
30
+ // Check for select
31
+ const select = cell.locator('select').first();
32
+ const selectCount = yield select.count().catch(() => 0);
33
+ // Check for checkbox/radio
34
+ const checkbox = cell.locator('input[type="checkbox"], input[type="radio"], [role="checkbox"]').first();
35
+ const checkboxCount = yield checkbox.count().catch(() => 0);
36
+ // Check for contenteditable or div-based inputs
37
+ const contentEditable = cell.locator('[contenteditable="true"]').first();
38
+ const contentEditableCount = yield contentEditable.count().catch(() => 0);
39
+ // Determine which input to use (prioritize by commonality)
40
+ if (textInputCount > 0 && selectCount === 0 && checkboxCount === 0) {
41
+ inputLocator = textInput;
42
+ }
43
+ else if (selectCount > 0) {
44
+ inputLocator = select;
45
+ }
46
+ else if (checkboxCount > 0) {
47
+ inputLocator = checkbox;
48
+ }
49
+ else if (contentEditableCount > 0) {
50
+ inputLocator = contentEditable;
51
+ }
52
+ else if (textInputCount > 0) {
53
+ // Fallback to text input even if others exist
54
+ inputLocator = textInput;
55
+ }
56
+ else {
57
+ // No input found - try to click the cell itself (might trigger an editor)
58
+ inputLocator = cell;
59
+ }
60
+ }
61
+ // Fill based on value type and input type
62
+ const inputTag = yield inputLocator.evaluate((el) => el.tagName.toLowerCase()).catch(() => 'unknown');
63
+ const inputType = yield inputLocator.getAttribute('type').catch(() => null);
64
+ const isContentEditable = yield inputLocator.getAttribute('contenteditable').catch(() => null);
65
+ // console.log(`[SmartTable] Filling "${columnName}" with value "${value}" (input: ${inputTag}, type: ${inputType})`);
66
+ if (inputType === 'checkbox' || inputType === 'radio') {
67
+ // Boolean value for checkbox/radio
68
+ const shouldBeChecked = Boolean(value);
69
+ const isChecked = yield inputLocator.isChecked().catch(() => false);
70
+ if (isChecked !== shouldBeChecked) {
71
+ yield inputLocator.click();
72
+ }
73
+ }
74
+ else if (inputTag === 'select') {
75
+ // Select dropdown
76
+ yield inputLocator.selectOption(String(value));
77
+ }
78
+ else if (isContentEditable === 'true') {
79
+ // Contenteditable div
80
+ yield inputLocator.click();
81
+ yield inputLocator.fill(String(value));
82
+ }
83
+ else {
84
+ // Text input, textarea, or generic
85
+ yield inputLocator.fill(String(value));
86
+ }
87
+ })
88
+ };
@@ -0,0 +1,29 @@
1
+ import { FillStrategy } from '../types';
2
+ export declare const glideFillStrategy: FillStrategy;
3
+ export declare const glidePaginationStrategy: import("../types").PaginationStrategy;
4
+ export declare const glideGetCellLocator: ({ row, columnIndex }: any) => any;
5
+ export declare const glideGetActiveCell: ({ page }: any) => Promise<{
6
+ rowIndex: number;
7
+ columnIndex: number;
8
+ locator: any;
9
+ } | null>;
10
+ export declare const GlideStrategies: {
11
+ fill: FillStrategy;
12
+ pagination: import("../types").PaginationStrategy;
13
+ header: (context: import("../types").StrategyContext, options?: {
14
+ limit?: number;
15
+ selector?: string;
16
+ scrollAmount?: number;
17
+ }) => Promise<string[]>;
18
+ cellNavigation: (context: import("../types").StrategyContext & {
19
+ column: string;
20
+ index: number;
21
+ rowIndex?: number;
22
+ }) => Promise<void>;
23
+ getCellLocator: ({ row, columnIndex }: any) => any;
24
+ getActiveCell: ({ page }: any) => Promise<{
25
+ rowIndex: number;
26
+ columnIndex: number;
27
+ locator: any;
28
+ } | null>;
29
+ };