@szc-ft/mcp-szcd-client 0.34.1 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/opencode-extension/skills/local-browser-test/SKILL.md +266 -111
- package/opencode-extension/skills/local-browser-test/lib/browser-engine.js +593 -11
- package/opencode-extension/skills/local-browser-test/local-browser-executor.js +200 -2
- package/package.json +1 -1
- package/qwen-extension/qwen-extension.json +1 -1
- package/qwen-extension/skills/local-browser-test/SKILL.md +266 -111
- package/qwen-extension/skills/local-browser-test/lib/browser-engine.js +593 -11
- package/qwen-extension/skills/local-browser-test/local-browser-executor.js +200 -2
- package/scripts/lib/common.js +1 -1
- package/standard-skill/local-browser-test/SKILL.md +266 -111
- package/standard-skill/local-browser-test/lib/browser-engine.js +593 -11
- package/standard-skill/local-browser-test/local-browser-executor.js +200 -2
- package/standard-skill/local-browser-test/local-browser-executor.cjs +0 -742
|
@@ -14,7 +14,7 @@ compatibility:
|
|
|
14
14
|
当用户出现以下意图时启动:
|
|
15
15
|
|
|
16
16
|
**用例自动化(用 testCase DSL):**
|
|
17
|
-
- "
|
|
17
|
+
- "执行测试用例" / "批量测试" / "测试用例自动化"
|
|
18
18
|
- "创建→审核→上线 全链路" / "生命周期测试"
|
|
19
19
|
- "表单填写验证" / "深度表单测试"
|
|
20
20
|
|
|
@@ -58,7 +58,10 @@ echo "SKILL_DIR=$SKILL_DIR"
|
|
|
58
58
|
export EXECUTOR="$SKILL_DIR/local-browser-executor.js"
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
> **⚠️ 每条命令必须带 NODE_PATH 前缀!**
|
|
62
|
+
> 各 `run_shell_command` 是独立的 shell 会话,`export` 不会跨会话持久化。
|
|
63
|
+
> 所有命令必须写成:`NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action ...`
|
|
64
|
+
> Windows 若 `~` 不展开,改用完整路径:`NODE_PATH="C:/Users/Lenovo/.szcd-mcp/deps/node_modules"`
|
|
62
65
|
|
|
63
66
|
**执行器 + lib 内置在 skill 目录中,自带 Ant 适配层 + expect 断言 + testCase 引擎。禁止自己 require puppeteer 写脚本**——自写脚本会丢失:
|
|
64
67
|
- 微前端 frame 探活 + 自动召回
|
|
@@ -67,10 +70,6 @@ export EXECUTOR="$SKILL_DIR/local-browser-executor.js"
|
|
|
67
70
|
- testCase recovery 重试
|
|
68
71
|
- API 按 frame scope 过滤
|
|
69
72
|
|
|
70
|
-
### 过渡兼容执行器
|
|
71
|
-
|
|
72
|
-
`local-browser-executor.cjs`(同目录)是 CommonJS 过渡版,仅支持基础诊断(diagnose/screenshot/list-pages/api-check/plan/snapshot/interact/perf/watch)。**Ant 适配层 / testCase DSL / expect 断言仅 `local-browser-executor.js`(ES Module 版)支持。** 优先用 `.js` 版本。
|
|
73
|
-
|
|
74
73
|
## 🎯 三种典型任务(场景驱动)
|
|
75
74
|
|
|
76
75
|
### 任务 A:P0 用例批量执行(首选)
|
|
@@ -99,7 +98,7 @@ cat > /tmp/case.json <<'EOF'
|
|
|
99
98
|
"recovery": { "maxRetriesPerStep": 2 }
|
|
100
99
|
}
|
|
101
100
|
EOF
|
|
102
|
-
node "$EXECUTOR" --action test-case --test-case-file /tmp/case.json --test-report /tmp/report.json
|
|
101
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action test-case --test-case-file /tmp/case.json --test-report /tmp/report.json
|
|
103
102
|
```
|
|
104
103
|
|
|
105
104
|
dataset 数组每行跑一遍同样的 steps,`{{name}}` / `{{type}}` 自动替换。`setup.findFrame` 一次定位后所有步骤自动走子应用 frame(微前端默认行为)。
|
|
@@ -110,7 +109,7 @@ dataset 数组每行跑一遍同样的 steps,`{{name}}` / `{{type}}` 自动替
|
|
|
110
109
|
|
|
111
110
|
```bash
|
|
112
111
|
# 单字段
|
|
113
|
-
node "$EXECUTOR" --action act --frame-content-contains "数据集" \
|
|
112
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action act --frame-content-contains "数据集" \
|
|
114
113
|
--selector ant-select=资源类型 --click ... # ❌ 不要这样
|
|
115
114
|
|
|
116
115
|
# ✅ 用 testCase 的 antSelect 步骤(在 testCase.steps 内):
|
|
@@ -131,10 +130,10 @@ node "$EXECUTOR" --action act --frame-content-contains "数据集" \
|
|
|
131
130
|
|
|
132
131
|
```bash
|
|
133
132
|
# 一次性看 JS 错误 + API 失败 + iframe 状态
|
|
134
|
-
node "$EXECUTOR" --action diagnose --url-contains "platform.aicityos.com" --wait 10000
|
|
133
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action diagnose --url-contains "platform.aicityos.com" --wait 10000
|
|
135
134
|
|
|
136
135
|
# 仅看 API
|
|
137
|
-
node "$EXECUTOR" --action api-capture --url-contains "platform.aicityos.com" \
|
|
136
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action api-capture --url-contains "platform.aicityos.com" \
|
|
138
137
|
--url-pattern "/api/" --wait 10000 --include-response-body \
|
|
139
138
|
--output /tmp/api.json
|
|
140
139
|
```
|
|
@@ -169,8 +168,6 @@ Agent / SKILL.md
|
|
|
169
168
|
- API 按 frame scope 过滤
|
|
170
169
|
- 浏览器凭据自动解密(使用内置 `decrypt-creds.mjs`)
|
|
171
170
|
|
|
172
|
-
`local-browser-executor.cjs`(同目录)是 CommonJS 过渡版,仅支持基础诊断。**Ant 适配层 / testCase DSL / expect 断言仅 `local-browser-executor.js`(ES Module 版)支持。**
|
|
173
|
-
|
|
174
171
|
## 浏览器模式
|
|
175
172
|
|
|
176
173
|
支持两种模式,执行器自动检测优先使用 connect:
|
|
@@ -270,6 +267,18 @@ AI 直接构造测试计划 JSON:
|
|
|
270
267
|
|
|
271
268
|
> 微前端场景端口可变,优先用 `findPage` + `findFrame` 发现页面,而非硬编码 URL 导航。
|
|
272
269
|
|
|
270
|
+
**场景 F:登录/登出(探索后匹配内置脚本)**
|
|
271
|
+
`observe/evaluate(获取表单结构)→ 判断匹配 cdpScript login → 直接执行`
|
|
272
|
+
|
|
273
|
+
探索获取到"用户名+密码+验证码+登录按钮"的元信息后,直接用 cdpScript login,
|
|
274
|
+
不需要手动处理验证码、截图、问用户。内置脚本自带验证码重试。
|
|
275
|
+
|
|
276
|
+
**场景 G:执行失败后智能重构**
|
|
277
|
+
`执行 → failedSteps > 0 + autoRecovery.recovered = false → 读取 diagnosticHint → 推理原因 → 重构 JSON → 重新执行`
|
|
278
|
+
|
|
279
|
+
执行器内置 L1/L2/L3 自动恢复(机械重试),当自动恢复仍失败时,LLM 读取错误信息,
|
|
280
|
+
推理失败原因,重构 JSON 后重新执行(最多 3 次)。
|
|
281
|
+
|
|
273
282
|
### 构造步骤的指导原则
|
|
274
283
|
|
|
275
284
|
**⚠️ 强制流程:构造 test-case JSON 前必须先执行 DOM 预检查**
|
|
@@ -285,20 +294,84 @@ AI 直接构造测试计划 JSON:
|
|
|
285
294
|
- 表单字段 label(如“数据资源名称”vs“数据集名称”)
|
|
286
295
|
- 表格列头(确认列数和列名)
|
|
287
296
|
|
|
288
|
-
3.
|
|
297
|
+
3. **决策推理:根据元信息匹配实现层级**
|
|
298
|
+
|
|
299
|
+
先判断是否匹配内置 cdpScript(最高优先级,无需手动处理交互细节):
|
|
300
|
+
|
|
301
|
+
| DOM 元信息信号 | 匹配的内置 cdpScript | 说明 |
|
|
302
|
+
|---------------|---------------------|------|
|
|
303
|
+
| URL 含 login + 表单含 password + 验证码 | `login` | 内置验证码重试,无需手动处理 |
|
|
304
|
+
| 已登录状态 + 用户要求退出 | `logout` | 自动保留 appId/viewId |
|
|
305
|
+
| 存在 `.ant-drawer` 且需选择/搜索/确认 | `drawer-action` | 指定 action + radioIndex |
|
|
306
|
+
| 存在 `.ant-modal` / Popconfirm 需确认 | `modal-action` | 指定 action + buttonText |
|
|
307
|
+
| 多标签页场景需切换 | `switch-tab` | 按 url/title/index |
|
|
308
|
+
| 需等待某 API 响应后继续 | `wait-api` | 指定 urlPattern |
|
|
309
|
+
| 需提取表格数据 | `get-table-data` | 指定 selector + maxRows |
|
|
310
|
+
| 抽屉式导航(.operate + .page-left-menu)需点菜单项导航 | `drawer-menu-nav` | 开抽屉→定位→原生点击→验证路由,解决 React 事件委托 |
|
|
311
|
+
| 多模块系统,需探测导航结构 | `detect-menu` | 报告 sidebar + 抽屉模块列表 + 选择器,为遍历或切模块提供元信息 |
|
|
312
|
+
| 需切换到指定模块(抽屉式模块切换器) | `switch-module` | 开抽屉→点模块/快捷项→等 sidebar 刷新→返回 newSidebar |
|
|
313
|
+
| 需全量遍历所有模块的菜单路由 | `traverse-all-menus` | 自动逐模块切换 + URL 去重,一次拿完整菜单树+截图 |
|
|
314
|
+
| 需对单页做只读深度探索(表格/按钮/Tab/接口) | `explore-page` | 自动扫描交互元素、遍历 Tab、点详情,识别但不点击写按钮 |
|
|
315
|
+
|
|
316
|
+
不匹配内置 cdpScript 时,再判断是否能用 antXxx 步骤组合:
|
|
317
|
+
- 存在 `.ant-select` → antSelect
|
|
318
|
+
- 存在 `.ant-tree-select` → antTreeSelect
|
|
319
|
+
- 存在 `.ant-input` + placeholder → antInput
|
|
320
|
+
- 存在 `.ant-radio` → antRadio
|
|
321
|
+
- 多字段表单 → formFill + validateAfter
|
|
322
|
+
|
|
323
|
+
都不匹配时,用 rawScript/registerCdpScript 自定义:
|
|
324
|
+
- 跨 frame 操作 → rawScript(完整 puppeteer API)
|
|
325
|
+
- 可复用的自定义交互 → registerCdpScript 注册后用 cdpScript 调用
|
|
326
|
+
|
|
327
|
+
4. **根据决策结果构造 plan/test-case JSON**
|
|
328
|
+
- 匹配内置 cdpScript → 直接用,不需要手动处理验证码/选项渲染/浮层等细节
|
|
329
|
+
- 匹配 antXxx → 从 observe/evaluate 获取 selector 和文字,构造 testCase
|
|
330
|
+
- 自定义 → 从 DOM 元信息推导交互逻辑,写 rawScript
|
|
289
331
|
- selector 从 observe 获取(不猜测)
|
|
290
332
|
- 选项文字从 evaluate 获取(不猜测)
|
|
291
333
|
- 按钮文字从 evaluate 获取(注意空格)
|
|
292
334
|
- 无项目代码时,DOM 检查替代代码读取
|
|
293
335
|
|
|
294
|
-
|
|
295
|
-
- `text=新建` → 模糊匹配(includes
|
|
296
|
-
- `text==新建` →
|
|
297
|
-
-
|
|
336
|
+
5. **act 文字匹配**
|
|
337
|
+
- `text=新建` → 模糊匹配(includes),可能误匹配"新建目录"
|
|
338
|
+
- `text==新建` → 精确匹配(===),只匹配文字恰好为"新建"的元素
|
|
339
|
+
- 按钮文字可能有空格(如"提 交"),evaluate 检查后再选择匹配方式
|
|
340
|
+
|
|
341
|
+
6. **API 路径从代码中的接口调用读取,或从 API 文档获取**
|
|
342
|
+
7. **checkElement 的 expect 从代码逻辑推断**
|
|
343
|
+
8. **compare 的 regions 从设计稿分析结果或组件布局推断**
|
|
344
|
+
|
|
345
|
+
9. **执行反馈循环:LLM 层面的智能重构**
|
|
346
|
+
|
|
347
|
+
执行器内置 L1/L2/L3 自动恢复(机械重试),当自动恢复仍失败时,LLM 介入:
|
|
348
|
+
|
|
349
|
+
**触发条件**:执行结果中 `failedSteps > 0` 且 `autoRecovery.recovered = false`
|
|
298
350
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
351
|
+
**LLM 读取执行结果,推理失败原因,重构 JSON 后重新执行**:
|
|
352
|
+
|
|
353
|
+
| 失败信号 | 推理方向 | 重构动作 |
|
|
354
|
+
|---------|---------|---------|
|
|
355
|
+
| option not found | 选项文字不匹配 | observe 重新获取选项列表,修正 option |
|
|
356
|
+
| not visible / timeout | 元素未渲染 | 增加 waitFor 或延长 timeout |
|
|
357
|
+
| 401 | 登录态过期 | steps 前插入 cdpScript login |
|
|
358
|
+
| 403 | 权限不足 | 换账号或跳过 |
|
|
359
|
+
| 步骤间时序问题 | 渲染未完成 | 增加 waitAfterEach |
|
|
360
|
+
| 内置脚本不匹配 | 场景未被覆盖 | 降级为 antXxx 或 rawScript |
|
|
361
|
+
|
|
362
|
+
**重构循环**:
|
|
363
|
+
- 最大重构次数:3 次
|
|
364
|
+
- 每次重构前可重新 observe(DOM 可能已变化)
|
|
365
|
+
- 每次重构后重新执行整个 plan/test-case
|
|
366
|
+
- 超过最大次数 → 报告用户失败原因 + diagnosticHint
|
|
367
|
+
|
|
368
|
+
**完整四环流程**:
|
|
369
|
+
```
|
|
370
|
+
环 1:DOM 探索 → 获取元信息
|
|
371
|
+
环 2:决策推理 → 匹配实现层级(内置 cdpScript / antXxx / rawScript)
|
|
372
|
+
环 3:构造 JSON
|
|
373
|
+
环 4:执行 → 失败 → LLM 推理重构 → 重新执行(loop,最多 3 次)
|
|
374
|
+
```
|
|
302
375
|
|
|
303
376
|
## 微前端测试指南(实战经验)
|
|
304
377
|
|
|
@@ -389,7 +462,7 @@ wujie 微前端子应用渲染在 blob URL iframe 中,**坐标点击无法到
|
|
|
389
462
|
|
|
390
463
|
### 结构化观察(主链路起点)
|
|
391
464
|
```bash
|
|
392
|
-
node "$EXECUTOR" \
|
|
465
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
393
466
|
--action observe \
|
|
394
467
|
--url-contains "platform.aicityos.com" \
|
|
395
468
|
--frame-content-contains "数据集目录" \
|
|
@@ -401,7 +474,7 @@ node "$EXECUTOR" \
|
|
|
401
474
|
|
|
402
475
|
### 观察后按 index 交互(主链路闭环)
|
|
403
476
|
```bash
|
|
404
|
-
node "$EXECUTOR" \
|
|
477
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
405
478
|
--action act \
|
|
406
479
|
--url-contains "platform.aicityos.com" \
|
|
407
480
|
--frame-content-contains "数据集目录" \
|
|
@@ -412,7 +485,7 @@ node "$EXECUTOR" \
|
|
|
412
485
|
|
|
413
486
|
### 左侧菜单路径稳定点击
|
|
414
487
|
```bash
|
|
415
|
-
node "$EXECUTOR" \
|
|
488
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
416
489
|
--action act \
|
|
417
490
|
--url-contains "platform.aicityos.com" \
|
|
418
491
|
--menu-path "目录管理/数据集目录" \
|
|
@@ -422,9 +495,124 @@ node "$EXECUTOR" \
|
|
|
422
495
|
|
|
423
496
|
`--menu-path` 会按路径逐级匹配菜单项,非末级优先执行展开,末级执行点击;匹配优先考虑 `role=menuitem`、`.ant-menu-item`、`.ant-menu-submenu-title` 和文本精确/包含匹配。
|
|
424
497
|
|
|
498
|
+
### 菜单的两种设计(探索时必须先区分)
|
|
499
|
+
|
|
500
|
+
导航菜单有两种设计,**探索阶段先调 `cdpScript detect-menu` 探测导航拓扑**,再据结果编排遍历:
|
|
501
|
+
|
|
502
|
+
```json
|
|
503
|
+
{ "type": "cdpScript", "script": "detect-menu", "frameContentContains": "<页面特征文字>" }
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
返回关键字段:
|
|
507
|
+
- `currentSidebar`:当前模块的左侧菜单(**权威完整菜单**)
|
|
508
|
+
- `hasDrawer` / `triggerSelector` / `drawerSelector`:是否有抽屉切换器及其选择器
|
|
509
|
+
- `drawerModules`:抽屉里可切换的模块入口(含 `moduleTitle` + `shortcutSample`)
|
|
510
|
+
- `navModel`:遍历策略说明
|
|
511
|
+
|
|
512
|
+
**⚠️ 核心模型:抽屉是「模块切换器」,不是完整菜单**
|
|
513
|
+
- 抽屉(如 `.page-left-menu`,触发器 `.operate`)只用来在大模块间切换,是用户自定义的快捷入口,**可能不全**
|
|
514
|
+
- 点抽屉里任一项 → 跳转该页 + **左侧 sidebar 刷新成那个模块的完整菜单**
|
|
515
|
+
- 每个模块的**权威完整菜单是切进去后的左侧 sidebar**,不能拿抽屉里的项当模块页面清单
|
|
516
|
+
|
|
517
|
+
| 场景 | 点击方式 |
|
|
518
|
+
|------|---------|
|
|
519
|
+
| 目标已在当前 `currentSidebar` | 直接 `act --menu-path "一级/二级"`(无需开抽屉) |
|
|
520
|
+
| 目标在其它模块 | `cdpScript switch-module` 切过去 → 读返回的 `newSidebar` → 再 `act --menu-path` 点二级 |
|
|
521
|
+
| 纯 sidebar(无抽屉) | 直接 `act --menu-path` 遍历 `currentSidebar` |
|
|
522
|
+
|
|
523
|
+
**完整遍历全部菜单的正确流程**:
|
|
524
|
+
|
|
525
|
+
**首选:一步拿到全量菜单树**(`traverse-all-menus` 已封装"逐个切模块+URL去重+读sidebar"):
|
|
526
|
+
```json
|
|
527
|
+
{ "type": "cdpScript", "script": "traverse-all-menus", "frameContentContains": "<页面特征>",
|
|
528
|
+
"params": { "triggerSelector": ".operate", "drawerSelector": ".page-left-menu" } }
|
|
529
|
+
// 返回 modules[],每项 { moduleTitle, url, urlKey, sidebar[] };撞 urlKey 的自动标 skipped
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**手动编排(理解底层 / traverse 不适用时)**:
|
|
533
|
+
```
|
|
534
|
+
1. detect-menu → 拿到 currentSidebar + drawerModules
|
|
535
|
+
2. 先遍历当前模块:对 currentSidebar 每项 act --menu-path
|
|
536
|
+
3. 对 drawerModules 里每个未探索的模块:
|
|
537
|
+
switch-module(用模块下任一快捷项文字切过去)
|
|
538
|
+
→ 拿到 newSidebar(该模块完整菜单)
|
|
539
|
+
→ 对 newSidebar 每项 act --menu-path 逐项探索
|
|
540
|
+
4. 用「URL 前缀(urlKey)」做已探索缓存,命中则跳过,保证不重复、不遗漏
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
> **去重锚点用 URL 而非模块名**:抽屉模块名可能重复/拆分,但落地 URL 唯一。实测中第8个模块"基础支撑中心"用首个快捷项切换时落到了已访问的 data-security,按 urlKey 去重才能正确跳过——遍历的去重锚点是"实际到达的页面",不是"以为点了哪个模块"。
|
|
544
|
+
|
|
545
|
+
> ⚠️ `traverse-all-menus` 只做**导航广度**覆盖(拿到所有页面入口 URL + sidebar),不展开页面内部的 tab/详情/接口/写操作。页面内深度探索由 `explore-page` 负责。
|
|
546
|
+
|
|
547
|
+
### 完整探索:两阶段(导航广度 + 页面深度)
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
阶段1 traverse-all-menus → 全量页面 URL 清单(广度)
|
|
551
|
+
阶段2 对每个 URL:导航过去 → explore-page → 页面内深度探索
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`explore-page` 产出(只读,绝不触发写操作):
|
|
555
|
+
- `loadApis`:页面加载时触发的接口 `[{method,url,status}]`
|
|
556
|
+
- `tabs[]`:每个 tab + 切换后捕获的接口
|
|
557
|
+
- `detailEntries[]`:查看/详情入口点进去捕获的接口 + URL + 详情内 tab(递归最多 3 层)
|
|
558
|
+
- `writeEntries[]`:新增/编辑/删除/提交等写按钮——**只识别记录文本,绝不点击**
|
|
559
|
+
|
|
560
|
+
**写操作是独立下一步**:explore-page 拿到 `writeEntries` 后,由 LLM **向用户确认**要触发哪些,再单独用 `act`/`antInput`/`formFill` + 原生点击执行(表单自动填默认值)。这样 explore-page 本身永远只读、安全、可重入。
|
|
561
|
+
|
|
562
|
+
```json
|
|
563
|
+
{ "type": "cdpScript", "script": "explore-page", "frameContentContains": "<页面特征>",
|
|
564
|
+
"params": { "maxDepth": 3, "apiPattern": "/api/" } }
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**⚠️ wujie 微前端 shadow DOM**:业务内容渲染在**主 frame 的 `wujie-app.shadowRoot`** 内(子应用 frame 反而可能是空的),普通 `document.querySelectorAll` 探不到。explore-page 已内置三重适配:
|
|
568
|
+
1. **frame 自动选择**:遍历所有 frame(含 shadow 穿透)按可点元素数选最丰富的,避免 `_getTargetFrame` 选到空的子应用 frame(实测主 frame btns=3/pointers=67,子应用 frame 全 0)
|
|
569
|
+
2. **shadow 穿透**:遍历 `wujie-app` 的 shadowRoot 作为额外查询根
|
|
570
|
+
3. **坐标点击**:`frame.$` 选不到 shadow 元素,改用 evaluate 取 rect + `page.mouse.click`
|
|
571
|
+
|
|
572
|
+
手写探索 shadow 页面时必须同样三重适配。
|
|
573
|
+
|
|
574
|
+
> **explore-page 验证状态**:框架 + frame 自动选择 + shadow 穿透 + 坐标点击 + rawClickables 兜底快照均已实现验证。**接口捕获实测有效**:① 查询接口 `dp-corpus/dataset/converge/page`;② 详情页 4 个接口(`queryDatasetConvergeDetail`/`getDataUsage`/`datasetFileList`);③ **tab 遍历**——智能模型仓库 7 个 tab 全部切换并各捕获到 `dp-label/model/*` 接口(7/7)。详情入口词表已扩充(查看/详情/明细/管理/检测/进入/预览)。
|
|
575
|
+
|
|
576
|
+
> **apiPattern 默认不过滤**:默认空 = 捕获所有 XHR/Fetch。**切勿默认按 `/api/` 过滤**——实测不同模块接口前缀不同(语料汇聚 `dp-corpus`、模型仓库 `dp-label`),默认过滤会漏抓。只在确实要收窄到某类接口时才传 `apiPattern`。
|
|
577
|
+
|
|
578
|
+
> **explore-page 已知约束**:
|
|
579
|
+
> - **不保证从干净列表态开始**:若进来时已有打开的弹窗/抽屉/已展开的内嵌面板,识别的是当前态而非列表。批量探索前建议先关闭残留弹窗(点 `.ant-drawer-close`/`.ant-modal-close`/取消按钮)。
|
|
580
|
+
> - **详情多为弹窗/抽屉式**(URL 不变、内容更新):用 `urlChanged:false + apis 非空` 判断详情已加载,不能只看 URL。
|
|
581
|
+
> - **行操作识别盲区**:纯 `<a>`/图标按钮/hover 才显示的操作可能漏识别 → 看 `rawClickables` 由 LLM 推理补充。
|
|
582
|
+
|
|
583
|
+
### explore-page 不满足时:LLM 观测 DOM 推理补充
|
|
584
|
+
|
|
585
|
+
`explore-page` 是**固化的通用启发式**,只覆盖标准模式(`.ant-table` / `.ant-tabs-tab` / 常见中文按钮词 / 标准 drawer-modal)。它是「打底」,不是全部。遇到下列情况,**不要因为它返回空就判定页面没内容**,应由 LLM 用 `observe`/`evaluate` 看 DOM 现场推理补充:
|
|
586
|
+
|
|
587
|
+
| explore-page 覆盖不到的场景 | LLM 兜底做法 |
|
|
588
|
+
|---------------------------|-------------|
|
|
589
|
+
| 卡片式/自定义布局(无 ant-table,如"点击进入"卡片) | `evaluate` 穿透 shadow 列出可点元素(`cursor:pointer` / `onclick` / 自定义类名),按文本/位置推理入口 |
|
|
590
|
+
| 非标准 tab(非 `.ant-tabs-tab`,用 div+active 类自实现) | `evaluate` 找出"一组同级、点击切换 active 态"的元素,推断为 tab |
|
|
591
|
+
| 按钮文字不在内置写/读词表(如"派发""归档""调拨") | `evaluate` 列出所有按钮文字,LLM 判断语义归类(读/写/危险) |
|
|
592
|
+
| 详情不是 drawer/modal 而是路由跳转或内嵌面板 | `observe` 比对点击前后 DOM/URL,推断详情承载方式 |
|
|
593
|
+
| 接口 pattern 非 `/api/`(如 `/gateway/`、GraphQL) | explore-page 传 `apiPattern` 调整,或用 `api-capture` 单独抓 |
|
|
594
|
+
|
|
595
|
+
> **实测语料平台接口前缀是 `/dp-corpus/`**(非 `/api/`)。explore-page 默认 `apiPattern:'/api/'` 会漏抓——对该平台传 `apiPattern: "dp-corpus"`。不确定前缀时先用 `api-capture` 不加 pattern 抓一次看真实路径。
|
|
596
|
+
|
|
597
|
+
**协作模型**:
|
|
598
|
+
```
|
|
599
|
+
通用模式 → explore-page 一把梭(快、稳、可重入)
|
|
600
|
+
非标准 → explore-page 返回空/不全 → LLM observe+evaluate 看现场 → 推理补充 → 必要时 rawScript 自定义交互
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
这与整个 skill 的「内置脚本打底 + LLM 推理兜底」一致:固化脚本省去重复踩坑,LLM 推理处理长尾。explore-page 的空结果是「这里没有标准模式」的信号,不是「这里没东西」的结论。
|
|
604
|
+
|
|
605
|
+
> `switch-module` 的 `moduleText` 用**抽屉里的叶子文本精确匹配**(如模块下快捷项"数据集目录")。
|
|
606
|
+
> 模块标题常被拆成"数据资产-主"+"语料资产中心"两个叶子,传不完整的"资产中心"会匹配失败——用快捷项文字更可靠。
|
|
607
|
+
|
|
608
|
+
**抽屉式菜单的三个坑(switch-module / drawer-menu-nav 已内置处理,手写时注意)**:
|
|
609
|
+
- **开关判断用可见性**:抽屉常驻 DOM,`querySelector` 永远命中,必须按 `getBoundingClientRect` 宽高 + display/visibility/opacity 判断是否真打开
|
|
610
|
+
- **菜单项是裸 span**:`.ant-menu-item` 选择器命不中,需按叶子文本精确匹配(避免命中含整块文字的父容器)
|
|
611
|
+
- **必须用原生鼠标点击**:React 事件委托菜单用 `el.click()` 不触发路由,必须 puppeteer `page.mouse.click(坐标)` 发真实 mousedown/mouseup/click
|
|
612
|
+
|
|
425
613
|
### 轻量 DOM 断言
|
|
426
614
|
```bash
|
|
427
|
-
node "$EXECUTOR" \
|
|
615
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
428
616
|
--action assert \
|
|
429
617
|
--url-contains "platform.aicityos.com" \
|
|
430
618
|
--selector ".ant-table" \
|
|
@@ -435,7 +623,7 @@ node "$EXECUTOR" \
|
|
|
435
623
|
|
|
436
624
|
### API 请求/响应捕获(真实浏览器 + 微前端)
|
|
437
625
|
```bash
|
|
438
|
-
node "$EXECUTOR" \
|
|
626
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
439
627
|
--action api-capture \
|
|
440
628
|
--url-contains "platform.aicityos.com" \
|
|
441
629
|
--frame-content-contains "数据集目录" \
|
|
@@ -450,7 +638,7 @@ node "$EXECUTOR" \
|
|
|
450
638
|
|
|
451
639
|
### API 捕获结果断言
|
|
452
640
|
```bash
|
|
453
|
-
node "$EXECUTOR" \
|
|
641
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
454
642
|
--action assert-api \
|
|
455
643
|
--capture-file /tmp/browser-api-capture.json \
|
|
456
644
|
--max-failed 0 \
|
|
@@ -463,7 +651,7 @@ node "$EXECUTOR" \
|
|
|
463
651
|
|
|
464
652
|
### 浏览器端 JS 执行(构造 testCase 前提取表单元数据)
|
|
465
653
|
```bash
|
|
466
|
-
node "$EXECUTOR" \
|
|
654
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
467
655
|
--action evaluate \
|
|
468
656
|
--url-contains "platform.aicityos.com" \
|
|
469
657
|
--frame-index 1 \
|
|
@@ -485,7 +673,7 @@ node "$EXECUTOR" \
|
|
|
485
673
|
|
|
486
674
|
### 执行测试计划 JSON(ES Module 版)
|
|
487
675
|
```bash
|
|
488
|
-
node "$EXECUTOR" \
|
|
676
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
489
677
|
--action plan \
|
|
490
678
|
--plan-file /tmp/test-plan.json \
|
|
491
679
|
--output /tmp/browser-test-result.json
|
|
@@ -493,9 +681,9 @@ node "$EXECUTOR" \
|
|
|
493
681
|
|
|
494
682
|
`--action plan` 使用 ES Module 执行器运行测试计划 JSON,支持全部步骤类型(包括 evaluate、antSelect 等)。与 `--action test-case` 的区别:plan 不走 dataset 循环,适合单次执行。
|
|
495
683
|
|
|
496
|
-
###
|
|
684
|
+
### 快速诊断(JS错误 + 网络 + 资源 + iframe)
|
|
497
685
|
```bash
|
|
498
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$
|
|
686
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
499
687
|
--action diagnose \
|
|
500
688
|
--url-contains "platform.aicityos.com" \
|
|
501
689
|
--wait 15000 \
|
|
@@ -504,97 +692,28 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.
|
|
|
504
692
|
|
|
505
693
|
### 列出所有标签页
|
|
506
694
|
```bash
|
|
507
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$
|
|
508
|
-
--action list-pages
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
### 检查 API 请求状态
|
|
512
|
-
```bash
|
|
513
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
514
|
-
--action api-check \
|
|
515
|
-
--url-contains "platform.aicityos.com" \
|
|
516
|
-
--wait 15000
|
|
695
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action list-pages
|
|
517
696
|
```
|
|
518
697
|
|
|
519
698
|
### 截图
|
|
520
699
|
```bash
|
|
521
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$
|
|
700
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
522
701
|
--action screenshot \
|
|
523
702
|
--url-contains "platform.aicityos.com" \
|
|
524
703
|
--filename current-page.png \
|
|
525
704
|
--output-dir /tmp/browser-test
|
|
526
705
|
```
|
|
527
706
|
|
|
528
|
-
###
|
|
529
|
-
```bash
|
|
530
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
531
|
-
--action plan \
|
|
532
|
-
--plan-file /tmp/test-plan.json \
|
|
533
|
-
--output /tmp/browser-test-result.json
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
### DOM 快照(结构化页面树,类似 browser-use take_snapshot)
|
|
537
|
-
```bash
|
|
538
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
539
|
-
--action snapshot \
|
|
540
|
-
--url-contains "platform.aicityos.com" \
|
|
541
|
-
--depth 4 \
|
|
542
|
-
--selector ".page-container"
|
|
543
|
-
```
|
|
544
|
-
输出示例:
|
|
545
|
-
```
|
|
546
|
-
<div.page-container>
|
|
547
|
-
<section.page-layout>
|
|
548
|
-
<header.page-header>
|
|
549
|
-
<div.page-header-left> <div.logo>
|
|
550
|
-
<section.h-box>
|
|
551
|
-
<aside.ant-layout-sider> 🔗
|
|
552
|
-
<main.ant-layout-content>
|
|
553
|
-
<div.layout-wrap>
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### 智能交互(支持 text=、role=、~ 选择器,类似 Playwright)
|
|
557
|
-
```bash
|
|
558
|
-
# 点击包含“构建”文本的按钮
|
|
559
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
560
|
-
--action interact \
|
|
561
|
-
--url-contains "jenkins" \
|
|
562
|
-
--click "text=Build"
|
|
563
|
-
|
|
564
|
-
# 在输入框中输入文本
|
|
565
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
566
|
-
--action interact \
|
|
567
|
-
--click "#search-input" \
|
|
568
|
-
--type "hello world"
|
|
569
|
-
|
|
570
|
-
# 悬停 + 截图
|
|
571
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
572
|
-
--action interact \
|
|
573
|
-
--hover "~ant-menu-item" \
|
|
574
|
-
--screenshot-after
|
|
575
|
-
|
|
576
|
-
# 滚动页面
|
|
577
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
578
|
-
--action interact \
|
|
579
|
-
--scroll down --scroll-amount 500
|
|
580
|
-
```
|
|
581
|
-
选择器语法:
|
|
582
|
-
- `text=Build` → 按文本内容模糊查找,多个匹配时选 innerText 最短的元素(叶子节点优先,避免命中父容器)
|
|
583
|
-
- `text==Build` → 精确文本匹配(Playwright 风格),只匹配 innerText 完全相等的元素
|
|
584
|
-
- `role=button` → 按 ARIA role 查找
|
|
585
|
-
- `~ant-menu-item` → CSS class 模糊匹配(`[class*="ant-menu-item"]`)
|
|
586
|
-
- `.my-class` → 精确 CSS 选择器
|
|
587
|
-
|
|
588
|
-
### 性能指标采集(FCP/TTFB/资源统计)
|
|
707
|
+
### 性能指标采集(FCP/TTFB/资源统计/Chrome内部指标)
|
|
589
708
|
```bash
|
|
590
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$
|
|
709
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
591
710
|
--action perf \
|
|
592
711
|
--url-contains "platform.aicityos.com"
|
|
593
712
|
```
|
|
594
713
|
|
|
595
714
|
### 持续监控模式(定时截图 + 错误检测)
|
|
596
715
|
```bash
|
|
597
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$
|
|
716
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
598
717
|
--action watch \
|
|
599
718
|
--url-contains "platform.aicityos.com" \
|
|
600
719
|
--interval 5000 \
|
|
@@ -602,6 +721,8 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.
|
|
|
602
721
|
--output-dir /tmp/browser-watch
|
|
603
722
|
```
|
|
604
723
|
|
|
724
|
+
> snapshot / interact / api-check 已由 `observe` + `act` + `api-capture` 完全覆盖,不再单独提供。
|
|
725
|
+
|
|
605
726
|
### 可选参数
|
|
606
727
|
|
|
607
728
|
- `--cdp-url http://localhost:9222`:CDP 地址(默认 9222)
|
|
@@ -656,7 +777,32 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.
|
|
|
656
777
|
- 主链路(`observe/act/assert/diagnose/screenshot` + 凭据解密)安装 `puppeteer-core` + `sql.js` 到 `~/.szcd-mcp/deps/`
|
|
657
778
|
- 只有执行设计稿像素对比 `compare` 时才安装 `pixelmatch/pngjs/sharp`
|
|
658
779
|
- 后续所有项目共享复用,无需重复安装
|
|
659
|
-
|
|
780
|
+
|
|
781
|
+
### 依赖预检(首次执行前必做)
|
|
782
|
+
|
|
783
|
+
```bash
|
|
784
|
+
# 检查依赖是否已安装(不报错则已就绪)
|
|
785
|
+
node -e "require.resolve('puppeteer-core', { paths: [process.env.HOME + '/.szcd-mcp/deps/node_modules'] }); console.log('puppeteer-core ✓')" 2>/dev/null || echo "puppeteer-core ✗ 需要安装"
|
|
786
|
+
node -e "require.resolve('sql.js', { paths: [process.env.HOME + '/.szcd-mcp/deps/node_modules'] }); console.log('sql.js ✓')" 2>/dev/null || echo "sql.js ✗ 需要安装"
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
> **Windows (Git Bash)** 将 `$HOME` 替换为完整路径如 `C:/Users/Lenovo`。
|
|
790
|
+
|
|
791
|
+
### 排障:"Cannot find module" 错误
|
|
792
|
+
|
|
793
|
+
| 报错信息 | 原因 | 解决 |
|
|
794
|
+
|---------|------|------|
|
|
795
|
+
| `Cannot find module 'puppeteer-core'` | NODE_PATH 未设置或路径错误 | 命令前加 `NODE_PATH=~/.szcd-mcp/deps/node_modules` |
|
|
796
|
+
| `Cannot find module 'sql.js'` | 同上(sql.js 与 puppeteer-core 在同一目录) | 同上 |
|
|
797
|
+
| `ENOENT: no such file or directory` (Windows) | `~` 未正确展开 | 改用完整路径 `C:/Users/xxx/.szcd-mcp/deps/node_modules` |
|
|
798
|
+
| 依赖确实未安装 | 首次使用或被清理 | 执行下方安装命令 |
|
|
799
|
+
|
|
800
|
+
> **⚠️ 遇到 "Cannot find module" 时,先执行依赖预检确认是否真的缺失,不要直接 npm install。**
|
|
801
|
+
> 大多数情况是 NODE_PATH 未设置,依赖实际已安装在 `~/.szcd-mcp/deps/node_modules/`。
|
|
802
|
+
|
|
803
|
+
### 安装命令
|
|
804
|
+
|
|
805
|
+
如果核心依赖自动安装失败,引导用户手动执行:
|
|
660
806
|
```bash
|
|
661
807
|
cd ~/.szcd-mcp/deps && npm install --registry=https://registry.npmmirror.com puppeteer-core sql.js
|
|
662
808
|
```
|
|
@@ -702,7 +848,7 @@ cd ~/.szcd-mcp/deps && npm install --registry=https://registry.npmmirror.com --@
|
|
|
702
848
|
|
|
703
849
|
1. **第一步:先抓菜单接口**(一次即可,作为整张菜单树的来源)
|
|
704
850
|
```bash
|
|
705
|
-
|
|
851
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action api-capture \
|
|
706
852
|
--url-pattern "listUserViewMenus" \
|
|
707
853
|
--include-response-body \
|
|
708
854
|
--include-all-targets \
|
|
@@ -712,7 +858,7 @@ cd ~/.szcd-mcp/deps && npm install --registry=https://registry.npmmirror.com --@
|
|
|
712
858
|
|
|
713
859
|
2. **第二步:把菜单树喂给 runTask**
|
|
714
860
|
```bash
|
|
715
|
-
|
|
861
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action run-task \
|
|
716
862
|
--task menu-api-scan \
|
|
717
863
|
--capture-input-file /tmp/menu-tree.json \
|
|
718
864
|
--menu-path-prefix /catalog \
|
|
@@ -820,7 +966,7 @@ cat > /tmp/login.json <<EOF
|
|
|
820
966
|
EOF
|
|
821
967
|
|
|
822
968
|
# 3. 执行登录
|
|
823
|
-
node "$EXECUTOR" --action plan --plan-file /tmp/login.json --url-contains "platform.aicityos.com" --wait 8000
|
|
969
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action plan --plan-file /tmp/login.json --url-contains "platform.aicityos.com" --wait 8000
|
|
824
970
|
```
|
|
825
971
|
|
|
826
972
|
**LLM 处理流程:**
|
|
@@ -875,6 +1021,8 @@ node "$EXECUTOR" --action plan --plan-file /tmp/login.json --url-contains "platf
|
|
|
875
1021
|
| `setRequestInterception(true)` 抓接口 | 仅 CDP `Network.responseReceived` + `getResponseBody` | request interception 会拦下 wujie iframe 的 RPC 帧造成挂起 |
|
|
876
1022
|
| `page.click("text=保存")` 不加可见性过滤 | 默认走 `act` 的 `text=`,已自动只匹配 `visible+enabled` 的元素 | 隐藏在收起菜单/未渲染抽屉里的同名文本会被错误命中 |
|
|
877
1023
|
| 多级菜单只用一次 `dispatchEvent("mouseover")` | `useNativeHover: true`(默认) + `drawerTriggerSelector` 每级前重开 | Vue 的 v-on:mouseenter 对 dispatchEvent 不触发;抽屉式菜单点完一次会自动收起 |
|
|
1024
|
+
| 抽屉菜单项用 `el.click()` 点击 | 抽屉式导航用 `cdpScript drawer-menu-nav`,内部走 `page.mouse.click(坐标)` | React 事件委托型菜单(无类名裸 span)`el.click()` 不触发合成事件→路由不切换,必须原生鼠标事件 |
|
|
1025
|
+
| 按 `querySelector` 是否命中判断抽屉开关 | 按可见性判断(rect 宽高 + display/visibility/opacity) | `.page-left-menu` 类抽屉常驻 DOM、靠 CSS 隐藏,存在性判断会误判 already open |
|
|
878
1026
|
| `setTimeout(awaitPage.bringToFront(), 30000)` 之外不做兜底 | 调用前自查"目标页是否还活着"(`findPage` / `_findFrame`),失败立刻退而求其次操作主帧 | bringToFront 在 OS 抢焦点失败时会无限挂起,没超时 |
|
|
879
1027
|
| iframe attach 失败就报错退出 | `attachRetries` 默认 3 次、间隔 1.5s 重抓 `browser.targets()` | wujie 的 blob iframe target 在主页面 ready 后才注册,时序敏感 |
|
|
880
1028
|
| 大批量探索每条都 throw | `_actMenuPath` 失败时返回 `acted:false + candidates`,配合 `runTask --continue-on-fail` | 探索性扫描遇到一条点不开就全部中断,没法发现整体面 |
|
|
@@ -991,7 +1139,7 @@ testCase 执行
|
|
|
991
1139
|
把多个用例统一为参数化 testCase JSON:
|
|
992
1140
|
|
|
993
1141
|
```bash
|
|
994
|
-
node
|
|
1142
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action test-case --test-case-file /tmp/case.json [--dataset-index 0] [--test-report /tmp/report.json]
|
|
995
1143
|
```
|
|
996
1144
|
|
|
997
1145
|
```json
|
|
@@ -1017,8 +1165,8 @@ node local-browser-executor.js --action test-case --test-case-file /tmp/case.jso
|
|
|
1017
1165
|
|------|---------|---------|------|---------|
|
|
1018
1166
|
| 1 | evaluate | 浏览器端 | DOM 查询/操作 | 简单检查、点击元素 |
|
|
1019
1167
|
| 2 | rawScript | Node.js 端 | 完整 puppeteer API | 跨 frame、等待、多标签页 |
|
|
1020
|
-
| 3 | cdpScript | Node.js 端 | 调用内置或用户注册脚本 |
|
|
1021
|
-
| 4 | 内置 cdpScript | Node.js 端 | 预定义通用 UI 交互 + 凭据管理 | login/logout/
|
|
1168
|
+
| 3 | cdpScript | Node.js 端 | 调用内置或用户注册脚本 | 登录/登出/抽屉导航/模块切换/页面探索/弹窗/标签切换/API 等待等通用交互 |
|
|
1169
|
+
| 4 | 内置 cdpScript | Node.js 端 | 预定义通用 UI 交互 + 凭据管理 | login/logout/drawer-action/drawer-menu-nav/detect-menu/switch-module/traverse-all-menus/explore-page/modal-action/switch-tab/wait-api/get-table-data |
|
|
1022
1170
|
|
|
1023
1171
|
**evaluate**:浏览器端执行 JS,只能操作 DOM,不能等待/跨 frame/调用 puppeteer
|
|
1024
1172
|
**rawScript**:Node.js 端执行 async 函数,接收 `(page, frame, params, browser)`,可调用所有 puppeteer API
|
|
@@ -1032,6 +1180,11 @@ node local-browser-executor.js --action test-case --test-case-file /tmp/case.jso
|
|
|
1032
1180
|
| `login` | **自动登录**(填表+提交) | `username`, `password`, `captcha?`(默认6), `loginUrl?` |
|
|
1033
1181
|
| `logout` | **登出**(清 cookie+跳转登录页) | `appUrl?`(默认/szc-ai-data/) |
|
|
1034
1182
|
| `drawer-action` | Drawer 操作 | `action: 'search'\|'select'\|'confirm'\|'cancel'`, `radioIndex?`, `searchText?`, `searchPlaceholder?` |
|
|
1183
|
+
| `detect-menu` | **导航拓扑探测**(当前 sidebar + 抽屉模块 + 选择器,编排遍历前先调) | `triggerHints?`, `drawerHints?` |
|
|
1184
|
+
| `switch-module` | **切模块**(开抽屉→点模块/快捷项→等 sidebar 刷新→返回 newSidebar) | `moduleText`(必填,用抽屉叶子文本), `triggerSelector?`, `drawerSelector?`, `waitAfter?` |
|
|
1185
|
+
| `traverse-all-menus` | **全量菜单路由遍历**(自动逐个切模块+按 URL 去重,一次拿到完整菜单树) | `triggerSelector?`(默认.operate), `drawerSelector?`(默认.page-left-menu), `maxModules?`(默认30), `waitAfter?`(默认1600) |
|
|
1186
|
+
| `explore-page` | **单页深度探索**(只读:捕获加载接口+遍历tab+点详情,识别但不点写按钮) | `maxDepth?`(默认3,最大3), `apiPattern?`(默认空=捕获全部XHR/Fetch,传了才收窄过滤), `settleMs?`(默认1500) |
|
|
1187
|
+
| `drawer-menu-nav` | **抽屉式直达导航**(开抽屉→定位→原生点击→验证路由) | `menuText`(必填), `triggerSelector?`(默认.operate), `openSelector?`(默认.page-left-menu), `waitAfter?`(默认1500) |
|
|
1035
1188
|
| `modal-action` | Modal/Popconfirm 操作 | `action: 'confirm'\|'cancel'`, `buttonText?` |
|
|
1036
1189
|
| `switch-tab` | 多标签页切换 | `urlContains?`, `titleIncludes?`, `pageIndex?` |
|
|
1037
1190
|
| `wait-api` | 等待 API 响应 | `urlPattern`, `timeout?`, `method?` |
|
|
@@ -1046,6 +1199,8 @@ node local-browser-executor.js --action test-case --test-case-file /tmp/case.jso
|
|
|
1046
1199
|
{ "type": "cdpScript", "script": "drawer-action", "params": { "action": "confirm" } }
|
|
1047
1200
|
{ "type": "cdpScript", "script": "modal-action", "params": { "action": "confirm" } }
|
|
1048
1201
|
{ "type": "cdpScript", "script": "switch-tab", "params": { "urlContains": "localhost:8088" } }
|
|
1202
|
+
// 抽屉式导航菜单:开抽屉 + 点菜单项 + 验证路由切换,一步完成(frameContentContains 锁定含菜单的 frame)
|
|
1203
|
+
{ "type": "cdpScript", "script": "drawer-menu-nav", "frameContentContains": "语料资产中心", "params": { "menuText": "数据探索", "triggerSelector": ".operate", "openSelector": ".page-left-menu" } }
|
|
1049
1204
|
|
|
1050
1205
|
// rawScript 示例(LLM 动态生成业务逻辑)
|
|
1051
1206
|
{ "type": "rawScript", "code": "async (page, frame, params, browser) => { var count = await frame.evaluate(() => document.querySelectorAll('tr').length); return { acted: true, passed: count > 0, rowCount: count, status: 'PASS' } }" }
|