@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,98 @@
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.GlideStrategies = exports.glideGetActiveCell = exports.glideGetCellLocator = exports.glidePaginationStrategy = exports.glideFillStrategy = void 0;
13
+ const columns_1 = require("../../examples/glide-strategies/columns");
14
+ const headers_1 = require("../../examples/glide-strategies/headers");
15
+ const pagination_1 = require("./pagination");
16
+ const stabilization_1 = require("./stabilization");
17
+ const glideFillStrategy = (_a) => __awaiter(void 0, [_a], void 0, function* ({ value, page }) {
18
+ // Edit Cell
19
+ yield page.keyboard.press('Enter');
20
+ // Wait for editor to appear
21
+ const textarea = page.locator('textarea.gdg-input');
22
+ yield textarea.waitFor({ state: 'visible', timeout: 2000 });
23
+ yield page.keyboard.type(String(value));
24
+ // Wait for textarea value to match what we typed
25
+ yield textarea.evaluate((el, expectedValue) => {
26
+ return new Promise((resolve) => {
27
+ const checkValue = () => {
28
+ if (el.value === expectedValue) {
29
+ resolve();
30
+ }
31
+ else {
32
+ setTimeout(checkValue, 10);
33
+ }
34
+ };
35
+ checkValue();
36
+ });
37
+ }, String(value));
38
+ // Small delay to let the grid process the value
39
+ yield page.waitForTimeout(50);
40
+ yield page.keyboard.press('Enter');
41
+ // Wait for editor to close (commit completed)
42
+ yield textarea.waitFor({ state: 'detached', timeout: 2000 });
43
+ // Wait for accessibility layer to sync with canvas state
44
+ yield page.waitForTimeout(300);
45
+ });
46
+ exports.glideFillStrategy = glideFillStrategy;
47
+ exports.glidePaginationStrategy = pagination_1.PaginationStrategies.infiniteScroll({
48
+ scrollTarget: 'xpath=//ancestor::body//div[contains(@class, "dvn-scroller")]',
49
+ scrollAmount: 500,
50
+ action: 'js-scroll',
51
+ stabilization: stabilization_1.StabilizationStrategies.contentChanged({ timeout: 5000 }),
52
+ timeout: 5000 // Overall timeout
53
+ });
54
+ const glideGetCellLocator = ({ row, columnIndex }) => {
55
+ // Use relative locator to support virtualization (where rowIndex resets or is offsets)
56
+ // The accessibility DOM usually contains 'td' elements with the data.
57
+ return row.locator('td').nth(columnIndex);
58
+ };
59
+ exports.glideGetCellLocator = glideGetCellLocator;
60
+ const glideGetActiveCell = (_a) => __awaiter(void 0, [_a], void 0, function* ({ page }) {
61
+ // Find the focused cell/element
62
+ // Use broad selector for focused element
63
+ const focused = page.locator('*:focus').first();
64
+ if ((yield focused.count()) === 0)
65
+ return null;
66
+ // Debug log
67
+ if (process.env.DEBUG)
68
+ console.log('Found focused element:', yield focused.evaluate((e) => e.outerHTML));
69
+ // Try to extract position from ID if possible
70
+ const id = (yield focused.getAttribute('id')) || '';
71
+ // Expected format: glide-cell-COL-ROW
72
+ const parts = id.split('-');
73
+ let rowIndex = -1;
74
+ let columnIndex = -1;
75
+ if (parts.length >= 4 && parts[0] === 'glide' && parts[1] === 'cell') {
76
+ columnIndex = parseInt(parts[2]) - 1; // 1-based in ID to 0-based
77
+ rowIndex = parseInt(parts[3]);
78
+ }
79
+ else {
80
+ // Fallback: If we can't parse ID, we assume it's the correct cell
81
+ // because we just navigated to it.
82
+ // Returning -1 indices might be confusing but won't stop smartRow from using the locator.
83
+ }
84
+ return {
85
+ rowIndex,
86
+ columnIndex,
87
+ locator: focused
88
+ };
89
+ });
90
+ exports.glideGetActiveCell = glideGetActiveCell;
91
+ exports.GlideStrategies = {
92
+ fill: exports.glideFillStrategy,
93
+ pagination: exports.glidePaginationStrategy,
94
+ header: headers_1.scrollRightHeader,
95
+ cellNavigation: columns_1.keyboardCellNavigation,
96
+ getCellLocator: exports.glideGetCellLocator,
97
+ getActiveCell: exports.glideGetActiveCell
98
+ };
@@ -0,0 +1,13 @@
1
+ import { StrategyContext } from '../types';
2
+ /**
3
+ * Defines the contract for a header retrieval strategy.
4
+ * Returns a list of unique header names found in the table.
5
+ */
6
+ export type HeaderStrategy = (context: StrategyContext) => Promise<string[]>;
7
+ export declare const HeaderStrategies: {
8
+ /**
9
+ * Default strategy: Returns only the headers currently visible in the DOM.
10
+ * This is fast but won't find virtualized columns off-screen.
11
+ */
12
+ visible: ({ config, resolve, root }: StrategyContext) => Promise<string[]>;
13
+ };
@@ -0,0 +1,30 @@
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.HeaderStrategies = void 0;
13
+ exports.HeaderStrategies = {
14
+ /**
15
+ * Default strategy: Returns only the headers currently visible in the DOM.
16
+ * This is fast but won't find virtualized columns off-screen.
17
+ */
18
+ visible: (_a) => __awaiter(void 0, [_a], void 0, function* ({ config, resolve, root }) {
19
+ const headerLoc = resolve(config.headerSelector, root);
20
+ try {
21
+ // Wait for at least one header to be visible
22
+ yield headerLoc.first().waitFor({ state: 'visible', timeout: 3000 });
23
+ }
24
+ catch (e) {
25
+ // Ignore hydration/timeout issues, return what we have
26
+ }
27
+ const texts = yield headerLoc.allInnerTexts();
28
+ return texts.map(t => t.trim());
29
+ })
30
+ };
@@ -0,0 +1,54 @@
1
+ export * from './pagination';
2
+ export * from './sorting';
3
+ export * from './columns';
4
+ export * from './headers';
5
+ export * from './fill';
6
+ export * from './resolution';
7
+ export * from './dedupe';
8
+ export * from './loading';
9
+ export declare const Strategies: {
10
+ Pagination: {
11
+ clickNext: (nextButtonSelector: import("..").Selector, options?: {
12
+ stabilization?: import("./stabilization").StabilizationStrategy;
13
+ timeout?: number;
14
+ }) => import("..").PaginationStrategy;
15
+ infiniteScroll: (options?: {
16
+ action?: "scroll" | "js-scroll";
17
+ scrollTarget?: import("..").Selector;
18
+ scrollAmount?: number;
19
+ stabilization?: import("./stabilization").StabilizationStrategy;
20
+ timeout?: number;
21
+ }) => import("..").PaginationStrategy;
22
+ };
23
+ Sorting: {
24
+ AriaSort: () => import("..").SortingStrategy;
25
+ };
26
+ CellNavigation: {
27
+ default: () => Promise<void>;
28
+ };
29
+ Header: {
30
+ visible: ({ config, resolve, root }: import("..").StrategyContext) => Promise<string[]>;
31
+ };
32
+ Fill: {
33
+ default: ({ row, columnName, value, fillOptions }: Parameters<import("..").FillStrategy>[0]) => Promise<void>;
34
+ };
35
+ Resolution: {
36
+ default: import("./resolution").ColumnResolutionStrategy;
37
+ };
38
+ Dedupe: {
39
+ byTopPosition: (tolerance?: number) => import("..").DedupeStrategy;
40
+ };
41
+ Loading: {
42
+ Table: {
43
+ hasSpinner: (selector?: string) => ({ root }: import("..").TableContext) => Promise<boolean>;
44
+ custom: (fn: (context: import("..").TableContext) => Promise<boolean>) => (context: import("..").TableContext) => Promise<boolean>;
45
+ never: () => Promise<boolean>;
46
+ };
47
+ Row: {
48
+ hasClass: (className?: string) => (row: import("..").SmartRow) => Promise<boolean>;
49
+ hasText: (text?: string | RegExp) => (row: import("..").SmartRow) => Promise<boolean>;
50
+ hasEmptyCells: () => (row: import("..").SmartRow) => Promise<boolean>;
51
+ never: () => Promise<boolean>;
52
+ };
53
+ };
54
+ };
@@ -0,0 +1,43 @@
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
+ exports.Strategies = void 0;
18
+ const pagination_1 = require("./pagination");
19
+ const sorting_1 = require("./sorting");
20
+ const columns_1 = require("./columns");
21
+ const headers_1 = require("./headers");
22
+ const fill_1 = require("./fill");
23
+ const resolution_1 = require("./resolution");
24
+ const dedupe_1 = require("./dedupe");
25
+ const loading_1 = require("./loading");
26
+ __exportStar(require("./pagination"), exports);
27
+ __exportStar(require("./sorting"), exports);
28
+ __exportStar(require("./columns"), exports);
29
+ __exportStar(require("./headers"), exports);
30
+ __exportStar(require("./fill"), exports);
31
+ __exportStar(require("./resolution"), exports);
32
+ __exportStar(require("./dedupe"), exports);
33
+ __exportStar(require("./loading"), exports);
34
+ exports.Strategies = {
35
+ Pagination: pagination_1.PaginationStrategies,
36
+ Sorting: sorting_1.SortingStrategies,
37
+ CellNavigation: columns_1.CellNavigationStrategies,
38
+ Header: headers_1.HeaderStrategies,
39
+ Fill: fill_1.FillStrategies,
40
+ Resolution: resolution_1.ResolutionStrategies,
41
+ Dedupe: dedupe_1.DedupeStrategies,
42
+ Loading: loading_1.LoadingStrategies,
43
+ };
@@ -0,0 +1,48 @@
1
+ import { SmartRow, TableContext } from '../types';
2
+ /**
3
+ * Strategies for detecting loading states.
4
+ * Return `true` if the item is loading/busy, `false` if it is ready.
5
+ */
6
+ export declare const LoadingStrategies: {
7
+ /**
8
+ * Strategies for detecting if the entire table is loading.
9
+ */
10
+ Table: {
11
+ /**
12
+ * Checks if a global spinner or loading overlay is visible.
13
+ * @param selector Selector for the loading indicator (e.g. '.loading-spinner')
14
+ */
15
+ hasSpinner: (selector?: string) => ({ root }: TableContext) => Promise<boolean>;
16
+ /**
17
+ * Custom function to determine table loading state.
18
+ */
19
+ custom: (fn: (context: TableContext) => Promise<boolean>) => (context: TableContext) => Promise<boolean>;
20
+ /**
21
+ * Assume table is never loading (default).
22
+ */
23
+ never: () => Promise<boolean>;
24
+ };
25
+ /**
26
+ * Strategies for detecting if a specific row is loading (e.g. Skeleton).
27
+ */
28
+ Row: {
29
+ /**
30
+ * Checks if the row contains a specific class indicating it's a skeleton/loading row.
31
+ * @param className Class name acting as the loading indicator (default: 'skeleton')
32
+ */
33
+ hasClass: (className?: string) => (row: SmartRow) => Promise<boolean>;
34
+ /**
35
+ * Checks if the row's text content matches a "Loading..." string or regex.
36
+ */
37
+ hasText: (text?: string | RegExp) => (row: SmartRow) => Promise<boolean>;
38
+ /**
39
+ * Checks if the row has any cell with empty/falsy content (if strict).
40
+ * Useful if rows render with empty cells before populating.
41
+ */
42
+ hasEmptyCells: () => (row: SmartRow) => Promise<boolean>;
43
+ /**
44
+ * Assume row is never loading (default).
45
+ */
46
+ never: () => Promise<boolean>;
47
+ };
48
+ };
@@ -0,0 +1,82 @@
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.LoadingStrategies = void 0;
13
+ /**
14
+ * Strategies for detecting loading states.
15
+ * Return `true` if the item is loading/busy, `false` if it is ready.
16
+ */
17
+ exports.LoadingStrategies = {
18
+ /**
19
+ * Strategies for detecting if the entire table is loading.
20
+ */
21
+ Table: {
22
+ /**
23
+ * Checks if a global spinner or loading overlay is visible.
24
+ * @param selector Selector for the loading indicator (e.g. '.loading-spinner')
25
+ */
26
+ hasSpinner: (selector = '.loading-spinner') => (_a) => __awaiter(void 0, [_a], void 0, function* ({ root }) {
27
+ // Check if spinner exists and is visible within the table wrapper or page
28
+ const spinner = root.locator(selector).first();
29
+ try {
30
+ return yield spinner.isVisible();
31
+ }
32
+ catch (_b) {
33
+ return false;
34
+ }
35
+ }),
36
+ /**
37
+ * Custom function to determine table loading state.
38
+ */
39
+ custom: (fn) => fn,
40
+ /**
41
+ * Assume table is never loading (default).
42
+ */
43
+ never: () => __awaiter(void 0, void 0, void 0, function* () { return false; })
44
+ },
45
+ /**
46
+ * Strategies for detecting if a specific row is loading (e.g. Skeleton).
47
+ */
48
+ Row: {
49
+ /**
50
+ * Checks if the row contains a specific class indicating it's a skeleton/loading row.
51
+ * @param className Class name acting as the loading indicator (default: 'skeleton')
52
+ */
53
+ hasClass: (className = 'skeleton') => (row) => __awaiter(void 0, void 0, void 0, function* () {
54
+ const cls = yield row.getAttribute('class');
55
+ return cls ? cls.includes(className) : false;
56
+ }),
57
+ /**
58
+ * Checks if the row's text content matches a "Loading..." string or regex.
59
+ */
60
+ hasText: (text = 'Loading...') => (row) => __awaiter(void 0, void 0, void 0, function* () {
61
+ const content = yield row.innerText();
62
+ if (typeof text === 'string')
63
+ return content.includes(text);
64
+ return text.test(content);
65
+ }),
66
+ /**
67
+ * Checks if the row has any cell with empty/falsy content (if strict).
68
+ * Useful if rows render with empty cells before populating.
69
+ */
70
+ hasEmptyCells: () => (row) => __awaiter(void 0, void 0, void 0, function* () {
71
+ // Logic: Get all cells, check if any are empty.
72
+ // Note: This might be expensive if done for every row check.
73
+ // Simplified: check if InnerText is empty or very short?
74
+ const text = yield row.innerText();
75
+ return !text.trim();
76
+ }),
77
+ /**
78
+ * Assume row is never loading (default).
79
+ */
80
+ never: () => __awaiter(void 0, void 0, void 0, function* () { return false; })
81
+ }
82
+ };
@@ -0,0 +1,33 @@
1
+ import type { PaginationStrategy, Selector } from '../types';
2
+ import { StabilizationStrategy } from './stabilization';
3
+ export declare const PaginationStrategies: {
4
+ /**
5
+ * Strategy: Clicks a "Next" button and waits for stabilization.
6
+ * @param nextButtonSelector Selector for the next page button.
7
+ * @param options.stabilization Strategy to determine when the page has updated.
8
+ * Defaults to `contentChanged({ scope: 'first' })`.
9
+ * @param options.timeout Timeout for the click action.
10
+ */
11
+ clickNext: (nextButtonSelector: Selector, options?: {
12
+ stabilization?: StabilizationStrategy;
13
+ timeout?: number;
14
+ }) => PaginationStrategy;
15
+ /**
16
+ * Strategy: Infinite Scroll (generic).
17
+ * Supports both simple "Scroll to Bottom" and "Virtualized Scroll".
18
+ *
19
+ * @param options.action 'scroll' (mouse wheel) or 'js-scroll' (direct scrollTop).
20
+ * @param options.scrollTarget Selector for the scroll container (defaults to table root).
21
+ * @param options.scrollAmount Amount to scroll in pixels (default 500).
22
+ * @param options.stabilization Strategy to determine if new content loaded.
23
+ * Defaults to `rowCountIncreased` (simple append).
24
+ * Use `contentChanged` for virtualization.
25
+ */
26
+ infiniteScroll: (options?: {
27
+ action?: "scroll" | "js-scroll";
28
+ scrollTarget?: Selector;
29
+ scrollAmount?: number;
30
+ stabilization?: StabilizationStrategy;
31
+ timeout?: number;
32
+ }) => PaginationStrategy;
33
+ };
@@ -0,0 +1,79 @@
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.PaginationStrategies = void 0;
13
+ const stabilization_1 = require("./stabilization");
14
+ exports.PaginationStrategies = {
15
+ /**
16
+ * Strategy: Clicks a "Next" button and waits for stabilization.
17
+ * @param nextButtonSelector Selector for the next page button.
18
+ * @param options.stabilization Strategy to determine when the page has updated.
19
+ * Defaults to `contentChanged({ scope: 'first' })`.
20
+ * @param options.timeout Timeout for the click action.
21
+ */
22
+ clickNext: (nextButtonSelector, options = {}) => {
23
+ return (context) => __awaiter(void 0, void 0, void 0, function* () {
24
+ var _a;
25
+ const { root, resolve, page } = context;
26
+ const nextBtn = resolve(nextButtonSelector, root).first();
27
+ if (!(yield nextBtn.isVisible()) || !(yield nextBtn.isEnabled())) {
28
+ return false;
29
+ }
30
+ // Default stabilization: Wait for first row content to change
31
+ const stabilization = (_a = options.stabilization) !== null && _a !== void 0 ? _a : stabilization_1.StabilizationStrategies.contentChanged({ scope: 'first', timeout: options.timeout });
32
+ // Stabilization: Wrap action
33
+ const success = yield stabilization(context, () => __awaiter(void 0, void 0, void 0, function* () {
34
+ yield nextBtn.click({ timeout: 2000 }).catch(() => { });
35
+ }));
36
+ return success;
37
+ });
38
+ },
39
+ /**
40
+ * Strategy: Infinite Scroll (generic).
41
+ * Supports both simple "Scroll to Bottom" and "Virtualized Scroll".
42
+ *
43
+ * @param options.action 'scroll' (mouse wheel) or 'js-scroll' (direct scrollTop).
44
+ * @param options.scrollTarget Selector for the scroll container (defaults to table root).
45
+ * @param options.scrollAmount Amount to scroll in pixels (default 500).
46
+ * @param options.stabilization Strategy to determine if new content loaded.
47
+ * Defaults to `rowCountIncreased` (simple append).
48
+ * Use `contentChanged` for virtualization.
49
+ */
50
+ infiniteScroll: (options = {}) => {
51
+ return (context) => __awaiter(void 0, void 0, void 0, function* () {
52
+ var _a, _b;
53
+ const { root, resolve, page } = context;
54
+ const scrollTarget = options.scrollTarget
55
+ ? resolve(options.scrollTarget, root)
56
+ : root;
57
+ // Default stabilization: Wait for row count to increase (Append mode)
58
+ const stabilization = (_a = options.stabilization) !== null && _a !== void 0 ? _a : stabilization_1.StabilizationStrategies.rowCountIncreased({ timeout: options.timeout });
59
+ const amount = (_b = options.scrollAmount) !== null && _b !== void 0 ? _b : 500;
60
+ const doScroll = () => __awaiter(void 0, void 0, void 0, function* () {
61
+ const box = yield scrollTarget.boundingBox();
62
+ // Action: Scroll
63
+ if (options.action === 'js-scroll' || !box) {
64
+ yield scrollTarget.evaluate((el, y) => {
65
+ el.scrollTop += y;
66
+ }, amount);
67
+ }
68
+ else {
69
+ // Mouse Wheel
70
+ yield page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
71
+ yield page.mouse.wheel(0, amount);
72
+ }
73
+ });
74
+ // Stabilization: Wait
75
+ const success = yield stabilization(context, doScroll);
76
+ return success;
77
+ });
78
+ }
79
+ };
@@ -0,0 +1,25 @@
1
+ import { TableContext } from '../types';
2
+ /**
3
+ * Scrolls the grid horizontally to collect all column headers.
4
+ * Handles empty headers by labeling them (e.g. "Checkbox").
5
+ */
6
+ export declare const scrollRightHeaderRDG: (context: TableContext) => Promise<string[]>;
7
+ /**
8
+ * Uses a row-relative locator to avoid issues with absolute aria-rowindex
9
+ * changing during pagination/scrolling.
10
+ */
11
+ export declare const rdgGetCellLocator: ({ row, columnIndex }: any) => any;
12
+ /**
13
+ * Scrolls virtualized columns into view before reading.
14
+ */
15
+ export declare const rdgCellNavigation: ({ root, page, index }: any) => Promise<void>;
16
+ /**
17
+ * Scrolls the grid vertically to load more virtualized rows.
18
+ */
19
+ export declare const rdgPaginationStrategy: import("../types").PaginationStrategy;
20
+ export declare const RDGStrategies: {
21
+ header: (context: TableContext) => Promise<string[]>;
22
+ getCellLocator: ({ row, columnIndex }: any) => any;
23
+ cellNavigation: ({ root, page, index }: any) => Promise<void>;
24
+ pagination: import("../types").PaginationStrategy;
25
+ };
@@ -0,0 +1,100 @@
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.RDGStrategies = exports.rdgPaginationStrategy = exports.rdgCellNavigation = exports.rdgGetCellLocator = exports.scrollRightHeaderRDG = void 0;
13
+ /**
14
+ * Scrolls the grid horizontally to collect all column headers.
15
+ * Handles empty headers by labeling them (e.g. "Checkbox").
16
+ */
17
+ const scrollRightHeaderRDG = (context) => __awaiter(void 0, void 0, void 0, function* () {
18
+ const { resolve, config, root, page } = context;
19
+ const collectedHeaders = new Set();
20
+ const gridHandle = yield root.evaluateHandle((el) => {
21
+ return el.querySelector('[role="grid"]') || el.closest('[role="grid"]');
22
+ });
23
+ const expectedColumns = yield gridHandle.evaluate(el => el ? parseInt(el.getAttribute('aria-colcount') || '0', 10) : 0);
24
+ const getVisible = () => __awaiter(void 0, void 0, void 0, function* () {
25
+ const headerLoc = resolve(config.headerSelector, root);
26
+ const texts = yield headerLoc.allInnerTexts();
27
+ return texts.map(t => {
28
+ const trimmed = t.trim();
29
+ // Assign a name to empty headers (like selection checkboxes)
30
+ return trimmed.length > 0 ? trimmed : 'Checkbox';
31
+ });
32
+ });
33
+ let currentHeaders = yield getVisible();
34
+ currentHeaders.forEach(h => collectedHeaders.add(h));
35
+ const hasScroll = yield gridHandle.evaluate(el => el ? el.scrollWidth > el.clientWidth : false);
36
+ if (hasScroll) {
37
+ yield gridHandle.evaluate(el => el.scrollLeft = 0);
38
+ yield page.waitForTimeout(200);
39
+ let iteration = 0;
40
+ // Safety break at 30 iterations to prevent infinite loops
41
+ while (collectedHeaders.size < expectedColumns && iteration < 30) {
42
+ yield gridHandle.evaluate(el => el.scrollLeft += 500);
43
+ yield page.waitForTimeout(300);
44
+ const newHeaders = yield getVisible();
45
+ newHeaders.forEach(h => collectedHeaders.add(h));
46
+ const atEnd = yield gridHandle.evaluate(el => el.scrollLeft >= el.scrollWidth - el.clientWidth - 10);
47
+ iteration++;
48
+ if (atEnd)
49
+ break;
50
+ }
51
+ yield gridHandle.evaluate(el => el.scrollLeft = 0);
52
+ yield page.waitForTimeout(200);
53
+ }
54
+ return Array.from(collectedHeaders);
55
+ });
56
+ exports.scrollRightHeaderRDG = scrollRightHeaderRDG;
57
+ /**
58
+ * Uses a row-relative locator to avoid issues with absolute aria-rowindex
59
+ * changing during pagination/scrolling.
60
+ */
61
+ const rdgGetCellLocator = ({ row, columnIndex }) => {
62
+ const ariaColIndex = columnIndex + 1;
63
+ return row.locator(`[role="gridcell"][aria-colindex="${ariaColIndex}"]`);
64
+ };
65
+ exports.rdgGetCellLocator = rdgGetCellLocator;
66
+ /**
67
+ * Scrolls virtualized columns into view before reading.
68
+ */
69
+ const rdgCellNavigation = (_a) => __awaiter(void 0, [_a], void 0, function* ({ root, page, index }) {
70
+ // Check if the column header is visible and scroll horizontally if needed
71
+ const headerCell = root.locator(`[role="columnheader"][aria-colindex="${index + 1}"]`);
72
+ const isVisible = yield headerCell.isVisible().catch(() => false);
73
+ if (!isVisible) {
74
+ const estimatedScroll = index * 150;
75
+ yield root.evaluate((el, scrollAmount) => {
76
+ el.scrollLeft = scrollAmount;
77
+ }, estimatedScroll);
78
+ yield page.waitForTimeout(300);
79
+ }
80
+ });
81
+ exports.rdgCellNavigation = rdgCellNavigation;
82
+ /**
83
+ * Scrolls the grid vertically to load more virtualized rows.
84
+ */
85
+ const pagination_1 = require("./pagination");
86
+ const stabilization_1 = require("./stabilization");
87
+ /**
88
+ * Scrolls the grid vertically to load more virtualized rows.
89
+ */
90
+ exports.rdgPaginationStrategy = pagination_1.PaginationStrategies.infiniteScroll({
91
+ action: 'js-scroll',
92
+ scrollAmount: 500,
93
+ stabilization: stabilization_1.StabilizationStrategies.contentChanged({ timeout: 5000 })
94
+ });
95
+ exports.RDGStrategies = {
96
+ header: exports.scrollRightHeaderRDG,
97
+ getCellLocator: exports.rdgGetCellLocator,
98
+ cellNavigation: exports.rdgCellNavigation,
99
+ pagination: exports.rdgPaginationStrategy
100
+ };
@@ -0,0 +1,22 @@
1
+ import { StrategyContext } from '../types';
2
+ export interface ColumnResolutionStrategy {
3
+ /**
4
+ * Resolves a column name (string or Regex) to a column index.
5
+ * Returns undefined if not found.
6
+ */
7
+ resolveIndex(options: {
8
+ query: string | RegExp;
9
+ headerMap: Map<string, number>;
10
+ context: StrategyContext;
11
+ }): number | undefined;
12
+ /**
13
+ * Resolves a column name to a clean string name (for error messages or debugging).
14
+ */
15
+ resolveName(options: {
16
+ query: string | RegExp;
17
+ headerMap: Map<string, number>;
18
+ }): string;
19
+ }
20
+ export declare const ResolutionStrategies: {
21
+ default: ColumnResolutionStrategy;
22
+ };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResolutionStrategies = void 0;
4
+ exports.ResolutionStrategies = {
5
+ default: {
6
+ resolveIndex: ({ query, headerMap }) => {
7
+ // 1. Exact / String Match
8
+ if (typeof query === 'string') {
9
+ if (headerMap.has(query))
10
+ return headerMap.get(query);
11
+ }
12
+ // 2. Regex Match
13
+ if (query instanceof RegExp) {
14
+ for (const [colName, idx] of headerMap.entries()) {
15
+ if (query.test(colName))
16
+ return idx;
17
+ }
18
+ return undefined;
19
+ }
20
+ // 3. (Optional) Fuzzy String Match fallback could go here
21
+ // But for strict default strategy, we might want to keep it simple first
22
+ // The original code didn't do fuzzy *resolution* logic inside the get(), it just did strict get().
23
+ // The fuzzy logic was only for *suggestions* on error.
24
+ return undefined;
25
+ },
26
+ resolveName: ({ query }) => {
27
+ return query.toString();
28
+ }
29
+ }
30
+ };