@wwkit/harness 1.0.27 → 1.0.29
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/bin/index.js +10 -0
- package/package.json +3 -2
- package/skills/extract/SKILL.md +43 -68
- package/skills/extract/references/detail.md +1 -1
- package/skills/extract/references/list.md +3 -3
- package/skills/extract/references/navi.md +1 -1
- package/skills/jstest/SKILL.md +561 -0
- package/skills/jstest/references/case-create.md +327 -0
- package/skills/jstest/references/case-fix.md +273 -0
- package/skills/jstest/references/config.md +148 -0
- package/skills/jstest/references/coverage-analyze.md +247 -0
- package/skills/jstest/references/env-ensure.md +210 -0
- package/skills/jstest/references/execute.md +168 -0
- package/skills/jstest/references/sample.md +166 -0
- package/skills/jstest/references/scoring-rules.md +75 -0
- package/skills/jstest/references/src/jstest-sample/Calculator.js +80 -0
- package/skills/jstest/references/src/jstest-sample/ConfigManager.js +72 -0
- package/skills/jstest/references/src/jstest-sample/FileProcessor.js +57 -0
- package/skills/jstest/references/src/jstest-sample/OrderService.js +98 -0
- package/skills/jstest/references/src/jstest-sample/TokenGenerator.js +60 -0
- package/skills/jstest/references/src/jstest-sample/UserService.js +56 -0
- package/skills/jstest/references/src/jstest-sample/index.js +6 -0
- package/skills/jstest/references/suitability-check.md +232 -0
- package/skills/jstest/references/test-standards.md +288 -0
- package/skills/pytest/SKILL.md +38 -30
- package/skills/query/SKILL.md +16 -47
- package/skills/revise/SKILL.md +52 -96
- package/skills/revise/references/article.md +12 -15
- package/skills/revise/references/gallery.md +11 -15
- package/skills/revise/references/question.md +11 -15
- package/skills/revise/references/status.md +10 -14
- package/src/config.js +29 -0
- package/src/config.json5 +19 -0
- package/skills/extract/references/format-aliases.json5 +0 -22
- package/skills/extract/references/input.schema.json5 +0 -23
- package/skills/query/references/input.schema.json5 +0 -25
- package/skills/revise/references/format-aliases.json5 +0 -22
- package/skills/revise/references/input.schema.json5 +0 -28
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jstest
|
|
3
|
+
description: |
|
|
4
|
+
自包含 JS/TS 测试 skill:参照 pytest skill 架构,使用 Jest 作为测试工具,支持单元测试和集成测试。
|
|
5
|
+
通过 test_type=unit|integration|sample 区分:unit 全 Mock 目标覆盖率 90%,integration 最少 Mock 目标覆盖率 70%,sample 注入 demo 样例代码。
|
|
6
|
+
unit/integration 协调适用性评估→环境检查→覆盖率分析→用例创建→测试执行→用例修复的完整流程,内外两层 loop;sample 读取 references/sample.md 注入 demo。
|
|
7
|
+
子工作流(suitability-check/env-ensure/coverage-analyze/case-create/execute/case-fix/sample)位于 references/ 下,由主流程用 read 加载并按其指令执行,不再调用独立 skill。
|
|
8
|
+
调用方直接传原始任务消息,skill 自解析自包含。内部用 todowrite 管理 7 步。适用于 npm/pnpm 管理的 JS/TS 项目(ESM 或 CommonJS)。
|
|
9
|
+
license: MIT
|
|
10
|
+
metadata:
|
|
11
|
+
workflow: sequential
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# jstest 技能(固定 Sprint 冲刺)
|
|
15
|
+
|
|
16
|
+
## 执行范式
|
|
17
|
+
|
|
18
|
+
每次调用 = 一个固定 Sprint,三个事件:
|
|
19
|
+
|
|
20
|
+
1. **Sprint Planning**:解析入参 → 定义 Sprint Goal → todowrite 落固定 7 项 Sprint Backlog(每项带 DoD)
|
|
21
|
+
2. **Sprint Execution**:7 步 + 双层 loop 逐项执行,每项 = 执行 → 验证 DoD → todowrite 勾单 Done
|
|
22
|
+
3. **Sprint Review**:对照 Sprint Goal 验证 Increment(测试报告)及输出语义,沉淀 1 条回顾
|
|
23
|
+
|
|
24
|
+
**Sprint Goal** = `对 {{source}} 执行 {{test_type}} 测试,产出测试质量报告(unit 达标 score≥90,integration 达标 score≥70)`。
|
|
25
|
+
|
|
26
|
+
**铁律(最高优先级):**
|
|
27
|
+
|
|
28
|
+
- **没有 Sprint Backlog 不能开始执行**:收到任务消息后,先做 Sprint Planning(解析入参)再 todowrite 落固定 7 项 Sprint Backlog,然后才进入 Sprint Execution。
|
|
29
|
+
- **每项执行周期** = 进度检查(Daily Scrum 映射)→ 执行 → 验证 DoD → 勾单 Done。
|
|
30
|
+
- **子工作流通过 `read` 加载 `references/xxx.md` 并按其指令执行**,**不再调用独立 skill**。
|
|
31
|
+
- **禁止**:混合测试类型(unit 不混合 integration,反之亦然)。
|
|
32
|
+
- **禁止**:修改 jest 配置文件或 setupFiles(除非直接导致失败)。
|
|
33
|
+
- **禁止**:使用 WebFetch 或任何网络请求。
|
|
34
|
+
|
|
35
|
+
## 配置表(按 test_type 分支)
|
|
36
|
+
|
|
37
|
+
| 维度 | unit | integration |
|
|
38
|
+
|------|------|-------------|
|
|
39
|
+
| coverage_threshold | 90 | 70 |
|
|
40
|
+
| mock_strategy | full(全部 Mock 外部依赖) | minimal(最少 Mock,优先真实调用) |
|
|
41
|
+
| test_dir 前缀 | tests/unit/ | tests/integration/ |
|
|
42
|
+
| 适用性过滤 | 不过滤,所有模块都适合 | 仅外部依赖模块(Service/Integration Point/External Adapter),过滤 Pure Utility / Data Model |
|
|
43
|
+
| 环境补充检查 | 无 | DB连接 / 外部服务连通性 / 测试数据目录(非阻断式警告) |
|
|
44
|
+
| 环境依赖 | jest | jest, supertest(可选) |
|
|
45
|
+
| jest test category | unit | integration |
|
|
46
|
+
| 覆盖率权重 | 行30 / 分支30 / 函数20 | 行20 / 分支20 / 函数10 |
|
|
47
|
+
| 覆盖率阈值(行/分支/函数) | 90% / 70% / 90% | 70% / 50% / 70% |
|
|
48
|
+
| 质量维度 | 断言完整性5 / 异常路径5 / 边界值5 / 命名规范5 | 场景完整性15 / 数据流验证10 / 断言完整性10 / setup/teardown使用10 / 命名规范5 |
|
|
49
|
+
| 特殊规则 | 不修改 jest 配置;不混合集成测试 | 按业务场景组织用例;不混合单元测试 |
|
|
50
|
+
| 报告标题 | jest unit 测试质量报告 | jest integration 测试质量报告 |
|
|
51
|
+
| 退出达标 | score ≥ 90 | score ≥ 70 |
|
|
52
|
+
|
|
53
|
+
**sample 分支**:test_type=sample 时不走 7 步 loop,Step 1 解析后直接读取 `references/sample.md` 按 demo 注入流程执行(在当前工程 src/ 下创建 jstest-sample 包,写入 7 个 demo 模块,含 15 个故意 bug 用于端到端验证),完成后结束。
|
|
54
|
+
|
|
55
|
+
通用配置(unit/integration 共享):
|
|
56
|
+
|
|
57
|
+
- outer_max: **3**(外层 loop 最大轮次)
|
|
58
|
+
- inner_max: **3**(内层 loop 最大轮次)
|
|
59
|
+
- report 路径:`{test_dir}/reports/{YYYYMMDD_HHMMSS}/test_report.md`
|
|
60
|
+
|
|
61
|
+
## 输入参数(自包含)
|
|
62
|
+
|
|
63
|
+
入参为调用方传入的**原始任务消息**,可为 JSON 对象、key=value、自然语言或命令风格等任意形态,agent 依据字段语义自主提取(详见 Step 1)。字段清单:
|
|
64
|
+
|
|
65
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
66
|
+
|------|------|------|------|
|
|
67
|
+
| test_type | string | 是 | `unit`、`integration` 或 `sample`,其他值报错退出 |
|
|
68
|
+
| source | string | 是 | 源码目录或 .js/.ts 文件路径 |
|
|
69
|
+
| test_dir | string | 否 | 测试目录;未指定时按 `tests/{test_type}/{归一化路径}/` 自动推导 |
|
|
70
|
+
|
|
71
|
+
**提取原则**:从消息中自主识别三个字段——命令风格(`unit src/mymodule` 或 `integration src/mymodule tests/integration/mymodule`,第一段为 test_type,第二段为 source,第三段可选为 test_dir)、JSON 对象(`{test_type, source, test_dir}`)、key=value(`test_type=..., source=...`)、或自然语言(含 "集成/integration" → integration;含 "单元/unit" → unit;默认 unit;`source` 从出现的路径推断)。
|
|
72
|
+
|
|
73
|
+
**必填校验**:若无法解析出 `test_type` 或 `source`,将错误信息输出到 stderr 并结束,**禁止**继续执行。
|
|
74
|
+
|
|
75
|
+
**test_type 校验**:解析后若 `test_type` 不在 `{unit, integration, sample}` 中,输出 "test_type 只能是 unit、integration 或 sample,得到: {value}" 并结束。
|
|
76
|
+
|
|
77
|
+
## 输出
|
|
78
|
+
|
|
79
|
+
- 流程结束后将最终报告写入 `{report_dir}/test_report.md`,同时在 chat 中输出报告摘要。
|
|
80
|
+
- 源码 Bug 汇总(若有)写入 `{report_dir}/bug_list.md`。
|
|
81
|
+
- 中间产物(临时评分文件等)写入 `report_dir`,不污染源码目录。
|
|
82
|
+
|
|
83
|
+
## 工作流程
|
|
84
|
+
|
|
85
|
+
### 阶段 0:todowrite 落单(Sprint Backlog)
|
|
86
|
+
|
|
87
|
+
收到任务消息后,先用 `todowrite` 创建固定 7 项 Sprint Backlog(status=pending),每项带 DoD:
|
|
88
|
+
|
|
89
|
+
| # | 任务 | 对应 Step | DoD |
|
|
90
|
+
|---|------|----------|-----|
|
|
91
|
+
| 1 | 入参校验与目录映射 | Step 1 | test_type/source/test_dir 已校验,target/test_dir/report_dir/flow_start_time 已确定,7 项清单已建 |
|
|
92
|
+
| 2 | 模块适用性评估 | Step 2 | 得到 testable_modules(或为空已按原因退出) |
|
|
93
|
+
| 3 | 环境检查 | Step 3 | 得到 config(或 env-ensure 失败已退出);integration 补充检查已记录警告 |
|
|
94
|
+
| 4 | 覆盖率分析(外层 loop) | Step 4 | 得到 score/uncovered_areas,按退出条件结束或进入下一轮 |
|
|
95
|
+
| 5 | 用例创建 | Step 5 | 得到 new_cases_count(=0 已跳过 execute+fix 直接轮尾评分) |
|
|
96
|
+
| 6 | 测试执行 | Step 6 | 得到通过/失败结果(有失败已进入 Step 7) |
|
|
97
|
+
| 7 | 用例修复(内层 loop) | Step 7 | 得到修复结果,按内层退出条件回到 Step 6 或 Step 4 轮尾评分 |
|
|
98
|
+
|
|
99
|
+
每步完成立即 todowrite 勾单(status=completed)。
|
|
100
|
+
|
|
101
|
+
### Step 1:入参校验与目录映射
|
|
102
|
+
|
|
103
|
+
将第 1 步标记为 in_progress,按"输入参数"小节解析原始任务消息,得到 `test_type`、`source`、`test_dir`。
|
|
104
|
+
|
|
105
|
+
#### test_type 校验
|
|
106
|
+
|
|
107
|
+
| 检查项 | 失败提示 |
|
|
108
|
+
|--------|---------|
|
|
109
|
+
| 为空 | "请提供 test_type(unit、integration 或 sample)" |
|
|
110
|
+
| 非 unit/integration/sample | "test_type 只能是 unit、integration 或 sample,得到: {value}" |
|
|
111
|
+
|
|
112
|
+
#### 参数 source 校验(源码路径)
|
|
113
|
+
|
|
114
|
+
| 检查项 | 失败提示 |
|
|
115
|
+
|--------|---------|
|
|
116
|
+
| 为空 | "请提供源码路径,如 jstest unit src/mymodule(test_type=unit)" |
|
|
117
|
+
| 不存在 | "路径不存在: {path}" |
|
|
118
|
+
| 是 .js/.ts 文件 | 直接使用该文件作为 target(单文件测试模式) |
|
|
119
|
+
| 是非 .js/.ts 文件 | "非 JS/TS 文件: {path}" |
|
|
120
|
+
| 是目录但无 .js/.ts 文件 | "目录下无 JS/TS 文件: {path}" |
|
|
121
|
+
|
|
122
|
+
#### 参数 test_dir 校验(可选)
|
|
123
|
+
|
|
124
|
+
| 条件 | 行为 |
|
|
125
|
+
|------|------|
|
|
126
|
+
| 未指定 | 按映射规则自动推导(见下方) |
|
|
127
|
+
| 指定但不存在 | "测试目录不存在: {path}" |
|
|
128
|
+
| 指定但非目录 | "测试路径不是目录: {path}" |
|
|
129
|
+
| 合法 | 直接使用,跳过映射推导 |
|
|
130
|
+
|
|
131
|
+
#### 目录映射规则(test_dir 未指定时自动推导)
|
|
132
|
+
|
|
133
|
+
核心规则:`test_dir = tests/{test_type}/{归一化路径}/`,归一化仅去除 `src/` 前缀,保留包名。
|
|
134
|
+
|
|
135
|
+
**映射示例(unit)**:
|
|
136
|
+
|
|
137
|
+
| 源码路径 | 归一化 | 自动推导 test_dir |
|
|
138
|
+
|---------|--------|------------------|
|
|
139
|
+
| `src/mymodule` | `mymodule` | `tests/unit/mymodule/` |
|
|
140
|
+
| `src/mypackage/sub` | `mypackage/sub` | `tests/unit/mypackage/sub/` |
|
|
141
|
+
| `src/mypackage/MyService.js` | `mypackage` | `tests/unit/mypackage/` |
|
|
142
|
+
| `mypackage/sub`(flat layout) | `mypackage/sub` | `tests/unit/mypackage/sub/` |
|
|
143
|
+
|
|
144
|
+
**映射示例(integration)**:
|
|
145
|
+
|
|
146
|
+
| 源码路径 | 归一化 | 自动推导 test_dir |
|
|
147
|
+
|---------|--------|------------------|
|
|
148
|
+
| `src/mymodule` | `mymodule` | `tests/integration/mymodule/` |
|
|
149
|
+
| `src/mypackage/sub` | `mypackage/sub` | `tests/integration/mypackage/sub/` |
|
|
150
|
+
| `src/mypackage/MyService.js` | `mypackage` | `tests/integration/mypackage/` |
|
|
151
|
+
| `lib/util` | `lib/util` | `tests/integration/lib/util/` |
|
|
152
|
+
| `mypackage/sub`(flat layout) | `mypackage/sub` | `tests/integration/mypackage/sub/` |
|
|
153
|
+
|
|
154
|
+
#### 产出
|
|
155
|
+
|
|
156
|
+
Step 1 结束后,确定以下变量,传递给后续所有步骤:
|
|
157
|
+
|
|
158
|
+
- `target`:源码路径
|
|
159
|
+
- `test_dir`:测试目录路径
|
|
160
|
+
- `report_dir`:报告输出目录,格式为 `{test_dir}/reports/{YYYYMMDD_HHMMSS}/`
|
|
161
|
+
- `test_type`:unit 或 integration
|
|
162
|
+
- `flow_start_time`:流程开始时间戳(执行 `date +%s` 获取)
|
|
163
|
+
|
|
164
|
+
**报告目录规则**:
|
|
165
|
+
- 每次执行生成独立的时间戳子目录,避免覆盖历史报告
|
|
166
|
+
- 目录不存在时自动创建
|
|
167
|
+
|
|
168
|
+
**退出条件**:参数校验失败 → 直接退出,不进入后续流程。
|
|
169
|
+
|
|
170
|
+
**sample 分支**:若 `test_type == sample`,Step 1 完成后**直接读取 `references/sample.md`** 并按其指令执行 demo 注入流程(创建 src/jstest-sample/ 包 + 7 个 demo 模块 + 语法验证),完成后 todowrite 勾单第 1 步并输出创建报告,**跳过 Step 2~7**,流程结束。
|
|
171
|
+
|
|
172
|
+
完成后 todowrite 勾单第 1 步。
|
|
173
|
+
|
|
174
|
+
### Step 2:模块适用性评估
|
|
175
|
+
|
|
176
|
+
读取 `references/suitability-check.md` 并按其指令执行,传入参数:
|
|
177
|
+
|
|
178
|
+
- target: 源码目录路径
|
|
179
|
+
- test_type: 当前 test_type
|
|
180
|
+
|
|
181
|
+
**分支:test_type == unit**
|
|
182
|
+
|
|
183
|
+
UT 理论上对所有模块都适合,此步骤主要提供分类信息,**不做过滤**。所有模块都进入 `testable_modules`。
|
|
184
|
+
|
|
185
|
+
**分支:test_type == integration**
|
|
186
|
+
|
|
187
|
+
IT 仅适合有外部依赖的模块(Service Class / Integration Point / External Adapter),Pure Utility 和 Data Model 模块将被过滤。
|
|
188
|
+
|
|
189
|
+
**产出**:
|
|
190
|
+
|
|
191
|
+
- `testable_modules`:可测试模块清单(文件路径 + 模块名 + 分类),传递给 Step 5
|
|
192
|
+
|
|
193
|
+
**退出条件**:
|
|
194
|
+
|
|
195
|
+
- 可测试清单非空 → 继续 Step 3,保存 `testable_modules` 供 Step 5 使用
|
|
196
|
+
- 可测试清单为空 → 根据原因提示并终止:
|
|
197
|
+
- 无 .js/.ts 文件 → "目录下无 JS/TS 文件"
|
|
198
|
+
- 有文件但无导出 → "未发现导出的模块,仅有副作用代码"
|
|
199
|
+
- 有导出但全部被过滤(test_type=integration 无外部依赖) → "未发现适合集成测试的模块(需有外部依赖),建议改用 test_type=unit"
|
|
200
|
+
- 有导出但全部被跳过(test_type=unit) → "可测试模块被全部跳过: {跳过原因}"
|
|
201
|
+
|
|
202
|
+
完成后 todowrite 勾单第 2 步。
|
|
203
|
+
|
|
204
|
+
### Step 3:环境检查
|
|
205
|
+
|
|
206
|
+
读取 `references/env-ensure.md` 并按其指令执行:
|
|
207
|
+
|
|
208
|
+
- 检查是否为 npm/pnpm 项目
|
|
209
|
+
- 检查并安装依赖(test_type=unit: jest;test_type=integration: jest, supertest(可选))
|
|
210
|
+
- 检查 jest 配置(package.json "jest" 字段或 jest.config.js)
|
|
211
|
+
- 检测模块系统(ESM/CJS)和 TypeScript 支持
|
|
212
|
+
- 加载配置清单(`references/config.md`)
|
|
213
|
+
|
|
214
|
+
**分支:test_type == integration 的环境补充检查**(env-ensure 之后执行)
|
|
215
|
+
|
|
216
|
+
| 检查项 | 方法 | 失败处理 |
|
|
217
|
+
|--------|------|---------|
|
|
218
|
+
| 数据库连接 | 尝试连接配置的测试库 URL | 记录警告,提示"数据库不可用,集成测试可能全部失败" |
|
|
219
|
+
| 外部服务连通性 | 检查源码中引用的外部服务地址 | 记录警告,提示对应服务不可用 |
|
|
220
|
+
| 测试数据目录 | 检查 test_dir 是否存在,若存在则检查 fixtures/ 子目录 | test_dir 不存在则提示首轮会自动创建;fixtures/ 不存在则提示 case-create 会自动创建 |
|
|
221
|
+
|
|
222
|
+
> 集成测试环境检查为**非阻断式**:记录警告但不退出流程。集成测试可能依赖运行时环境,skill 无法自动修复,仅提示用户。
|
|
223
|
+
|
|
224
|
+
**分支:test_type == unit**
|
|
225
|
+
|
|
226
|
+
无补充检查。env-ensure 失败即退出。
|
|
227
|
+
|
|
228
|
+
**退出条件**:
|
|
229
|
+
|
|
230
|
+
- env-ensure 失败 → 直接退出,提示用户。
|
|
231
|
+
- test_type=integration 的环境警告 → 继续流程(非阻断)。
|
|
232
|
+
|
|
233
|
+
完成后 todowrite 勾单第 3 步。
|
|
234
|
+
|
|
235
|
+
### Step 4:覆盖率分析(外层 loop 开始)
|
|
236
|
+
|
|
237
|
+
#### 首轮检测
|
|
238
|
+
|
|
239
|
+
检查 `test_dir` 是否存在及是否有 `*.test.js` 或 `*.test.ts` 文件:
|
|
240
|
+
|
|
241
|
+
| 条件 | 行为 |
|
|
242
|
+
|------|------|
|
|
243
|
+
| 目录不存在 | 等同于"无测试文件",设置 `score = 0`、`round_start_score = 0`、`uncovered_areas = "all"`,直接进入 Step 5 |
|
|
244
|
+
| 目录存在但无 *.test.js/*.test.ts | 设置 `score = 0`、`round_start_score = 0`、`uncovered_areas = "all"`(全部源码文件视为未覆盖),直接进入 Step 5 |
|
|
245
|
+
| 有测试文件 | 正常执行覆盖率分析(下方流程) |
|
|
246
|
+
|
|
247
|
+
#### 覆盖率分析(非首轮或有已有测试时执行)
|
|
248
|
+
|
|
249
|
+
读取 `references/coverage-analyze.md` 并按其指令执行,传入参数:
|
|
250
|
+
- target: 用户指定的模块路径
|
|
251
|
+
- test_dir: 从 Step 1 确定的测试目录路径
|
|
252
|
+
- report_dir: 从 Step 1 确定的报告输出目录
|
|
253
|
+
- test_type: 当前 test_type(unit 或 integration)
|
|
254
|
+
- config: 从 Step 3 获得的配置
|
|
255
|
+
- previous_score: round_start_score 值(首轮不传,由 coverage-analyze 自行处理 null)
|
|
256
|
+
|
|
257
|
+
**调用时机区分(关键)**:
|
|
258
|
+
|
|
259
|
+
每轮外层 loop 中 coverage-analyze 被调用两次,作用不同:
|
|
260
|
+
1. **轮首调用**(round 2+,或 round 1 有已有测试时):用于识别未覆盖区域,供 Step 5 case-create 使用。将得到的 score 保存到 `round_start_score`,作为本轮改进比较的基准。**此调用不检查退出条件**。
|
|
261
|
+
2. **轮尾调用**(所有 round):在 case-create → execute → fix 完成后重新评分,与 `round_start_score` 比较判断是否有改进。**此调用检查所有退出条件**。
|
|
262
|
+
|
|
263
|
+
设 `threshold = config[test_type].coverage_threshold`(unit=90, integration=70)。
|
|
264
|
+
|
|
265
|
+
**退出条件**(仅在轮尾调用时检查):
|
|
266
|
+
- score ≥ threshold → 输出最终报告,流程结束
|
|
267
|
+
- score ≤ round_start_score → 输出"无改进"报告,流程结束
|
|
268
|
+
- score < threshold 且 outer_round < 3 → 进入下一轮外层 loop(outer_round + 1)
|
|
269
|
+
- score < threshold 且 outer_round = 3 → 输出"超限"报告,流程结束
|
|
270
|
+
|
|
271
|
+
**轮首调用**(round 2+,或 round 1 有已有测试时):
|
|
272
|
+
- 获取 `uncovered_areas` 和 `score`,将 `score` 保存到 `round_start_score`(供轮尾比较)
|
|
273
|
+
- 不检查退出条件
|
|
274
|
+
- 直接进入 Step 5
|
|
275
|
+
|
|
276
|
+
完成后 todowrite 勾单第 4 步。
|
|
277
|
+
|
|
278
|
+
### Step 5:用例创建
|
|
279
|
+
|
|
280
|
+
读取 `references/case-create.md` 并按其指令执行,传入参数:
|
|
281
|
+
- uncovered_areas: 从 Step 4 获得的未覆盖区域清单(首轮为 "all")
|
|
282
|
+
- testable_modules: 从 Step 2 获得的可测试模块清单
|
|
283
|
+
- target: 目标模块路径
|
|
284
|
+
- test_dir: 从 Step 1 确定的测试目录路径
|
|
285
|
+
- test_type: 当前 test_type
|
|
286
|
+
- config: 配置对象
|
|
287
|
+
|
|
288
|
+
**注意**:mock_strategy = config[test_type].mock_strategy(unit=full 全部 Mock,integration=minimal 最少 Mock)。该技能直接使用传入的 test_dir 扫描已有测试,增量追加不覆盖。
|
|
289
|
+
|
|
290
|
+
**退出条件**:
|
|
291
|
+
- 新增用例 > 0 → 进入 Step 6
|
|
292
|
+
- 新增用例 = 0 → 跳过 Step 6+7,直接回到 Step 4 轮尾评分(轮尾 score 将等于 round_start_score,触发"无改进"退出)
|
|
293
|
+
- uncovered_areas 为空 → 跳过 Step 6+7,直接回到 Step 4 轮尾评分
|
|
294
|
+
|
|
295
|
+
完成后 todowrite 勾单第 5 步。
|
|
296
|
+
|
|
297
|
+
### Step 6:测试执行
|
|
298
|
+
|
|
299
|
+
读取 `references/execute.md` 并按其指令执行,传入参数:
|
|
300
|
+
- target: 目标模块路径
|
|
301
|
+
- test_dir: 从 Step 1 确定的测试目录路径
|
|
302
|
+
- test_type: 当前 test_type
|
|
303
|
+
- config: 配置对象
|
|
304
|
+
- test_files: Step 5 新创建的测试文件(可选)
|
|
305
|
+
|
|
306
|
+
**退出条件**:
|
|
307
|
+
- 全部通过 → 回到 Step 4 轮尾评分(当前轮次,outer_round 递增在轮尾评分后发生)
|
|
308
|
+
- 有失败 → 进入 Step 7
|
|
309
|
+
|
|
310
|
+
完成后 todowrite 勾单第 6 步。
|
|
311
|
+
|
|
312
|
+
### Step 7:用例修复(内层 loop)
|
|
313
|
+
|
|
314
|
+
读取 `references/case-fix.md` 并按其指令执行,传入参数:
|
|
315
|
+
- failed_tests: 从 Step 6 获得的失败用例清单
|
|
316
|
+
- test_dir: 从 Step 1 确定的测试目录路径
|
|
317
|
+
- report_dir: 从 Step 1 确定的报告输出目录
|
|
318
|
+
- test_type: 当前 test_type
|
|
319
|
+
- config: 配置对象
|
|
320
|
+
|
|
321
|
+
**退出条件**:
|
|
322
|
+
- 有修复 → 回到 Step 6(inner_round + 1,内层 loop 继续)
|
|
323
|
+
- 无修复(全部标记人工处理)→ 内层 loop 退出,回到 Step 4 轮尾评分
|
|
324
|
+
- inner_round ≥ 3 → 内层 loop 退出,回到 Step 4 轮尾评分
|
|
325
|
+
|
|
326
|
+
**源码修改注意**:如果 case-fix 修改了源码文件,回到外层 loop 时 `testable_modules`(Step 2 产出)可能已过期。应在状态快照中标记 `[STATE] source_modified=true`,并在外层 loop round 2+ 时提示"源码已修改,适用性清单可能过期"。
|
|
327
|
+
|
|
328
|
+
完成后 todowrite 勾单第 7 步。
|
|
329
|
+
|
|
330
|
+
## Loop 控制状态机
|
|
331
|
+
|
|
332
|
+
设 `threshold = config[test_type].coverage_threshold`(unit=90, integration=70)。
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
外层 loop (max 3):
|
|
336
|
+
round 1: [首轮跳过 coverage-analyze] → case-create → execute → [fix → execute] → coverage-analyze(轮尾评分)
|
|
337
|
+
round 2: coverage-analyze(轮首,仅识别未覆盖) → case-create → execute → [fix → execute] → coverage-analyze(轮尾评分)
|
|
338
|
+
round 3: coverage-analyze(轮首,仅识别未覆盖) → case-create → execute → [fix → execute] → coverage-analyze(轮尾评分)
|
|
339
|
+
|
|
340
|
+
注意:round 1 首轮检测到 test_dir 为空时跳过 coverage-analyze,
|
|
341
|
+
直接进入 case-create,以 uncovered_areas="all" + testable_modules 作为输入。
|
|
342
|
+
|
|
343
|
+
注意:round 2+ 的轮首 coverage-analyze 仅用于识别未覆盖区域,不检查退出条件。
|
|
344
|
+
退出条件只在轮尾 coverage-analyze(case-create→execute→fix 完成后)检查。
|
|
345
|
+
|
|
346
|
+
外层退出条件(仅轮尾 coverage-analyze 检查):
|
|
347
|
+
- score ≥ threshold → 达标退出
|
|
348
|
+
- score ≤ round_start_score → 无改进退出
|
|
349
|
+
- score < threshold 且 outer_round = 3 → 超限退出
|
|
350
|
+
- score < threshold 且 outer_round < 3 → outer_round + 1,进入下一轮
|
|
351
|
+
|
|
352
|
+
内层 loop (max 3):
|
|
353
|
+
fix → execute → fix → execute → fix → execute
|
|
354
|
+
|
|
355
|
+
内层退出条件:
|
|
356
|
+
- 全部通过 → 回到外层(轮尾评分)
|
|
357
|
+
- fix 无修改 → 回到外层(轮尾评分)
|
|
358
|
+
- inner_round ≥ 3 → 回到外层(轮尾评分)
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## 状态跟踪
|
|
362
|
+
|
|
363
|
+
为防止长对话中 loop 计数丢失,**每个 Step 开始时**必须输出当前状态快照:
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
[STATE] outer_round=X/3, inner_round=X/3, round_start_score=XX, current_step=Step N, elapsed=Xs
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 状态变量
|
|
370
|
+
|
|
371
|
+
| 变量 | 初始值 | 更新时机 |
|
|
372
|
+
|------|--------|---------|
|
|
373
|
+
| outer_round | 1 | 轮尾评分且 score < threshold 且 outer_round < 3 时 → +1;轮首调用不递增 |
|
|
374
|
+
| inner_round | 0 | 内层 loop 每完成一轮 fix → +1;回到外层时重置为 0 |
|
|
375
|
+
| round_start_score | 0 | 轮首 coverage-analyze 结束后更新(首轮跳过时保持 0);轮尾调用时作为改进比较基准 |
|
|
376
|
+
| new_cases_count | 0 | 每轮 case-create 结束后更新。若为 0 则跳过 execute+fix,直接进入轮尾评分 |
|
|
377
|
+
| flow_start_time | null | Step 1 开始时记录(`date +%s`) |
|
|
378
|
+
| step_timings | {} | 每个 Step 结束时记录 `{step_name: 耗时秒数}` |
|
|
379
|
+
|
|
380
|
+
### 耗时记录规则
|
|
381
|
+
|
|
382
|
+
每个 Step 开始和结束时执行 `date +%s` 记录时间戳:
|
|
383
|
+
- Step 开始时:`step_start = $(date +%s)`
|
|
384
|
+
- Step 结束时:`step_timings[step_name] = $(date +%s) - step_start`
|
|
385
|
+
- 状态快照中 `elapsed = $(date +%s) - flow_start_time`
|
|
386
|
+
|
|
387
|
+
### 状态检查规则
|
|
388
|
+
|
|
389
|
+
1. 进入 Step 4 时(轮首调用,round 2+ 或 round 1 有已有测试):获取 `uncovered_areas` 和 `score`,将 `score` 保存到 `round_start_score`,**不检查退出条件**
|
|
390
|
+
2. 进入 Step 4 时(轮尾调用,所有 round):检查退出条件(score ≥ threshold / score ≤ round_start_score / outer_round = 3 且 score < threshold),不更新 `round_start_score`(轮首已设置)
|
|
391
|
+
3. 进入 Step 5 后:检查 `new_cases_count == 0` → 跳过 execute+fix,直接回到 Step 4 轮尾评分
|
|
392
|
+
4. 进入 Step 7 前:检查 `inner_round ≥ 3` → 内层超限,回到外层(轮尾评分)
|
|
393
|
+
5. 每个报告中的"各轮得分变化"表必须基于状态变量填写,不可凭记忆
|
|
394
|
+
6. 每个 Step 开始时记录 `date +%s`,结束时计算耗时并更新 `step_timings`,状态快照中 `elapsed` 为 `$(date +%s) - flow_start_time`
|
|
395
|
+
|
|
396
|
+
## 最终报告格式
|
|
397
|
+
|
|
398
|
+
流程结束后,输出最终报告(`test_type_label` = unit→"单元", integration→"集成"):
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
> 生成时间: {YYYY-MM-DD HH:MM:SS}
|
|
402
|
+
> 测试类型: {test_type}
|
|
403
|
+
> 测试目录: {test_dir}
|
|
404
|
+
|
|
405
|
+
## jest {test_type} 测试质量报告
|
|
406
|
+
|
|
407
|
+
### 环境信息
|
|
408
|
+
- 项目类型: npm/pnpm 项目 ✓
|
|
409
|
+
- 依赖检查: 全部通过 ✓
|
|
410
|
+
- 模块系统: {ESM/CommonJS}
|
|
411
|
+
- 测试类型: {test_type}
|
|
412
|
+
- Mock 策略: {config[test_type].mock_strategy}
|
|
413
|
+
|
|
414
|
+
### 最终结果
|
|
415
|
+
- 得分: XX / 100(等级: X)
|
|
416
|
+
- 覆盖率阈值: {threshold}
|
|
417
|
+
- 外层迭代: X / 3
|
|
418
|
+
- 是否达标: [是/否]
|
|
419
|
+
- 总耗时: Xs
|
|
420
|
+
|
|
421
|
+
### 耗时统计
|
|
422
|
+
| 阶段 | 耗时 |
|
|
423
|
+
|------|------|
|
|
424
|
+
| Step 1 入参校验 | Xs |
|
|
425
|
+
| Step 2 适用性评估 | Xs |
|
|
426
|
+
| Step 3 环境检查 | Xs |
|
|
427
|
+
| Step 4 覆盖率分析 | Xs |
|
|
428
|
+
| Step 5 用例创建 | Xs |
|
|
429
|
+
| Step 6 测试执行 | Xs |
|
|
430
|
+
| Step 7 用例修复 | Xs |
|
|
431
|
+
| **总计** | **Xs** |
|
|
432
|
+
|
|
433
|
+
### 各轮得分变化
|
|
434
|
+
| 轮次 | 得分 | 等级 | 新增用例 | 通过/失败 | 耗时 |
|
|
435
|
+
|------|------|------|---------|----------|------|
|
|
436
|
+
| 1 | XX | X | X | X/X | Xs |
|
|
437
|
+
| 2 | XX | X | X | X/X | Xs |
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**覆盖率明细**(权重按配置表):
|
|
441
|
+
|
|
442
|
+
unit:
|
|
443
|
+
```
|
|
444
|
+
| 维度 | 实际 | 阈值 | 权重 | 得分 |
|
|
445
|
+
|------|------|------|------|------|
|
|
446
|
+
| 行覆盖率 | XX% | 90% | 30 | XX |
|
|
447
|
+
| 分支覆盖率 | XX% | 70% | 30 | XX |
|
|
448
|
+
| 函数覆盖率 | XX% | 90% | 20 | XX |
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
integration:
|
|
452
|
+
```
|
|
453
|
+
| 维度 | 实际 | 阈值 | 权重 | 得分 |
|
|
454
|
+
|------|------|------|------|------|
|
|
455
|
+
| 行覆盖率 | XX% | 70% | 20 | XX |
|
|
456
|
+
| 分支覆盖率 | XX% | 50% | 20 | XX |
|
|
457
|
+
| 函数覆盖率 | XX% | 70% | 10 | XX |
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**质量明细**(维度按配置表):
|
|
461
|
+
|
|
462
|
+
unit:
|
|
463
|
+
```
|
|
464
|
+
| 维度 | 达标/应测 | 权重 | 得分 |
|
|
465
|
+
|------|----------|------|------|
|
|
466
|
+
| 断言完整性 | X/X | 5 | X |
|
|
467
|
+
| 异常路径覆盖 | X/X | 5 | X |
|
|
468
|
+
| 边界值覆盖 | X/X | 5 | X |
|
|
469
|
+
| 命名规范 | X/X | 5 | X |
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
integration:
|
|
473
|
+
```
|
|
474
|
+
| 维度 | 达标/应测 | 权重 | 得分 |
|
|
475
|
+
|------|----------|------|------|
|
|
476
|
+
| 场景完整性 | X/X | 15 | X |
|
|
477
|
+
| 数据流验证 | X/X | 10 | X |
|
|
478
|
+
| 断言完整性 | X/X | 10 | X |
|
|
479
|
+
| setup/teardown 使用 | X/X | 10 | X |
|
|
480
|
+
| 命名规范 | X/X | 5 | X |
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
```
|
|
484
|
+
### 未解决项(如有)
|
|
485
|
+
- [ ] xxx: [原因]
|
|
486
|
+
|
|
487
|
+
### 源码 Bug 汇总(如有)
|
|
488
|
+
> 汇总整个流程中通过测试失败发现的源码缺陷,详见 `{report_dir}/bug_list.md`
|
|
489
|
+
|
|
490
|
+
| # | 源码文件 | 方法 | Bug 描述 | 状态 | 修复内容/原因 |
|
|
491
|
+
|---|---------|------|---------|------|-------------|
|
|
492
|
+
| 1 | MyService.js | create | 返回值逻辑错误 | 已修复 | 修正返回值 |
|
|
493
|
+
|
|
494
|
+
### 退出原因
|
|
495
|
+
- [达标] score ≥ {threshold}
|
|
496
|
+
- [无改进] score ≤ round_start_score(本轮无改进)
|
|
497
|
+
- [超限] outer_round = 3 且 score < {threshold}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### 报告输出
|
|
501
|
+
|
|
502
|
+
流程结束后,将最终报告写入文件:
|
|
503
|
+
- 路径:`{report_dir}/test_report.md`
|
|
504
|
+
- 内容:上述完整报告(含源码 Bug 汇总)
|
|
505
|
+
- 同时在 chat 中输出报告摘要
|
|
506
|
+
|
|
507
|
+
## Sprint Review
|
|
508
|
+
|
|
509
|
+
所有 Backlog Items 完成后:
|
|
510
|
+
|
|
511
|
+
- **对照 Sprint Goal 验证**:是否对 `{{source}}` 完成 `{{test_type}}` 测试并产出测试质量报告?得分是否达标(unit≥90,integration≥70)?报告是否写入 `report_dir/test_report.md`?
|
|
512
|
+
- **展示 Increment**:测试报告路径 + chat 摘要。
|
|
513
|
+
- **简要回顾(Retrospective)**:沉淀 1 条改进项供下一轮采纳。
|
|
514
|
+
|
|
515
|
+
## 规则
|
|
516
|
+
|
|
517
|
+
**共同规则**:
|
|
518
|
+
1. 只处理 `tests/{test_type}/` 目录下的测试
|
|
519
|
+
2. 测试命名:`*.test.js` 或 `*.test.ts`(文件名与被测模块对应)
|
|
520
|
+
3. 不混合测试类型(unit 不混合 integration,反之亦然)
|
|
521
|
+
4. ESM 项目使用 `--experimental-vm-modules` 标志运行 jest
|
|
522
|
+
5. CommonJS 项目可直接使用 `npx jest` 或 `node_modules/.bin/jest`
|
|
523
|
+
|
|
524
|
+
**unit 专属规则**:
|
|
525
|
+
- 全部 Mock 外部依赖(HTTP/DB/文件/时间/随机/外部服务)
|
|
526
|
+
- 不修改 jest 配置文件或 setupFiles(除非直接导致失败)
|
|
527
|
+
- 使用 `jest.mock()` / `jest.fn()` / `jest.spyOn()` 进行 Mock
|
|
528
|
+
|
|
529
|
+
**integration 专属规则**:
|
|
530
|
+
- 最少 Mock,优先真实调用(仅 Mock 不可控的生产 API)
|
|
531
|
+
- 按业务场景组织用例,不按函数
|
|
532
|
+
- 验证端到端数据流完整性
|
|
533
|
+
- 使用 `beforeAll/afterAll` 进行场景级 setup/teardown
|
|
534
|
+
|
|
535
|
+
## references/ 目录结构
|
|
536
|
+
|
|
537
|
+
子工作流与资源文件均位于 `references/` 下,由主流程用 `read` 加载:
|
|
538
|
+
|
|
539
|
+
```
|
|
540
|
+
references/
|
|
541
|
+
├── suitability-check.md # Step 2 模块适用性评估子工作流
|
|
542
|
+
├── env-ensure.md # Step 3 环境检查子工作流
|
|
543
|
+
├── config.md # 环境检查配置清单(env-ensure.md 引用)
|
|
544
|
+
├── coverage-analyze.md # Step 4 覆盖率分析子工作流
|
|
545
|
+
├── scoring-rules.md # 评分规则(coverage-analyze.md 引用)
|
|
546
|
+
├── case-create.md # Step 5 用例创建子工作流
|
|
547
|
+
├── test-standards.md # 测试用例规范(case-create.md 引用)
|
|
548
|
+
├── execute.md # Step 6 测试执行子工作流
|
|
549
|
+
├── case-fix.md # Step 7 用例修复子工作流
|
|
550
|
+
├── sample.md # test_type=sample 时 demo 注入子工作流
|
|
551
|
+
└── src/jstest-sample/*.js # demo 样例源码模板(sample.md 引用,7 个文件)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
子工作流 .md 之间互不直接调用,均由主流程在对应 Step 用 `read` 加载后按其指令执行。子工作流内部对其他子工作流的引用以 `references/xxx.md` 路径或 Step 编号形式标注。
|
|
555
|
+
|
|
556
|
+
## 工具使用约束
|
|
557
|
+
|
|
558
|
+
- 写文件一律用 `write` 工具;读文件用 `read` 工具。
|
|
559
|
+
- 加载子工作流用 `read` 读取 `references/xxx.md`,按其指令执行(**不再通过 skill tool 调用独立子 skill**)。
|
|
560
|
+
- 需要中间数据时,用 `write` 工具写入临时文件,再以 stdin 重定向传给 node。
|
|
561
|
+
- 禁止使用未授权的 `cp`/`rm`/`mv` 等命令;需要复制、移动或删除临时文件时,一律用允许的 `node -e` 的 fs 模块完成。
|