@szc-ft/mcp-szcd-client 0.28.1 → 0.29.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/agents/szcd-component-expert.qoder.md +1 -0
- package/opencode-extension/skills/local-browser-test/SKILL.md +189 -53
- package/opencode-extension/skills/local-browser-test/lib/ant-adapter.js +735 -0
- package/opencode-extension/skills/local-browser-test/lib/browser-engine.js +2427 -0
- package/opencode-extension/skills/local-browser-test/lib/chrome-finder.js +125 -0
- package/opencode-extension/skills/local-browser-test/lib/expect.js +392 -0
- package/opencode-extension/skills/local-browser-test/lib/shared-deps.js +137 -0
- package/opencode-extension/skills/local-browser-test/lib/test-plan.js +48 -0
- package/opencode-extension/skills/local-browser-test/lib/visual-compare.js +192 -0
- package/opencode-extension/skills/local-browser-test/local-browser-executor.js +735 -0
- package/opencode-extension/skills/local-browser-test/package.json +1 -0
- package/opencode-extension/skills/szcd-upload-zip/SKILL.md +122 -0
- package/package.json +1 -1
- package/qwen-extension/qwen-extension.json +1 -1
- package/qwen-extension/skills/local-browser-test/SKILL.md +189 -53
- package/qwen-extension/skills/local-browser-test/lib/ant-adapter.js +735 -0
- package/qwen-extension/skills/local-browser-test/lib/browser-engine.js +2427 -0
- package/qwen-extension/skills/local-browser-test/lib/chrome-finder.js +125 -0
- package/qwen-extension/skills/local-browser-test/lib/expect.js +392 -0
- package/qwen-extension/skills/local-browser-test/lib/shared-deps.js +137 -0
- package/qwen-extension/skills/local-browser-test/lib/test-plan.js +48 -0
- package/qwen-extension/skills/local-browser-test/lib/visual-compare.js +192 -0
- package/qwen-extension/skills/local-browser-test/local-browser-executor.js +735 -0
- package/qwen-extension/skills/local-browser-test/package.json +1 -0
- package/qwen-extension/skills/szcd-upload-zip/SKILL.md +122 -0
- package/standard-skill/local-browser-test/SKILL.md +189 -53
- package/standard-skill/local-browser-test/lib/ant-adapter.js +735 -0
- package/standard-skill/local-browser-test/lib/browser-engine.js +2427 -0
- package/standard-skill/local-browser-test/lib/chrome-finder.js +125 -0
- package/standard-skill/local-browser-test/lib/expect.js +392 -0
- package/standard-skill/local-browser-test/lib/shared-deps.js +137 -0
- package/standard-skill/local-browser-test/lib/test-plan.js +48 -0
- package/standard-skill/local-browser-test/lib/visual-compare.js +192 -0
- package/standard-skill/local-browser-test/local-browser-executor.js +735 -0
- package/standard-skill/local-browser-test/package.json +1 -0
- package/standard-skill/szcd-upload-zip/SKILL.md +122 -0
|
@@ -27,16 +27,46 @@ compatibility:
|
|
|
27
27
|
|
|
28
28
|
## ⚡ 路径解析(必读,避免 ad-hoc 脚本)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
**执行器内置在 skill 目录中(自包含),会话开始时先解析 skill 路径:**
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
#
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
# 解析 skill 目录路径(执行器 + lib 都在 skill 目录内)
|
|
34
|
+
export SKILL_DIR=$(node -p "
|
|
35
|
+
const path = require('path');
|
|
36
|
+
// 尝试从 npm 包解析
|
|
37
|
+
try { return path.dirname(require.resolve('@szc-ft/mcp-szcd-client/standard-skill/local-browser-test/SKILL.md')); } catch {}
|
|
38
|
+
// 尝试从 IDE skill 目录查找
|
|
39
|
+
const os = require('os');
|
|
40
|
+
const candidates = [
|
|
41
|
+
path.join(os.homedir(), '.qwen/extensions/szcd-component-helper/skills/local-browser-test'),
|
|
42
|
+
path.join(os.homedir(), '.claude/skills/local-browser-test'),
|
|
43
|
+
path.join(os.homedir(), '.config/opencode/skills/local-browser-test'),
|
|
44
|
+
path.join(os.homedir(), '.trae-cn/skills/local-browser-test'),
|
|
45
|
+
path.join(os.homedir(), '.qoder/skills/local-browser-test'),
|
|
46
|
+
];
|
|
47
|
+
for (const c of candidates) {
|
|
48
|
+
if (require('fs').existsSync(path.join(c, 'local-browser-executor.js'))) return c;
|
|
49
|
+
}
|
|
50
|
+
throw new Error('local-browser-test skill not found');
|
|
51
|
+
")
|
|
52
|
+
echo "SKILL_DIR=$SKILL_DIR"
|
|
53
|
+
|
|
54
|
+
# 执行器路径
|
|
55
|
+
export EXECUTOR="$SKILL_DIR/local-browser-executor.js"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
后续所有命令直接 `node "$EXECUTOR" --action ...`。
|
|
59
|
+
|
|
60
|
+
**执行器 + lib 内置在 skill 目录中,自带 Ant 适配层 + expect 断言 + testCase 引擎。禁止自己 require puppeteer 写脚本**——自写脚本会丢失:
|
|
61
|
+
- 微前端 frame 探活 + 自动召回
|
|
62
|
+
- `closeAllDropdowns` 跨 frame 清理
|
|
63
|
+
- Ant Select mousedown + VERIFY 回写
|
|
64
|
+
- testCase recovery 重试
|
|
65
|
+
- API 按 frame scope 过滤
|
|
38
66
|
|
|
39
|
-
|
|
67
|
+
### 过渡兼容执行器
|
|
68
|
+
|
|
69
|
+
`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` 版本。
|
|
40
70
|
|
|
41
71
|
## 🎯 三种典型任务(场景驱动)
|
|
42
72
|
|
|
@@ -66,7 +96,7 @@ cat > /tmp/case.json <<'EOF'
|
|
|
66
96
|
"recovery": { "maxRetriesPerStep": 2 }
|
|
67
97
|
}
|
|
68
98
|
EOF
|
|
69
|
-
node "$
|
|
99
|
+
node "$EXECUTOR" --action test-case --test-case-file /tmp/case.json --test-report /tmp/report.json
|
|
70
100
|
```
|
|
71
101
|
|
|
72
102
|
dataset 数组每行跑一遍同样的 steps,`{{name}}` / `{{type}}` 自动替换。`setup.findFrame` 一次定位后所有步骤自动走子应用 frame(微前端默认行为)。
|
|
@@ -77,7 +107,7 @@ dataset 数组每行跑一遍同样的 steps,`{{name}}` / `{{type}}` 自动替
|
|
|
77
107
|
|
|
78
108
|
```bash
|
|
79
109
|
# 单字段
|
|
80
|
-
node "$
|
|
110
|
+
node "$EXECUTOR" --action act --frame-content-contains "数据集" \
|
|
81
111
|
--selector ant-select=资源类型 --click ... # ❌ 不要这样
|
|
82
112
|
|
|
83
113
|
# ✅ 用 testCase 的 antSelect 步骤(在 testCase.steps 内):
|
|
@@ -98,10 +128,10 @@ node "$CLIENT_PATH" --action act --frame-content-contains "数据集" \
|
|
|
98
128
|
|
|
99
129
|
```bash
|
|
100
130
|
# 一次性看 JS 错误 + API 失败 + iframe 状态
|
|
101
|
-
node "$
|
|
131
|
+
node "$EXECUTOR" --action diagnose --url-contains "platform.aicityos.com" --wait 10000
|
|
102
132
|
|
|
103
133
|
# 仅看 API
|
|
104
|
-
node "$
|
|
134
|
+
node "$EXECUTOR" --action api-capture --url-contains "platform.aicityos.com" \
|
|
105
135
|
--url-pattern "/api/" --wait 10000 --include-response-body \
|
|
106
136
|
--output /tmp/api.json
|
|
107
137
|
```
|
|
@@ -113,21 +143,24 @@ node "$CLIENT_PATH" --action api-capture --url-contains "platform.aicityos.com"
|
|
|
113
143
|
```
|
|
114
144
|
Agent / SKILL.md
|
|
115
145
|
→ testCase JSON ← P0 用例首选入口
|
|
116
|
-
→
|
|
117
|
-
→ lib/browser-engine.js (executeTestCase + Ant 适配 + expect)
|
|
118
|
-
→ lib/ant-adapter.js (Select/TreeSelect/Input/Radio/...)
|
|
119
|
-
→ lib/expect.js (validation/locator/url/api + poll)
|
|
146
|
+
→ $SKILL_DIR/local-browser-executor.js ($EXECUTOR)
|
|
147
|
+
→ $SKILL_DIR/lib/browser-engine.js (executeTestCase + Ant 适配 + expect)
|
|
148
|
+
→ $SKILL_DIR/lib/ant-adapter.js (Select/TreeSelect/Input/Radio/...)
|
|
149
|
+
→ $SKILL_DIR/lib/expect.js (validation/locator/url/api + poll)
|
|
120
150
|
→ puppeteer-core + CDP
|
|
121
151
|
→ 用户真实 Chrome / Edge
|
|
122
152
|
```
|
|
123
153
|
|
|
124
|
-
|
|
154
|
+
执行器 + lib 内置在 skill 目录中(自包含),`postinstall` 会把整个 skill 目录复制到各 IDE。
|
|
155
|
+
**禁止 AI 在用户机器上写 ad-hoc puppeteer 脚本**——会丢失:
|
|
125
156
|
- 微前端 frame 探活 + 自动召回
|
|
126
157
|
- `closeAllDropdowns` 跨 frame 清理
|
|
127
158
|
- Ant Select VERIFY 回写
|
|
128
159
|
- testCase recovery 重试
|
|
129
160
|
- API 按 frame scope 过滤
|
|
130
161
|
|
|
162
|
+
`local-browser-executor.cjs`(同目录)是 CommonJS 过渡版,仅支持基础诊断。**Ant 适配层 / testCase DSL / expect 断言仅 `local-browser-executor.js`(ES Module 版)支持。**
|
|
163
|
+
|
|
131
164
|
## 浏览器模式
|
|
132
165
|
|
|
133
166
|
支持两种模式,执行器自动检测优先使用 connect:
|
|
@@ -182,13 +215,18 @@ AI 直接构造测试计划 JSON:
|
|
|
182
215
|
| click | 点击元素(兼容) | `selector`, `waitForNavigation` | `{ clicked, tagName }` |
|
|
183
216
|
| type | 输入文本(兼容) | `selector`, `text`, `clear` | `{ typed, value }` |
|
|
184
217
|
| waitFor | 等待条件 | `selector` 或 `url`, `timeout` | `{ matched, waited }` |
|
|
185
|
-
| evaluate | 执行 JS | `expression` | `{ result }` |
|
|
218
|
+
| evaluate | 执行 JS(浏览器端) | `expression`, `expectResult?`, `expectResultContains?`, `failOnFalsy?` | `{ result, passed, reason }` |
|
|
186
219
|
| checkElement | 元素断言(兼容) | `selector`, `expect: {visible, minCount, maxCount, hasText}` | `{ passed, checks, elementCount }` |
|
|
187
220
|
| findPage | 查找已打开的标签页 | `urlIncludes` 或 `urlRegex` 或 `titleIncludes` | `{ found, url, title, totalPages }` |
|
|
188
|
-
| findFrame | 查找微前端 iframe | `
|
|
221
|
+
| findFrame | 查找微前端 iframe | `frameSelector`(CSS选择器) 或 `frameUrlContains` 或 `frameContentContains` 或 `frameIndex` | `{ found, frameUrl, matchedBy, totalFrames }` |
|
|
189
222
|
| loginWait | 等待 SSO 登录完成 | `loginSelector`, `timeout`, `interval` | `{ loggedIn, elapsed, currentUrl }` |
|
|
190
223
|
| aiAssert | 语义断言(截图+审查) | `assertion`, `filename` | `{ screenshotPath, assertion, needsVisualReview }` |
|
|
191
224
|
| runTask | 高层任务 runner(批量动态菜单扫描) | `task: "menu-api-scan"`, `menuApiPattern`, `menuLimit`, `menuPathPrefix[]`, `menuFilter[]`, `drawerTriggerSelector`, `useNativeHover`, `apiCaptureOptions`, `continueOnFail` | `{ task, discovered, attempted, byOutcome, results[] }` |
|
|
225
|
+
| **apiCall** | **API 直调(绕过 UI)** | `method`, `url`, `body?`, `headers?` | `{ status, data, passed }` |
|
|
226
|
+
| **switchPage** | **多标签页切换** | `urlContains?`/`titleIncludes?`/`pageIndex?` | `{ passed, url, title }` |
|
|
227
|
+
| **cdpScript** | **调用内置/已注册脚本** | `script`, `params?` | `{ acted, passed, ...result }` |
|
|
228
|
+
| **rawScript** | **Node.js 端执行任意 async** | `code`, `params?` | `{ acted, passed, ...result }` |
|
|
229
|
+
| **registerCdpScript** | **注册可复用脚本** | `name`, `code` | `{ registered, passed }` |
|
|
192
230
|
|
|
193
231
|
### 场景选择策略
|
|
194
232
|
|
|
@@ -213,14 +251,33 @@ AI 直接构造测试计划 JSON:
|
|
|
213
251
|
|
|
214
252
|
### 构造步骤的指导原则
|
|
215
253
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
-
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
**⚠️ 强制流程:构造 test-case JSON 前必须先执行 DOM 预检查**
|
|
255
|
+
|
|
256
|
+
1. **observe 检查目标页面 DOM 结构**
|
|
257
|
+
- 确认 frame 结构(主页面 + 子应用 iframe)
|
|
258
|
+
- 确认表格/表单/按钮的 selector
|
|
259
|
+
- 确认 setup.findFrame 的匹配方式(frameSelector/frameUrlContains)
|
|
260
|
+
|
|
261
|
+
2. **evaluate 检查关键元素文字**
|
|
262
|
+
- 按钮文字(如“提 交”中间有空格,不能用“提交”匹配)
|
|
263
|
+
- Select 选项文字(如“AI知识主题库”可能与 UI 显示不一致)
|
|
264
|
+
- 表单字段 label(如“数据资源名称”vs“数据集名称”)
|
|
265
|
+
- 表格列头(确认列数和列名)
|
|
266
|
+
|
|
267
|
+
3. **根据 DOM 检查结果构造 test-case JSON**
|
|
268
|
+
- selector 从 observe 获取(不猜测)
|
|
269
|
+
- 选项文字从 evaluate 获取(不猜测)
|
|
270
|
+
- 按钮文字从 evaluate 获取(注意空格)
|
|
271
|
+
- 无项目代码时,DOM 检查替代代码读取
|
|
272
|
+
|
|
273
|
+
4. **act 文字匹配**
|
|
274
|
+
- `text=新建` → 模糊匹配(includes),可能误匹配“新建目录”
|
|
275
|
+
- `text==新建` → 精确匹配(===),只匹配文字恰好为“新建”的元素
|
|
276
|
+
- 按钮文字可能有空格(如“提 交”),evaluate 检查后再选择匹配方式
|
|
277
|
+
|
|
278
|
+
5. **API 路径从代码中的接口调用读取,或从 API 文档获取**
|
|
279
|
+
6. **checkElement 的 expect 从代码逻辑推断**
|
|
280
|
+
7. **compare 的 regions 从设计稿分析结果或组件布局推断**
|
|
224
281
|
|
|
225
282
|
## 微前端测试指南(实战经验)
|
|
226
283
|
|
|
@@ -311,7 +368,7 @@ wujie 微前端子应用渲染在 blob URL iframe 中,**坐标点击无法到
|
|
|
311
368
|
|
|
312
369
|
### 结构化观察(主链路起点)
|
|
313
370
|
```bash
|
|
314
|
-
node
|
|
371
|
+
node "$EXECUTOR" \
|
|
315
372
|
--action observe \
|
|
316
373
|
--url-contains "platform.aicityos.com" \
|
|
317
374
|
--frame-content-contains "数据集目录" \
|
|
@@ -323,7 +380,7 @@ node {client_path}/local-browser-executor.js \
|
|
|
323
380
|
|
|
324
381
|
### 观察后按 index 交互(主链路闭环)
|
|
325
382
|
```bash
|
|
326
|
-
node
|
|
383
|
+
node "$EXECUTOR" \
|
|
327
384
|
--action act \
|
|
328
385
|
--url-contains "platform.aicityos.com" \
|
|
329
386
|
--frame-content-contains "数据集目录" \
|
|
@@ -334,7 +391,7 @@ node {client_path}/local-browser-executor.js \
|
|
|
334
391
|
|
|
335
392
|
### 左侧菜单路径稳定点击
|
|
336
393
|
```bash
|
|
337
|
-
node
|
|
394
|
+
node "$EXECUTOR" \
|
|
338
395
|
--action act \
|
|
339
396
|
--url-contains "platform.aicityos.com" \
|
|
340
397
|
--menu-path "目录管理/数据集目录" \
|
|
@@ -346,7 +403,7 @@ node {client_path}/local-browser-executor.js \
|
|
|
346
403
|
|
|
347
404
|
### 轻量 DOM 断言
|
|
348
405
|
```bash
|
|
349
|
-
node
|
|
406
|
+
node "$EXECUTOR" \
|
|
350
407
|
--action assert \
|
|
351
408
|
--url-contains "platform.aicityos.com" \
|
|
352
409
|
--selector ".ant-table" \
|
|
@@ -357,7 +414,7 @@ node {client_path}/local-browser-executor.js \
|
|
|
357
414
|
|
|
358
415
|
### API 请求/响应捕获(真实浏览器 + 微前端)
|
|
359
416
|
```bash
|
|
360
|
-
node
|
|
417
|
+
node "$EXECUTOR" \
|
|
361
418
|
--action api-capture \
|
|
362
419
|
--url-contains "platform.aicityos.com" \
|
|
363
420
|
--frame-content-contains "数据集目录" \
|
|
@@ -372,7 +429,7 @@ node {client_path}/local-browser-executor.js \
|
|
|
372
429
|
|
|
373
430
|
### API 捕获结果断言
|
|
374
431
|
```bash
|
|
375
|
-
node
|
|
432
|
+
node "$EXECUTOR" \
|
|
376
433
|
--action assert-api \
|
|
377
434
|
--capture-file /tmp/browser-api-capture.json \
|
|
378
435
|
--max-failed 0 \
|
|
@@ -385,7 +442,7 @@ node {client_path}/local-browser-executor.js \
|
|
|
385
442
|
|
|
386
443
|
### 快速诊断(过渡兼容)
|
|
387
444
|
```bash
|
|
388
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
445
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
389
446
|
--action diagnose \
|
|
390
447
|
--url-contains "platform.aicityos.com" \
|
|
391
448
|
--wait 15000 \
|
|
@@ -394,13 +451,13 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node {skill_path}/local-browser-executor
|
|
|
394
451
|
|
|
395
452
|
### 列出所有标签页
|
|
396
453
|
```bash
|
|
397
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
454
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
398
455
|
--action list-pages
|
|
399
456
|
```
|
|
400
457
|
|
|
401
458
|
### 检查 API 请求状态
|
|
402
459
|
```bash
|
|
403
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
460
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
404
461
|
--action api-check \
|
|
405
462
|
--url-contains "platform.aicityos.com" \
|
|
406
463
|
--wait 15000
|
|
@@ -408,7 +465,7 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node {skill_path}/local-browser-executor
|
|
|
408
465
|
|
|
409
466
|
### 截图
|
|
410
467
|
```bash
|
|
411
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
468
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
412
469
|
--action screenshot \
|
|
413
470
|
--url-contains "platform.aicityos.com" \
|
|
414
471
|
--filename current-page.png \
|
|
@@ -417,7 +474,7 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node {skill_path}/local-browser-executor
|
|
|
417
474
|
|
|
418
475
|
### 执行测试计划 JSON
|
|
419
476
|
```bash
|
|
420
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
477
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
421
478
|
--action plan \
|
|
422
479
|
--plan-file /tmp/test-plan.json \
|
|
423
480
|
--output /tmp/browser-test-result.json
|
|
@@ -425,7 +482,7 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node {skill_path}/local-browser-executor
|
|
|
425
482
|
|
|
426
483
|
### DOM 快照(结构化页面树,类似 browser-use take_snapshot)
|
|
427
484
|
```bash
|
|
428
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
485
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
429
486
|
--action snapshot \
|
|
430
487
|
--url-contains "platform.aicityos.com" \
|
|
431
488
|
--depth 4 \
|
|
@@ -446,25 +503,25 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node {skill_path}/local-browser-executor
|
|
|
446
503
|
### 智能交互(支持 text=、role=、~ 选择器,类似 Playwright)
|
|
447
504
|
```bash
|
|
448
505
|
# 点击包含“构建”文本的按钮
|
|
449
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
506
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
450
507
|
--action interact \
|
|
451
508
|
--url-contains "jenkins" \
|
|
452
509
|
--click "text=Build"
|
|
453
510
|
|
|
454
511
|
# 在输入框中输入文本
|
|
455
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
512
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
456
513
|
--action interact \
|
|
457
514
|
--click "#search-input" \
|
|
458
515
|
--type "hello world"
|
|
459
516
|
|
|
460
517
|
# 悬停 + 截图
|
|
461
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
518
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
462
519
|
--action interact \
|
|
463
520
|
--hover "~ant-menu-item" \
|
|
464
521
|
--screenshot-after
|
|
465
522
|
|
|
466
523
|
# 滚动页面
|
|
467
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
524
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
468
525
|
--action interact \
|
|
469
526
|
--scroll down --scroll-amount 500
|
|
470
527
|
```
|
|
@@ -476,14 +533,14 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node {skill_path}/local-browser-executor
|
|
|
476
533
|
|
|
477
534
|
### 性能指标采集(FCP/TTFB/资源统计)
|
|
478
535
|
```bash
|
|
479
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
536
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
480
537
|
--action perf \
|
|
481
538
|
--url-contains "platform.aicityos.com"
|
|
482
539
|
```
|
|
483
540
|
|
|
484
541
|
### 持续监控模式(定时截图 + 错误检测)
|
|
485
542
|
```bash
|
|
486
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node
|
|
543
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$SKILL_DIR/local-browser-executor.cjs" \
|
|
487
544
|
--action watch \
|
|
488
545
|
--url-contains "platform.aicityos.com" \
|
|
489
546
|
--interval 5000 \
|
|
@@ -659,11 +716,15 @@ cd ~/.szcd-mcp/deps && npm install --registry=https://registry.npmmirror.com --@
|
|
|
659
716
|
| `formFill` | 批量填充 + 可选 `validateAfter` | `fields[]` |
|
|
660
717
|
| `closeAllDropdowns` | 关闭所有下拉浮层 | - |
|
|
661
718
|
|
|
662
|
-
|
|
663
|
-
- Ant Select 必须 `mousedown`
|
|
664
|
-
- 选项异步渲染,内置
|
|
665
|
-
-
|
|
666
|
-
-
|
|
719
|
+
**关键约束(6/22 反馈后更新)**:
|
|
720
|
+
- Ant Select 必须 `mousedown` + `click` 触发(click 触发 React onChange)
|
|
721
|
+
- 选项异步渲染,内置 poll 轮询(timeout 默认 3s,不限制 1.5s)
|
|
722
|
+
- `closeAllDropdowns` 去掉 `body >` 限制(适配微前端浮层)+ 增加 300ms sleep
|
|
723
|
+
- 选项点击后 mousedown + mouseup + **click**(三步组合)
|
|
724
|
+
- VERIFY 全局兜底:marker 丢失时全局搜索 `.ant-select-selection-item`
|
|
725
|
+
- VERIFY 检查 `title` + `innerText` + `textContent`(三重兜底)
|
|
726
|
+
- `popupResolved` 变量提升到 for 循环外(避免作用域 bug)
|
|
727
|
+
- 浮层选择器去掉 `body >` 限制(`.ant-select-dropdown` 而非 `body > .ant-select-dropdown`)
|
|
667
728
|
|
|
668
729
|
```json
|
|
669
730
|
{ "type": "antSelect", "field": "数据集名称", "option": "测试集A" }
|
|
@@ -699,6 +760,75 @@ node local-browser-executor.js --action test-case --test-case-file /tmp/case.jso
|
|
|
699
760
|
}
|
|
700
761
|
```
|
|
701
762
|
|
|
763
|
+
## 脚本执行能力分层
|
|
764
|
+
|
|
765
|
+
| 层级 | 步骤类型 | 执行环境 | 能力 | 适用场景 |
|
|
766
|
+
|------|---------|---------|------|---------|
|
|
767
|
+
| 1 | evaluate | 浏览器端 | DOM 查询/操作 | 简单检查、点击元素 |
|
|
768
|
+
| 2 | rawScript | Node.js 端 | 完整 puppeteer API | 跨 frame、等待、多标签页 |
|
|
769
|
+
| 3 | cdpScript | Node.js 端 | 调用已注册脚本 | 复用通用交互模式 |
|
|
770
|
+
| 4 | 内置 cdpScript | Node.js 端 | 预定义通用 UI 交互 | Drawer/Modal/表格等 |
|
|
771
|
+
|
|
772
|
+
**evaluate**:浏览器端执行 JS,只能操作 DOM,不能等待/跨 frame/调用 puppeteer
|
|
773
|
+
**rawScript**:Node.js 端执行 async 函数,接收 `(page, frame, params, browser)`,可调用所有 puppeteer API
|
|
774
|
+
**registerCdpScript**:注册可复用脚本,后续通过 cdpScript 步骤按 name 调用
|
|
775
|
+
**cdpScript**:调用 registerCdpScript 注册的脚本或内置脚本
|
|
776
|
+
|
|
777
|
+
### 内置 cdpScript(通用 UI 交互模式)
|
|
778
|
+
|
|
779
|
+
| script | 功能 | params |
|
|
780
|
+
|--------|------|--------|
|
|
781
|
+
| `drawer-action` | Drawer 操作 | `action: 'search'\|'select'\|'confirm'\|'cancel'`, `radioIndex?`, `searchText?`, `searchPlaceholder?` |
|
|
782
|
+
| `modal-action` | Modal/Popconfirm 操作 | `action: 'confirm'\|'cancel'`, `buttonText?` |
|
|
783
|
+
| `switch-tab` | 多标签页切换 | `urlContains?`, `titleIncludes?`, `pageIndex?` |
|
|
784
|
+
| `wait-api` | 等待 API 响应 | `urlPattern`, `timeout?`, `method?` |
|
|
785
|
+
| `get-table-data` | 提取表格数据 | `selector?`, `maxRows?` |
|
|
786
|
+
|
|
787
|
+
**设计原则**:cdpScript 内置通用 UI 交互模式(跨项目复用),业务流程由 LLM 用 rawScript 动态生成
|
|
788
|
+
|
|
789
|
+
```json
|
|
790
|
+
// 内置 cdpScript 示例
|
|
791
|
+
{ "type": "cdpScript", "script": "get-table-data", "params": { "maxRows": 5 } }
|
|
792
|
+
{ "type": "cdpScript", "script": "drawer-action", "params": { "action": "select", "radioIndex": 0 } }
|
|
793
|
+
{ "type": "cdpScript", "script": "drawer-action", "params": { "action": "confirm" } }
|
|
794
|
+
{ "type": "cdpScript", "script": "modal-action", "params": { "action": "confirm" } }
|
|
795
|
+
{ "type": "cdpScript", "script": "switch-tab", "params": { "urlContains": "localhost:8088" } }
|
|
796
|
+
|
|
797
|
+
// rawScript 示例(LLM 动态生成业务逻辑)
|
|
798
|
+
{ "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' } }" }
|
|
799
|
+
|
|
800
|
+
// registerCdpScript + cdpScript(先注册再调用)
|
|
801
|
+
{ "type": "registerCdpScript", "name": "my-check", "code": "async (page, frame, params) => { ... }" }
|
|
802
|
+
{ "type": "cdpScript", "script": "my-check", "params": { "id": "123" } }
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### evaluate 增强
|
|
806
|
+
|
|
807
|
+
```json
|
|
808
|
+
// expectResult:精确匹配返回值
|
|
809
|
+
{ "type": "evaluate", "expression": "...", "expectResult": "found" }
|
|
810
|
+
|
|
811
|
+
// expectResultContains:包含匹配返回值
|
|
812
|
+
{ "type": "evaluate", "expression": "...", "expectResultContains": "数据资源" }
|
|
813
|
+
|
|
814
|
+
// failOnFalsy:返回 falsy 时标记 FAIL
|
|
815
|
+
{ "type": "evaluate", "expression": "...", "failOnFalsy": true }
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
### apiCall(API 直调)
|
|
819
|
+
|
|
820
|
+
```json
|
|
821
|
+
{ "type": "apiCall", "method": "POST", "url": "http://localhost:8088/api/submit", "body": { "id": "123" } }
|
|
822
|
+
// → { status: 200, data: { code: "200" }, passed: true }
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### switchPage(多标签页切换)
|
|
826
|
+
|
|
827
|
+
```json
|
|
828
|
+
{ "type": "switchPage", "urlContains": "localhost:8088" }
|
|
829
|
+
// → { passed: true, url: "http://localhost:8088/...", title: "..." }
|
|
830
|
+
```
|
|
831
|
+
|
|
702
832
|
## 语义断言(expect)
|
|
703
833
|
|
|
704
834
|
| assertion | 功能 |
|
|
@@ -729,20 +859,26 @@ node local-browser-executor.js --action test-case --test-case-file /tmp/case.jso
|
|
|
729
859
|
{
|
|
730
860
|
"title": "数据集创建 P0",
|
|
731
861
|
"setup": {
|
|
732
|
-
"findFrame": { "
|
|
862
|
+
"findFrame": { "frameUrlContains": "#/catalog/" }
|
|
733
863
|
},
|
|
734
864
|
"dataset": [{ "name": "A" }, { "name": "B" }],
|
|
735
865
|
"steps": [
|
|
736
|
-
{ "type": "
|
|
866
|
+
{ "type": "navigate", "url": "http://localhost:8088/#/szc-ai-data/catalog/catalog-manage", "waitUntil": "domcontentloaded" },
|
|
867
|
+
{ "type": "expect", "assertion": "locator.toBeVisible", "selector": ".ant-table", "timeout": 20000 },
|
|
737
868
|
{ "type": "antInput", "field": "名称", "value": "{{name}}" },
|
|
738
869
|
{ "type": "antSelect", "field": "类型", "option": "图像" },
|
|
739
|
-
{ "type": "act", "click": "text
|
|
870
|
+
{ "type": "act", "click": "text==提 交" },
|
|
740
871
|
{ "type": "expect", "assertion": "validation.toPass" },
|
|
741
|
-
{ "type": "expect", "assertion": "
|
|
872
|
+
{ "type": "expect", "assertion": "locator.toBeVisible", "selector": ".ant-table", "timeout": 15000 }
|
|
742
873
|
]
|
|
743
874
|
}
|
|
744
875
|
```
|
|
745
876
|
|
|
877
|
+
**findFrame 三种方案(按稳定性排序)**:
|
|
878
|
+
- `frameSelector: "iframe"` — 基于 DOM 选择器(最稳定,推荐)
|
|
879
|
+
- `frameUrlContains: "#/catalog/"` — 基于 URL 子串(navigate 后可能失效)
|
|
880
|
+
- `frameContentContains: "数据集目录"` — 基于内容文字(页面未加载时不可用)
|
|
881
|
+
|
|
746
882
|
**关键参数:**
|
|
747
883
|
|
|
748
884
|
- `setup.findFrame.contentContains` — 一次定位子应用 frame
|