browser-opt 1.0.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/dist/browser-opt/index.d.ts +9 -0
- package/dist/browser-opt/index.d.ts.map +1 -0
- package/dist/browser-opt/index.js +7 -0
- package/dist/browser-opt/index.js.map +1 -0
- package/dist/browser-opt/runner/actions/index.d.ts +13 -0
- package/dist/browser-opt/runner/actions/index.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/index.js +44 -0
- package/dist/browser-opt/runner/actions/index.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/click-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/click-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/click-action.js +17 -0
- package/dist/browser-opt/runner/actions/utils/click-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/date-action.d.ts +21 -0
- package/dist/browser-opt/runner/actions/utils/date-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/date-action.js +464 -0
- package/dist/browser-opt/runner/actions/utils/date-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.d.ts +8 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.js +167 -0
- package/dist/browser-opt/runner/actions/utils/dom-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.js +20 -0
- package/dist/browser-opt/runner/actions/utils/fill-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.js +6 -0
- package/dist/browser-opt/runner/actions/utils/handoff-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/open-action.d.ts +10 -0
- package/dist/browser-opt/runner/actions/utils/open-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/open-action.js +9 -0
- package/dist/browser-opt/runner/actions/utils/open-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.d.ts +17 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.js +532 -0
- package/dist/browser-opt/runner/actions/utils/select-option-action.js.map +1 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.d.ts +7 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.d.ts.map +1 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.js +173 -0
- package/dist/browser-opt/runner/actions/utils/upload-action.js.map +1 -0
- package/dist/browser-opt/runner/evidence.d.ts +18 -0
- package/dist/browser-opt/runner/evidence.d.ts.map +1 -0
- package/dist/browser-opt/runner/evidence.js +116 -0
- package/dist/browser-opt/runner/evidence.js.map +1 -0
- package/dist/browser-opt/runner/handoff.d.ts +28 -0
- package/dist/browser-opt/runner/handoff.d.ts.map +1 -0
- package/dist/browser-opt/runner/handoff.js +132 -0
- package/dist/browser-opt/runner/handoff.js.map +1 -0
- package/dist/browser-opt/runner/index.d.ts +11 -0
- package/dist/browser-opt/runner/index.d.ts.map +1 -0
- package/dist/browser-opt/runner/index.js +244 -0
- package/dist/browser-opt/runner/index.js.map +1 -0
- package/dist/browser-opt/runner/step-executor.d.ts +5 -0
- package/dist/browser-opt/runner/step-executor.d.ts.map +1 -0
- package/dist/browser-opt/runner/step-executor.js +302 -0
- package/dist/browser-opt/runner/step-executor.js.map +1 -0
- package/dist/browser-opt/type.d.ts +120 -0
- package/dist/browser-opt/type.d.ts.map +1 -0
- package/dist/browser-opt/type.js +2 -0
- package/dist/browser-opt/type.js.map +1 -0
- package/dist/browser-opt/utils.d.ts +48 -0
- package/dist/browser-opt/utils.d.ts.map +1 -0
- package/dist/browser-opt/utils.js +885 -0
- package/dist/browser-opt/utils.js.map +1 -0
- package/dist/browser-opt/workflow/index.d.ts +7 -0
- package/dist/browser-opt/workflow/index.d.ts.map +1 -0
- package/dist/browser-opt/workflow/index.js +6 -0
- package/dist/browser-opt/workflow/index.js.map +1 -0
- package/dist/browser-opt/workflow/matcher.d.ts +12 -0
- package/dist/browser-opt/workflow/matcher.d.ts.map +1 -0
- package/dist/browser-opt/workflow/matcher.js +102 -0
- package/dist/browser-opt/workflow/matcher.js.map +1 -0
- package/dist/browser-opt/workflow/store.d.ts +15 -0
- package/dist/browser-opt/workflow/store.d.ts.map +1 -0
- package/dist/browser-opt/workflow/store.js +175 -0
- package/dist/browser-opt/workflow/store.js.map +1 -0
- package/dist/browser-opt/workflow/type.d.ts +43 -0
- package/dist/browser-opt/workflow/type.d.ts.map +1 -0
- package/dist/browser-opt/workflow/type.js +6 -0
- package/dist/browser-opt/workflow/type.js.map +1 -0
- package/dist/cli/browser-opt.d.ts +3 -0
- package/dist/cli/browser-opt.d.ts.map +1 -0
- package/dist/cli/browser-opt.js +11 -0
- package/dist/cli/browser-opt.js.map +1 -0
- package/dist/cli/commands/browser-opt.d.ts +2 -0
- package/dist/cli/commands/browser-opt.d.ts.map +1 -0
- package/dist/cli/commands/browser-opt.js +514 -0
- package/dist/cli/commands/browser-opt.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +10 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +35 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/utils/args.d.ts +17 -0
- package/dist/cli/utils/args.d.ts.map +1 -0
- package/dist/cli/utils/args.js +74 -0
- package/dist/cli/utils/args.js.map +1 -0
- package/dist/cli/utils/constants.d.ts +8 -0
- package/dist/cli/utils/constants.d.ts.map +1 -0
- package/dist/cli/utils/constants.js +18 -0
- package/dist/cli/utils/constants.js.map +1 -0
- package/dist/cli/utils/output.d.ts +7 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +45 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/package.json +35 -0
- package/skills/browser-opt/SKILL.md +249 -0
- package/skills/browser-opt/manifest.json +75 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import { findSelectableFieldRef, findSelectableOption, } from '../../../utils.js';
|
|
2
|
+
import { captureTransientSnapshot } from '../../evidence.js';
|
|
3
|
+
import { executeDateSelectOptionAction, resolveDateSelectOption, verifyDateSelectOptionActionEffect, } from './date-action.js';
|
|
4
|
+
/** 执行选项选择动作,包含 switch、原生单选复选框和长表单滚动搜索兜底。 */
|
|
5
|
+
export function executeSelectOptionAction(agent, action, snapshot, options) {
|
|
6
|
+
const normalizedDate = resolveDateSelectOption(action);
|
|
7
|
+
if (normalizedDate) {
|
|
8
|
+
return executeDateSelectOptionAction(agent, action, snapshot, normalizedDate);
|
|
9
|
+
}
|
|
10
|
+
let option = findSelectableOption(snapshot, action.field, action.option);
|
|
11
|
+
let searchSnapshot = snapshot;
|
|
12
|
+
const fieldLabel = action.field ?? '选项';
|
|
13
|
+
if (option.alreadySelected) {
|
|
14
|
+
if (!isSwitchSelectable(option.role)) {
|
|
15
|
+
return `selection skipped: ${fieldLabel} 已是 ${action.option}`;
|
|
16
|
+
}
|
|
17
|
+
const domState = action.field ? verifySwitchDomState(agent, action.field, action.option) : null;
|
|
18
|
+
if (domState === true) {
|
|
19
|
+
return `selection skipped: ${fieldLabel} 已是 ${action.option}`;
|
|
20
|
+
}
|
|
21
|
+
option = { ...option, alreadySelected: false };
|
|
22
|
+
}
|
|
23
|
+
const switchDomTarget = isSwitchSelectable(option.role) && action.field
|
|
24
|
+
? clickSwitchDomTarget(agent, action.field, action.option)
|
|
25
|
+
: null;
|
|
26
|
+
if (switchDomTarget) {
|
|
27
|
+
return switchDomTarget;
|
|
28
|
+
}
|
|
29
|
+
const nativeSelectableDomTarget = clickNativeSelectableDomTarget(agent, option.role, action.option);
|
|
30
|
+
if (nativeSelectableDomTarget) {
|
|
31
|
+
return nativeSelectableDomTarget;
|
|
32
|
+
}
|
|
33
|
+
const searchOutput = [];
|
|
34
|
+
const dropdownDomTarget = action.field ? clickDropdownDomTarget(agent, action.field, action.option) : { attempted: false, output: null };
|
|
35
|
+
if (dropdownDomTarget.output) {
|
|
36
|
+
return dropdownDomTarget.output;
|
|
37
|
+
}
|
|
38
|
+
if (dropdownDomTarget.attempted && !option.ref && findSelectableFieldRef(searchSnapshot, action.field)) {
|
|
39
|
+
throw new Error(`无法通过 DOM 安全选择下拉选项:${fieldLabel} -> ${action.option}`);
|
|
40
|
+
}
|
|
41
|
+
if (!option.ref) {
|
|
42
|
+
const fieldRef = findSelectableFieldRef(searchSnapshot, action.field);
|
|
43
|
+
if (fieldRef) {
|
|
44
|
+
searchOutput.push(`open select @${fieldRef}\n${agent.click(fieldRef)}`.trim());
|
|
45
|
+
agent.waitMs(300);
|
|
46
|
+
const openedSnapshot = captureTransientSnapshot(agent);
|
|
47
|
+
option = findSelectableOption(openedSnapshot, null, action.option);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!option.ref && action.field && options.allowViewportSearch) {
|
|
51
|
+
const scrolled = searchSelectableInLongForm(agent, action);
|
|
52
|
+
if (scrolled) {
|
|
53
|
+
searchSnapshot = scrolled.snapshot;
|
|
54
|
+
option = scrolled.option;
|
|
55
|
+
searchOutput.push(...scrolled.logs);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!option.ref) {
|
|
59
|
+
const fieldRef = findSelectableFieldRef(searchSnapshot, action.field);
|
|
60
|
+
if (fieldRef) {
|
|
61
|
+
searchOutput.push(`open select @${fieldRef}\n${agent.click(fieldRef)}`.trim());
|
|
62
|
+
agent.waitMs(300);
|
|
63
|
+
const openedSnapshot = captureTransientSnapshot(agent);
|
|
64
|
+
option = findSelectableOption(openedSnapshot, null, action.option);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!option.ref) {
|
|
68
|
+
throw new Error(`无法找到选项:${fieldLabel} -> ${action.option}`);
|
|
69
|
+
}
|
|
70
|
+
const output = agent.click(option.ref);
|
|
71
|
+
return [
|
|
72
|
+
...searchOutput,
|
|
73
|
+
`${formatSelectableActionName(option.role)} @${option.ref} ${fieldLabel}=${action.option}`,
|
|
74
|
+
output,
|
|
75
|
+
].filter(Boolean).join('\n').trim();
|
|
76
|
+
}
|
|
77
|
+
/** 对选择动作做后置状态确认,防止命令发出但页面没有完成目标状态时误报成功。 */
|
|
78
|
+
export function verifySelectOptionActionEffect(agent, action, afterSnapshot) {
|
|
79
|
+
const normalizedDate = resolveDateSelectOption(action);
|
|
80
|
+
if (normalizedDate) {
|
|
81
|
+
return verifyDateSelectOptionActionEffect(agent, action, afterSnapshot, normalizedDate);
|
|
82
|
+
}
|
|
83
|
+
const selected = findSelectableOption(afterSnapshot, action.field, action.option);
|
|
84
|
+
if (isSwitchSelectable(selected.role) && action.field) {
|
|
85
|
+
const domState = verifySwitchDomState(agent, action.field, action.option);
|
|
86
|
+
if (domState === true) {
|
|
87
|
+
return { passed: true, message: `已确认开关状态:${action.field}=${action.option}` };
|
|
88
|
+
}
|
|
89
|
+
if (domState === false) {
|
|
90
|
+
return { passed: false, message: `DOM 确认开关未达到目标状态:${action.field}=${action.option}` };
|
|
91
|
+
}
|
|
92
|
+
return { passed: false, message: `无法可靠确认开关状态:${action.field}=${action.option}` };
|
|
93
|
+
}
|
|
94
|
+
if (selected.alreadySelected) {
|
|
95
|
+
return { passed: true, message: `已确认选择状态:${action.field ?? '选项'}=${action.option}` };
|
|
96
|
+
}
|
|
97
|
+
if (isSelectedValueVisible(afterSnapshot.text, action.field, action.option)) {
|
|
98
|
+
return { passed: true, message: `已确认页面显示选择值:${action.field ?? '选项'}=${action.option}` };
|
|
99
|
+
}
|
|
100
|
+
if (isOrdinalOptionIntent(action.option) && action.field && isFieldSelectedValueVisible(afterSnapshot.text, action.field)) {
|
|
101
|
+
return { passed: true, message: `已确认按位置选择:${action.field}=${action.option}` };
|
|
102
|
+
}
|
|
103
|
+
return { passed: false, message: `动作后未确认目标已选中:${action.field ?? '选项'}=${action.option}` };
|
|
104
|
+
}
|
|
105
|
+
/** radio/checkbox 的无障碍 ref 可能指向隐藏 input,文案唯一时改点真实 label 以触发组件事件。 */
|
|
106
|
+
function clickNativeSelectableDomTarget(agent, role, option) {
|
|
107
|
+
if (!/radio|checkbox/i.test(role ?? '')) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const script = `(() => {
|
|
111
|
+
const normalize = (value) => (value || '').replace(/\\s+/g, '').trim();
|
|
112
|
+
const target = normalize(${JSON.stringify(option)});
|
|
113
|
+
const labels = [...document.querySelectorAll('label')].filter((label) => normalize(label.textContent) === target);
|
|
114
|
+
if (labels.length !== 1) {
|
|
115
|
+
return JSON.stringify({ matchedCount: labels.length, clicked: false });
|
|
116
|
+
}
|
|
117
|
+
const input = labels[0].querySelector('input[type="radio"], input[type="checkbox"]');
|
|
118
|
+
if (!input || input.disabled) {
|
|
119
|
+
return JSON.stringify({ matchedCount: 1, clicked: false, disabled: Boolean(input?.disabled) });
|
|
120
|
+
}
|
|
121
|
+
labels[0].click();
|
|
122
|
+
return JSON.stringify({ matchedCount: 1, clicked: true, checked: input.checked });
|
|
123
|
+
})()`;
|
|
124
|
+
try {
|
|
125
|
+
const parsed = parseEvalJson(agent.evaluate(script));
|
|
126
|
+
return parsed.matchedCount === 1 && parsed.clicked === true
|
|
127
|
+
? `selectable dom click ${option}`
|
|
128
|
+
: null;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** 普通下拉优先按 DOM 中字段同组控件和可见弹层选项点击,避开嵌套布局里的 ref 坐标漂移。 */
|
|
135
|
+
function clickDropdownDomTarget(agent, field, option) {
|
|
136
|
+
const openScript = `(() => {
|
|
137
|
+
const selectHelper = ${dropdownDomHelperSource()};
|
|
138
|
+
return JSON.stringify(selectHelper.openDropdownByField(${JSON.stringify(field)}));
|
|
139
|
+
})()`;
|
|
140
|
+
try {
|
|
141
|
+
const opened = parseEvalJson(agent.evaluate(openScript));
|
|
142
|
+
if (typeof opened.opened !== 'boolean') {
|
|
143
|
+
return { attempted: false, output: null };
|
|
144
|
+
}
|
|
145
|
+
if (!opened.found || opened.opened === false) {
|
|
146
|
+
return { attempted: true, output: null };
|
|
147
|
+
}
|
|
148
|
+
agent.waitMs(300);
|
|
149
|
+
const clickScript = `(() => {
|
|
150
|
+
const selectHelper = ${dropdownDomHelperSource()};
|
|
151
|
+
return JSON.stringify(selectHelper.clickVisibleOption(${JSON.stringify(option)}));
|
|
152
|
+
})()`;
|
|
153
|
+
const clicked = parseEvalJson(agent.evaluate(clickScript));
|
|
154
|
+
if (clicked.clicked === true) {
|
|
155
|
+
agent.waitMs(300);
|
|
156
|
+
const selectedText = clicked.selectedText ? ` (${clicked.selectedText})` : '';
|
|
157
|
+
return { attempted: true, output: `select dom click ${field}=${option}${selectedText}` };
|
|
158
|
+
}
|
|
159
|
+
return { attempted: true, output: null };
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { attempted: true, output: null };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** 长表单中字段可能不在当前可交互快照里,按视口上下搜索字段和目标选项。 */
|
|
166
|
+
function searchSelectableInLongForm(agent, action) {
|
|
167
|
+
const logs = [];
|
|
168
|
+
const moves = [
|
|
169
|
+
{ direction: 'up', amount: 900 },
|
|
170
|
+
{ direction: 'up', amount: 900 },
|
|
171
|
+
{ direction: 'down', amount: 900 },
|
|
172
|
+
{ direction: 'down', amount: 900 },
|
|
173
|
+
{ direction: 'down', amount: 900 },
|
|
174
|
+
];
|
|
175
|
+
for (const move of moves) {
|
|
176
|
+
const output = agent.scroll(move.direction, move.amount);
|
|
177
|
+
logs.push(`scroll ${move.direction} ${move.amount}${output.trim() ? `\n${output.trim()}` : ''}`);
|
|
178
|
+
agent.waitMs(200);
|
|
179
|
+
const snapshot = captureTransientSnapshot(agent);
|
|
180
|
+
const option = findSelectableOption(snapshot, action.field, action.option);
|
|
181
|
+
if (option.ref || option.alreadySelected || findSelectableFieldRef(snapshot, action.field)) {
|
|
182
|
+
return { snapshot, option, logs };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
/** 对 switch 优先使用 DOM 中字段同一行的最近开关,避免无障碍 ref 指向或 checked 状态失真。 */
|
|
188
|
+
function clickSwitchDomTarget(agent, field, option) {
|
|
189
|
+
const desiredChecked = /^(是|开|开启|打开|启用|true|yes|on)$/i.test(option.trim());
|
|
190
|
+
const script = `(() => {
|
|
191
|
+
const switchHelper = ${switchDomHelperSource()};
|
|
192
|
+
const result = switchHelper.findSwitchByField(${JSON.stringify(field)});
|
|
193
|
+
if (!result.found || !result.switchId) {
|
|
194
|
+
return JSON.stringify(result);
|
|
195
|
+
}
|
|
196
|
+
if (result.checked === ${JSON.stringify(desiredChecked)}) {
|
|
197
|
+
return JSON.stringify({ ...result, clicked: false });
|
|
198
|
+
}
|
|
199
|
+
const element = document.querySelector('[data-browser-opt-switch-id="' + result.switchId + '"]');
|
|
200
|
+
if (!element) {
|
|
201
|
+
return JSON.stringify({ ...result, clicked: false, missingElement: true });
|
|
202
|
+
}
|
|
203
|
+
element.click();
|
|
204
|
+
return JSON.stringify({ ...result, clicked: true });
|
|
205
|
+
})()
|
|
206
|
+
`;
|
|
207
|
+
try {
|
|
208
|
+
const parsed = parseEvalJson(agent.evaluate(script));
|
|
209
|
+
if (!parsed.found) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
if (parsed.clicked === false && parsed.checked === desiredChecked) {
|
|
213
|
+
return `switch dom skipped: ${field} 已是 ${option}`;
|
|
214
|
+
}
|
|
215
|
+
if (parsed.clicked === true) {
|
|
216
|
+
agent.waitMs(300);
|
|
217
|
+
return `switch dom click ${field}=${option}`;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** switch 的 accessibility checked 在部分业务页不可靠,单独走 DOM 近邻状态确认。 */
|
|
226
|
+
function verifySwitchDomState(agent, field, option) {
|
|
227
|
+
const desiredChecked = /^(是|开|开启|打开|启用|true|yes|on)$/i.test(option.trim());
|
|
228
|
+
const script = `(() => {
|
|
229
|
+
const switchHelper = ${switchDomHelperSource()};
|
|
230
|
+
const result = switchHelper.findSwitchByField(${JSON.stringify(field)});
|
|
231
|
+
return JSON.stringify({ ...result, desired: ${JSON.stringify(desiredChecked)} });
|
|
232
|
+
})()
|
|
233
|
+
`;
|
|
234
|
+
try {
|
|
235
|
+
const parsed = parseEvalJson(agent.evaluate(script));
|
|
236
|
+
if (!parsed.found || typeof parsed.checked !== 'boolean') {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return parsed.checked === parsed.desired;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function parseEvalJson(raw) {
|
|
246
|
+
const decoded = JSON.parse(raw.trim());
|
|
247
|
+
return typeof decoded === 'string'
|
|
248
|
+
? JSON.parse(decoded)
|
|
249
|
+
: decoded;
|
|
250
|
+
}
|
|
251
|
+
/** 返回在页面上下文执行的下拉定位工具源码,兼容 Ant Design 和常见伪下拉结构。 */
|
|
252
|
+
function dropdownDomHelperSource() {
|
|
253
|
+
return `
|
|
254
|
+
(() => {
|
|
255
|
+
const normalizeBrowserOptText = (value) => String(value || '').replace(/[\\s::,,。;*]/g, '').toLowerCase();
|
|
256
|
+
const browserOptVisible = (element) => {
|
|
257
|
+
const style = window.getComputedStyle(element);
|
|
258
|
+
const rect = element.getBoundingClientRect();
|
|
259
|
+
return style.visibility !== 'hidden' && style.display !== 'none' && rect.width > 0 && rect.height > 0;
|
|
260
|
+
};
|
|
261
|
+
const browserOptCenterDistance = (a, b) => {
|
|
262
|
+
const ax = (a.left + a.right) / 2;
|
|
263
|
+
const ay = (a.top + a.bottom) / 2;
|
|
264
|
+
const bx = (b.left + b.right) / 2;
|
|
265
|
+
const by = (b.top + b.bottom) / 2;
|
|
266
|
+
return Math.abs(ax - bx) + Math.abs(ay - by);
|
|
267
|
+
};
|
|
268
|
+
const browserOptDispatchMouse = (element) => {
|
|
269
|
+
const rect = element.getBoundingClientRect();
|
|
270
|
+
const init = {
|
|
271
|
+
bubbles: true,
|
|
272
|
+
cancelable: true,
|
|
273
|
+
view: window,
|
|
274
|
+
clientX: (rect.left + rect.right) / 2,
|
|
275
|
+
clientY: (rect.top + rect.bottom) / 2,
|
|
276
|
+
};
|
|
277
|
+
element.dispatchEvent(new MouseEvent('mousedown', init));
|
|
278
|
+
element.dispatchEvent(new MouseEvent('mouseup', init));
|
|
279
|
+
element.dispatchEvent(new MouseEvent('click', init));
|
|
280
|
+
};
|
|
281
|
+
const browserOptSelects = () => [...document.querySelectorAll([
|
|
282
|
+
'.ant-select:not(.ant-select-disabled)',
|
|
283
|
+
'.el-select:not(.is-disabled)',
|
|
284
|
+
'.ant-cascader-picker:not(.ant-cascader-picker-disabled)',
|
|
285
|
+
'[role="combobox"]:not([aria-disabled="true"])',
|
|
286
|
+
'select:not(:disabled)'
|
|
287
|
+
].join(','))].filter(browserOptVisible);
|
|
288
|
+
const browserOptClickableSelect = (element) =>
|
|
289
|
+
element.matches('select')
|
|
290
|
+
? element
|
|
291
|
+
: element.querySelector('.ant-select-selector, .el-input, .ant-cascader-input, [role="combobox"]') || element;
|
|
292
|
+
const browserOptDropdownContainers = () => [...document.querySelectorAll([
|
|
293
|
+
'.ant-select-dropdown:not(.ant-select-dropdown-hidden)',
|
|
294
|
+
'.el-select-dropdown:not([style*="display: none"])',
|
|
295
|
+
'[role="listbox"]'
|
|
296
|
+
].join(','))]
|
|
297
|
+
.filter(browserOptVisible)
|
|
298
|
+
.map((element) => ({ element, rect: element.getBoundingClientRect() }));
|
|
299
|
+
const browserOptOrdinalIndex = (value) => {
|
|
300
|
+
const normalized = normalizeBrowserOptText(value);
|
|
301
|
+
const exact = normalized.match(/^(?:第)?(\\d+)(?:个|项|个选项|项选项)?$/);
|
|
302
|
+
if (exact) {
|
|
303
|
+
return Math.max(Number(exact[1]) - 1, 0);
|
|
304
|
+
}
|
|
305
|
+
const aliases = [
|
|
306
|
+
['第一个选项', '第一个', '第一项', '首个', '首项'],
|
|
307
|
+
['第二个选项', '第二个', '第二项'],
|
|
308
|
+
['第三个选项', '第三个', '第三项'],
|
|
309
|
+
['第四个选项', '第四个', '第四项'],
|
|
310
|
+
['第五个选项', '第五个', '第五项'],
|
|
311
|
+
].map((items) => items.map(normalizeBrowserOptText));
|
|
312
|
+
const matched = aliases.findIndex((items) => items.includes(normalized));
|
|
313
|
+
if (matched >= 0) return matched;
|
|
314
|
+
if (['最后一个选项', '最后一个', '最后一项', '末项'].map(normalizeBrowserOptText).includes(normalized)) {
|
|
315
|
+
return -1;
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
};
|
|
319
|
+
const browserOptOptionCandidates = () => {
|
|
320
|
+
const active = document.querySelector('[data-browser-opt-active-select="true"]');
|
|
321
|
+
const activeRect = active?.getBoundingClientRect();
|
|
322
|
+
const containers = browserOptDropdownContainers()
|
|
323
|
+
.map((item) => ({
|
|
324
|
+
...item,
|
|
325
|
+
distance: activeRect ? browserOptCenterDistance(activeRect, item.rect) : 0,
|
|
326
|
+
}))
|
|
327
|
+
.sort((a, b) => a.distance - b.distance);
|
|
328
|
+
const scopedRoot = containers[0]?.element;
|
|
329
|
+
if (!scopedRoot) {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
return [...scopedRoot.querySelectorAll([
|
|
333
|
+
'.ant-select-item-option',
|
|
334
|
+
'.ant-select-dropdown-menu-item',
|
|
335
|
+
'.el-select-dropdown__item',
|
|
336
|
+
'[role="option"]'
|
|
337
|
+
].join(','))]
|
|
338
|
+
.filter(browserOptVisible)
|
|
339
|
+
.filter((element) => {
|
|
340
|
+
const ariaDisabled = element.getAttribute('aria-disabled') === 'true';
|
|
341
|
+
const className = String(element.className || '');
|
|
342
|
+
return !ariaDisabled && !className.includes('disabled');
|
|
343
|
+
})
|
|
344
|
+
.map((element) => ({ element, text: normalizeBrowserOptText(element.textContent), rawText: String(element.textContent || '').trim(), rect: element.getBoundingClientRect() }))
|
|
345
|
+
.filter((item) => item.text)
|
|
346
|
+
.sort((a, b) => a.rect.top - b.rect.top || a.rect.left - b.rect.left);
|
|
347
|
+
};
|
|
348
|
+
const browserOptClosestFieldContainers = (labelElement) => {
|
|
349
|
+
const containers = [];
|
|
350
|
+
let current = labelElement;
|
|
351
|
+
for (let depth = 0; current && depth < 8; depth += 1, current = current.parentElement) {
|
|
352
|
+
if (normalizeBrowserOptText(current.textContent).length > 0) {
|
|
353
|
+
containers.push(current);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return containers
|
|
357
|
+
.filter((element) => browserOptSelects().some((select) => element.contains(select)))
|
|
358
|
+
.map((element) => ({ element, rect: element.getBoundingClientRect() }))
|
|
359
|
+
.sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height));
|
|
360
|
+
};
|
|
361
|
+
function openDropdownByField(field) {
|
|
362
|
+
const fieldText = normalizeBrowserOptText(field);
|
|
363
|
+
const labels = [...document.querySelectorAll('body *')]
|
|
364
|
+
.filter((element) => browserOptVisible(element) && normalizeBrowserOptText(element.textContent).includes(fieldText))
|
|
365
|
+
.map((element) => ({ element, rect: element.getBoundingClientRect() }))
|
|
366
|
+
.sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height));
|
|
367
|
+
const selects = browserOptSelects().map((element) => ({ element, rect: element.getBoundingClientRect() }));
|
|
368
|
+
for (const label of labels) {
|
|
369
|
+
for (const container of browserOptClosestFieldContainers(label.element)) {
|
|
370
|
+
const target = browserOptSelects()
|
|
371
|
+
.filter((select) => container.element.contains(select))
|
|
372
|
+
.map((element) => ({ element, rect: element.getBoundingClientRect(), distance: browserOptCenterDistance(label.rect, element.getBoundingClientRect()) }))
|
|
373
|
+
.sort((a, b) => a.distance - b.distance)[0];
|
|
374
|
+
if (target) {
|
|
375
|
+
document.querySelectorAll('[data-browser-opt-active-select="true"]').forEach((element) => element.removeAttribute('data-browser-opt-active-select'));
|
|
376
|
+
target.element.setAttribute('data-browser-opt-active-select', 'true');
|
|
377
|
+
browserOptDispatchMouse(browserOptClickableSelect(target.element));
|
|
378
|
+
return { found: true, opened: true, strategy: 'container' };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const sameGroup = selects
|
|
383
|
+
.map((item) => {
|
|
384
|
+
const verticalOverlap = Math.min(label.rect.bottom, item.rect.bottom) - Math.max(label.rect.top, item.rect.top);
|
|
385
|
+
const yDistance = Math.abs((label.rect.top + label.rect.bottom) / 2 - (item.rect.top + item.rect.bottom) / 2);
|
|
386
|
+
const rightSide = item.rect.left >= label.rect.left - 4;
|
|
387
|
+
return { ...item, verticalOverlap, yDistance, rightSide, distance: browserOptCenterDistance(label.rect, item.rect) };
|
|
388
|
+
})
|
|
389
|
+
.filter((item) => item.rightSide && (item.verticalOverlap > 0 || item.yDistance < Math.max(label.rect.height, item.rect.height) * 1.5))
|
|
390
|
+
.sort((a, b) => a.yDistance - b.yDistance || a.distance - b.distance);
|
|
391
|
+
const target = sameGroup[0];
|
|
392
|
+
if (target) {
|
|
393
|
+
document.querySelectorAll('[data-browser-opt-active-select="true"]').forEach((element) => element.removeAttribute('data-browser-opt-active-select'));
|
|
394
|
+
target.element.setAttribute('data-browser-opt-active-select', 'true');
|
|
395
|
+
browserOptDispatchMouse(browserOptClickableSelect(target.element));
|
|
396
|
+
return { found: true, opened: true, strategy: 'row' };
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return { found: false, opened: false };
|
|
400
|
+
}
|
|
401
|
+
function clickVisibleOption(option) {
|
|
402
|
+
const optionText = normalizeBrowserOptText(option);
|
|
403
|
+
const options = browserOptOptionCandidates();
|
|
404
|
+
const ordinalIndex = browserOptOrdinalIndex(option);
|
|
405
|
+
const target = typeof ordinalIndex === 'number'
|
|
406
|
+
? options[ordinalIndex === -1 ? options.length - 1 : ordinalIndex]
|
|
407
|
+
: options
|
|
408
|
+
.filter((item) => item.text.includes(optionText) || optionText.includes(item.text))
|
|
409
|
+
.sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height))[0];
|
|
410
|
+
if (!target) {
|
|
411
|
+
return { found: false, clicked: false };
|
|
412
|
+
}
|
|
413
|
+
browserOptDispatchMouse(target.element);
|
|
414
|
+
return { found: true, clicked: true, selectedText: target.rawText };
|
|
415
|
+
}
|
|
416
|
+
return { openDropdownByField, clickVisibleOption };
|
|
417
|
+
})()
|
|
418
|
+
`;
|
|
419
|
+
}
|
|
420
|
+
/** 返回在页面上下文执行的 switch 定位工具源码,按字段同一行最近 switch 选择目标。 */
|
|
421
|
+
function switchDomHelperSource() {
|
|
422
|
+
return `
|
|
423
|
+
(() => {
|
|
424
|
+
const normalizeBrowserOptText = (value) => String(value || '').replace(/[\\s::,,。;*]/g, '').toLowerCase();
|
|
425
|
+
const browserOptVisible = (element) => {
|
|
426
|
+
const style = window.getComputedStyle(element);
|
|
427
|
+
const rect = element.getBoundingClientRect();
|
|
428
|
+
return style.visibility !== 'hidden' && style.display !== 'none' && rect.width > 0 && rect.height > 0;
|
|
429
|
+
};
|
|
430
|
+
const browserOptSwitchState = (element) => {
|
|
431
|
+
const aria = element.getAttribute('aria-checked');
|
|
432
|
+
if (aria === 'true') return true;
|
|
433
|
+
if (aria === 'false') return false;
|
|
434
|
+
if (element.checked === true) return true;
|
|
435
|
+
if (element.checked === false) return false;
|
|
436
|
+
const className = String(element.className || '');
|
|
437
|
+
if (element.classList.contains('ant-switch-checked') || element.classList.contains('is-checked') || className.includes('switch-checked')) return true;
|
|
438
|
+
if (element.classList.contains('ant-switch-disabled') && element.classList.contains('ant-switch-checked')) return true;
|
|
439
|
+
const text = normalizeBrowserOptText(element.textContent || '');
|
|
440
|
+
const hasTruthyText = ['是', '开启', '打开', '启用', '展示', 'true', 'yes', 'on'].some((item) => text.includes(item));
|
|
441
|
+
const hasFalsyText = ['否', '关闭', '停用', '禁用', '不展示', 'false', 'no', 'off'].some((item) => text.includes(item));
|
|
442
|
+
if (hasTruthyText && !hasFalsyText) return true;
|
|
443
|
+
if (hasFalsyText && !hasTruthyText) return false;
|
|
444
|
+
return null;
|
|
445
|
+
};
|
|
446
|
+
const browserOptSwitches = () => [...document.querySelectorAll('[role="switch"], .ant-switch, button')]
|
|
447
|
+
.filter((element) => browserOptVisible(element) && (element.getAttribute('role') === 'switch' || String(element.className || '').includes('switch')));
|
|
448
|
+
function findSwitchByField(field) {
|
|
449
|
+
const fieldText = normalizeBrowserOptText(field);
|
|
450
|
+
const labels = [...document.querySelectorAll('body *')]
|
|
451
|
+
.filter((element) => browserOptVisible(element) && normalizeBrowserOptText(element.textContent).includes(fieldText))
|
|
452
|
+
.map((element) => ({ element, rect: element.getBoundingClientRect() }))
|
|
453
|
+
.sort((a, b) => (a.rect.width * a.rect.height) - (b.rect.width * b.rect.height));
|
|
454
|
+
const switches = browserOptSwitches().map((element, index) => {
|
|
455
|
+
const id = 'browser-opt-switch-' + index;
|
|
456
|
+
element.setAttribute('data-browser-opt-switch-id', id);
|
|
457
|
+
return { element, id, rect: element.getBoundingClientRect(), checked: browserOptSwitchState(element) };
|
|
458
|
+
});
|
|
459
|
+
for (const label of labels) {
|
|
460
|
+
const sameRow = switches
|
|
461
|
+
.map((item) => {
|
|
462
|
+
const verticalOverlap = Math.min(label.rect.bottom, item.rect.bottom) - Math.max(label.rect.top, item.rect.top);
|
|
463
|
+
const yDistance = Math.abs((label.rect.top + label.rect.bottom) / 2 - (item.rect.top + item.rect.bottom) / 2);
|
|
464
|
+
const xDistance = Math.abs((label.rect.left + label.rect.right) / 2 - (item.rect.left + item.rect.right) / 2);
|
|
465
|
+
return { ...item, verticalOverlap, yDistance, xDistance };
|
|
466
|
+
})
|
|
467
|
+
.filter((item) => item.checked !== null && (item.verticalOverlap > 0 || item.yDistance < Math.max(label.rect.height, item.rect.height)))
|
|
468
|
+
.sort((a, b) => a.yDistance - b.yDistance || a.xDistance - b.xDistance);
|
|
469
|
+
const target = sameRow[0];
|
|
470
|
+
if (target) {
|
|
471
|
+
return { found: true, checked: target.checked, switchId: target.id };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return { found: false, checked: null };
|
|
475
|
+
}
|
|
476
|
+
return { findSwitchByField };
|
|
477
|
+
})()
|
|
478
|
+
`;
|
|
479
|
+
}
|
|
480
|
+
function isSwitchSelectable(role) {
|
|
481
|
+
return Boolean(role && /switch/i.test(role));
|
|
482
|
+
}
|
|
483
|
+
/** 下拉选择通常会收起选项列表,只保留字段和值文案,因此补充基于页面文案的确认。 */
|
|
484
|
+
function isSelectedValueVisible(text, field, option) {
|
|
485
|
+
const normalizedText = normalizeVisibleText(text);
|
|
486
|
+
const normalizedOption = normalizeVisibleText(option);
|
|
487
|
+
if (!normalizedOption || !normalizedText.includes(normalizedOption)) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
if (!field) {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
return normalizedText.includes(normalizeVisibleText(field));
|
|
494
|
+
}
|
|
495
|
+
/** 识别“第一个选项/最后一个”这类按位置选择的意图。 */
|
|
496
|
+
function isOrdinalOptionIntent(option) {
|
|
497
|
+
const normalized = normalizeVisibleText(option);
|
|
498
|
+
return /^(?:第)?\d+(?:个|项|个选项|项选项)?$/.test(normalized)
|
|
499
|
+
|| /^(?:第)?[一二三四五六七八九十]+(?:个|项|个选项|项选项)$/.test(normalized)
|
|
500
|
+
|| /^(首个|首项|最后一个选项|最后一个|最后一项|末项)$/.test(normalized);
|
|
501
|
+
}
|
|
502
|
+
/** 位置型选择无法用原始目标文案确认,改看字段附近是否存在具体选择值。 */
|
|
503
|
+
function isFieldSelectedValueVisible(text, field) {
|
|
504
|
+
const lines = text.split('\n');
|
|
505
|
+
const normalizedField = normalizeVisibleText(field);
|
|
506
|
+
const fieldIndex = lines.findIndex((line) => normalizeVisibleText(line).includes(normalizedField));
|
|
507
|
+
if (fieldIndex < 0) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
const start = Math.max(0, fieldIndex - 3);
|
|
511
|
+
const end = Math.min(lines.length, fieldIndex + 4);
|
|
512
|
+
for (let index = start; index < end; index += 1) {
|
|
513
|
+
const match = lines[index]?.match(/-\s*(?:generic|combobox|button)\s+"([^"]+)"/i);
|
|
514
|
+
const value = normalizeVisibleText(match?.[1] ?? '');
|
|
515
|
+
if (value && !value.includes(normalizedField) && !normalizedField.includes(value)) {
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
/** 归一化页面可见文案,减少空白和标点对选择结果确认的影响。 */
|
|
522
|
+
function normalizeVisibleText(value) {
|
|
523
|
+
return value.toLowerCase().replace(/\s+/g, '').replace(/[::,,。;"'‘’“”]/g, '').trim();
|
|
524
|
+
}
|
|
525
|
+
/** 根据控件角色生成 agent-browser 已支持的动作名称,报告文本需与实际命令集一致。 */
|
|
526
|
+
function formatSelectableActionName(role) {
|
|
527
|
+
if (role && /checkbox|switch/i.test(role)) {
|
|
528
|
+
return 'check';
|
|
529
|
+
}
|
|
530
|
+
return 'click';
|
|
531
|
+
}
|
|
532
|
+
//# sourceMappingURL=select-option-action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-option-action.js","sourceRoot":"","sources":["../../../../../src/browser-opt/runner/actions/utils/select-option-action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BrowserAgent } from '@browser-automated/browser-core/agent';
|
|
2
|
+
import type { DeterministicAction, SnapshotEvidence, DeterministicExecutionOptions } from '../../../type.js';
|
|
3
|
+
/** 执行上传动作,优先使用快照 ref,再用隐藏 input 和长表单滚动搜索兜底。 */
|
|
4
|
+
export declare function executeUploadAction(agent: BrowserAgent, action: Extract<DeterministicAction, {
|
|
5
|
+
type: 'upload';
|
|
6
|
+
}>, snapshot: SnapshotEvidence, outputDir: string, options: DeterministicExecutionOptions): Promise<string>;
|
|
7
|
+
//# sourceMappingURL=upload-action.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-action.d.ts","sourceRoot":"","sources":["../../../../../src/browser-opt/runner/actions/utils/upload-action.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAI7G,+CAA+C;AAC/C,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,OAAO,CAAC,mBAAmB,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC,EACxD,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,MAAM,CAAC,CA8BjB"}
|