testchimp-runner-core 0.0.33 → 0.0.35
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/dist/execution-service.d.ts +1 -4
- package/dist/execution-service.d.ts.map +1 -1
- package/dist/execution-service.js +155 -468
- package/dist/execution-service.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/llm-facade.d.ts.map +1 -1
- package/dist/llm-facade.js +7 -7
- package/dist/llm-facade.js.map +1 -1
- package/dist/llm-provider.d.ts +9 -0
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/model-constants.d.ts +16 -5
- package/dist/model-constants.d.ts.map +1 -1
- package/dist/model-constants.js +17 -6
- package/dist/model-constants.js.map +1 -1
- package/dist/orchestrator/decision-parser.d.ts +18 -0
- package/dist/orchestrator/decision-parser.d.ts.map +1 -0
- package/dist/orchestrator/decision-parser.js +127 -0
- package/dist/orchestrator/decision-parser.js.map +1 -0
- package/dist/orchestrator/index.d.ts +4 -2
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +15 -2
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/orchestrator-agent.d.ts +17 -22
- package/dist/orchestrator/orchestrator-agent.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator-agent.js +708 -577
- package/dist/orchestrator/orchestrator-agent.js.map +1 -1
- package/dist/orchestrator/orchestrator-prompts.d.ts +32 -0
- package/dist/orchestrator/orchestrator-prompts.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator-prompts.js +737 -0
- package/dist/orchestrator/orchestrator-prompts.js.map +1 -0
- package/dist/orchestrator/page-som-handler.d.ts +106 -0
- package/dist/orchestrator/page-som-handler.d.ts.map +1 -0
- package/dist/orchestrator/page-som-handler.js +1353 -0
- package/dist/orchestrator/page-som-handler.js.map +1 -0
- package/dist/orchestrator/som-types.d.ts +149 -0
- package/dist/orchestrator/som-types.d.ts.map +1 -0
- package/dist/orchestrator/som-types.js +87 -0
- package/dist/orchestrator/som-types.js.map +1 -0
- package/dist/orchestrator/tool-registry.d.ts +2 -0
- package/dist/orchestrator/tool-registry.d.ts.map +1 -1
- package/dist/orchestrator/tool-registry.js.map +1 -1
- package/dist/orchestrator/tools/index.d.ts +5 -1
- package/dist/orchestrator/tools/index.d.ts.map +1 -1
- package/dist/orchestrator/tools/index.js +9 -2
- package/dist/orchestrator/tools/index.js.map +1 -1
- package/dist/orchestrator/tools/refresh-som-markers.d.ts +12 -0
- package/dist/orchestrator/tools/refresh-som-markers.d.ts.map +1 -0
- package/dist/orchestrator/tools/refresh-som-markers.js +64 -0
- package/dist/orchestrator/tools/refresh-som-markers.js.map +1 -0
- package/dist/orchestrator/tools/verify-action-result.d.ts +17 -0
- package/dist/orchestrator/tools/verify-action-result.d.ts.map +1 -0
- package/dist/orchestrator/tools/verify-action-result.js +140 -0
- package/dist/orchestrator/tools/verify-action-result.js.map +1 -0
- package/dist/orchestrator/tools/view-previous-screenshot.d.ts +15 -0
- package/dist/orchestrator/tools/view-previous-screenshot.d.ts.map +1 -0
- package/dist/orchestrator/tools/view-previous-screenshot.js +92 -0
- package/dist/orchestrator/tools/view-previous-screenshot.js.map +1 -0
- package/dist/orchestrator/types.d.ts +49 -1
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/orchestrator/types.js +11 -1
- package/dist/orchestrator/types.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +40 -34
- package/dist/prompts.js.map +1 -1
- package/dist/scenario-service.d.ts +5 -0
- package/dist/scenario-service.d.ts.map +1 -1
- package/dist/scenario-service.js +17 -0
- package/dist/scenario-service.js.map +1 -1
- package/dist/scenario-worker-class.d.ts +4 -0
- package/dist/scenario-worker-class.d.ts.map +1 -1
- package/dist/scenario-worker-class.js +21 -3
- package/dist/scenario-worker-class.js.map +1 -1
- package/dist/testing/agent-tester.d.ts +35 -0
- package/dist/testing/agent-tester.d.ts.map +1 -0
- package/dist/testing/agent-tester.js +84 -0
- package/dist/testing/agent-tester.js.map +1 -0
- package/dist/testing/ref-translator-tester.d.ts +44 -0
- package/dist/testing/ref-translator-tester.d.ts.map +1 -0
- package/dist/testing/ref-translator-tester.js +104 -0
- package/dist/testing/ref-translator-tester.js.map +1 -0
- package/dist/utils/coordinate-converter.d.ts +32 -0
- package/dist/utils/coordinate-converter.d.ts.map +1 -0
- package/dist/utils/coordinate-converter.js +130 -0
- package/dist/utils/coordinate-converter.js.map +1 -0
- package/dist/utils/hierarchical-selector.d.ts +47 -0
- package/dist/utils/hierarchical-selector.d.ts.map +1 -0
- package/dist/utils/hierarchical-selector.js +212 -0
- package/dist/utils/hierarchical-selector.js.map +1 -0
- package/dist/utils/page-info-retry.d.ts +14 -0
- package/dist/utils/page-info-retry.d.ts.map +1 -0
- package/dist/utils/page-info-retry.js +60 -0
- package/dist/utils/page-info-retry.js.map +1 -0
- package/dist/utils/page-info-utils.d.ts +1 -0
- package/dist/utils/page-info-utils.d.ts.map +1 -1
- package/dist/utils/page-info-utils.js +46 -18
- package/dist/utils/page-info-utils.js.map +1 -1
- package/dist/utils/ref-attacher.d.ts +21 -0
- package/dist/utils/ref-attacher.d.ts.map +1 -0
- package/dist/utils/ref-attacher.js +149 -0
- package/dist/utils/ref-attacher.js.map +1 -0
- package/dist/utils/ref-translator.d.ts +49 -0
- package/dist/utils/ref-translator.d.ts.map +1 -0
- package/dist/utils/ref-translator.js +276 -0
- package/dist/utils/ref-translator.js.map +1 -0
- package/package.json +1 -1
- package/plandocs/BEFORE_AFTER_VERIFICATION.md +148 -0
- package/plandocs/COORDINATE_MODE_DIAGNOSIS.md +144 -0
- package/plandocs/IMPLEMENTATION_STATUS.md +108 -0
- package/plandocs/PHASE_1_COMPLETE.md +165 -0
- package/plandocs/PHASE_1_SUMMARY.md +184 -0
- package/plandocs/PROMPT_OPTIMIZATION_ANALYSIS.md +120 -0
- package/plandocs/PROMPT_SANITY_CHECK.md +120 -0
- package/plandocs/SESSION_SUMMARY_v0.0.33.md +151 -0
- package/plandocs/TROUBLESHOOTING_SESSION.md +72 -0
- package/plandocs/VISUAL_AGENT_EVOLUTION_PLAN.md +396 -0
- package/plandocs/WHATS_NEW_v0.0.33.md +183 -0
- package/plandocs/exploratory-mode-support-v2.plan.md +953 -0
- package/plandocs/exploratory-mode-support.plan.md +928 -0
- package/plandocs/journey-id-tracking-addendum.md +227 -0
- package/src/execution-service.ts +179 -596
- package/src/index.ts +10 -0
- package/src/llm-facade.ts +8 -8
- package/src/llm-provider.ts +11 -1
- package/src/model-constants.ts +17 -5
- package/src/orchestrator/decision-parser.ts +139 -0
- package/src/orchestrator/index.ts +27 -2
- package/src/orchestrator/orchestrator-agent.ts +868 -623
- package/src/orchestrator/orchestrator-prompts.ts +786 -0
- package/src/orchestrator/page-som-handler.ts +1565 -0
- package/src/orchestrator/som-types.ts +188 -0
- package/src/orchestrator/tool-registry.ts +2 -0
- package/src/orchestrator/tools/index.ts +5 -1
- package/src/orchestrator/tools/refresh-som-markers.ts +69 -0
- package/src/orchestrator/tools/verify-action-result.ts +159 -0
- package/src/orchestrator/tools/view-previous-screenshot.ts +103 -0
- package/src/orchestrator/types.ts +95 -4
- package/src/prompts.ts +40 -34
- package/src/scenario-service.ts +20 -0
- package/src/scenario-worker-class.ts +30 -4
- package/src/utils/coordinate-converter.ts +162 -0
- package/src/utils/page-info-retry.ts +65 -0
- package/src/utils/page-info-utils.ts +53 -18
- package/testchimp-runner-core-0.0.35.tgz +0 -0
- /package/{CREDIT_CALLBACK_ARCHITECTURE.md → plandocs/CREDIT_CALLBACK_ARCHITECTURE.md} +0 -0
- /package/{INTEGRATION_COMPLETE.md → plandocs/INTEGRATION_COMPLETE.md} +0 -0
- /package/{VISION_DIAGNOSTICS_IMPROVEMENTS.md → plandocs/VISION_DIAGNOSTICS_IMPROVEMENTS.md} +0 -0
- /package/{RELEASE_0.0.26.md → releasenotes/RELEASE_0.0.26.md} +0 -0
- /package/{RELEASE_0.0.27.md → releasenotes/RELEASE_0.0.27.md} +0 -0
- /package/{RELEASE_0.0.28.md → releasenotes/RELEASE_0.0.28.md} +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Coordinate Converter Utility
|
|
4
|
+
* Converts percentage-based coordinates to pixel coordinates and generates Playwright commands
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.CoordinateConverter = void 0;
|
|
8
|
+
class CoordinateConverter {
|
|
9
|
+
/**
|
|
10
|
+
* Convert percentage coordinates to actual pixel coordinates
|
|
11
|
+
*/
|
|
12
|
+
static percentToPixels(xPercent, yPercent, viewportWidth, viewportHeight) {
|
|
13
|
+
return {
|
|
14
|
+
x: Math.round((xPercent / 100) * viewportWidth),
|
|
15
|
+
y: Math.round((yPercent / 100) * viewportHeight)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get viewport dimensions from page
|
|
20
|
+
*/
|
|
21
|
+
static async getViewportSize(page) {
|
|
22
|
+
return await page.evaluate(() => {
|
|
23
|
+
const win = globalThis.window;
|
|
24
|
+
return {
|
|
25
|
+
width: win.innerWidth,
|
|
26
|
+
height: win.innerHeight
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Convert coordinate action with percentages to Playwright commands
|
|
32
|
+
* Returns array of command strings
|
|
33
|
+
*/
|
|
34
|
+
static async generateCommands(action, page) {
|
|
35
|
+
const viewport = await this.getViewportSize(page);
|
|
36
|
+
const { x, y } = this.percentToPixels(action.xPercent, action.yPercent, viewport.width, viewport.height);
|
|
37
|
+
const commands = [];
|
|
38
|
+
switch (action.action) {
|
|
39
|
+
case 'click':
|
|
40
|
+
commands.push(`await page.mouse.click(${x}, ${y});`);
|
|
41
|
+
break;
|
|
42
|
+
case 'doubleClick':
|
|
43
|
+
commands.push(`await page.mouse.dblclick(${x}, ${y});`);
|
|
44
|
+
break;
|
|
45
|
+
case 'rightClick':
|
|
46
|
+
commands.push(`await page.mouse.click(${x}, ${y}, { button: 'right' });`);
|
|
47
|
+
break;
|
|
48
|
+
case 'hover':
|
|
49
|
+
commands.push(`await page.mouse.move(${x}, ${y});`);
|
|
50
|
+
break;
|
|
51
|
+
case 'drag':
|
|
52
|
+
if (action.toXPercent === undefined || action.toYPercent === undefined) {
|
|
53
|
+
throw new Error('Drag action requires toXPercent and toYPercent');
|
|
54
|
+
}
|
|
55
|
+
const to = this.percentToPixels(action.toXPercent, action.toYPercent, viewport.width, viewport.height);
|
|
56
|
+
commands.push(`await page.mouse.move(${x}, ${y});`);
|
|
57
|
+
commands.push(`await page.mouse.down();`);
|
|
58
|
+
commands.push(`await page.mouse.move(${to.x}, ${to.y});`);
|
|
59
|
+
commands.push(`await page.mouse.up();`);
|
|
60
|
+
break;
|
|
61
|
+
case 'fill':
|
|
62
|
+
if (!action.value) {
|
|
63
|
+
throw new Error('Fill action requires value');
|
|
64
|
+
}
|
|
65
|
+
// Click to focus, wait briefly, then type
|
|
66
|
+
commands.push(`await page.mouse.click(${x}, ${y});`);
|
|
67
|
+
commands.push(`await page.waitForTimeout(100);`);
|
|
68
|
+
commands.push(`await page.keyboard.type(${JSON.stringify(action.value)});`);
|
|
69
|
+
break;
|
|
70
|
+
case 'scroll':
|
|
71
|
+
const scrollAmount = action.scrollAmount || 100;
|
|
72
|
+
// Move to position, then scroll
|
|
73
|
+
commands.push(`await page.mouse.move(${x}, ${y});`);
|
|
74
|
+
commands.push(`await page.mouse.wheel(0, ${scrollAmount});`);
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unknown coordinate action: ${action.action}`);
|
|
78
|
+
}
|
|
79
|
+
return commands;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Execute coordinate action directly on page
|
|
83
|
+
* Used during agent execution (converts and runs immediately)
|
|
84
|
+
*/
|
|
85
|
+
static async executeAction(action, page) {
|
|
86
|
+
const viewport = await this.getViewportSize(page);
|
|
87
|
+
const { x, y } = this.percentToPixels(action.xPercent, action.yPercent, viewport.width, viewport.height);
|
|
88
|
+
switch (action.action) {
|
|
89
|
+
case 'click':
|
|
90
|
+
await page.mouse.click(x, y);
|
|
91
|
+
break;
|
|
92
|
+
case 'doubleClick':
|
|
93
|
+
await page.mouse.dblclick(x, y);
|
|
94
|
+
break;
|
|
95
|
+
case 'rightClick':
|
|
96
|
+
await page.mouse.click(x, y, { button: 'right' });
|
|
97
|
+
break;
|
|
98
|
+
case 'hover':
|
|
99
|
+
await page.mouse.move(x, y);
|
|
100
|
+
break;
|
|
101
|
+
case 'drag':
|
|
102
|
+
if (action.toXPercent === undefined || action.toYPercent === undefined) {
|
|
103
|
+
throw new Error('Drag requires toXPercent and toYPercent');
|
|
104
|
+
}
|
|
105
|
+
const to = this.percentToPixels(action.toXPercent, action.toYPercent, viewport.width, viewport.height);
|
|
106
|
+
await page.mouse.move(x, y);
|
|
107
|
+
await page.mouse.down();
|
|
108
|
+
await page.mouse.move(to.x, to.y);
|
|
109
|
+
await page.mouse.up();
|
|
110
|
+
break;
|
|
111
|
+
case 'fill':
|
|
112
|
+
if (!action.value) {
|
|
113
|
+
throw new Error('Fill requires value');
|
|
114
|
+
}
|
|
115
|
+
await page.mouse.click(x, y);
|
|
116
|
+
await page.waitForTimeout(100);
|
|
117
|
+
await page.keyboard.type(action.value);
|
|
118
|
+
break;
|
|
119
|
+
case 'scroll':
|
|
120
|
+
const scrollAmount = action.scrollAmount || 100;
|
|
121
|
+
await page.mouse.move(x, y);
|
|
122
|
+
await page.mouse.wheel(0, scrollAmount);
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
throw new Error(`Unknown coordinate action: ${action.action}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.CoordinateConverter = CoordinateConverter;
|
|
130
|
+
//# sourceMappingURL=coordinate-converter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coordinate-converter.js","sourceRoot":"","sources":["../../src/utils/coordinate-converter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hierarchical Selector Generator
|
|
3
|
+
* Generates Playwright selectors using ARIA tree hierarchy for uniqueness
|
|
4
|
+
* Mimics Playwright's own selector generation strategy
|
|
5
|
+
*/
|
|
6
|
+
export interface AriaNode {
|
|
7
|
+
role: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
ref?: string;
|
|
10
|
+
children?: AriaNode[];
|
|
11
|
+
}
|
|
12
|
+
export interface AriaTreePath {
|
|
13
|
+
nodes: AriaNode[];
|
|
14
|
+
targetIndex: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class HierarchicalSelectorGenerator {
|
|
17
|
+
private logger?;
|
|
18
|
+
constructor(logger?: (message: string, level?: 'log' | 'error' | 'warn') => void);
|
|
19
|
+
/**
|
|
20
|
+
* Generate Playwright selectors for a ref using ARIA tree hierarchy
|
|
21
|
+
* Returns selectors in priority order, considering parent context for uniqueness
|
|
22
|
+
*/
|
|
23
|
+
generateSelectorsForRef(ref: string, refMap: Map<string, any>, ariaTree: any): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Strategy 1: Unique attributes (most reliable)
|
|
26
|
+
*/
|
|
27
|
+
private generateUniqueAttributeSelectors;
|
|
28
|
+
/**
|
|
29
|
+
* Strategy 2: Parent-scoped selectors (uses hierarchy for uniqueness)
|
|
30
|
+
*/
|
|
31
|
+
private generateParentScopedSelectors;
|
|
32
|
+
/**
|
|
33
|
+
* Strategy 3: nth-match based on sibling position in ARIA tree
|
|
34
|
+
*/
|
|
35
|
+
private generateNthMatchSelectors;
|
|
36
|
+
/**
|
|
37
|
+
* Strategy 4: Generic fallbacks (last resort)
|
|
38
|
+
*/
|
|
39
|
+
private generateGenericFallbacks;
|
|
40
|
+
/**
|
|
41
|
+
* Walk ARIA tree to find path from root to target ref
|
|
42
|
+
*/
|
|
43
|
+
private getAriaTreePath;
|
|
44
|
+
private walkTree;
|
|
45
|
+
private escape;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=hierarchical-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hierarchical-selector.d.ts","sourceRoot":"","sources":["../../src/utils/hierarchical-selector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,6BAA6B;IACxC,OAAO,CAAC,MAAM,CAAC,CAA8D;gBAEjE,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,MAAM,KAAK,IAAI;IAIhF;;;OAGG;IACH,uBAAuB,CACrB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EACxB,QAAQ,EAAE,GAAG,GACZ,MAAM,EAAE;IA+BX;;OAEG;IACH,OAAO,CAAC,gCAAgC;IAgCxC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IA4CrC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA2CjC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA2BhC;;OAEG;IACH,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,QAAQ;IAyBhB,OAAO,CAAC,MAAM;CAGf"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hierarchical Selector Generator
|
|
4
|
+
* Generates Playwright selectors using ARIA tree hierarchy for uniqueness
|
|
5
|
+
* Mimics Playwright's own selector generation strategy
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.HierarchicalSelectorGenerator = void 0;
|
|
9
|
+
class HierarchicalSelectorGenerator {
|
|
10
|
+
constructor(logger) {
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate Playwright selectors for a ref using ARIA tree hierarchy
|
|
15
|
+
* Returns selectors in priority order, considering parent context for uniqueness
|
|
16
|
+
*/
|
|
17
|
+
generateSelectorsForRef(ref, refMap, ariaTree) {
|
|
18
|
+
const element = refMap.get(ref);
|
|
19
|
+
if (!element) {
|
|
20
|
+
this.logger?.(`[HierarchicalSelector] Ref '${ref}' not found in refMap`, 'warn');
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
// Get ARIA tree path to understand hierarchy
|
|
24
|
+
const path = this.getAriaTreePath(ariaTree, ref);
|
|
25
|
+
const selectors = [];
|
|
26
|
+
// Strategy 1: BEST - Unique attribute (name, id, data-testid)
|
|
27
|
+
selectors.push(...this.generateUniqueAttributeSelectors(element));
|
|
28
|
+
// Strategy 2: GOOD - Parent-scoped semantic selectors
|
|
29
|
+
if (path) {
|
|
30
|
+
selectors.push(...this.generateParentScopedSelectors(element, path));
|
|
31
|
+
}
|
|
32
|
+
// Strategy 3: FALLBACK - nth-match based on sibling position
|
|
33
|
+
if (path) {
|
|
34
|
+
selectors.push(...this.generateNthMatchSelectors(element, path, ref));
|
|
35
|
+
}
|
|
36
|
+
// Strategy 4: LAST RESORT - Generic selectors with first()
|
|
37
|
+
selectors.push(...this.generateGenericFallbacks(element));
|
|
38
|
+
return selectors;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Strategy 1: Unique attributes (most reliable)
|
|
42
|
+
*/
|
|
43
|
+
generateUniqueAttributeSelectors(element) {
|
|
44
|
+
const selectors = [];
|
|
45
|
+
const { tag, name, id, ariaLabel, placeholder, type } = element;
|
|
46
|
+
// name attribute (best for forms)
|
|
47
|
+
if (name && (tag === 'input' || tag === 'textarea' || tag === 'select')) {
|
|
48
|
+
selectors.push(`${tag}[name="${name}"]`);
|
|
49
|
+
}
|
|
50
|
+
// data-testid
|
|
51
|
+
if (element.dataTestId) {
|
|
52
|
+
selectors.push(`[data-testid="${element.dataTestId}"]`);
|
|
53
|
+
}
|
|
54
|
+
// ID (only if stable - avoid auto-generated)
|
|
55
|
+
if (id && !id.match(/^(rc_|:r[0-9]+:|__|«)/)) {
|
|
56
|
+
selectors.push(`#${id}`);
|
|
57
|
+
}
|
|
58
|
+
// aria-label (semantic)
|
|
59
|
+
if (ariaLabel && ariaLabel.length > 0 && ariaLabel.length < 50) {
|
|
60
|
+
selectors.push(`[aria-label="${this.escape(ariaLabel)}"]`);
|
|
61
|
+
}
|
|
62
|
+
// placeholder
|
|
63
|
+
if (placeholder && placeholder.length > 0) {
|
|
64
|
+
selectors.push(`[placeholder="${this.escape(placeholder)}"]`);
|
|
65
|
+
}
|
|
66
|
+
return selectors;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Strategy 2: Parent-scoped selectors (uses hierarchy for uniqueness)
|
|
70
|
+
*/
|
|
71
|
+
generateParentScopedSelectors(element, path) {
|
|
72
|
+
const selectors = [];
|
|
73
|
+
const { role, text, tag } = element;
|
|
74
|
+
const nodes = path.nodes;
|
|
75
|
+
if (nodes.length < 2)
|
|
76
|
+
return selectors; // Need at least parent + target
|
|
77
|
+
// Get parent and grandparent for context
|
|
78
|
+
const parent = nodes[nodes.length - 2];
|
|
79
|
+
const grandparent = nodes.length >= 3 ? nodes[nodes.length - 3] : null;
|
|
80
|
+
// Parent has text content - use it for scoping
|
|
81
|
+
if (parent.name && parent.name.length > 0 && parent.name.length < 50) {
|
|
82
|
+
// Approach 1: Scope by parent text, then find by role
|
|
83
|
+
if (role) {
|
|
84
|
+
selectors.push(`text=${parent.name} >> [role="${role}"]`);
|
|
85
|
+
}
|
|
86
|
+
// Approach 2: Scope by parent text, then find by tag
|
|
87
|
+
if (tag) {
|
|
88
|
+
selectors.push(`text=${parent.name} >> ${tag}`);
|
|
89
|
+
}
|
|
90
|
+
// Approach 3: Use getByText parent chaining (Playwright style)
|
|
91
|
+
if (text && text.length > 0) {
|
|
92
|
+
selectors.push(`text=${parent.name} >> text=${text}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Grandparent scoping (for nested structures)
|
|
96
|
+
if (grandparent && grandparent.name && grandparent.name.length > 0) {
|
|
97
|
+
if (role) {
|
|
98
|
+
selectors.push(`text=${grandparent.name} >> [role="${role}"]`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Text content with role (if element has visible text)
|
|
102
|
+
if (text && text.length > 0 && text.length < 30 && role) {
|
|
103
|
+
selectors.push(`[role="${role}"][aria-label*="${this.escape(text)}"]`);
|
|
104
|
+
}
|
|
105
|
+
return selectors;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Strategy 3: nth-match based on sibling position in ARIA tree
|
|
109
|
+
*/
|
|
110
|
+
generateNthMatchSelectors(element, path, ref) {
|
|
111
|
+
const selectors = [];
|
|
112
|
+
const { role, tag, text } = element;
|
|
113
|
+
const nodes = path.nodes;
|
|
114
|
+
if (nodes.length < 2)
|
|
115
|
+
return selectors;
|
|
116
|
+
const parent = nodes[nodes.length - 2];
|
|
117
|
+
// Find position among siblings with same role
|
|
118
|
+
const siblingsWithSameRole = (parent.children || []).filter((child) => child.role === role);
|
|
119
|
+
const position = siblingsWithSameRole.findIndex((child) => child.ref === ref);
|
|
120
|
+
if (position >= 0) {
|
|
121
|
+
// nth-match by role
|
|
122
|
+
if (role) {
|
|
123
|
+
selectors.push(`[role="${role}"] >> nth=${position}`);
|
|
124
|
+
// Playwright's getByRole with nth
|
|
125
|
+
selectors.push(`getByRole('${role}').nth(${position})`);
|
|
126
|
+
}
|
|
127
|
+
// nth-match by tag
|
|
128
|
+
if (tag) {
|
|
129
|
+
selectors.push(`${tag} >> nth=${position}`);
|
|
130
|
+
selectors.push(`locator('${tag}').nth(${position})`);
|
|
131
|
+
}
|
|
132
|
+
// Special case: first or last
|
|
133
|
+
if (position === 0) {
|
|
134
|
+
if (role)
|
|
135
|
+
selectors.push(`[role="${role}"] >> first`);
|
|
136
|
+
if (tag)
|
|
137
|
+
selectors.push(`${tag} >> first`);
|
|
138
|
+
}
|
|
139
|
+
else if (position === siblingsWithSameRole.length - 1) {
|
|
140
|
+
if (role)
|
|
141
|
+
selectors.push(`[role="${role}"] >> last`);
|
|
142
|
+
if (tag)
|
|
143
|
+
selectors.push(`${tag} >> last`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return selectors;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Strategy 4: Generic fallbacks (last resort)
|
|
150
|
+
*/
|
|
151
|
+
generateGenericFallbacks(element) {
|
|
152
|
+
const selectors = [];
|
|
153
|
+
const { tag, role, text, type } = element;
|
|
154
|
+
// Text content
|
|
155
|
+
if (text && text.length > 0 && text.length < 30) {
|
|
156
|
+
selectors.push(`text=${this.escape(text)}`);
|
|
157
|
+
selectors.push(`getByText('${this.escape(text)}')`);
|
|
158
|
+
}
|
|
159
|
+
// Role only (use first())
|
|
160
|
+
if (role) {
|
|
161
|
+
selectors.push(`[role="${role}"] >> first`);
|
|
162
|
+
selectors.push(`getByRole('${role}').first()`);
|
|
163
|
+
}
|
|
164
|
+
// Type selector
|
|
165
|
+
if (type) {
|
|
166
|
+
selectors.push(`${tag}[type="${type}"] >> first`);
|
|
167
|
+
}
|
|
168
|
+
// Ultimate fallback
|
|
169
|
+
selectors.push(`${tag} >> first`);
|
|
170
|
+
return selectors;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Walk ARIA tree to find path from root to target ref
|
|
174
|
+
*/
|
|
175
|
+
getAriaTreePath(tree, targetRef) {
|
|
176
|
+
const path = [];
|
|
177
|
+
const found = this.walkTree(tree, targetRef, path);
|
|
178
|
+
if (found) {
|
|
179
|
+
return {
|
|
180
|
+
nodes: path,
|
|
181
|
+
targetIndex: path.length - 1
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
walkTree(node, targetRef, path) {
|
|
187
|
+
if (!node)
|
|
188
|
+
return false;
|
|
189
|
+
// Add current node to path
|
|
190
|
+
path.push(node);
|
|
191
|
+
// Found it!
|
|
192
|
+
if (node.ref === targetRef) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
// Search children
|
|
196
|
+
if (node.children && Array.isArray(node.children)) {
|
|
197
|
+
for (const child of node.children) {
|
|
198
|
+
if (this.walkTree(child, targetRef, path)) {
|
|
199
|
+
return true; // Found in this subtree
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Not found in this subtree - backtrack
|
|
204
|
+
path.pop();
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
escape(str) {
|
|
208
|
+
return str.replace(/'/g, "\\'").replace(/"/g, '\\"');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.HierarchicalSelectorGenerator = HierarchicalSelectorGenerator;
|
|
212
|
+
//# sourceMappingURL=hierarchical-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hierarchical-selector.js","sourceRoot":"","sources":["../../src/utils/hierarchical-selector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Info Retry Utility
|
|
3
|
+
* Handles adaptive page loading with exponential backoff
|
|
4
|
+
*/
|
|
5
|
+
import { PageInfo } from './page-info-utils';
|
|
6
|
+
export declare class PageInfoRetry {
|
|
7
|
+
/**
|
|
8
|
+
* Get page info with retry logic - waits for interactive elements to appear
|
|
9
|
+
* Uses exponential backoff to handle slow-loading React/Vue/Angular apps
|
|
10
|
+
*/
|
|
11
|
+
static getWithRetry(page: any, maxAttempts?: number): Promise<PageInfo>;
|
|
12
|
+
private static calculateTotalWaitTime;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=page-info-retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-info-retry.d.ts","sourceRoot":"","sources":["../../src/utils/page-info-retry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAuB,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElE,qBAAa,aAAa;IACxB;;;OAGG;WACU,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAE,MAAU,GAAG,OAAO,CAAC,QAAQ,CAAC;IA0ChF,OAAO,CAAC,MAAM,CAAC,sBAAsB;CAStC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Page Info Retry Utility
|
|
4
|
+
* Handles adaptive page loading with exponential backoff
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.PageInfoRetry = void 0;
|
|
8
|
+
const page_info_utils_1 = require("./page-info-utils");
|
|
9
|
+
class PageInfoRetry {
|
|
10
|
+
/**
|
|
11
|
+
* Get page info with retry logic - waits for interactive elements to appear
|
|
12
|
+
* Uses exponential backoff to handle slow-loading React/Vue/Angular apps
|
|
13
|
+
*/
|
|
14
|
+
static async getWithRetry(page, maxAttempts = 6) {
|
|
15
|
+
// Wait for initial page load (generous timeout for slow apps)
|
|
16
|
+
try {
|
|
17
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 20000 }).catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
catch (waitError) {
|
|
20
|
+
// Continue even if wait fails
|
|
21
|
+
}
|
|
22
|
+
let attempt = 0;
|
|
23
|
+
let backoffMs = 1000; // Start with 1 second (adequate for most sites)
|
|
24
|
+
while (attempt < maxAttempts) {
|
|
25
|
+
attempt++;
|
|
26
|
+
// Try to extract page info
|
|
27
|
+
const pageInfo = await (0, page_info_utils_1.getEnhancedPageInfo)(page);
|
|
28
|
+
// If we got a reasonable number of elements, we're done
|
|
29
|
+
if (pageInfo.interactiveElements && pageInfo.interactiveElements.length >= 3) {
|
|
30
|
+
if (attempt > 1) {
|
|
31
|
+
console.log(`[PageInfoRetry] ✓ Page elements loaded after ${attempt} attempts`);
|
|
32
|
+
}
|
|
33
|
+
return pageInfo;
|
|
34
|
+
}
|
|
35
|
+
// If this is the last attempt, return what we have
|
|
36
|
+
if (attempt >= maxAttempts) {
|
|
37
|
+
const totalWait = this.calculateTotalWaitTime(maxAttempts);
|
|
38
|
+
console.log(`[PageInfoRetry] ⚠️ Only found ${pageInfo.interactiveElements?.length || 0} elements after ${maxAttempts} attempts (total wait: ~${totalWait}ms)`);
|
|
39
|
+
return pageInfo;
|
|
40
|
+
}
|
|
41
|
+
// Wait with exponential backoff before retrying
|
|
42
|
+
console.log(`[PageInfoRetry] Only ${pageInfo.interactiveElements?.length || 0} elements found (attempt ${attempt}/${maxAttempts}), waiting ${backoffMs}ms...`);
|
|
43
|
+
await page.waitForTimeout(backoffMs);
|
|
44
|
+
backoffMs = Math.min(backoffMs * 1.6, 15000); // Cap at 15 seconds per attempt
|
|
45
|
+
}
|
|
46
|
+
// Fallback (shouldn't reach here, but for type safety)
|
|
47
|
+
return await (0, page_info_utils_1.getEnhancedPageInfo)(page);
|
|
48
|
+
}
|
|
49
|
+
static calculateTotalWaitTime(maxAttempts) {
|
|
50
|
+
let total = 0;
|
|
51
|
+
let backoffMs = 1000;
|
|
52
|
+
for (let i = 1; i < maxAttempts; i++) {
|
|
53
|
+
total += backoffMs;
|
|
54
|
+
backoffMs = Math.min(backoffMs * 1.6, 15000);
|
|
55
|
+
}
|
|
56
|
+
return Math.round(total);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.PageInfoRetry = PageInfoRetry;
|
|
60
|
+
//# sourceMappingURL=page-info-retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-info-retry.js","sourceRoot":"","sources":["../../src/utils/page-info-retry.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,uDAAkE;AAElE,MAAa,aAAa;IACxB;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAS,EAAE,cAAsB,CAAC;QAC1D,8DAA8D;QAC9D,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACtF,CAAC;QAAC,OAAO,SAAS,EAAE,CAAC;YACnB,8BAA8B;QAChC,CAAC;QAED,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,IAAI,SAAS,GAAG,IAAI,CAAC,CAAC,gDAAgD;QAEtE,OAAO,OAAO,GAAG,WAAW,EAAE,CAAC;YAC7B,OAAO,EAAE,CAAC;YAEV,2BAA2B;YAC3B,MAAM,QAAQ,GAAG,MAAM,IAAA,qCAAmB,EAAC,IAAI,CAAC,CAAC;YAEjD,wDAAwD;YACxD,IAAI,QAAQ,CAAC,mBAAmB,IAAI,QAAQ,CAAC,mBAAmB,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBAC7E,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,OAAO,CAAC,GAAG,CAAC,gDAAgD,OAAO,WAAW,CAAC,CAAC;gBAClF,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,mDAAmD;YACnD,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;gBAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;gBAC3D,OAAO,CAAC,GAAG,CAAC,kCAAkC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,IAAI,CAAC,mBAAmB,WAAW,2BAA2B,SAAS,KAAK,CAAC,CAAC;gBAChK,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,gDAAgD;YAChD,OAAO,CAAC,GAAG,CAAC,wBAAwB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,IAAI,CAAC,4BAA4B,OAAO,IAAI,WAAW,cAAc,SAAS,OAAO,CAAC,CAAC;YAC/J,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YACrC,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,gCAAgC;QAChF,CAAC;QAED,uDAAuD;QACvD,OAAO,MAAM,IAAA,qCAAmB,EAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAEO,MAAM,CAAC,sBAAsB,CAAC,WAAmB;QACvD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,KAAK,IAAI,SAAS,CAAC;YACnB,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;CACF;AAxDD,sCAwDC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-info-utils.d.ts","sourceRoot":"","sources":["../../src/utils/page-info-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IAC5D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,GAAG,CAAC;IAClB,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACxE,wBAAsB,mBAAmB,CAAC,WAAW,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"page-info-utils.d.ts","sourceRoot":"","sources":["../../src/utils/page-info-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAAC;IAC5D,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,GAAG,CAAC;IAClB,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACxE,wBAAsB,mBAAmB,CAAC,WAAW,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,GAAG,CAAA;CAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC"}
|
|
@@ -13,7 +13,9 @@ async function getEnhancedPageInfo(input) {
|
|
|
13
13
|
if ('accessibility' in input) {
|
|
14
14
|
// Input is a Page object
|
|
15
15
|
hasPageObject = true;
|
|
16
|
-
|
|
16
|
+
// CRITICAL: Use interestingOnly: false to get FULL ARIA tree like Playwright MCP
|
|
17
|
+
// This includes all structural nodes (generic, paragraph, etc.) not just interactive ones
|
|
18
|
+
const snapshot = await input.accessibility.snapshot({ interestingOnly: false });
|
|
17
19
|
const url = input.url();
|
|
18
20
|
const title = await input.title();
|
|
19
21
|
domSnapshot = { url, title, accessibilityTree: snapshot };
|
|
@@ -34,17 +36,34 @@ async function getEnhancedPageInfo(input) {
|
|
|
34
36
|
'[role="textbox"]',
|
|
35
37
|
'[role="checkbox"]',
|
|
36
38
|
'[role="radio"]',
|
|
37
|
-
'[onclick]'
|
|
39
|
+
'[onclick]',
|
|
40
|
+
'[type="submit"]'
|
|
38
41
|
];
|
|
39
42
|
const allInteractive = new Set();
|
|
40
43
|
interactiveSelectors.forEach((selector) => {
|
|
41
44
|
doc.querySelectorAll(selector).forEach((el) => allInteractive.add(el));
|
|
42
45
|
});
|
|
46
|
+
// CRITICAL: Also detect elements that are styled/behave as interactive
|
|
47
|
+
// (e.g., divs/spans with cursor:pointer, click handlers)
|
|
48
|
+
// This catches modern web apps that use non-semantic HTML
|
|
49
|
+
const allElements = doc.querySelectorAll('div, span, p, li, td');
|
|
50
|
+
allElements.forEach((el) => {
|
|
51
|
+
const styles = globalThis.window.getComputedStyle(el);
|
|
52
|
+
const hasClickHandler = el.onclick ||
|
|
53
|
+
el.getAttribute('onclick') ||
|
|
54
|
+
el.hasAttribute('data-action') ||
|
|
55
|
+
el.hasAttribute('data-click');
|
|
56
|
+
// Include if: cursor is pointer, has click handler, or has tabindex (keyboard focusable)
|
|
57
|
+
if (styles.cursor === 'pointer' || hasClickHandler || el.tabIndex >= 0) {
|
|
58
|
+
allInteractive.add(el);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
43
61
|
allInteractive.forEach((el) => {
|
|
44
62
|
const rect = el.getBoundingClientRect();
|
|
45
63
|
// Skip invisible elements
|
|
46
|
-
if (rect.width === 0 || rect.height === 0)
|
|
64
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
47
65
|
return;
|
|
66
|
+
}
|
|
48
67
|
const id = el.id || null;
|
|
49
68
|
const tag = el.tagName.toLowerCase();
|
|
50
69
|
const text = el.textContent?.trim().substring(0, 40) || '';
|
|
@@ -65,6 +84,11 @@ async function getEnhancedPageInfo(input) {
|
|
|
65
84
|
if (text && text.length > 0 && text.length < 30) {
|
|
66
85
|
selectors.push(`getByRole('${role}', {name: '${text}'})`);
|
|
67
86
|
}
|
|
87
|
+
// 2b. BEST: getByRole for buttons with type attribute (more specific)
|
|
88
|
+
if (tag === 'button' && el.type) {
|
|
89
|
+
selectors.push(`getByRole('button', {name: '${text}'})`);
|
|
90
|
+
selectors.push(`button[type="${el.type}"]`);
|
|
91
|
+
}
|
|
68
92
|
// 3. BEST: getByPlaceholder for inputs with placeholders
|
|
69
93
|
if (placeholder && placeholder.length > 0 && placeholder.length < 50) {
|
|
70
94
|
selectors.push(`getByPlaceholder('${placeholder}')`);
|
|
@@ -78,7 +102,19 @@ async function getEnhancedPageInfo(input) {
|
|
|
78
102
|
el.getAttribute('data-id') || el.getAttribute('data-test');
|
|
79
103
|
if (dataTestId)
|
|
80
104
|
selectors.push(`getByTestId('${dataTestId}')`);
|
|
81
|
-
// 6.
|
|
105
|
+
// 6. GOOD: name attribute (especially for form inputs - very common and stable)
|
|
106
|
+
const nameAttr = el.getAttribute('name');
|
|
107
|
+
if (nameAttr && (tag === 'input' || tag === 'textarea' || tag === 'select')) {
|
|
108
|
+
selectors.push(`input[name="${nameAttr}"]`);
|
|
109
|
+
}
|
|
110
|
+
// 6b. GOOD: class-based selectors for buttons (common in modern frameworks)
|
|
111
|
+
if (tag === 'button' && el.className) {
|
|
112
|
+
const classes = el.className.split(' ').filter((c) => c.length > 0 && !c.includes('userfront-'));
|
|
113
|
+
if (classes.length > 0) {
|
|
114
|
+
selectors.push(`button.${classes[0]}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 7. LAST RESORT: ID selector (only if stable - avoid auto-generated IDs with unicode)
|
|
82
118
|
// Skip auto-generated Ant Design IDs like «r3»-form-item or rc_select_xxx
|
|
83
119
|
if (id && !id.includes('«') && !id.match(/^(rc_|:r[0-9]+:|__)/)) {
|
|
84
120
|
selectors.push(`#${id}`);
|
|
@@ -88,6 +124,7 @@ async function getEnhancedPageInfo(input) {
|
|
|
88
124
|
id,
|
|
89
125
|
text,
|
|
90
126
|
type,
|
|
127
|
+
name: nameAttr || '', // CRITICAL: Capture name attribute for form inputs
|
|
91
128
|
role,
|
|
92
129
|
ariaLabel,
|
|
93
130
|
placeholder,
|
|
@@ -144,22 +181,13 @@ async function getEnhancedPageInfo(input) {
|
|
|
144
181
|
}
|
|
145
182
|
}
|
|
146
183
|
formattedElements = selectedElements.map((e, i) => {
|
|
147
|
-
const idInfo = e.id ? `#${e.id}` : '
|
|
184
|
+
const idInfo = e.id ? `#${e.id}` : '';
|
|
148
185
|
const typeInfo = e.type ? ` type="${e.type}"` : '';
|
|
186
|
+
const nameInfo = e.name ? ` name="${e.name}"` : '';
|
|
149
187
|
const placeholderInfo = e.placeholder ? ` placeholder="${e.placeholder}"` : '';
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
selectorsInfo = e.selectors[0]; // Best option
|
|
154
|
-
if (e.selectors.length > 1) {
|
|
155
|
-
selectorsInfo += ` (or: ${e.selectors.slice(1, 3).join(', ')})`; // Show up to 2 alternatives
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
selectorsInfo = 'No reliable selector available';
|
|
160
|
-
}
|
|
161
|
-
return `${i + 1}. [${e.bbox.x},${e.bbox.y} ${e.bbox.width}x${e.bbox.height}] <${e.tag}> ${idInfo}${typeInfo}${placeholderInfo} "${e.text}"
|
|
162
|
-
→ ${selectorsInfo}`;
|
|
188
|
+
const roleInfo = e.role ? ` role="${e.role}"` : '';
|
|
189
|
+
// Compact format: show all relevant attributes for LLM to construct selectors
|
|
190
|
+
return `${i + 1}. [${e.bbox.x},${e.bbox.y} ${e.bbox.width}x${e.bbox.height}] <${e.tag}>${idInfo}${typeInfo}${nameInfo}${placeholderInfo}${roleInfo} "${e.text}"`;
|
|
163
191
|
}).join('\n');
|
|
164
192
|
// Show stats for omitted elements
|
|
165
193
|
const omitted = interactiveElements.length - selectedElements.length;
|