pw-core 1.2.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.
- package/README.md +1 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +777 -0
- package/dist/codegen/config/context-weights.d.ts +8 -0
- package/dist/codegen/config/context-weights.js +11 -0
- package/dist/codegen/config/ignored-attributes.d.ts +1 -0
- package/dist/codegen/config/ignored-attributes.js +4 -0
- package/dist/codegen/config/ignored-classes.d.ts +1 -0
- package/dist/codegen/config/ignored-classes.js +144 -0
- package/dist/codegen/config/ignored-values.d.ts +1 -0
- package/dist/codegen/config/ignored-values.js +4 -0
- package/dist/codegen/config/index.d.ts +29 -0
- package/dist/codegen/config/index.js +17 -0
- package/dist/codegen/config/strategy-order.d.ts +1 -0
- package/dist/codegen/config/strategy-order.js +18 -0
- package/dist/codegen/config/weights.d.ts +15 -0
- package/dist/codegen/config/weights.js +18 -0
- package/dist/codegen/floating-panel/client.d.ts +1 -0
- package/dist/codegen/floating-panel/client.js +248 -0
- package/dist/codegen/floating-panel/index.d.ts +4 -0
- package/dist/codegen/floating-panel/index.js +20 -0
- package/dist/codegen/floating-panel/manager.d.ts +19 -0
- package/dist/codegen/floating-panel/manager.js +90 -0
- package/dist/codegen/floating-panel/template.d.ts +2 -0
- package/dist/codegen/floating-panel/template.js +58 -0
- package/dist/codegen/floating-panel/types.d.ts +7 -0
- package/dist/codegen/floating-panel/types.js +2 -0
- package/dist/codegen/generator/action.validator.d.ts +5 -0
- package/dist/codegen/generator/action.validator.js +27 -0
- package/dist/codegen/generator/index.d.ts +13 -0
- package/dist/codegen/generator/index.js +724 -0
- package/dist/codegen/generator/locator.restricted.d.ts +36 -0
- package/dist/codegen/generator/locator.restricted.js +127 -0
- package/dist/codegen/generator/locator.validator.d.ts +8 -0
- package/dist/codegen/generator/locator.validator.js +38 -0
- package/dist/codegen/hover-tracker/client.d.ts +1 -0
- package/dist/codegen/hover-tracker/client.js +75 -0
- package/dist/codegen/hover-tracker/index.d.ts +3 -0
- package/dist/codegen/hover-tracker/index.js +19 -0
- package/dist/codegen/hover-tracker/manager.d.ts +11 -0
- package/dist/codegen/hover-tracker/manager.js +28 -0
- package/dist/codegen/hover-tracker/types.d.ts +3 -0
- package/dist/codegen/hover-tracker/types.js +2 -0
- package/dist/codegen/index.d.ts +35 -0
- package/dist/codegen/index.js +1147 -0
- package/dist/codegen/key-utils.d.ts +23 -0
- package/dist/codegen/key-utils.js +68 -0
- package/dist/codegen/scorer/base-score.d.ts +1 -0
- package/dist/codegen/scorer/base-score.js +7 -0
- package/dist/codegen/scorer/context-score.d.ts +2 -0
- package/dist/codegen/scorer/context-score.js +27 -0
- package/dist/codegen/scorer/index.d.ts +5 -0
- package/dist/codegen/scorer/index.js +23 -0
- package/dist/codegen/scorer/locator.ranking.d.ts +15 -0
- package/dist/codegen/scorer/locator.ranking.js +83 -0
- package/dist/codegen/scorer/semantic-score.d.ts +1 -0
- package/dist/codegen/scorer/semantic-score.js +47 -0
- package/dist/codegen/types.d.ts +35 -0
- package/dist/codegen/types.js +2 -0
- package/dist/component/table.js +14 -10
- package/dist/constants/methods.d.ts +2 -0
- package/dist/constants/methods.js +33 -0
- package/dist/constants/strategies.d.ts +2 -0
- package/dist/constants/strategies.js +47 -0
- package/dist/page/actions/locator-actions.js +15 -4
- package/dist/page/assertions/verify-chain.d.ts +39 -32
- package/dist/page/assertions/verify-chain.js +18 -4
- package/dist/page/assertions/verify-helpers.js +12 -3
- package/dist/page/config.d.ts +109 -71
- package/dist/page/config.js +10 -31
- package/dist/page/locators/dynamic-locator-resolver.js +29 -9
- package/dist/page/locators/resolver.d.ts +15 -15
- package/dist/page/locators/resolver.js +151 -32
- package/dist/page/registry.d.ts +43 -6
- package/dist/page/registry.js +90 -18
- package/dist/page/typed-page.d.ts +33 -29
- package/dist/page/typed-page.js +40 -11
- package/dist/page/types/proxy-methods.d.ts +69 -54
- package/dist/page/types/validation.d.ts +48 -1
- package/dist/page/utils/caller-location.js +13 -9
- package/dist/page/utils/formatter.js +6 -3
- package/dist/types/methods.d.ts +4 -0
- package/dist/types/methods.js +2 -0
- package/dist/types/strategies.d.ts +17 -0
- package/dist/types/strategies.js +2 -0
- 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,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,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
|
+
}
|
package/dist/component/table.js
CHANGED
|
@@ -20,15 +20,15 @@ class TableRows extends Array {
|
|
|
20
20
|
return this[0] ? this[0][key] : undefined;
|
|
21
21
|
}
|
|
22
22
|
// Return the first matching row
|
|
23
|
-
return this.find(row => row[key] === value);
|
|
23
|
+
return this.find((row) => row[key] === value);
|
|
24
24
|
}
|
|
25
25
|
getAll(key, value) {
|
|
26
26
|
if (arguments.length === 1) {
|
|
27
27
|
// Return the values of the key from all rows
|
|
28
|
-
return this.map(row => row[key]);
|
|
28
|
+
return this.map((row) => row[key]);
|
|
29
29
|
}
|
|
30
30
|
// Return all matching rows
|
|
31
|
-
return this.filter(row => row[key] === value);
|
|
31
|
+
return this.filter((row) => row[key] === value);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
exports.TableRows = TableRows;
|
|
@@ -42,7 +42,9 @@ class Table {
|
|
|
42
42
|
*/
|
|
43
43
|
async getHeaders() {
|
|
44
44
|
return test_1.test.step(`Get headers of table`, async () => {
|
|
45
|
-
const headers = await this.root
|
|
45
|
+
const headers = await this.root
|
|
46
|
+
.locator('th')
|
|
47
|
+
.evaluateAll((ths) => ths.map((th) => th.textContent?.trim().toLowerCase() || ''));
|
|
46
48
|
return headers;
|
|
47
49
|
}, { box: true, location: (0, caller_location_1.getCallerLocation)() });
|
|
48
50
|
}
|
|
@@ -53,9 +55,11 @@ class Table {
|
|
|
53
55
|
async getRows() {
|
|
54
56
|
return test_1.test.step('Get table rows', async () => {
|
|
55
57
|
const headers = await this.getHeaders();
|
|
56
|
-
const rowsData = await this.root
|
|
57
|
-
|
|
58
|
-
|
|
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() || '');
|
|
59
63
|
if (cells.length === 0)
|
|
60
64
|
return null; // Skip header or empty rows
|
|
61
65
|
const rowData = {};
|
|
@@ -83,8 +87,8 @@ class Table {
|
|
|
83
87
|
*/
|
|
84
88
|
async getRowCount() {
|
|
85
89
|
return test_1.test.step('Get table row count', async () => {
|
|
86
|
-
return await this.root.locator('tbody tr, tr[data-testid="transaction-row"]').evaluateAll(trs => {
|
|
87
|
-
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;
|
|
88
92
|
});
|
|
89
93
|
}, { box: true, location: (0, caller_location_1.getCallerLocation)() });
|
|
90
94
|
}
|
|
@@ -100,7 +104,7 @@ class Table {
|
|
|
100
104
|
}
|
|
101
105
|
return await this.root.evaluate((tableEl, { rowIndex, columnIndex }) => {
|
|
102
106
|
const trs = Array.from(tableEl.querySelectorAll('tbody tr, tr[data-testid="transaction-row"]'));
|
|
103
|
-
const dataRows = trs.filter(tr => tr.querySelectorAll('td').length > 0);
|
|
107
|
+
const dataRows = trs.filter((tr) => tr.querySelectorAll('td').length > 0);
|
|
104
108
|
if (rowIndex < 0 || rowIndex >= dataRows.length) {
|
|
105
109
|
throw new Error(`Row index ${rowIndex} is out of bounds (0 to ${dataRows.length - 1})`);
|
|
106
110
|
}
|
|
@@ -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
|
+
];
|
|
@@ -23,7 +23,13 @@ function executeAction(prop, resolveLocatorFn, timeout, args) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
const isCount = prop === 'count';
|
|
26
|
-
const
|
|
26
|
+
const userOptions = optionsIndex !== -1 && methodArgs.length > optionsIndex ? methodArgs[optionsIndex] : undefined;
|
|
27
|
+
const locator = resolveLocatorFn(locatorKey, {
|
|
28
|
+
nth: optNth,
|
|
29
|
+
hasText: optHasText,
|
|
30
|
+
raw: isCount,
|
|
31
|
+
...userOptions
|
|
32
|
+
});
|
|
27
33
|
const method = locator[prop];
|
|
28
34
|
if (typeof method !== 'function') {
|
|
29
35
|
throw new Error(`Property '${prop}' does not exist on Locator.`);
|
|
@@ -31,6 +37,13 @@ function executeAction(prop, resolveLocatorFn, timeout, args) {
|
|
|
31
37
|
if (prop === 'dragTo' && methodArgs.length > 0) {
|
|
32
38
|
methodArgs[0] = resolveLocatorFn(methodArgs[0]);
|
|
33
39
|
}
|
|
40
|
+
if (optionsIndex !== -1 && methodArgs.length > optionsIndex) {
|
|
41
|
+
const opts = methodArgs[optionsIndex];
|
|
42
|
+
if (opts && typeof opts === 'object') {
|
|
43
|
+
const { exact, checked, disabled, expanded, includeHidden, level, pressed, selected, ...rest } = opts;
|
|
44
|
+
methodArgs[optionsIndex] = rest;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
34
47
|
if (timeout !== undefined) {
|
|
35
48
|
if (optionsIndex !== -1) {
|
|
36
49
|
while (methodArgs.length < optionsIndex) {
|
|
@@ -65,9 +78,7 @@ function executeAction(prop, resolveLocatorFn, timeout, args) {
|
|
|
65
78
|
await locator.focus();
|
|
66
79
|
await locator.evaluate((el, val) => {
|
|
67
80
|
const inputEl = el;
|
|
68
|
-
const prototype = el.tagName === 'TEXTAREA'
|
|
69
|
-
? window.HTMLTextAreaElement.prototype
|
|
70
|
-
: window.HTMLInputElement.prototype;
|
|
81
|
+
const prototype = el.tagName === 'TEXTAREA' ? window.HTMLTextAreaElement.prototype : window.HTMLInputElement.prototype;
|
|
71
82
|
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'value');
|
|
72
83
|
if (descriptor && descriptor.set) {
|
|
73
84
|
descriptor.set.call(inputEl, val);
|