pw-core 1.1.0 → 1.2.1

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 (96) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +777 -0
  4. package/dist/codegen/config/context-weights.d.ts +8 -0
  5. package/dist/codegen/config/context-weights.js +11 -0
  6. package/dist/codegen/config/ignored-attributes.d.ts +1 -0
  7. package/dist/codegen/config/ignored-attributes.js +4 -0
  8. package/dist/codegen/config/ignored-classes.d.ts +1 -0
  9. package/dist/codegen/config/ignored-classes.js +144 -0
  10. package/dist/codegen/config/ignored-values.d.ts +1 -0
  11. package/dist/codegen/config/ignored-values.js +4 -0
  12. package/dist/codegen/config/index.d.ts +29 -0
  13. package/dist/codegen/config/index.js +17 -0
  14. package/dist/codegen/config/strategy-order.d.ts +1 -0
  15. package/dist/codegen/config/strategy-order.js +18 -0
  16. package/dist/codegen/config/weights.d.ts +15 -0
  17. package/dist/codegen/config/weights.js +18 -0
  18. package/dist/codegen/floating-panel/client.d.ts +1 -0
  19. package/dist/codegen/floating-panel/client.js +248 -0
  20. package/dist/codegen/floating-panel/index.d.ts +4 -0
  21. package/dist/codegen/floating-panel/index.js +20 -0
  22. package/dist/codegen/floating-panel/manager.d.ts +19 -0
  23. package/dist/codegen/floating-panel/manager.js +90 -0
  24. package/dist/codegen/floating-panel/template.d.ts +2 -0
  25. package/dist/codegen/floating-panel/template.js +58 -0
  26. package/dist/codegen/floating-panel/types.d.ts +7 -0
  27. package/dist/codegen/floating-panel/types.js +2 -0
  28. package/dist/codegen/generator/action.validator.d.ts +5 -0
  29. package/dist/codegen/generator/action.validator.js +27 -0
  30. package/dist/codegen/generator/index.d.ts +13 -0
  31. package/dist/codegen/generator/index.js +724 -0
  32. package/dist/codegen/generator/locator.restricted.d.ts +36 -0
  33. package/dist/codegen/generator/locator.restricted.js +127 -0
  34. package/dist/codegen/generator/locator.validator.d.ts +8 -0
  35. package/dist/codegen/generator/locator.validator.js +38 -0
  36. package/dist/codegen/hover-tracker/client.d.ts +1 -0
  37. package/dist/codegen/hover-tracker/client.js +75 -0
  38. package/dist/codegen/hover-tracker/index.d.ts +3 -0
  39. package/dist/codegen/hover-tracker/index.js +19 -0
  40. package/dist/codegen/hover-tracker/manager.d.ts +11 -0
  41. package/dist/codegen/hover-tracker/manager.js +28 -0
  42. package/dist/codegen/hover-tracker/types.d.ts +3 -0
  43. package/dist/codegen/hover-tracker/types.js +2 -0
  44. package/dist/codegen/index.d.ts +35 -0
  45. package/dist/codegen/index.js +1147 -0
  46. package/dist/codegen/key-utils.d.ts +23 -0
  47. package/dist/codegen/key-utils.js +68 -0
  48. package/dist/codegen/scorer/base-score.d.ts +1 -0
  49. package/dist/codegen/scorer/base-score.js +7 -0
  50. package/dist/codegen/scorer/context-score.d.ts +2 -0
  51. package/dist/codegen/scorer/context-score.js +27 -0
  52. package/dist/codegen/scorer/index.d.ts +5 -0
  53. package/dist/codegen/scorer/index.js +23 -0
  54. package/dist/codegen/scorer/locator.ranking.d.ts +15 -0
  55. package/dist/codegen/scorer/locator.ranking.js +83 -0
  56. package/dist/codegen/scorer/semantic-score.d.ts +1 -0
  57. package/dist/codegen/scorer/semantic-score.js +47 -0
  58. package/dist/codegen/types.d.ts +35 -0
  59. package/dist/codegen/types.js +2 -0
  60. package/dist/component/table.js +19 -14
  61. package/dist/constants/methods.d.ts +2 -0
  62. package/dist/constants/methods.js +33 -0
  63. package/dist/constants/strategies.d.ts +2 -0
  64. package/dist/constants/strategies.js +47 -0
  65. package/dist/helpers.d.ts +1 -1
  66. package/dist/helpers.js +21 -9
  67. package/dist/page/actions/locator-actions.js +46 -2
  68. package/dist/page/actions/page-actions.d.ts +11 -6
  69. package/dist/page/actions/page-actions.js +12 -12
  70. package/dist/page/assertions/verify-chain.d.ts +39 -32
  71. package/dist/page/assertions/verify-chain.js +21 -6
  72. package/dist/page/assertions/verify-helpers.d.ts +9 -3
  73. package/dist/page/assertions/verify-helpers.js +18 -9
  74. package/dist/page/config.d.ts +126 -38
  75. package/dist/page/config.js +10 -31
  76. package/dist/page/index.d.ts +1 -0
  77. package/dist/page/index.js +1 -0
  78. package/dist/page/locators/dynamic-locator-resolver.d.ts +49 -0
  79. package/dist/page/locators/dynamic-locator-resolver.js +187 -0
  80. package/dist/page/locators/resolver.d.ts +15 -14
  81. package/dist/page/locators/resolver.js +187 -30
  82. package/dist/page/registry.d.ts +51 -12
  83. package/dist/page/registry.js +90 -18
  84. package/dist/page/typed-page.d.ts +33 -28
  85. package/dist/page/typed-page.js +89 -12
  86. package/dist/page/types/proxy-methods.d.ts +69 -54
  87. package/dist/page/types/validation.d.ts +69 -0
  88. package/dist/page/types/validation.js +2 -0
  89. package/dist/page/utils/caller-location.d.ts +5 -0
  90. package/dist/page/utils/caller-location.js +82 -0
  91. package/dist/page/utils/formatter.js +14 -4
  92. package/dist/types/methods.d.ts +4 -0
  93. package/dist/types/methods.js +2 -0
  94. package/dist/types/strategies.d.ts +17 -0
  95. package/dist/types/strategies.js +2 -0
  96. package/package.json +8 -1
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Key creation utilities for codegen-generated registry keys.
3
+ *
4
+ * Rules:
5
+ * - Custom keys (testId, selector) are always camelCase
6
+ * - No spaces, hyphens, or special characters
7
+ * - Leading digits are trimmed
8
+ * - Trailing digits are preserved (they may be disambiguation suffixes)
9
+ * - Role suffixes (Btn, Input) are appended after sanitization
10
+ */
11
+ /**
12
+ * Convert an arbitrary string into a clean camelCase registry key.
13
+ *
14
+ * @example
15
+ * toRegistryKey('GET api/app/projects List all projects') => 'getApiAppProjectsListAllProjects'
16
+ * toRegistryKey('01Dynamic-Locators') => 'dynamicLocators'
17
+ * toRegistryKey('POST api/app/projects Create-a-project') => 'postApiAppProjectsCreateAProject'
18
+ * toRegistryKey('switch-to-k6-core') => 'switchToK6Core'
19
+ * toRegistryKey('hello world') => 'helloWorld'
20
+ */
21
+ export declare function toRegistryKey(str: string, options?: {
22
+ roleSuffix?: string;
23
+ }): string;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * Key creation utilities for codegen-generated registry keys.
4
+ *
5
+ * Rules:
6
+ * - Custom keys (testId, selector) are always camelCase
7
+ * - No spaces, hyphens, or special characters
8
+ * - Leading digits are trimmed
9
+ * - Trailing digits are preserved (they may be disambiguation suffixes)
10
+ * - Role suffixes (Btn, Input) are appended after sanitization
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.toRegistryKey = toRegistryKey;
14
+ /**
15
+ * Convert an arbitrary string into a clean camelCase registry key.
16
+ *
17
+ * @example
18
+ * toRegistryKey('GET api/app/projects List all projects') => 'getApiAppProjectsListAllProjects'
19
+ * toRegistryKey('01Dynamic-Locators') => 'dynamicLocators'
20
+ * toRegistryKey('POST api/app/projects Create-a-project') => 'postApiAppProjectsCreateAProject'
21
+ * toRegistryKey('switch-to-k6-core') => 'switchToK6Core'
22
+ * toRegistryKey('hello world') => 'helloWorld'
23
+ */
24
+ function toRegistryKey(str, options) {
25
+ if (!str)
26
+ return 'element';
27
+ // Strip all non-alphanumeric characters except spaces (used as word separators)
28
+ // First, normalize common separators (hyphens, underscores, slashes, dots) to spaces
29
+ let normalized = str
30
+ .replace(/[-_/\\.]+/g, ' ')
31
+ // Remove any remaining special characters (keep letters, digits, spaces)
32
+ .replace(/[^a-zA-Z0-9\s]/g, '')
33
+ .trim();
34
+ if (!normalized)
35
+ return 'element';
36
+ // Split into words on whitespace and camelCase boundaries
37
+ const words = normalized
38
+ .replace(/([a-z])([A-Z])/g, '$1 $2') // split camelCase
39
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // split consecutive uppercase
40
+ .split(/\s+/)
41
+ .filter(Boolean);
42
+ if (words.length === 0)
43
+ return 'element';
44
+ // Build camelCase
45
+ const camel = words
46
+ .map((word, idx) => {
47
+ if (idx === 0)
48
+ return word.toLowerCase();
49
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
50
+ })
51
+ .join('');
52
+ // Trim leading digits
53
+ let result = camel.replace(/^\d+/, '');
54
+ if (!result)
55
+ return 'element';
56
+ // Append role suffix if requested (e.g. 'Btn' for buttons)
57
+ if (options?.roleSuffix) {
58
+ const lowerResult = result.toLowerCase();
59
+ const lowerSuffix = options.roleSuffix.toLowerCase();
60
+ if (!lowerResult.endsWith(lowerSuffix)) {
61
+ result += options.roleSuffix;
62
+ }
63
+ }
64
+ // Hard cap at 40 characters
65
+ if (result.length > 40)
66
+ result = result.slice(0, 40);
67
+ return result;
68
+ }
@@ -0,0 +1 @@
1
+ export declare function getBaseScore(strategyName: string): number;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBaseScore = getBaseScore;
4
+ const config_1 = require("../config");
5
+ function getBaseScore(strategyName) {
6
+ return config_1.codegenConfig.weights[strategyName] ?? 0;
7
+ }
@@ -0,0 +1,2 @@
1
+ import { DOMContext } from '../types';
2
+ export declare function getContextScore(context: DOMContext): number;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getContextScore = getContextScore;
4
+ const config_1 = require("../config");
5
+ function getContextScore(context) {
6
+ let score = 0;
7
+ const cw = config_1.codegenConfig.contextWeights;
8
+ if (context.accessibleName) {
9
+ score += cw.accessibleName || 0;
10
+ }
11
+ if (context.siblingText) {
12
+ score += cw.siblingText || 0;
13
+ }
14
+ if (context.parentText) {
15
+ score += cw.parentText || 0;
16
+ }
17
+ if (context.nearbyHeading) {
18
+ score += cw.heading || 0;
19
+ }
20
+ if (context.parentAttributes) {
21
+ const hasSemanticParent = Object.keys(context.parentAttributes).some((attr) => attr.startsWith('data-') || attr === 'id' || attr === 'role');
22
+ if (hasSemanticParent) {
23
+ score += cw.semanticParent || 0;
24
+ }
25
+ }
26
+ return score;
27
+ }
@@ -0,0 +1,5 @@
1
+ export * from './base-score';
2
+ export { getBaseScore as getStrategyScore } from './base-score';
3
+ export * from './semantic-score';
4
+ export * from './context-score';
5
+ export * from './locator.ranking';
@@ -0,0 +1,23 @@
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.getStrategyScore = void 0;
18
+ __exportStar(require("./base-score"), exports);
19
+ var base_score_1 = require("./base-score");
20
+ Object.defineProperty(exports, "getStrategyScore", { enumerable: true, get: function () { return base_score_1.getBaseScore; } });
21
+ __exportStar(require("./semantic-score"), exports);
22
+ __exportStar(require("./context-score"), exports);
23
+ __exportStar(require("./locator.ranking"), exports);
@@ -0,0 +1,15 @@
1
+ export interface RankingRule {
2
+ strategy: string;
3
+ source?: 'target' | 'child' | 'sibling' | 'parent' | 'ancestor';
4
+ attrName?: string | RegExp;
5
+ roleVal?: string;
6
+ score: number;
7
+ }
8
+ export declare const priorityRankingRules: RankingRule[];
9
+ export declare function getPriorityScore(c: {
10
+ strategy: string;
11
+ source: string;
12
+ depth: number;
13
+ attrName?: string;
14
+ roleVal?: string;
15
+ }): number;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.priorityRankingRules = void 0;
4
+ exports.getPriorityScore = getPriorityScore;
5
+ exports.priorityRankingRules = [
6
+ { strategy: 'testId', source: 'target', score: 1000 },
7
+ { strategy: 'testId', source: 'sibling', score: 980 },
8
+ { strategy: 'testId', source: 'parent', score: 970 },
9
+ { strategy: 'testId', source: 'ancestor', score: 960 },
10
+ { strategy: 'dataAttribute', attrName: /data-test-id|data-testid|datatestid/i, source: 'target', score: 980 },
11
+ { strategy: 'dataAttribute', attrName: /data-test-id|data-testid|datatestid/i, source: 'sibling', score: 960 },
12
+ { strategy: 'dataAttribute', attrName: /data-test-id|data-testid|datatestid/i, source: 'parent', score: 950 },
13
+ { strategy: 'dataAttribute', attrName: /data-test-id|data-testid|datatestid/i, source: 'ancestor', score: 940 },
14
+ { strategy: 'id', source: 'target', score: 960 },
15
+ { strategy: 'id', source: 'sibling', score: 940 },
16
+ { strategy: 'id', source: 'parent', score: 930 },
17
+ { strategy: 'id', source: 'ancestor', score: 920 },
18
+ { strategy: 'dataAttribute', attrName: 'data-parent-id', source: 'target', score: 940 },
19
+ { strategy: 'dataAttribute', attrName: 'data-parent-id', source: 'sibling', score: 925 },
20
+ { strategy: 'dataAttribute', attrName: 'data-parent-id', source: 'parent', score: 915 },
21
+ { strategy: 'dataAttribute', attrName: 'data-parent-id', source: 'ancestor', score: 905 },
22
+ { strategy: 'dataAttribute', attrName: /data-.*id$/i, source: 'target', score: 920 },
23
+ { strategy: 'dataAttribute', attrName: /data-.*id$/i, source: 'sibling', score: 875 },
24
+ { strategy: 'dataAttribute', attrName: /data-.*id$/i, source: 'parent', score: 825 },
25
+ { strategy: 'dataAttribute', attrName: /data-.*id$/i, source: 'ancestor', score: 775 },
26
+ { strategy: 'dataAttribute', attrName: /id/i, source: 'target', score: 900 },
27
+ { strategy: 'dataAttribute', attrName: /id/i, source: 'sibling', score: 860 },
28
+ { strategy: 'dataAttribute', attrName: /id/i, source: 'parent', score: 810 },
29
+ { strategy: 'dataAttribute', attrName: /id/i, source: 'ancestor', score: 760 },
30
+ { strategy: 'role', roleVal: 'link', source: 'target', score: 880 },
31
+ { strategy: 'role', roleVal: 'link', source: 'sibling', score: 840 },
32
+ { strategy: 'role', roleVal: 'link', source: 'parent', score: 790 },
33
+ { strategy: 'role', roleVal: 'link', source: 'ancestor', score: 740 },
34
+ { strategy: 'role', roleVal: 'button', source: 'target', score: 870 },
35
+ { strategy: 'role', roleVal: 'button', source: 'sibling', score: 830 },
36
+ { strategy: 'role', roleVal: 'button', source: 'parent', score: 780 },
37
+ { strategy: 'role', roleVal: 'button', source: 'ancestor', score: 730 },
38
+ { strategy: 'text', source: 'target', score: 550 },
39
+ { strategy: 'text', source: 'sibling', score: 500 },
40
+ { strategy: 'text', source: 'parent', score: 450 },
41
+ { strategy: 'text', source: 'ancestor', score: 400 },
42
+ { strategy: 'role', source: 'target', score: 750 },
43
+ { strategy: 'role', source: 'sibling', score: 710 },
44
+ { strategy: 'role', source: 'parent', score: 660 },
45
+ { strategy: 'role', source: 'ancestor', score: 610 },
46
+ { strategy: 'dataAttribute', attrName: 'data-section', score: 625 },
47
+ { strategy: 'dataAttribute', attrName: 'data-page', score: 620 },
48
+ { strategy: 'dataAttribute', attrName: 'data-component', score: 615 },
49
+ { strategy: 'dataAttribute', score: 600 },
50
+ { strategy: 'label', attrName: 'aria-labelledby', score: 575 },
51
+ { strategy: 'label', attrName: 'title', score: 550 },
52
+ { strategy: 'label', score: 570 },
53
+ { strategy: 'placeholder', score: 565 },
54
+ { strategy: 'altText', score: 563 },
55
+ { strategy: 'title', score: 562 },
56
+ { strategy: 'class', score: 500 },
57
+ { strategy: 'css', score: 400 },
58
+ { strategy: 'xpath', score: 300 }
59
+ ];
60
+ function getPriorityScore(c) {
61
+ const isIdAttr = c.attrName && /id/i.test(c.attrName);
62
+ for (const rule of exports.priorityRankingRules) {
63
+ if (rule.strategy !== c.strategy)
64
+ continue;
65
+ if (rule.source && rule.source !== c.source)
66
+ continue;
67
+ if (rule.roleVal && rule.roleVal !== c.roleVal)
68
+ continue;
69
+ if (rule.attrName) {
70
+ if (typeof rule.attrName === 'string') {
71
+ if (rule.attrName !== c.attrName)
72
+ continue;
73
+ }
74
+ else {
75
+ const nameToTest = isIdAttr ? 'id' : (c.attrName || '');
76
+ if (!rule.attrName.test(nameToTest))
77
+ continue;
78
+ }
79
+ }
80
+ return rule.score;
81
+ }
82
+ return 100; // default CSS
83
+ }
@@ -0,0 +1 @@
1
+ export declare function getSemanticScore(value: string | null): number;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSemanticScore = getSemanticScore;
4
+ function getSemanticScore(value) {
5
+ if (!value)
6
+ return 0;
7
+ let score = 0;
8
+ // Hyphenated values are intentional naming (e.g. "why-pw-core", "page-registry")
9
+ if (value.includes('-'))
10
+ score += 10;
11
+ // Longer values are more descriptive
12
+ if (value.length > 5)
13
+ score += 5;
14
+ if (value.length > 10)
15
+ score += 5;
16
+ // Exclude purely numeric or long random sequences
17
+ if (!/\d{4,}/.test(value))
18
+ score += 10;
19
+ // Pure words (no digits) are more semantic
20
+ if (/^[a-zA-Z-_]+$/.test(value))
21
+ score += 5;
22
+ // Penalize extremely short generic values
23
+ if (value.length <= 3)
24
+ score -= 15;
25
+ // Penalize single-word utility-like values (e.g. "flex", "block", "hidden")
26
+ const utilityNames = [
27
+ 'flex', 'grid', 'block', 'inline', 'hidden', 'visible', 'relative', 'absolute',
28
+ 'fixed', 'sticky', 'static', 'auto', 'none', 'inherit', 'initial', 'unset',
29
+ 'revert', 'polyline', 'svg', 'path', 'circle', 'rect', 'div', 'span', 'section',
30
+ 'article', 'main', 'header', 'footer', 'nav', 'aside', 'tbody', 'thead', 'tr',
31
+ 'td', 'th'
32
+ ];
33
+ if (utilityNames.includes(value.toLowerCase()))
34
+ score -= 30;
35
+ // Penalize CSS utility-pattern values (e.g. transition-all, border-0, shadow-lg)
36
+ const utilityPatterns = [
37
+ /^transition-/, /^transform-/, /^duration-/, /^ease-/, /^delay-/, /^animate-/,
38
+ /^shadow-/, /^opacity-/, /^blur-/, /^scale-/, /^translate-/, /^rotate-/,
39
+ /^skew-/, /^origin-/, /^border-/, /^rounded-/, /^ring-/, /^outline-/, /^bg-/,
40
+ /^text-/, /^font-/, /^leading-/, /^tracking-/, /^w-/, /^h-/, /^min-/, /^max-/,
41
+ /^gap-/, /^space-/, /^m[tblrxy]?-/, /^p[tblrxy]?-/, /^col-/, /^row-/, /^grid-/,
42
+ /^flex-/, /^z-/, /^inset-/, /^top-/, /^right-/, /^bottom-/, /^left-/
43
+ ];
44
+ if (utilityPatterns.some((p) => p.test(value)))
45
+ score -= 25;
46
+ return score;
47
+ }
@@ -0,0 +1,35 @@
1
+ export interface DOMContext {
2
+ parentText?: string;
3
+ siblingText?: string;
4
+ nearbyHeading?: string;
5
+ accessibleName?: string;
6
+ parentAttributes?: Record<string, string>;
7
+ siblingAttributes?: Record<string, string>;
8
+ }
9
+ export interface Candidate {
10
+ selector: string;
11
+ source: 'target' | 'child' | 'sibling' | 'parent' | 'ancestor';
12
+ strategy: string;
13
+ depth: number;
14
+ baseScore: number;
15
+ semanticScore: number;
16
+ proximityScore: number;
17
+ similarityScore: number;
18
+ uniquenessScore: number;
19
+ totalScore: number;
20
+ unique: boolean;
21
+ valueToScore?: string;
22
+ }
23
+ export interface LocatorCandidate extends Candidate {
24
+ locator: string;
25
+ contextScore: number;
26
+ nearbyText?: string;
27
+ parentText?: string;
28
+ accessibleName?: string;
29
+ generatedKey?: string;
30
+ }
31
+ export interface LocatorStrategy {
32
+ name: string;
33
+ matches(element: Element): boolean;
34
+ build(element: Element): LocatorCandidate | null;
35
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Table = exports.TableRows = void 0;
4
4
  const test_1 = require("@playwright/test");
5
+ const caller_location_1 = require("../page/utils/caller-location");
5
6
  /**
6
7
  * A custom Array subclass representing a list of table rows.
7
8
  * Inherits all standard Array methods while adding type-safe get and getAll capabilities.
@@ -19,15 +20,15 @@ class TableRows extends Array {
19
20
  return this[0] ? this[0][key] : undefined;
20
21
  }
21
22
  // Return the first matching row
22
- return this.find(row => row[key] === value);
23
+ return this.find((row) => row[key] === value);
23
24
  }
24
25
  getAll(key, value) {
25
26
  if (arguments.length === 1) {
26
27
  // Return the values of the key from all rows
27
- return this.map(row => row[key]);
28
+ return this.map((row) => row[key]);
28
29
  }
29
30
  // Return all matching rows
30
- return this.filter(row => row[key] === value);
31
+ return this.filter((row) => row[key] === value);
31
32
  }
32
33
  }
33
34
  exports.TableRows = TableRows;
@@ -41,9 +42,11 @@ class Table {
41
42
  */
42
43
  async getHeaders() {
43
44
  return test_1.test.step(`Get headers of table`, async () => {
44
- const headers = await this.root.locator('th').evaluateAll(ths => ths.map(th => th.textContent?.trim().toLowerCase() || ''));
45
+ const headers = await this.root
46
+ .locator('th')
47
+ .evaluateAll((ths) => ths.map((th) => th.textContent?.trim().toLowerCase() || ''));
45
48
  return headers;
46
- });
49
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
47
50
  }
48
51
  /**
49
52
  * Retrieves all the rows of the table as typed objects.
@@ -52,9 +55,11 @@ class Table {
52
55
  async getRows() {
53
56
  return test_1.test.step('Get table rows', async () => {
54
57
  const headers = await this.getHeaders();
55
- const rowsData = await this.root.locator('tbody tr, tr[data-testid="transaction-row"]').evaluateAll((trs, headers) => {
56
- return trs.map(tr => {
57
- const cells = Array.from(tr.querySelectorAll('td')).map(td => td.textContent?.trim() || '');
58
+ const rowsData = await this.root
59
+ .locator('tbody tr, tr[data-testid="transaction-row"]')
60
+ .evaluateAll((trs, headers) => {
61
+ return trs.map((tr) => {
62
+ const cells = Array.from(tr.querySelectorAll('td')).map((td) => td.textContent?.trim() || '');
58
63
  if (cells.length === 0)
59
64
  return null; // Skip header or empty rows
60
65
  const rowData = {};
@@ -67,7 +72,7 @@ class Table {
67
72
  });
68
73
  }, headers);
69
74
  return rowsData.filter((r) => r !== null);
70
- });
75
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
71
76
  }
72
77
  /**
73
78
  * Retrieves all rows from the table, returned as a custom TableRows collection
@@ -82,10 +87,10 @@ class Table {
82
87
  */
83
88
  async getRowCount() {
84
89
  return test_1.test.step('Get table row count', async () => {
85
- return await this.root.locator('tbody tr, tr[data-testid="transaction-row"]').evaluateAll(trs => {
86
- return trs.filter(tr => tr.querySelectorAll('td').length > 0).length;
90
+ return await this.root.locator('tbody tr, tr[data-testid="transaction-row"]').evaluateAll((trs) => {
91
+ return trs.filter((tr) => tr.querySelectorAll('td').length > 0).length;
87
92
  });
88
- });
93
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
89
94
  }
90
95
  /**
91
96
  * Retrieves the value of a specific cell by row index and column key.
@@ -99,14 +104,14 @@ class Table {
99
104
  }
100
105
  return await this.root.evaluate((tableEl, { rowIndex, columnIndex }) => {
101
106
  const trs = Array.from(tableEl.querySelectorAll('tbody tr, tr[data-testid="transaction-row"]'));
102
- const dataRows = trs.filter(tr => tr.querySelectorAll('td').length > 0);
107
+ const dataRows = trs.filter((tr) => tr.querySelectorAll('td').length > 0);
103
108
  if (rowIndex < 0 || rowIndex >= dataRows.length) {
104
109
  throw new Error(`Row index ${rowIndex} is out of bounds (0 to ${dataRows.length - 1})`);
105
110
  }
106
111
  const cell = dataRows[rowIndex].querySelectorAll('td')[columnIndex];
107
112
  return cell?.textContent?.trim() ?? '';
108
113
  }, { rowIndex, columnIndex });
109
- });
114
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
110
115
  }
111
116
  }
112
117
  exports.Table = Table;
@@ -0,0 +1,2 @@
1
+ export declare const zeroArgMethodsList: readonly ["click", "dblclick", "hover", "focus", "blur", "check", "uncheck", "clear", "waitFor", "isChecked", "isDisabled", "isVisible", "textContent", "innerText", "allInnerTexts", "allTextContents", "count", "scrollIntoViewIfNeeded", "boundingBox"];
2
+ export declare const oneArgMethodsList: readonly ["fill", "press", "pressSequentially", "selectOption", "setInputFiles", "getAttribute", "dragTo"];
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.oneArgMethodsList = exports.zeroArgMethodsList = void 0;
4
+ exports.zeroArgMethodsList = [
5
+ 'click',
6
+ 'dblclick',
7
+ 'hover',
8
+ 'focus',
9
+ 'blur',
10
+ 'check',
11
+ 'uncheck',
12
+ 'clear',
13
+ 'waitFor',
14
+ 'isChecked',
15
+ 'isDisabled',
16
+ 'isVisible',
17
+ 'textContent',
18
+ 'innerText',
19
+ 'allInnerTexts',
20
+ 'allTextContents',
21
+ 'count',
22
+ 'scrollIntoViewIfNeeded',
23
+ 'boundingBox'
24
+ ];
25
+ exports.oneArgMethodsList = [
26
+ 'fill',
27
+ 'press',
28
+ 'pressSequentially',
29
+ 'selectOption',
30
+ 'setInputFiles',
31
+ 'getAttribute',
32
+ 'dragTo'
33
+ ];
@@ -0,0 +1,2 @@
1
+ export declare const rolesList: readonly ["button", "checkbox", "radio", "heading", "link", "dialog", "list", "listbox", "textbox", "menu", "menuitem", "option", "row", "cell", "tab", "tabpanel", "grid", "gridcell", "combobox", "switch", "progressbar", "status", "tooltip", "tree", "treeitem", "article", "banner", "navigation", "main", "form", "region", "searchbox"];
2
+ export declare const strategyList: readonly ["testId", "selector", "text", "label", "title", "placeholder", "altText", "button", "checkbox", "radio", "heading", "link", "dialog", "list", "listbox", "textbox", "menu", "menuitem", "option", "row", "cell", "tab", "tabpanel", "grid", "gridcell", "combobox", "switch", "progressbar", "status", "tooltip", "tree", "treeitem", "article", "banner", "navigation", "main", "form", "region", "searchbox"];
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.strategyList = exports.rolesList = void 0;
4
+ exports.rolesList = [
5
+ 'button',
6
+ 'checkbox',
7
+ 'radio',
8
+ 'heading',
9
+ 'link',
10
+ 'dialog',
11
+ 'list',
12
+ 'listbox',
13
+ 'textbox',
14
+ 'menu',
15
+ 'menuitem',
16
+ 'option',
17
+ 'row',
18
+ 'cell',
19
+ 'tab',
20
+ 'tabpanel',
21
+ 'grid',
22
+ 'gridcell',
23
+ 'combobox',
24
+ 'switch',
25
+ 'progressbar',
26
+ 'status',
27
+ 'tooltip',
28
+ 'tree',
29
+ 'treeitem',
30
+ 'article',
31
+ 'banner',
32
+ 'navigation',
33
+ 'main',
34
+ 'form',
35
+ 'region',
36
+ 'searchbox'
37
+ ];
38
+ exports.strategyList = [
39
+ 'testId',
40
+ 'selector',
41
+ 'text',
42
+ 'label',
43
+ 'title',
44
+ 'placeholder',
45
+ 'altText',
46
+ ...exports.rolesList
47
+ ];
package/dist/helpers.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Page } from '@playwright/test';
1
+ import { Page } from '@playwright/test';
2
2
  /** Read a value from the browser's localStorage. Returns null if the key is absent. */
3
3
  export declare function getLocalStorage(page: Page, key: string): Promise<string | null>;
4
4
  /** Write a value to the browser's localStorage. */
package/dist/helpers.js CHANGED
@@ -5,21 +5,31 @@ exports.setLocalStorage = setLocalStorage;
5
5
  exports.getSessionStorage = getSessionStorage;
6
6
  exports.setSessionStorage = setSessionStorage;
7
7
  exports.seedSessionStorage = seedSessionStorage;
8
+ const test_1 = require("@playwright/test");
9
+ const caller_location_1 = require("./page/utils/caller-location");
8
10
  /** Read a value from the browser's localStorage. Returns null if the key is absent. */
9
11
  async function getLocalStorage(page, key) {
10
- return page.evaluate((k) => localStorage.getItem(k), key);
12
+ return test_1.test.step(`Get localStorage "${key}"`, async () => {
13
+ return page.evaluate((k) => localStorage.getItem(k), key);
14
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
11
15
  }
12
16
  /** Write a value to the browser's localStorage. */
13
17
  async function setLocalStorage(page, key, value) {
14
- await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value]);
18
+ await test_1.test.step(`Set localStorage "${key}"`, async () => {
19
+ await page.evaluate(([k, v]) => localStorage.setItem(k, v), [key, value]);
20
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
15
21
  }
16
22
  /** Read a value from the browser's sessionStorage. Returns null if the key is absent. */
17
23
  async function getSessionStorage(page, key) {
18
- return page.evaluate((k) => sessionStorage.getItem(k), key);
24
+ return test_1.test.step(`Get sessionStorage "${key}"`, async () => {
25
+ return page.evaluate((k) => sessionStorage.getItem(k), key);
26
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
19
27
  }
20
28
  /** Write a value to the browser's sessionStorage. */
21
29
  async function setSessionStorage(page, key, value) {
22
- await page.evaluate(([k, v]) => sessionStorage.setItem(k, v), [key, value]);
30
+ await test_1.test.step(`Set sessionStorage "${key}"`, async () => {
31
+ await page.evaluate(([k, v]) => sessionStorage.setItem(k, v), [key, value]);
32
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
23
33
  }
24
34
  /**
25
35
  * Seeds sessionStorage entries via addInitScript before any navigation.
@@ -30,10 +40,12 @@ async function seedSessionStorage(page, entries) {
30
40
  const cookies = await page.context().cookies();
31
41
  if (cookies.length > 0) {
32
42
  const pairs = Object.entries(entries);
33
- await page.addInitScript((items) => {
34
- for (const [key, value] of items) {
35
- sessionStorage.setItem(key, value);
36
- }
37
- }, pairs);
43
+ await test_1.test.step('Seed sessionStorage', async () => {
44
+ await page.addInitScript((items) => {
45
+ for (const [key, value] of items) {
46
+ sessionStorage.setItem(key, value);
47
+ }
48
+ }, pairs);
49
+ }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
38
50
  }
39
51
  }