@szc-ft/mcp-szcd-client 0.35.0 → 0.39.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/build.js +59 -1
- package/agents/platforms.json +7 -0
- package/agents/szcd-component-expert.qoder.md +2 -0
- package/opencode-extension/opencode.json +9 -0
- package/opencode-extension/skills/local-browser-test/SKILL.md +186 -18
- package/opencode-extension/skills/local-browser-test/config.example.yaml +51 -0
- package/opencode-extension/skills/local-browser-test/lib/api-call-executor.js +251 -0
- package/opencode-extension/skills/local-browser-test/lib/browser-engine.js +95 -21
- package/opencode-extension/skills/local-browser-test/lib/config-loader.js +104 -0
- package/opencode-extension/skills/local-browser-test/lib/console-archiver.js +100 -0
- package/opencode-extension/skills/local-browser-test/lib/global-setup.js +73 -0
- package/opencode-extension/skills/local-browser-test/lib/har-collector.js +124 -0
- package/opencode-extension/skills/local-browser-test/lib/html-reporter.js +425 -0
- package/opencode-extension/skills/local-browser-test/lib/projects.js +46 -0
- package/opencode-extension/skills/local-browser-test/lib/raw-script-helpers.js +93 -0
- package/opencode-extension/skills/local-browser-test/lib/redact.js +94 -0
- package/opencode-extension/skills/local-browser-test/lib/report-retain.js +84 -0
- package/opencode-extension/skills/local-browser-test/lib/retry-policy.js +160 -0
- package/opencode-extension/skills/local-browser-test/lib/storage-state.js +111 -0
- package/opencode-extension/skills/local-browser-test/lib/tag-runner.js +103 -0
- package/opencode-extension/skills/local-browser-test/lib/test-plan.js +2 -0
- package/opencode-extension/skills/local-browser-test/lib/trace-collector.js +94 -0
- package/opencode-extension/skills/local-browser-test/lib/trend-aggregator.js +97 -0
- package/opencode-extension/skills/local-browser-test/lib/workspace-init.js +178 -0
- package/opencode-extension/skills/local-browser-test/local-browser-executor.js +264 -4
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-01.test.js +254 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-02.test.js +263 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-03.test.js +308 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-04.test.js +168 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-05.test.js +183 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-06.test.js +252 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-07.test.js +168 -0
- package/opencode-extension/skills/local-browser-test/tests/integration/pr-08.test.js +293 -0
- package/opencode-extension/skills/shadcn-design-to-code/SKILL.md +352 -0
- package/opencode-extension/skills/shadcn-design-to-code/examples/1.1-overview-page.tsx +382 -0
- package/opencode-extension/skills/shadcn-design-to-code/references/install-pitfalls.md +526 -0
- package/opencode-extension/skills/shadcn-design-to-code/references/layouts.md +551 -0
- package/opencode-extension/skills/shadcn-design-to-code/references/sketch-mapping.md +247 -0
- package/opencode-extension/skills/shadcn-design-to-code/references/tokens.md +534 -0
- package/opencode-extension/skills/szcd-upload-zip/SKILL.md +45 -0
- package/package.json +70 -70
- package/qwen-extension/qwen-extension.json +10 -1
- package/qwen-extension/skills/local-browser-test/SKILL.md +186 -18
- package/qwen-extension/skills/local-browser-test/config.example.yaml +51 -0
- package/qwen-extension/skills/local-browser-test/lib/api-call-executor.js +251 -0
- package/qwen-extension/skills/local-browser-test/lib/browser-engine.js +95 -21
- package/qwen-extension/skills/local-browser-test/lib/config-loader.js +104 -0
- package/qwen-extension/skills/local-browser-test/lib/console-archiver.js +100 -0
- package/qwen-extension/skills/local-browser-test/lib/global-setup.js +73 -0
- package/qwen-extension/skills/local-browser-test/lib/har-collector.js +124 -0
- package/qwen-extension/skills/local-browser-test/lib/html-reporter.js +425 -0
- package/qwen-extension/skills/local-browser-test/lib/projects.js +46 -0
- package/qwen-extension/skills/local-browser-test/lib/raw-script-helpers.js +93 -0
- package/qwen-extension/skills/local-browser-test/lib/redact.js +94 -0
- package/qwen-extension/skills/local-browser-test/lib/report-retain.js +84 -0
- package/qwen-extension/skills/local-browser-test/lib/retry-policy.js +160 -0
- package/qwen-extension/skills/local-browser-test/lib/storage-state.js +111 -0
- package/qwen-extension/skills/local-browser-test/lib/tag-runner.js +103 -0
- package/qwen-extension/skills/local-browser-test/lib/test-plan.js +2 -0
- package/qwen-extension/skills/local-browser-test/lib/trace-collector.js +94 -0
- package/qwen-extension/skills/local-browser-test/lib/trend-aggregator.js +97 -0
- package/qwen-extension/skills/local-browser-test/lib/workspace-init.js +178 -0
- package/qwen-extension/skills/local-browser-test/local-browser-executor.js +264 -4
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-01.test.js +254 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-02.test.js +263 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-03.test.js +308 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-04.test.js +168 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-05.test.js +183 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-06.test.js +252 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-07.test.js +168 -0
- package/qwen-extension/skills/local-browser-test/tests/integration/pr-08.test.js +293 -0
- package/qwen-extension/skills/shadcn-design-to-code/SKILL.md +352 -0
- package/qwen-extension/skills/shadcn-design-to-code/examples/1.1-overview-page.tsx +382 -0
- package/qwen-extension/skills/shadcn-design-to-code/references/install-pitfalls.md +526 -0
- package/qwen-extension/skills/shadcn-design-to-code/references/layouts.md +551 -0
- package/qwen-extension/skills/shadcn-design-to-code/references/sketch-mapping.md +247 -0
- package/qwen-extension/skills/shadcn-design-to-code/references/tokens.md +534 -0
- package/qwen-extension/skills/szcd-upload-zip/SKILL.md +45 -0
- package/scripts/lib/common.js +7 -0
- package/standard-skill/local-browser-test/SKILL.md +186 -18
- package/standard-skill/local-browser-test/config.example.yaml +51 -0
- package/standard-skill/local-browser-test/lib/api-call-executor.js +251 -0
- package/standard-skill/local-browser-test/lib/browser-engine.js +95 -21
- package/standard-skill/local-browser-test/lib/config-loader.js +104 -0
- package/standard-skill/local-browser-test/lib/console-archiver.js +100 -0
- package/standard-skill/local-browser-test/lib/global-setup.js +73 -0
- package/standard-skill/local-browser-test/lib/har-collector.js +124 -0
- package/standard-skill/local-browser-test/lib/html-reporter.js +425 -0
- package/standard-skill/local-browser-test/lib/projects.js +46 -0
- package/standard-skill/local-browser-test/lib/raw-script-helpers.js +93 -0
- package/standard-skill/local-browser-test/lib/redact.js +94 -0
- package/standard-skill/local-browser-test/lib/report-retain.js +84 -0
- package/standard-skill/local-browser-test/lib/retry-policy.js +160 -0
- package/standard-skill/local-browser-test/lib/storage-state.js +111 -0
- package/standard-skill/local-browser-test/lib/tag-runner.js +103 -0
- package/standard-skill/local-browser-test/lib/test-plan.js +2 -0
- package/standard-skill/local-browser-test/lib/trace-collector.js +94 -0
- package/standard-skill/local-browser-test/lib/trend-aggregator.js +97 -0
- package/standard-skill/local-browser-test/lib/workspace-init.js +178 -0
- package/standard-skill/local-browser-test/local-browser-executor.js +264 -4
- package/standard-skill/local-browser-test/tests/integration/pr-01.test.js +254 -0
- package/standard-skill/local-browser-test/tests/integration/pr-02.test.js +263 -0
- package/standard-skill/local-browser-test/tests/integration/pr-03.test.js +308 -0
- package/standard-skill/local-browser-test/tests/integration/pr-04.test.js +168 -0
- package/standard-skill/local-browser-test/tests/integration/pr-05.test.js +183 -0
- package/standard-skill/local-browser-test/tests/integration/pr-06.test.js +252 -0
- package/standard-skill/local-browser-test/tests/integration/pr-07.test.js +168 -0
- package/standard-skill/local-browser-test/tests/integration/pr-08.test.js +293 -0
- package/standard-skill/shadcn-design-to-code/SKILL.md +352 -0
- package/standard-skill/shadcn-design-to-code/examples/1.1-overview-page.tsx +382 -0
- package/standard-skill/shadcn-design-to-code/references/install-pitfalls.md +526 -0
- package/standard-skill/shadcn-design-to-code/references/layouts.md +551 -0
- package/standard-skill/shadcn-design-to-code/references/sketch-mapping.md +247 -0
- package/standard-skill/shadcn-design-to-code/references/tokens.md +534 -0
- package/standard-skill/szcd-upload-zip/SKILL.md +45 -0
- package/opencode-extension/skills/local-browser-test/local-browser-executor.cjs +0 -742
- package/qwen-extension/skills/local-browser-test/local-browser-executor.cjs +0 -742
package/agents/build.js
CHANGED
|
@@ -346,6 +346,11 @@ function build() {
|
|
|
346
346
|
const srcSkillPath = path.join(srcSkillDir, "SKILL.md");
|
|
347
347
|
const destSkillDir = path.join(qwenExtDir, "skills", skillDir);
|
|
348
348
|
if (fs.existsSync(srcSkillPath)) {
|
|
349
|
+
// PR #0 v3: 先 rm 旧副本,避免脏数据残留(cjs 等已废弃文件)
|
|
350
|
+
if (fs.existsSync(destSkillDir)) {
|
|
351
|
+
fs.rmSync(destSkillDir, { recursive: true, force: true });
|
|
352
|
+
console.log(` ✓ Cleaned stale ${skillDir}/`);
|
|
353
|
+
}
|
|
349
354
|
copyDirRecursive(srcSkillDir, destSkillDir);
|
|
350
355
|
console.log(` ✓ Synced skill to qwen-extension/skills/${skillDir}/`);
|
|
351
356
|
}
|
|
@@ -376,6 +381,10 @@ function build() {
|
|
|
376
381
|
const srcSkillPath = path.join(srcSkillDir, "SKILL.md");
|
|
377
382
|
const destSkillDir = path.join(opencodeExtDir, "skills", skillDir);
|
|
378
383
|
if (fs.existsSync(srcSkillPath)) {
|
|
384
|
+
// PR #0 v3: 先 rm 旧副本(与 5a 同步逻辑保持一致)
|
|
385
|
+
if (fs.existsSync(destSkillDir)) {
|
|
386
|
+
fs.rmSync(destSkillDir, { recursive: true, force: true });
|
|
387
|
+
}
|
|
379
388
|
copyDirRecursive(srcSkillDir, destSkillDir);
|
|
380
389
|
}
|
|
381
390
|
}
|
|
@@ -496,8 +505,57 @@ function build() {
|
|
|
496
505
|
console.log("\n✅ Agent build complete!");
|
|
497
506
|
}
|
|
498
507
|
|
|
508
|
+
// ★ PR #0 v3 修复:build 锁(防止并发 build 时 ENOTEMPTY)
|
|
509
|
+
// 跨进程互斥:第一个 build 写 .build.lock,后续 build 等到锁释放
|
|
510
|
+
const lockFile = path.join(process.cwd(), ".build.lock");
|
|
511
|
+
const lockMaxWait = 30000; // 30s 超时
|
|
512
|
+
const lockPollInterval = 100; // 100ms 检查一次
|
|
513
|
+
const lockStaleAge = 60000; // 60s 视为 stale 锁
|
|
514
|
+
async function acquireBuildLock() {
|
|
515
|
+
const start = Date.now();
|
|
516
|
+
while (Date.now() - start < lockMaxWait) {
|
|
517
|
+
try {
|
|
518
|
+
const fd = fs.openSync(lockFile, "wx"); // 独占创建(原子操作)
|
|
519
|
+
fs.writeSync(fd, String(process.pid));
|
|
520
|
+
return fd;
|
|
521
|
+
} catch (e) {
|
|
522
|
+
if (e.code === "EEXIST") {
|
|
523
|
+
// 锁存在 → 检查是否 stale
|
|
524
|
+
try {
|
|
525
|
+
const stat = fs.statSync(lockFile);
|
|
526
|
+
if (Date.now() - stat.mtimeMs > lockStaleAge) {
|
|
527
|
+
console.log(` ⚠ 锁文件陈旧 (>${lockStaleAge/1000}s),强制删除`);
|
|
528
|
+
try { fs.unlinkSync(lockFile); } catch {}
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
} catch {}
|
|
532
|
+
// 等待
|
|
533
|
+
await new Promise((r) => setTimeout(r, lockPollInterval));
|
|
534
|
+
} else {
|
|
535
|
+
throw e;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return null; // 超时
|
|
540
|
+
}
|
|
541
|
+
function releaseBuildLock(fd) {
|
|
542
|
+
if (fd) {
|
|
543
|
+
try { fs.closeSync(fd); } catch {}
|
|
544
|
+
try { fs.unlinkSync(lockFile); } catch {}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
499
548
|
try {
|
|
500
|
-
|
|
549
|
+
const lockFd = await acquireBuildLock();
|
|
550
|
+
if (!lockFd) {
|
|
551
|
+
process.stderr.write(`❌ build 锁获取超时 (${lockMaxWait}ms)\n`);
|
|
552
|
+
process.exit(2);
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
build();
|
|
556
|
+
} finally {
|
|
557
|
+
releaseBuildLock(lockFd);
|
|
558
|
+
}
|
|
501
559
|
} catch (error) {
|
|
502
560
|
console.error("❌ Build failed:", error.message);
|
|
503
561
|
console.error(error.stack);
|
package/agents/platforms.json
CHANGED
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
"type": "stdio",
|
|
7
7
|
"command": "sketch-mcp-server",
|
|
8
8
|
"args": []
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "shadcn",
|
|
12
|
+
"type": "stdio",
|
|
13
|
+
"command": "npx",
|
|
14
|
+
"args": ["-y", "shadcn@latest", "mcp"],
|
|
15
|
+
"description": "shadcn/ui 官方 stdio MCP(registry 搜索/查看/add 命令)。需项目根目录有 components.json,否则调 list_items_in_registries 报 'No registries are configured'。"
|
|
9
16
|
}
|
|
10
17
|
],
|
|
11
18
|
"platforms": {
|
|
@@ -15,12 +15,14 @@ tools:
|
|
|
15
15
|
skills:
|
|
16
16
|
- local-api-tool
|
|
17
17
|
- local-browser-test
|
|
18
|
+
- shadcn-design-to-code
|
|
18
19
|
- szcd-component-helper
|
|
19
20
|
- szcd-design-to-code
|
|
20
21
|
- szcd-upload-zip
|
|
21
22
|
mcpServers:
|
|
22
23
|
- szcd-component-helper
|
|
23
24
|
- sketch-mcp-server
|
|
25
|
+
- shadcn
|
|
24
26
|
---
|
|
25
27
|
|
|
26
28
|
# szcd-component-expert: szcd 组件库专家
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: local-browser-test
|
|
3
|
-
description: 在用户真实 Chrome
|
|
3
|
+
description: 在用户真实 Chrome 中执行自动化测试,覆盖测试用例批量执行、深度表单填写、微前端测试、长任务生命周期、设计稿对比、JS 错误诊断。通过 puppeteer-core 连接 CDP,天然继承登录态/微前端环境。
|
|
4
4
|
compatibility:
|
|
5
5
|
tools:
|
|
6
6
|
- run_command
|
|
@@ -72,7 +72,7 @@ export EXECUTOR="$SKILL_DIR/local-browser-executor.js"
|
|
|
72
72
|
|
|
73
73
|
## 🎯 三种典型任务(场景驱动)
|
|
74
74
|
|
|
75
|
-
### 任务 A
|
|
75
|
+
### 任务 A:测试用例批量执行(首选)
|
|
76
76
|
|
|
77
77
|
用户说"执行 P0 用例 / 批量测试 / 生命周期测试"时,**不要写 ad-hoc puppeteer 脚本**,构造 testCase JSON:
|
|
78
78
|
|
|
@@ -144,7 +144,7 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action api-capture --
|
|
|
144
144
|
|
|
145
145
|
```
|
|
146
146
|
Agent / SKILL.md
|
|
147
|
-
→ testCase JSON ←
|
|
147
|
+
→ testCase JSON ← 测试用例首选入口
|
|
148
148
|
→ $SKILL_DIR/local-browser-executor.js ($EXECUTOR)
|
|
149
149
|
→ $SKILL_DIR/lib/browser-engine.js (executeTestCase + Ant 适配 + expect)
|
|
150
150
|
→ $SKILL_DIR/lib/ant-adapter.js (Select/TreeSelect/Input/Radio/...)
|
|
@@ -672,14 +672,32 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
|
672
672
|
- `--expect-result-contains <text>`:结果 JSON 包含指定文本
|
|
673
673
|
|
|
674
674
|
### 执行测试计划 JSON(ES Module 版)
|
|
675
|
+
|
|
676
|
+
**⚠️ 禁用 `--action plan`(该分支提前 `process.exit`,不会生成 HTML 报告)**
|
|
677
|
+
直接用 `--plan-file`:
|
|
678
|
+
|
|
675
679
|
```bash
|
|
676
680
|
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
677
|
-
--action plan \
|
|
678
681
|
--plan-file /tmp/test-plan.json \
|
|
679
|
-
--
|
|
682
|
+
--config /path/to/config.yaml
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
工作区默认在 CWD 的 `.lbt/`,无需手动传 `--workspace`:
|
|
686
|
+
|
|
687
|
+
```
|
|
688
|
+
.lbt/
|
|
689
|
+
├── config.yaml ← 项目配置
|
|
690
|
+
├── reports/ ← HTML 报告(趋势图 + 过滤 + 详情页 + 截图)
|
|
691
|
+
│ ├── index.html
|
|
692
|
+
│ └── runs/<id>/
|
|
693
|
+
├── tmp/ ← 执行过程临时文件(截图等)
|
|
694
|
+
├── auth/ ← 保存的登录态
|
|
695
|
+
└── plans/ ← 回归脚本
|
|
680
696
|
```
|
|
681
697
|
|
|
682
|
-
`--
|
|
698
|
+
不传 `--workspace` 时默认 `cwd/.lbt`,也可用 `--workspace <dir>` 指定其他目录。
|
|
699
|
+
|
|
700
|
+
与 `--action test-case` 的区别:plan 不走 dataset 循环,适合单次执行。
|
|
683
701
|
|
|
684
702
|
### 快速诊断(JS错误 + 网络 + 资源 + iframe)
|
|
685
703
|
```bash
|
|
@@ -726,12 +744,15 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
|
726
744
|
### 可选参数
|
|
727
745
|
|
|
728
746
|
- `--cdp-url http://localhost:9222`:CDP 地址(默认 9222)
|
|
729
|
-
- `--url <url>`:目标页面 URL(不存在则导航)
|
|
747
|
+
- `--page-url <url>`:目标页面 URL(不存在则导航)
|
|
730
748
|
- `--url-contains <keyword>`:按关键词查找已打开的页面
|
|
731
749
|
- `--frame-url-contains <keyword>`:observe 时按 iframe URL 片段匹配目标 frame
|
|
732
750
|
- `--frame-content-contains <keyword>`:observe 时按 iframe 文本内容匹配目标 frame
|
|
733
751
|
- `--frame-index <n>`:observe 时按 frame 下标选择目标 frame
|
|
734
|
-
- `--
|
|
752
|
+
- `--plan-file <path>`:测试计划 JSON 文件路径
|
|
753
|
+
- `--config <path>`:配置文件路径(如 config.yaml)
|
|
754
|
+
- `--workspace <dir>`:工作区目录,默认 `cwd/.lbt`。存放 `reports/`(HTML 报告)、`tmp/`(临时文件)、`auth/`(登录态)、`plans/`(回归脚本)
|
|
755
|
+
- `--wait <ms>`:额外等待时间(默认因 action 而异:api-capture 15000,其余 5000)
|
|
735
756
|
- `--output <path>`:JSON 结果输出路径
|
|
736
757
|
- `--output-dir <dir>`:截图/产物目录
|
|
737
758
|
- `--filename <name>`:截图文件名
|
|
@@ -741,12 +762,41 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" \
|
|
|
741
762
|
- `--click <selector>`:点击元素(支持 text=/role=/~)
|
|
742
763
|
- `--type <text>`:输入文本
|
|
743
764
|
- `--hover <selector>`:悬停元素
|
|
744
|
-
- `--
|
|
745
|
-
- `--
|
|
746
|
-
- `--
|
|
765
|
+
- `--expression <js>`:evaluate 执行的 JS 表达式(需 IIFE 包裹)
|
|
766
|
+
- `--fail-on-falsy`:evaluate 结果为 falsy 时标记 FAIL
|
|
767
|
+
- `--expect-result-contains <text>`:evaluate 结果包含指定文本
|
|
768
|
+
- `--include-response-body`:api-capture 包含响应体
|
|
769
|
+
- `--include-all-targets`:api-capture 监听全部 target(含非 frame 的 worker/extension)
|
|
770
|
+
- `--reload`:api-capture 前重载页面
|
|
771
|
+
- `--url-pattern <pattern>`:api-capture 按 URL 过滤
|
|
772
|
+
- `--method <method>`:api-capture 按 HTTP 方法过滤
|
|
773
|
+
- `--capture-file <path>`:assert-api 读取的捕获结果文件
|
|
774
|
+
- `--max-failed <n>`:assert-api 允许的最大失败请求数
|
|
775
|
+
- `--forbid-status-gte <code>`:assert-api 禁止的状态码阈值
|
|
776
|
+
- `--required-url <pattern>`:assert-api 必需出现的 URL 模式
|
|
777
|
+
- `--include-request-body`:api-capture 包含请求体
|
|
778
|
+
- `--no-request-body`:api-capture 排除请求体
|
|
779
|
+
- `--target-url-contains <text>`:api-capture 目标 frame URL 包含
|
|
747
780
|
- `--interval <ms>`:watch 模式间隔(默认 5000)
|
|
748
781
|
- `--max-rounds <n>`:watch 最大轮数(默认 60)
|
|
749
782
|
- `--allow-launch`:根执行器 action 模式允许无 CDP 时启动 headless Chrome;默认不启用,优先保障真实用户浏览器链路
|
|
783
|
+
- `--ci`:CI 模式(自动设置 screenshot=always, video=on, trace=on, retries=2)
|
|
784
|
+
- `--retries <n>`:用例级重试次数
|
|
785
|
+
- `--flaky-threshold <n>`:Flaky 测试阈值
|
|
786
|
+
- `--screenshot <always|only-on-failure>`:截图策略
|
|
787
|
+
- `--video <on|off|only-on-failure>`:录像策略
|
|
788
|
+
- `--trace <on|off>`:trace 录制
|
|
789
|
+
- `--record-har`:录制 HAR
|
|
790
|
+
- `--storage-state <path>`:认证存储状态文件路径(用于 global-setup)
|
|
791
|
+
- `--global-setup`:全局设置模式(保存登录态)
|
|
792
|
+
- `--init-force`:强制初始化工作区
|
|
793
|
+
- `--project <name>`:多环境项目选择
|
|
794
|
+
- `--env <name>`:环境选择
|
|
795
|
+
- `--tags <tag>`:按 tag 筛选用例
|
|
796
|
+
- `--exclude-tags <tag>`:排除 tag
|
|
797
|
+
- `--tag-mode <any|all>`:tag 匹配模式
|
|
798
|
+
- `--no-retry-actions`:禁用写操作重试
|
|
799
|
+
- `--no-reload`:diagnose 跳过页面重载
|
|
750
800
|
|
|
751
801
|
## AI 语义断言(aiAssert)
|
|
752
802
|
|
|
@@ -822,24 +872,47 @@ cd ~/.szcd-mcp/deps && npm install --registry=https://registry.npmmirror.com --@
|
|
|
822
872
|
- passed = total:全部通过
|
|
823
873
|
- 有失败步骤:根据 `step` + `error` 字段定位代码问题
|
|
824
874
|
|
|
825
|
-
###
|
|
875
|
+
### HTML 报告(推荐)
|
|
876
|
+
|
|
877
|
+
传 `--workspace <dir>` 时自动生成 Playwright 风格 HTML 报告:
|
|
878
|
+
|
|
879
|
+
```
|
|
880
|
+
<workspace>/reports/
|
|
881
|
+
├── index.html ← 入口:趋势图 + 过滤 + 运行列表
|
|
882
|
+
├── trend.json ← 按日聚合数据
|
|
883
|
+
└── runs/
|
|
884
|
+
└── <timestamp>-pid<N>/
|
|
885
|
+
├── index.html ← 详情页:步骤表格、截图、错误展开
|
|
886
|
+
├── result.json ← 原始结果(脱敏)
|
|
887
|
+
└── screenshots/ ← 截图副本
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
入口支持交互过滤(状态 + 关键词搜索)、echarts 趋势图、详情页"返回列表"导航、截图展示。
|
|
891
|
+
|
|
892
|
+
### 结果 JSON 结构(stdout)
|
|
826
893
|
```json
|
|
827
894
|
{
|
|
828
895
|
"planId": "xxx",
|
|
896
|
+
"description": "...",
|
|
829
897
|
"mode": "connect",
|
|
898
|
+
"startTime": "2026-07-01T10:00:00.000Z",
|
|
899
|
+
"duration": "1583",
|
|
830
900
|
"steps": [
|
|
831
|
-
{ "index": 1, "step": "navigate", "status": "PASS", "duration": 1200, ... },
|
|
832
|
-
{ "index": 2, "step": "screenshot", "status": "PASS", "duration": 500,
|
|
833
|
-
|
|
901
|
+
{ "index": 1, "step": "navigate", "status": "PASS", "duration": "1200", ... },
|
|
902
|
+
{ "index": 2, "step": "screenshot", "status": "PASS", "duration": "500",
|
|
903
|
+
"filepath": "/tmp/.../screenshot.png", ... }
|
|
834
904
|
],
|
|
835
905
|
"summary": {
|
|
836
|
-
"total":
|
|
837
|
-
"fidelity":
|
|
906
|
+
"total": "9", "passed": "9", "failed": "0",
|
|
907
|
+
"fidelity": null
|
|
838
908
|
},
|
|
909
|
+
"browserInfo": { "mode": "connect", "cdpUrl": "...", "currentUrl": "...", "totalPages": 6 },
|
|
839
910
|
"outputDir": "/tmp/browser-test-xxx"
|
|
840
911
|
}
|
|
841
912
|
```
|
|
842
913
|
|
|
914
|
+
注意:`startTime`(非 `startedAt`)、`duration`(非 `durationMs`)。HTML 报告器内部会把 `startTime` → `startedAt`、`duration` → `durationMs`、`summary.*` → `stats.*` 做字段归一化。
|
|
915
|
+
|
|
843
916
|
## 批量动态菜单扫描(runTask)
|
|
844
917
|
|
|
845
918
|
适用场景:测试系统有 N 条动态路由(来自后端菜单接口),要逐条点击触发并验证接口是否返回 200。6/18 反馈中,AI 最终用 130 余条 `act --menu-path` + `api-capture` 手工组合走完 129 条菜单——`runTask` 把这套流水固化为单步骤。
|
|
@@ -966,7 +1039,7 @@ cat > /tmp/login.json <<EOF
|
|
|
966
1039
|
EOF
|
|
967
1040
|
|
|
968
1041
|
# 3. 执行登录
|
|
969
|
-
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --
|
|
1042
|
+
NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --plan-file /tmp/login.json --config /path/to/config.yaml
|
|
970
1043
|
```
|
|
971
1044
|
|
|
972
1045
|
**LLM 处理流程:**
|
|
@@ -1014,6 +1087,10 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action plan --plan-fi
|
|
|
1014
1087
|
- ❌ **v20 格式**(生产环境,App-Bound Encryption):需 Python `pywin32` + `cryptography` 方案
|
|
1015
1088
|
- ⚠️ Windows 专属(DPAPI),macOS/Linux 需 Keychain/KWallet 方案
|
|
1016
1089
|
|
|
1090
|
+
## 已知技术债
|
|
1091
|
+
|
|
1092
|
+
- **代码重复**:`normalizeForReport`(executor.js)与 `normalizeRunResult`(html-reporter.js)是相同字段归一化逻辑的两份拷贝。修改任何字段映射时必须同步两处。
|
|
1093
|
+
|
|
1017
1094
|
## 反模式备忘(来自 6/17 + 6/18 实战反馈)
|
|
1018
1095
|
|
|
1019
1096
|
| 错误做法 | 正确做法 | 原因 |
|
|
@@ -1307,3 +1384,94 @@ NODE_PATH=~/.szcd-mcp/deps/node_modules node "$EXECUTOR" --action test-case --te
|
|
|
1307
1384
|
- ❌ 单元测试 / 组件级测试 → Vitest / Jest / RTL
|
|
1308
1385
|
- ❌ 视觉回归全量对比(无 baseline 设计稿)→ Percy / Chromatic
|
|
1309
1386
|
- ✅ 真实浏览器内的功能验证、API 时序捕获、设计稿单图对比、动态菜单批量扫描
|
|
1387
|
+
|
|
1388
|
+
## rawScript 可用 API(PR #4 v3)
|
|
1389
|
+
|
|
1390
|
+
`rawScript` 步骤支持两种模式:
|
|
1391
|
+
|
|
1392
|
+
### 模式 A:inline code
|
|
1393
|
+
```json
|
|
1394
|
+
{ "type": "rawScript", "code": "return { passed: document.title.includes('登录') }" }
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
### 模式 B:codeFile(PR #4 新增)
|
|
1398
|
+
```json
|
|
1399
|
+
{ "type": "rawScript", "codeFile": ".lbt/scripts/check-title.js" }
|
|
1400
|
+
```
|
|
1401
|
+
- 相对路径相对 `process.cwd()`
|
|
1402
|
+
- 文件不存在 → 明确报错(指出绝对路径)
|
|
1403
|
+
- 文件读失败 → 明确报错
|
|
1404
|
+
|
|
1405
|
+
### rawScript 函数签名
|
|
1406
|
+
|
|
1407
|
+
```js
|
|
1408
|
+
async (page, frame, params, browser) => {
|
|
1409
|
+
// page: puppeteer Page 对象
|
|
1410
|
+
// frame: PuppeteerFrame(iframe 上下文)
|
|
1411
|
+
// params: step.params 对象
|
|
1412
|
+
// browser: puppeteer Browser 对象
|
|
1413
|
+
return { passed: true, ... }; // 或 { passed: false, reason: '...' }
|
|
1414
|
+
}
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
### ✅ 浏览器侧可用 API
|
|
1418
|
+
|
|
1419
|
+
| 类别 | API |
|
|
1420
|
+
|------|-----|
|
|
1421
|
+
| DOM | `document.*` / `window.*` / `frame.document` |
|
|
1422
|
+
| 存储 | `localStorage.*` / `sessionStorage.*` / `cookieStore` |
|
|
1423
|
+
| 网络 | `fetch()` / `XMLHttpRequest` / `WebSocket` |
|
|
1424
|
+
| JS 内置 | `JSON` / `Date` / `Math` / `Array` / `Object` / `Promise` / `async/await` |
|
|
1425
|
+
| 浏览器事件 | `MouseEvent` / `KeyboardEvent` / `CustomEvent` |
|
|
1426
|
+
| puppeteer | `page.*`(evaluate / click / type / waitForSelector 等)|
|
|
1427
|
+
|
|
1428
|
+
### ❌ Node 侧 API 不可用(浏览器无)
|
|
1429
|
+
|
|
1430
|
+
| 错误信息 | 原因 | 浏览器侧替代 |
|
|
1431
|
+
|---------|------|------------|
|
|
1432
|
+
| `TypeError: require is not a function` | 浏览器无 `require` | `await fetch()` / `page.evaluate(() => ...)` |
|
|
1433
|
+
| `ReferenceError: __dirname is not defined` | 浏览器无 Node 全局 | `window.location.origin` / `frame.url()` |
|
|
1434
|
+
| `ReferenceError: process is not defined` | 浏览器无 `process` | `localStorage` / `URL` 替代 |
|
|
1435
|
+
| `ReferenceError: Buffer is not defined` | 浏览器无 Node `Buffer` | `Uint8Array` / `TextEncoder` |
|
|
1436
|
+
| `TypeError: require.main is not a function` | 浏览器无 `require.main` | 不需要 |
|
|
1437
|
+
|
|
1438
|
+
**★ PR #4 修复**:rawScript 执行器会在跑前**静态扫描**代码,发现 Node-only API 时**立即 fail**,错误信息包含"详见 SKILL.md > ## rawScript 可用 API",避免用户踩坑。
|
|
1439
|
+
|
|
1440
|
+
### 常见错误速查
|
|
1441
|
+
|
|
1442
|
+
| 错误 | 修复 |
|
|
1443
|
+
|------|------|
|
|
1444
|
+
| `codeFile 不存在: /path/to/file` | 检查路径或用 inline `code` 替代 |
|
|
1445
|
+
| `rawScript 检测到 Node-only API: require('fs')` | 改用 `page.evaluate(() => ...)` 走浏览器 |
|
|
1446
|
+
| `script compile error: Unexpected token` | 检查 `code` 字段 JSON 转义(`"` → `\"`) |
|
|
1447
|
+
| `script execution error: ...` | 看具体堆栈;可能是 page 已 close 或网络超时 |
|
|
1448
|
+
|
|
1449
|
+
### 示例:检查页面 title
|
|
1450
|
+
|
|
1451
|
+
```js
|
|
1452
|
+
// 模式 A:inline
|
|
1453
|
+
{ "type": "rawScript", "code": "return { passed: document.title.includes('数据集目录'), title: document.title }" }
|
|
1454
|
+
|
|
1455
|
+
// 模式 B:codeFile
|
|
1456
|
+
{ "type": "rawScript", "codeFile": ".lbt/scripts/check-dataset-title.js" }
|
|
1457
|
+
// .lbt/scripts/check-dataset-title.js 内容:
|
|
1458
|
+
// return { passed: document.title.includes('数据集目录'), title: document.title };
|
|
1459
|
+
```
|
|
1460
|
+
|
|
1461
|
+
### 示例:拿所有按钮文本
|
|
1462
|
+
|
|
1463
|
+
```js
|
|
1464
|
+
{
|
|
1465
|
+
"type": "rawScript",
|
|
1466
|
+
"code": "return { passed: true, buttons: [...document.querySelectorAll('button')].map(b => b.textContent.trim()) }"
|
|
1467
|
+
}
|
|
1468
|
+
```
|
|
1469
|
+
|
|
1470
|
+
### 示例:调浏览器 fetch 验证 API
|
|
1471
|
+
|
|
1472
|
+
```js
|
|
1473
|
+
{
|
|
1474
|
+
"type": "rawScript",
|
|
1475
|
+
"code": "const r = await fetch('/api/health'); const j = await r.json(); return { passed: j.status === 'ok', data: j }"
|
|
1476
|
+
}
|
|
1477
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# local-browser-test 配置示例
|
|
2
|
+
# 复制为 .lbt/config.yaml 后填值(config.yaml 已在 .gitignore 内)
|
|
3
|
+
#
|
|
4
|
+
# 占位符 ${ENV_VAR} 走 process.env,CI 推荐用:
|
|
5
|
+
# export LBT_TOKEN=xxx
|
|
6
|
+
# export LBT_PASSWORD=xxx
|
|
7
|
+
|
|
8
|
+
# 应用基础配置
|
|
9
|
+
app:
|
|
10
|
+
baseURL: http://10.2.7.60:30020
|
|
11
|
+
timeout: 30000
|
|
12
|
+
|
|
13
|
+
# 鉴权(强烈建议用环境变量,避免明文)
|
|
14
|
+
auth:
|
|
15
|
+
type: bearer # bearer | cookie | oauth2
|
|
16
|
+
token: ${LBT_TOKEN} # 走环境变量
|
|
17
|
+
# password: ${LBT_PASSWORD}
|
|
18
|
+
# username: ${LBT_USERNAME}
|
|
19
|
+
# verifyCode: ${LBT_VERIFY_CODE}
|
|
20
|
+
storageState: null # 可选:复用 .lbt/auth/<project>.json
|
|
21
|
+
|
|
22
|
+
# 多环境配置(配合 --project <name> 切换)
|
|
23
|
+
projects:
|
|
24
|
+
- name: corpus-test
|
|
25
|
+
baseURL: http://119.0.87.158
|
|
26
|
+
auth:
|
|
27
|
+
type: bearer
|
|
28
|
+
token: ${LBT_TOKEN}
|
|
29
|
+
- name: corpus-stg
|
|
30
|
+
baseURL: http://10.2.7.60:30020
|
|
31
|
+
auth:
|
|
32
|
+
type: bearer
|
|
33
|
+
token: ${LBT_TOKEN}
|
|
34
|
+
- name: corpus-prod
|
|
35
|
+
baseURL: http://172.16.0.172:30010
|
|
36
|
+
auth:
|
|
37
|
+
type: bearer
|
|
38
|
+
token: ${LBT_TOKEN}
|
|
39
|
+
|
|
40
|
+
# 重试策略
|
|
41
|
+
retry:
|
|
42
|
+
maxRetries: 0 # 用例级(CI 跑 2 次)
|
|
43
|
+
maxRetriesPerStep: 2 # 步骤级(验证码过期等)
|
|
44
|
+
flakyThreshold: 5 # 5 次中 1 挂 4 过 → flaky
|
|
45
|
+
noRetryActions: true # 写操作(删除/提交/上线)永不重试
|
|
46
|
+
|
|
47
|
+
# 报告(v4 Playwright 风格)
|
|
48
|
+
report:
|
|
49
|
+
retainRuns: 30 # 保留最近 30 次 run
|
|
50
|
+
retainDays: 7 # 保留最近 7 天
|
|
51
|
+
retainFailedForever: true # 失败 run 永久保留
|