openyida 0.1.2

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.
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * 安装脚本测试
5
+ *
6
+ * 验证 install-skills.sh 和 install-skills.ps1 的关键行为:
7
+ * - 脚本文件存在且语法正确
8
+ * - 包含必要的环境检测逻辑
9
+ * - 包含正确的镜像源配置
10
+ * - 包含正确的错误处理
11
+ */
12
+
13
+ const fs = require("fs");
14
+ const path = require("path");
15
+
16
+ const PROJECT_ROOT = path.resolve(__dirname, "..");
17
+ const INSTALL_SH = path.join(PROJECT_ROOT, "install-skills.sh");
18
+ const INSTALL_PS1 = path.join(PROJECT_ROOT, "install-skills.ps1");
19
+
20
+ // ── 公共辅助 ─────────────────────────────────────────────────────────
21
+
22
+ function readScript(filePath) {
23
+ return fs.readFileSync(filePath, "utf-8");
24
+ }
25
+
26
+ // ── install-skills.sh 测试 ────────────────────────────────────────────
27
+
28
+ describe("install-skills.sh", () => {
29
+ let scriptContent;
30
+
31
+ beforeAll(() => {
32
+ scriptContent = readScript(INSTALL_SH);
33
+ });
34
+
35
+ test("脚本文件存在", () => {
36
+ expect(fs.existsSync(INSTALL_SH)).toBe(true);
37
+ });
38
+
39
+ test("包含 shebang 头", () => {
40
+ expect(scriptContent.startsWith("#!/usr/bin/env sh")).toBe(true);
41
+ });
42
+
43
+ test("包含项目根目录检查", () => {
44
+ expect(scriptContent).toContain("config.json");
45
+ expect(scriptContent).toContain("请在项目根目录下运行此脚本");
46
+ });
47
+
48
+ // ── git 检测 ────────────────────────────────────────────────────
49
+
50
+ test("包含 git 环境检测", () => {
51
+ expect(scriptContent).toContain("command -v git");
52
+ });
53
+
54
+ test("git 缺失时退出并给出提示", () => {
55
+ expect(scriptContent).toContain("未找到 git");
56
+ expect(scriptContent).toMatch(/exit 1/);
57
+ });
58
+
59
+ // ── Node.js 检测与安装 ───────────────────────────────────────────
60
+
61
+ test("包含 Node.js 环境检测", () => {
62
+ expect(scriptContent).toContain("command -v node");
63
+ });
64
+
65
+ test("Node.js 缺失时支持 Homebrew 自动安装", () => {
66
+ expect(scriptContent).toContain("brew install node");
67
+ });
68
+
69
+ test("Node.js 缺失时支持 apt 自动安装(阿里云镜像)", () => {
70
+ expect(scriptContent).toContain("apt-get install -y nodejs");
71
+ expect(scriptContent).toContain("mirrors.aliyun.com/nodesource");
72
+ });
73
+
74
+ test("Node.js 缺失时支持 yum 自动安装(阿里云镜像)", () => {
75
+ expect(scriptContent).toContain("yum install -y nodejs");
76
+ expect(scriptContent).toContain("mirrors.aliyun.com/nodesource");
77
+ });
78
+
79
+ test("Node.js 安装后配置 npm 淘宝镜像", () => {
80
+ expect(scriptContent).toContain("npm config set registry https://registry.npmmirror.com");
81
+ });
82
+
83
+ test("Node.js 版本过低时给出升级提示", () => {
84
+ expect(scriptContent).toContain("NODE_MAJOR");
85
+ expect(scriptContent).toContain("版本过低");
86
+ });
87
+
88
+ // ── Python 检测与安装 ────────────────────────────────────────────
89
+
90
+ test("包含 Python 环境检测", () => {
91
+ expect(scriptContent).toContain("command -v python3");
92
+ });
93
+
94
+ test("Python 缺失时支持 Homebrew 自动安装", () => {
95
+ expect(scriptContent).toContain("brew install python");
96
+ });
97
+
98
+ test("Python 缺失时支持 apt 自动安装", () => {
99
+ expect(scriptContent).toContain("apt-get install -y python3");
100
+ });
101
+
102
+ test("Python 缺失时支持 yum 自动安装", () => {
103
+ expect(scriptContent).toContain("yum install -y python3");
104
+ });
105
+
106
+ test("Python 安装后配置 pip 阿里云镜像", () => {
107
+ expect(scriptContent).toContain("pip3 config set global.index-url https://mirrors.aliyun.com/pypi/simple/");
108
+ expect(scriptContent).toContain("pip3 config set global.trusted-host mirrors.aliyun.com");
109
+ });
110
+
111
+ test("Python 版本过低时给出升级提示", () => {
112
+ expect(scriptContent).toContain("PYTHON_MAJOR");
113
+ expect(scriptContent).toContain("PYTHON_MINOR");
114
+ expect(scriptContent).toContain("版本过低");
115
+ });
116
+
117
+ // ── 网络源检测 ───────────────────────────────────────────────────
118
+
119
+ test("支持 --cn 参数强制使用国内加速源", () => {
120
+ expect(scriptContent).toContain('--cn');
121
+ expect(scriptContent).toContain("ghproxy.com");
122
+ });
123
+
124
+ test("支持 --global 参数强制使用原始 GitHub 地址", () => {
125
+ expect(scriptContent).toContain('--global');
126
+ expect(scriptContent).toContain("github.com/openyida/yida-skills.git");
127
+ });
128
+
129
+ test("自动检测网络环境(curl 超时检测)", () => {
130
+ expect(scriptContent).toContain("connect-timeout");
131
+ expect(scriptContent).toContain("github.com");
132
+ });
133
+
134
+ // ── Skills 安装 ──────────────────────────────────────────────────
135
+
136
+ test("Skills 目录路径正确(.claude/skills)", () => {
137
+ expect(scriptContent).toContain('.claude/skills');
138
+ });
139
+
140
+ test("Skills 已存在时执行 git pull 更新", () => {
141
+ expect(scriptContent).toContain("git -C");
142
+ expect(scriptContent).toContain("pull origin");
143
+ });
144
+
145
+ test("Skills 不存在时执行 git clone", () => {
146
+ expect(scriptContent).toContain("git clone --branch");
147
+ expect(scriptContent).toContain("--depth 1");
148
+ });
149
+
150
+ test("安装完成后列出已安装的 Skills", () => {
151
+ expect(scriptContent).toContain("已安装的 Skills");
152
+ });
153
+ });
154
+
155
+ // ── install-skills.ps1 测试 ───────────────────────────────────────────
156
+
157
+ describe("install-skills.ps1", () => {
158
+ let scriptContent;
159
+
160
+ beforeAll(() => {
161
+ scriptContent = readScript(INSTALL_PS1);
162
+ });
163
+
164
+ test("脚本文件存在", () => {
165
+ expect(fs.existsSync(INSTALL_PS1)).toBe(true);
166
+ });
167
+
168
+ test("包含 param 声明(支持 --cn/--global 参数)", () => {
169
+ expect(scriptContent).toContain("param(");
170
+ expect(scriptContent).toContain('$Mode');
171
+ });
172
+
173
+ test("ErrorActionPreference 设置为 Continue(避免 winget 非零退出码中断)", () => {
174
+ expect(scriptContent).toContain('$ErrorActionPreference = "Continue"');
175
+ expect(scriptContent).not.toContain('$ErrorActionPreference = "Stop"');
176
+ });
177
+
178
+ test("包含项目根目录检查", () => {
179
+ expect(scriptContent).toContain("config.json");
180
+ expect(scriptContent).toContain("请在项目根目录下运行此脚本");
181
+ });
182
+
183
+ // ── git 检测 ────────────────────────────────────────────────────
184
+
185
+ test("包含 git 环境检测", () => {
186
+ expect(scriptContent).toContain("Get-Command git");
187
+ });
188
+
189
+ test("git 缺失时退出并给出提示", () => {
190
+ expect(scriptContent).toContain("未找到 git");
191
+ expect(scriptContent).toContain("exit 1");
192
+ });
193
+
194
+ // ── Node.js 检测与安装 ───────────────────────────────────────────
195
+
196
+ test("包含 Node.js 环境检测", () => {
197
+ expect(scriptContent).toContain("Get-Command node");
198
+ });
199
+
200
+ test("Node.js 缺失时使用 winget 自动安装", () => {
201
+ expect(scriptContent).toContain("winget install OpenJS.NodeJS.LTS");
202
+ expect(scriptContent).toContain("--accept-source-agreements");
203
+ });
204
+
205
+ test("Node.js 安装后刷新环境变量", () => {
206
+ expect(scriptContent).toContain("GetEnvironmentVariable");
207
+ expect(scriptContent).toContain('"Path"');
208
+ });
209
+
210
+ test("Node.js 安装后配置 npm 淘宝镜像", () => {
211
+ expect(scriptContent).toContain("npm config set registry https://registry.npmmirror.com");
212
+ });
213
+
214
+ test("Node.js 版本过低时给出升级提示", () => {
215
+ expect(scriptContent).toContain("nodeMajor");
216
+ expect(scriptContent).toContain("版本过低");
217
+ });
218
+
219
+ // ── Python 检测与安装 ────────────────────────────────────────────
220
+
221
+ test("包含 Python 环境检测", () => {
222
+ expect(scriptContent).toContain("Get-Command python");
223
+ });
224
+
225
+ test("Python 缺失时使用 winget 自动安装", () => {
226
+ expect(scriptContent).toContain("winget install Python.Python.3.12");
227
+ expect(scriptContent).toContain("--accept-source-agreements");
228
+ });
229
+
230
+ test("Python 安装后配置 pip 阿里云镜像", () => {
231
+ expect(scriptContent).toContain("pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/");
232
+ expect(scriptContent).toContain("pip config set global.trusted-host mirrors.aliyun.com");
233
+ });
234
+
235
+ test("Python 版本过低时给出升级提示", () => {
236
+ expect(scriptContent).toContain("pythonMajor");
237
+ expect(scriptContent).toContain("pythonMinor");
238
+ expect(scriptContent).toContain("版本过低");
239
+ });
240
+
241
+ // ── 网络源检测 ───────────────────────────────────────────────────
242
+
243
+ test("支持 --cn 参数强制使用国内加速源", () => {
244
+ expect(scriptContent).toContain('"--cn"');
245
+ expect(scriptContent).toContain("ghproxy.com");
246
+ });
247
+
248
+ test("支持 --global 参数强制使用原始 GitHub 地址", () => {
249
+ expect(scriptContent).toContain('"--global"');
250
+ expect(scriptContent).toContain("github.com/openyida/yida-skills.git");
251
+ });
252
+
253
+ test("自动检测网络环境(Invoke-WebRequest 超时检测)", () => {
254
+ expect(scriptContent).toContain("Invoke-WebRequest");
255
+ expect(scriptContent).toContain("TimeoutSec");
256
+ });
257
+
258
+ // ── Skills 安装 ──────────────────────────────────────────────────
259
+
260
+ test("Skills 目录路径正确(.claude\\skills)", () => {
261
+ expect(scriptContent).toContain('.claude\\skills');
262
+ });
263
+
264
+ test("Skills 已存在时执行 git pull 更新", () => {
265
+ expect(scriptContent).toContain("git -C");
266
+ expect(scriptContent).toContain("pull origin");
267
+ });
268
+
269
+ test("Skills 不存在时执行 git clone", () => {
270
+ expect(scriptContent).toContain("git clone --branch");
271
+ expect(scriptContent).toContain("--depth 1");
272
+ });
273
+
274
+ test("安装完成后列出已安装的 Skills", () => {
275
+ expect(scriptContent).toContain("已安装的 Skills");
276
+ });
277
+ });
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+
3
+ const { spawnSync } = require("child_process");
4
+ const path = require("path");
5
+
6
+ const SCRIPT_PATH = path.resolve(
7
+ __dirname,
8
+ "../.openclaw/skills/yida-issue/scripts/create-issue.js"
9
+ );
10
+
11
+ function runScript(args = []) {
12
+ const result = spawnSync("node", [SCRIPT_PATH, ...args], {
13
+ encoding: "utf-8",
14
+ });
15
+ return {
16
+ stdout: result.stdout || "",
17
+ stderr: result.stderr || "",
18
+ status: result.status ?? 1,
19
+ };
20
+ }
21
+
22
+ const OPENYIDA_KEYWORDS = [
23
+ "cli", "命令行", "命令", "install", "安装脚本", "安装",
24
+ "workflow", "ci", "cd", "github action", "贡献者", "contributor",
25
+ "readme", "文档", "package", "npm", "发布工具", "版本管理",
26
+ "openclaw", "openyida", "bin/yida", "yida shell", "yida config",
27
+ "yida login命令", "yida logout命令",
28
+ ];
29
+
30
+ const YIDA_SKILLS_KEYWORDS = [
31
+ "登录", "登出", "login", "logout", "扫码", "cookie", "token", "csrf",
32
+ "创建应用", "创建页面", "创建表单", "发布页面", "发布", "publish",
33
+ "schema", "宜搭 api", "宜搭api", "skill", "表单", "应用", "页面",
34
+ "宜搭", "aliwork", "yida-create", "yida-publish", "yida-get",
35
+ "get-schema", "create-app", "create-page", "create-form",
36
+ "批量", "导出", "字段", "数据", "接口",
37
+ ];
38
+
39
+ function detectTargetRepo(description) {
40
+ const lowerDesc = description.toLowerCase();
41
+ let openyidaScore = 0;
42
+ let yidaSkillsScore = 0;
43
+
44
+ for (const keyword of OPENYIDA_KEYWORDS) {
45
+ if (lowerDesc.includes(keyword.toLowerCase())) openyidaScore++;
46
+ }
47
+ for (const keyword of YIDA_SKILLS_KEYWORDS) {
48
+ if (lowerDesc.includes(keyword.toLowerCase())) yidaSkillsScore++;
49
+ }
50
+
51
+ if (openyidaScore === 0 && yidaSkillsScore === 0) return null;
52
+ if (openyidaScore > yidaSkillsScore) return "openyida/openyida";
53
+ if (yidaSkillsScore > openyidaScore) return "openyida/yida-skills";
54
+ return null;
55
+ }
56
+
57
+ function detectIssueType(description) {
58
+ const lowerDesc = description.toLowerCase();
59
+ const bugKeywords = ["bug", "错误", "报错", "失败", "异常", "不生效", "不显示", "崩溃", "修复", "fix"];
60
+ for (const keyword of bugKeywords) {
61
+ if (lowerDesc.includes(keyword)) return "bug";
62
+ }
63
+ return "feature";
64
+ }
65
+
66
+ function generateTitle(description, issueType) {
67
+ const cleanDesc = description
68
+ .replace(/^(bug|feat|feature|fix)\s*[::]\s*/i, "")
69
+ .trim();
70
+ if (issueType === "bug") return `bug: ${cleanDesc}`;
71
+ return `[Feature] ${cleanDesc}`;
72
+ }
73
+
74
+ describe("detectTargetRepo - 路由判断", () => {
75
+ test("宜搭操作类:批量导出表单数据 → yida-skills", () => {
76
+ expect(detectTargetRepo("希望支持批量导出表单数据到 Excel")).toBe("openyida/yida-skills");
77
+ });
78
+
79
+ test("宜搭操作类:创建应用 → yida-skills", () => {
80
+ expect(detectTargetRepo("创建应用时图标颜色不生效")).toBe("openyida/yida-skills");
81
+ });
82
+
83
+ test("宜搭操作类:登录态失效 → yida-skills", () => {
84
+ expect(detectTargetRepo("登录态失效时希望自动重新登录")).toBe("openyida/yida-skills");
85
+ });
86
+
87
+ test("宜搭操作类:发布页面 → yida-skills", () => {
88
+ expect(detectTargetRepo("发布页面时希望支持进度显示")).toBe("openyida/yida-skills");
89
+ });
90
+
91
+ test("宜搭操作类:获取 schema → yida-skills", () => {
92
+ expect(detectTargetRepo("get-schema 命令返回结果格式优化")).toBe("openyida/yida-skills");
93
+ });
94
+
95
+ test("平台工具类:CLI list 命令 → openyida", () => {
96
+ expect(detectTargetRepo("希望 yida CLI 支持 list 命令列出所有应用")).toBe("openyida/openyida");
97
+ });
98
+
99
+ test("平台工具类:CI workflow → openyida", () => {
100
+ expect(detectTargetRepo("希望 CI workflow 自动检查代码规范")).toBe("openyida/openyida");
101
+ });
102
+
103
+ test("平台工具类:贡献者管理 → openyida", () => {
104
+ expect(detectTargetRepo("贡献者头像显示错误")).toBe("openyida/openyida");
105
+ });
106
+
107
+ test("平台工具类:安装脚本 → openyida", () => {
108
+ expect(detectTargetRepo("安装脚本在 Windows 上执行失败")).toBe("openyida/openyida");
109
+ });
110
+
111
+ test("平台工具类:npm 包 → openyida", () => {
112
+ expect(detectTargetRepo("希望 npm 包支持全局安装后自动更新")).toBe("openyida/openyida");
113
+ });
114
+
115
+ test("无关键词时返回 null", () => {
116
+ expect(detectTargetRepo("希望有更好的体验")).toBeNull();
117
+ });
118
+
119
+ test("空字符串返回 null", () => {
120
+ expect(detectTargetRepo("")).toBeNull();
121
+ });
122
+ });
123
+
124
+ describe("detectIssueType - Issue 类型判断", () => {
125
+ test("包含 bug 关键词 → bug", () => {
126
+ expect(detectIssueType("bug: 创建应用时图标颜色不生效")).toBe("bug");
127
+ });
128
+
129
+ test("包含 错误 关键词 → bug", () => {
130
+ expect(detectIssueType("登录时出现错误提示")).toBe("bug");
131
+ });
132
+
133
+ test("包含 不生效 关键词 → bug", () => {
134
+ expect(detectIssueType("图标颜色设置不生效")).toBe("bug");
135
+ });
136
+
137
+ test("包含 修复 关键词 → bug", () => {
138
+ expect(detectIssueType("修复登录态失效问题")).toBe("bug");
139
+ });
140
+
141
+ test("包含 fix 关键词 → bug", () => {
142
+ expect(detectIssueType("fix: cookie parsing error")).toBe("bug");
143
+ });
144
+
145
+ test("普通功能需求 → feature", () => {
146
+ expect(detectIssueType("希望支持批量导出表单数据")).toBe("feature");
147
+ });
148
+
149
+ test("新增命令需求 → feature", () => {
150
+ expect(detectIssueType("CLI 新增 list 命令")).toBe("feature");
151
+ });
152
+ });
153
+
154
+ describe("generateTitle - 标题生成", () => {
155
+ test("feature 类型添加 [Feature] 前缀", () => {
156
+ expect(generateTitle("希望支持批量导出表单数据", "feature")).toBe(
157
+ "[Feature] 希望支持批量导出表单数据"
158
+ );
159
+ });
160
+
161
+ test("bug 类型添加 bug: 前缀", () => {
162
+ expect(generateTitle("创建应用时图标颜色不生效", "bug")).toBe(
163
+ "bug: 创建应用时图标颜色不生效"
164
+ );
165
+ });
166
+
167
+ test("去掉原有的 bug: 前缀再重新添加", () => {
168
+ expect(generateTitle("bug: 创建应用时图标颜色不生效", "bug")).toBe(
169
+ "bug: 创建应用时图标颜色不生效"
170
+ );
171
+ });
172
+
173
+ test("去掉原有的 feat: 前缀再添加 [Feature]", () => {
174
+ expect(generateTitle("feat: 支持批量导出", "feature")).toBe(
175
+ "[Feature] 支持批量导出"
176
+ );
177
+ });
178
+
179
+ test("去掉原有的 feature: 前缀", () => {
180
+ expect(generateTitle("feature: 支持批量导出", "feature")).toBe(
181
+ "[Feature] 支持批量导出"
182
+ );
183
+ });
184
+ });
185
+
186
+ describe("CLI 行为测试", () => {
187
+ test("无参数时输出错误提示并退出", () => {
188
+ const { stderr, status } = runScript([]);
189
+ expect(status).not.toBe(0);
190
+ expect(stderr).toContain("请提供需求描述");
191
+ });
192
+
193
+ test("宜搭操作类需求 dry-run 路由到 yida-skills", () => {
194
+ const { stdout, status } = runScript([
195
+ "希望支持批量导出表单数据到 Excel",
196
+ "--dry-run",
197
+ ]);
198
+ expect(status).toBe(0);
199
+ expect(stdout).toContain("openyida/yida-skills");
200
+ expect(stdout).toContain("dry-run 完成");
201
+ });
202
+
203
+ test("CLI 平台工具类需求 dry-run 路由到 openyida", () => {
204
+ const { stdout, status } = runScript([
205
+ "希望 yida CLI 支持 list 命令列出所有应用",
206
+ "--dry-run",
207
+ ]);
208
+ expect(status).toBe(0);
209
+ expect(stdout).toContain("openyida/openyida");
210
+ expect(stdout).toContain("dry-run 完成");
211
+ });
212
+
213
+ test("bug 类型需求 dry-run 生成 bug 标题", () => {
214
+ const { stdout, status } = runScript([
215
+ "bug: 创建应用时图标颜色不生效",
216
+ "--dry-run",
217
+ ]);
218
+ expect(status).toBe(0);
219
+ expect(stdout).toContain("🐛 Bug");
220
+ expect(stdout).toContain("bug: 创建应用时图标颜色不生效");
221
+ });
222
+
223
+ test("--repo openyida 强制路由到 openyida", () => {
224
+ const { stdout, status } = runScript([
225
+ "登录态失效",
226
+ "--repo",
227
+ "openyida",
228
+ "--dry-run",
229
+ ]);
230
+ expect(status).toBe(0);
231
+ expect(stdout).toContain("openyida/openyida");
232
+ });
233
+
234
+ test("--repo yida-skills 强制路由到 yida-skills", () => {
235
+ const { stdout, status } = runScript([
236
+ "CLI 新增 list 命令",
237
+ "--repo",
238
+ "yida-skills",
239
+ "--dry-run",
240
+ ]);
241
+ expect(status).toBe(0);
242
+ expect(stdout).toContain("openyida/yida-skills");
243
+ });
244
+
245
+ test("--type bug 强制设置为 bug 类型", () => {
246
+ const { stdout, status } = runScript([
247
+ "创建应用",
248
+ "--repo",
249
+ "yida-skills",
250
+ "--type",
251
+ "bug",
252
+ "--dry-run",
253
+ ]);
254
+ expect(status).toBe(0);
255
+ expect(stdout).toContain("🐛 Bug");
256
+ });
257
+
258
+ test("无法判断仓库时提示用户手动指定", () => {
259
+ const { stdout, status } = runScript(["希望有更好的体验"]);
260
+ expect(status).not.toBe(0);
261
+ expect(stdout).toContain("无法自动判断目标仓库");
262
+ expect(stdout).toContain("--repo");
263
+ });
264
+
265
+ test("--repo 参数无效时报错退出", () => {
266
+ const { stderr, status } = runScript([
267
+ "测试需求",
268
+ "--repo",
269
+ "invalid-repo",
270
+ ]);
271
+ expect(status).not.toBe(0);
272
+ expect(stderr).toContain("无效的 --repo 参数");
273
+ });
274
+
275
+ test("--type 参数无效时报错退出", () => {
276
+ const { stderr, status } = runScript([
277
+ "测试需求",
278
+ "--repo",
279
+ "yida-skills",
280
+ "--type",
281
+ "invalid",
282
+ ]);
283
+ expect(status).not.toBe(0);
284
+ expect(stderr).toContain("无效的 --type 参数");
285
+ });
286
+
287
+ test("dry-run 模式输出 Issue body 预览", () => {
288
+ const { stdout, status } = runScript([
289
+ "希望支持批量导出表单数据",
290
+ "--repo",
291
+ "yida-skills",
292
+ "--dry-run",
293
+ ]);
294
+ expect(status).toBe(0);
295
+ expect(stdout).toContain("## 需求描述");
296
+ expect(stdout).toContain("## 期望功能");
297
+ expect(stdout).toContain("yida-issue skill");
298
+ });
299
+
300
+ test("bug dry-run 模式输出 Bug body 预览", () => {
301
+ const { stdout, status } = runScript([
302
+ "登录失败",
303
+ "--repo",
304
+ "yida-skills",
305
+ "--type",
306
+ "bug",
307
+ "--dry-run",
308
+ ]);
309
+ expect(status).toBe(0);
310
+ expect(stdout).toContain("## Bug 描述");
311
+ expect(stdout).toContain("## 复现步骤");
312
+ expect(stdout).toContain("## 期望行为");
313
+ });
314
+ });