specops 0.2.5 → 0.3.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.
- package/.opencode/agent/specops-codebase-mapper.md +764 -0
- package/.opencode/agent/specops-debugger.md +1246 -0
- package/.opencode/agent/specops-executor.md +475 -0
- package/.opencode/agent/specops-integration-checker.md +443 -0
- package/.opencode/agent/specops-phase-researcher.md +547 -0
- package/.opencode/agent/specops-plan-checker.md +690 -0
- package/.opencode/agent/specops-planner.md +581 -0
- package/.opencode/agent/specops-project-researcher.md +354 -0
- package/.opencode/agent/specops-research-synthesizer.md +242 -0
- package/.opencode/agent/specops-roadmapper.md +642 -0
- package/.opencode/agent/specops-work-verifier.md +573 -0
- package/.opencode/references/checkpoints.md +776 -0
- package/.opencode/references/continuation-format.md +249 -0
- package/.opencode/references/decimal-phase-calculation.md +65 -0
- package/.opencode/references/git-integration.md +248 -0
- package/.opencode/references/git-planning-commit.md +38 -0
- package/.opencode/references/model-profile-resolution.md +34 -0
- package/.opencode/references/model-profiles.md +92 -0
- package/.opencode/references/phase-argument-parsing.md +61 -0
- package/.opencode/references/planning-config.md +196 -0
- package/.opencode/references/questioning.md +145 -0
- package/.opencode/references/tdd.md +263 -0
- package/.opencode/references/ui-brand.md +160 -0
- package/.opencode/references/verification-patterns.md +612 -0
- package/.opencode/templates/DEBUG.md +164 -0
- package/.opencode/templates/UAT.md +180 -0
- package/.opencode/templates/VALIDATION.md +76 -0
- package/.opencode/templates/codebase/architecture.md +255 -0
- package/.opencode/templates/codebase/concerns.md +310 -0
- package/.opencode/templates/codebase/conventions.md +307 -0
- package/.opencode/templates/codebase/integrations.md +280 -0
- package/.opencode/templates/codebase/stack.md +186 -0
- package/.opencode/templates/codebase/structure.md +285 -0
- package/.opencode/templates/codebase/testing.md +480 -0
- package/.opencode/templates/context.md +221 -0
- package/.opencode/templates/continue-here.md +78 -0
- package/.opencode/templates/debug-subagent-prompt.md +91 -0
- package/.opencode/templates/discovery.md +147 -0
- package/.opencode/templates/milestone-archive.md +123 -0
- package/.opencode/templates/milestone.md +115 -0
- package/.opencode/templates/phase-prompt.md +333 -0
- package/.opencode/templates/planner-subagent-prompt.md +117 -0
- package/.opencode/templates/project.md +184 -0
- package/.opencode/templates/requirements.md +130 -0
- package/.opencode/templates/research-project/ARCHITECTURE.md +204 -0
- package/.opencode/templates/research-project/FEATURES.md +147 -0
- package/.opencode/templates/research-project/PITFALLS.md +200 -0
- package/.opencode/templates/research-project/STACK.md +120 -0
- package/.opencode/templates/research-project/SUMMARY.md +170 -0
- package/.opencode/templates/research.md +278 -0
- package/.opencode/templates/retrospective.md +54 -0
- package/.opencode/templates/roadmap.md +202 -0
- package/.opencode/templates/state.md +176 -0
- package/.opencode/templates/summary-complex.md +59 -0
- package/.opencode/templates/summary-minimal.md +41 -0
- package/.opencode/templates/summary-standard.md +48 -0
- package/.opencode/templates/summary.md +248 -0
- package/.opencode/templates/user-setup.md +311 -0
- package/.opencode/templates/verification-report.md +322 -0
- package/.opencode/workflows/add-phase.md +111 -0
- package/.opencode/workflows/add-tests.md +350 -0
- package/.opencode/workflows/add-todo.md +157 -0
- package/.opencode/workflows/audit-milestone.md +297 -0
- package/.opencode/workflows/check-todos.md +176 -0
- package/.opencode/workflows/cleanup.md +152 -0
- package/.opencode/workflows/complete-milestone.md +763 -0
- package/.opencode/workflows/diagnose-issues.md +219 -0
- package/.opencode/workflows/discovery-phase.md +288 -0
- package/.opencode/workflows/discuss-phase.md +542 -0
- package/.opencode/workflows/execute-phase.md +449 -0
- package/.opencode/workflows/execute-plan.md +447 -0
- package/.opencode/workflows/health.md +156 -0
- package/.opencode/workflows/help.md +489 -0
- package/.opencode/workflows/insert-phase.md +129 -0
- package/.opencode/workflows/list-phase-assumptions.md +178 -0
- package/.opencode/workflows/map-codebase.md +315 -0
- package/.opencode/workflows/new-milestone.md +382 -0
- package/.opencode/workflows/new-project.md +1116 -0
- package/.opencode/workflows/pause-work.md +122 -0
- package/.opencode/workflows/plan-milestone-gaps.md +274 -0
- package/.opencode/workflows/plan-phase.md +569 -0
- package/.opencode/workflows/progress.md +381 -0
- package/.opencode/workflows/quick.md +453 -0
- package/.opencode/workflows/remove-phase.md +154 -0
- package/.opencode/workflows/research-phase.md +73 -0
- package/.opencode/workflows/resume-project.md +304 -0
- package/.opencode/workflows/set-profile.md +80 -0
- package/.opencode/workflows/settings.md +213 -0
- package/.opencode/workflows/transition.md +544 -0
- package/.opencode/workflows/update.md +219 -0
- package/.opencode/workflows/verify-phase.md +242 -0
- package/.opencode/workflows/verify-work.md +569 -0
- package/commands/specops/add-phase.md +43 -0
- package/commands/specops/add-tests.md +41 -0
- package/commands/specops/add-todo.md +47 -0
- package/commands/specops/audit-milestone.md +36 -0
- package/commands/specops/check-todos.md +45 -0
- package/commands/specops/cleanup.md +18 -0
- package/commands/specops/complete-milestone.md +136 -0
- package/commands/specops/debug.md +167 -0
- package/commands/specops/discuss-phase.md +83 -0
- package/commands/specops/execute-phase.md +41 -0
- package/commands/specops/health.md +22 -0
- package/commands/specops/help.md +22 -0
- package/commands/specops/insert-phase.md +32 -0
- package/commands/specops/join-discord.md +18 -0
- package/commands/specops/list-phase-assumptions.md +46 -0
- package/commands/specops/map-codebase.md +71 -0
- package/commands/specops/new-milestone.md +44 -0
- package/commands/specops/new-project.md +42 -0
- package/commands/specops/pause-work.md +38 -0
- package/commands/specops/plan-milestone-gaps.md +34 -0
- package/commands/specops/plan-phase.md +45 -0
- package/commands/specops/progress.md +24 -0
- package/commands/specops/quick.md +41 -0
- package/commands/specops/reapply-patches.md +111 -0
- package/commands/specops/remove-phase.md +31 -0
- package/commands/specops/research-phase.md +189 -0
- package/commands/specops/resume-work.md +40 -0
- package/commands/specops/set-profile.md +34 -0
- package/commands/specops/settings.md +36 -0
- package/commands/specops/update.md +37 -0
- package/commands/specops/verify-work.md +38 -0
- package/dist/__integration__/fixtures/generator.d.ts +4 -0
- package/dist/__integration__/fixtures/generator.js +1 -0
- package/dist/__integration__/mocks/server.d.ts +7 -0
- package/dist/__integration__/mocks/server.js +1 -0
- package/dist/__integration__/setup.d.ts +6 -0
- package/dist/__integration__/setup.js +1 -0
- package/dist/acceptance/lazyDetector.js +1 -1
- package/dist/acceptance/reporter.js +1 -1
- package/dist/acceptance/runner.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/context/index.js +1 -1
- package/dist/context/promptTemplate.js +1 -1
- package/dist/context/techContextLoader.js +1 -1
- package/dist/engine.d.ts +1 -0
- package/dist/engine.js +1 -1
- package/dist/evolution/distiller.js +1 -1
- package/dist/evolution/index.js +1 -1
- package/dist/evolution/memoryGraph.js +1 -1
- package/dist/evolution/selector.js +1 -1
- package/dist/evolution/signals.js +1 -1
- package/dist/evolution/solidify.js +1 -1
- package/dist/evolution/store.js +1 -1
- package/dist/evolution/types.js +1 -1
- package/dist/init.d.ts +4 -3
- package/dist/init.js +1 -1
- package/dist/machines/agentMachine.js +1 -1
- package/dist/machines/supervisorMachine.js +1 -1
- package/dist/persistence/schema.js +1 -1
- package/dist/persistence/stateFile.js +1 -1
- package/dist/plugin-engine.js +1 -1
- package/dist/plugin.js +1 -1
- package/dist/requirement-analysis/analyzers/repository-parser.d.ts +121 -0
- package/dist/requirement-analysis/analyzers/repository-parser.js +1 -0
- package/dist/requirement-analysis/generators/prd-generator.d.ts +90 -0
- package/dist/requirement-analysis/generators/prd-generator.js +1 -0
- package/dist/requirement-analysis/integrations/v1-integration.d.ts +73 -0
- package/dist/requirement-analysis/integrations/v1-integration.js +1 -0
- package/dist/requirement-analysis/tools/analyze-requirements.js +1 -0
- package/dist/requirement-analysis/types/analysis-result.d.ts +326 -0
- package/dist/requirement-analysis/types/analysis-result.js +1 -0
- package/dist/requirement-analysis/types/feature-mapping.d.ts +294 -0
- package/dist/requirement-analysis/types/feature-mapping.js +1 -0
- package/dist/requirement-analysis/types/index.d.ts +171 -0
- package/dist/requirement-analysis/types/index.js +1 -0
- package/dist/requirement-analysis/types/tech-stack.d.ts +213 -0
- package/dist/requirement-analysis/types/tech-stack.js +1 -0
- package/dist/requirement-analysis/utils/error-handler.d.ts +112 -0
- package/dist/requirement-analysis/utils/error-handler.js +1 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +1 -1
- package/dist/utils/id.js +1 -1
- package/package.json +4 -2
- package/skills/competitor-search/SKILL.md +169 -0
- package/skills/demand-analysis/SKILL.md +307 -0
- package/skills/feature-search/SKILL.md +182 -0
- package/skills/requirement-analysis/README.md +464 -0
- package/skills/requirement-analysis/SKILL.md +224 -0
- package/skills/requirement-analysis/templates/feature-mapping-template.json +210 -0
- package/skills/requirement-analysis/templates/prd-template.md +104 -0
- package/skills/tech-selection/SKILL.md +198 -0
- package/dist/__e2e__/01-state-engine.e2e.test.d.ts +0 -10
- package/dist/__e2e__/01-state-engine.e2e.test.js +0 -1
- package/dist/acceptance/lazyDetector.test.d.ts +0 -1
- package/dist/acceptance/lazyDetector.test.js +0 -1
- package/dist/acceptance/reporter.test.d.ts +0 -1
- package/dist/acceptance/reporter.test.js +0 -1
- package/dist/acceptance/runner.test.d.ts +0 -1
- package/dist/acceptance/runner.test.js +0 -1
- package/dist/context/promptTemplate.test.d.ts +0 -1
- package/dist/context/promptTemplate.test.js +0 -1
- package/dist/context/techContextLoader.test.d.ts +0 -1
- package/dist/context/techContextLoader.test.js +0 -1
- package/dist/machines/agentMachine.test.d.ts +0 -1
- package/dist/machines/agentMachine.test.js +0 -1
- package/dist/machines/supervisorMachine.test.d.ts +0 -1
- package/dist/machines/supervisorMachine.test.js +0 -1
- package/dist/persistence/stateFile.test.d.ts +0 -1
- package/dist/persistence/stateFile.test.js +0 -1
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# 测试模式模板
|
|
2
|
+
|
|
3
|
+
`.planning/codebase/TESTING.md` 的模板 - 记录测试框架和模式。
|
|
4
|
+
|
|
5
|
+
**用途:** 记录测试的编写和运行方式。为添加符合现有模式的测试提供指导。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 文件模板
|
|
10
|
+
|
|
11
|
+
```markdown
|
|
12
|
+
# 测试模式
|
|
13
|
+
|
|
14
|
+
**分析日期:** [YYYY-MM-DD]
|
|
15
|
+
|
|
16
|
+
## 测试框架
|
|
17
|
+
|
|
18
|
+
**运行器:**
|
|
19
|
+
- [框架:例如 "Jest 29.x"、"Vitest 1.x"]
|
|
20
|
+
- [配置:例如 "项目根目录的 jest.config.js"]
|
|
21
|
+
|
|
22
|
+
**断言库:**
|
|
23
|
+
- [库:例如 "内置 expect"、"chai"]
|
|
24
|
+
- [匹配器:例如 "toBe, toEqual, toThrow"]
|
|
25
|
+
|
|
26
|
+
**运行命令:**
|
|
27
|
+
```bash
|
|
28
|
+
[例如 "npm test" 或 "npm run test"] # 运行所有测试
|
|
29
|
+
[例如 "npm test -- --watch"] # 监听模式
|
|
30
|
+
[例如 "npm test -- path/to/file.test.ts"] # 单个文件
|
|
31
|
+
[例如 "npm run test:coverage"] # 覆盖率报告
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 测试文件组织
|
|
35
|
+
|
|
36
|
+
**位置:**
|
|
37
|
+
- [模式:例如 "*.test.ts 与源文件并列"]
|
|
38
|
+
- [替代方案:例如 "__tests__/ 目录" 或 "独立的 tests/ 目录树"]
|
|
39
|
+
|
|
40
|
+
**命名:**
|
|
41
|
+
- [单元测试:例如 "module-name.test.ts"]
|
|
42
|
+
- [集成测试:例如 "feature-name.integration.test.ts"]
|
|
43
|
+
- [端到端测试:例如 "user-flow.e2e.test.ts"]
|
|
44
|
+
|
|
45
|
+
**结构:**
|
|
46
|
+
```
|
|
47
|
+
[展示实际目录模式,例如:
|
|
48
|
+
src/
|
|
49
|
+
lib/
|
|
50
|
+
utils.ts
|
|
51
|
+
utils.test.ts
|
|
52
|
+
services/
|
|
53
|
+
user-service.ts
|
|
54
|
+
user-service.test.ts
|
|
55
|
+
]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## 测试结构
|
|
59
|
+
|
|
60
|
+
**套件组织:**
|
|
61
|
+
```typescript
|
|
62
|
+
[展示实际使用的模式,例如:
|
|
63
|
+
|
|
64
|
+
describe('ModuleName', () => {
|
|
65
|
+
describe('functionName', () => {
|
|
66
|
+
it('should handle success case', () => {
|
|
67
|
+
// 准备
|
|
68
|
+
// 执行
|
|
69
|
+
// 断言
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle error case', () => {
|
|
73
|
+
// 测试代码
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**模式:**
|
|
81
|
+
- [初始化:例如 "使用 beforeEach 进行共享初始化,避免 beforeAll"]
|
|
82
|
+
- [清理:例如 "使用 afterEach 清理,恢复 mock"]
|
|
83
|
+
- [结构:例如 "要求使用 arrange/act/assert 模式"]
|
|
84
|
+
|
|
85
|
+
## Mock
|
|
86
|
+
|
|
87
|
+
**框架:**
|
|
88
|
+
- [工具:例如 "Jest 内置 mock"、"Vitest vi"、"Sinon"]
|
|
89
|
+
- [导入 mock:例如 "在文件顶部使用 vi.mock()"]
|
|
90
|
+
|
|
91
|
+
**模式:**
|
|
92
|
+
```typescript
|
|
93
|
+
[展示实际 mock 模式,例如:
|
|
94
|
+
|
|
95
|
+
// Mock 外部依赖
|
|
96
|
+
vi.mock('./external-service', () => ({
|
|
97
|
+
fetchData: vi.fn()
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
// 在测试中 mock
|
|
101
|
+
const mockFetch = vi.mocked(fetchData);
|
|
102
|
+
mockFetch.mockResolvedValue({ data: 'test' });
|
|
103
|
+
]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**应该 Mock 的:**
|
|
107
|
+
- [例如 "外部 API、文件系统、数据库"]
|
|
108
|
+
- [例如 "时间/日期(使用 vi.useFakeTimers)"]
|
|
109
|
+
- [例如 "网络请求(使用 mock fetch)"]
|
|
110
|
+
|
|
111
|
+
**不应该 Mock 的:**
|
|
112
|
+
- [例如 "纯函数、工具函数"]
|
|
113
|
+
- [例如 "内部业务逻辑"]
|
|
114
|
+
|
|
115
|
+
## 测试数据与工厂
|
|
116
|
+
|
|
117
|
+
**测试数据:**
|
|
118
|
+
```typescript
|
|
119
|
+
[展示创建测试数据的模式,例如:
|
|
120
|
+
|
|
121
|
+
// 工厂模式
|
|
122
|
+
function createTestUser(overrides?: Partial<User>): User {
|
|
123
|
+
return {
|
|
124
|
+
id: 'test-id',
|
|
125
|
+
name: 'Test User',
|
|
126
|
+
email: 'test@example.com',
|
|
127
|
+
...overrides
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 固定数据文件
|
|
132
|
+
// tests/fixtures/users.ts
|
|
133
|
+
export const mockUsers = [/* ... */];
|
|
134
|
+
]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**位置:**
|
|
138
|
+
- [例如 "tests/fixtures/ 存放共享固定数据"]
|
|
139
|
+
- [例如 "工厂函数放在测试文件中或 tests/factories/"]
|
|
140
|
+
|
|
141
|
+
## 覆盖率
|
|
142
|
+
|
|
143
|
+
**要求:**
|
|
144
|
+
- [目标:例如 "80% 行覆盖率"、"无特定目标"]
|
|
145
|
+
- [执行:例如 "CI 在低于 80% 时阻断"、"覆盖率仅供参考"]
|
|
146
|
+
|
|
147
|
+
**配置:**
|
|
148
|
+
- [工具:例如 "通过 --coverage 标志使用内置覆盖率"]
|
|
149
|
+
- [排除:例如 "排除 *.test.ts、配置文件"]
|
|
150
|
+
|
|
151
|
+
**查看覆盖率:**
|
|
152
|
+
```bash
|
|
153
|
+
[例如 "npm run test:coverage"]
|
|
154
|
+
[例如 "open coverage/index.html"]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## 测试类型
|
|
158
|
+
|
|
159
|
+
**单元测试:**
|
|
160
|
+
- [范围:例如 "隔离测试单个函数/类"]
|
|
161
|
+
- [Mock:例如 "mock 所有外部依赖"]
|
|
162
|
+
- [速度:例如 "每个测试必须在 1 秒内完成"]
|
|
163
|
+
|
|
164
|
+
**集成测试:**
|
|
165
|
+
- [范围:例如 "一起测试多个模块"]
|
|
166
|
+
- [Mock:例如 "mock 外部服务,使用真实内部模块"]
|
|
167
|
+
- [初始化:例如 "使用测试数据库,填充数据"]
|
|
168
|
+
|
|
169
|
+
**端到端测试:**
|
|
170
|
+
- [框架:例如 "使用 Playwright 进行端到端测试"]
|
|
171
|
+
- [范围:例如 "测试完整用户流程"]
|
|
172
|
+
- [位置:例如 "e2e/ 目录与单元测试分开"]
|
|
173
|
+
|
|
174
|
+
## 常见模式
|
|
175
|
+
|
|
176
|
+
**异步测试:**
|
|
177
|
+
```typescript
|
|
178
|
+
[展示模式,例如:
|
|
179
|
+
|
|
180
|
+
it('should handle async operation', async () => {
|
|
181
|
+
const result = await asyncFunction();
|
|
182
|
+
expect(result).toBe('expected');
|
|
183
|
+
});
|
|
184
|
+
]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**错误测试:**
|
|
188
|
+
```typescript
|
|
189
|
+
[展示模式,例如:
|
|
190
|
+
|
|
191
|
+
it('should throw on invalid input', () => {
|
|
192
|
+
expect(() => functionCall()).toThrow('error message');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// 异步错误
|
|
196
|
+
it('should reject on failure', async () => {
|
|
197
|
+
await expect(asyncCall()).rejects.toThrow('error message');
|
|
198
|
+
});
|
|
199
|
+
]
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**快照测试:**
|
|
203
|
+
- [用法:例如 "仅用于 React 组件" 或 "未使用"]
|
|
204
|
+
- [位置:例如 "__snapshots__/ 目录"]
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
*测试分析:[date]*
|
|
209
|
+
*测试模式变更时更新*
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
<good_examples>
|
|
213
|
+
```markdown
|
|
214
|
+
# 测试模式
|
|
215
|
+
|
|
216
|
+
**分析日期:** 2025-01-20
|
|
217
|
+
|
|
218
|
+
## 测试框架
|
|
219
|
+
|
|
220
|
+
**运行器:**
|
|
221
|
+
- Vitest 1.0.4
|
|
222
|
+
- 配置:项目根目录的 vitest.config.ts
|
|
223
|
+
|
|
224
|
+
**断言库:**
|
|
225
|
+
- Vitest 内置 expect
|
|
226
|
+
- 匹配器:toBe, toEqual, toThrow, toMatchObject
|
|
227
|
+
|
|
228
|
+
**运行命令:**
|
|
229
|
+
```bash
|
|
230
|
+
npm test # 运行所有测试
|
|
231
|
+
npm test -- --watch # 监听模式
|
|
232
|
+
npm test -- path/to/file.test.ts # 单个文件
|
|
233
|
+
npm run test:coverage # 覆盖率报告
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## 测试文件组织
|
|
237
|
+
|
|
238
|
+
**位置:**
|
|
239
|
+
- *.test.ts 与源文件并列
|
|
240
|
+
- 无独立 tests/ 目录
|
|
241
|
+
|
|
242
|
+
**命名:**
|
|
243
|
+
- unit-name.test.ts 用于所有测试
|
|
244
|
+
- 文件名中不区分单元/集成测试
|
|
245
|
+
|
|
246
|
+
**结构:**
|
|
247
|
+
```
|
|
248
|
+
src/
|
|
249
|
+
lib/
|
|
250
|
+
parser.ts
|
|
251
|
+
parser.test.ts
|
|
252
|
+
services/
|
|
253
|
+
install-service.ts
|
|
254
|
+
install-service.test.ts
|
|
255
|
+
bin/
|
|
256
|
+
install.ts
|
|
257
|
+
(无测试 - 通过 CLI 进行集成测试)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## 测试结构
|
|
261
|
+
|
|
262
|
+
**套件组织:**
|
|
263
|
+
```typescript
|
|
264
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
265
|
+
|
|
266
|
+
describe('ModuleName', () => {
|
|
267
|
+
describe('functionName', () => {
|
|
268
|
+
beforeEach(() => {
|
|
269
|
+
// 重置状态
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should handle valid input', () => {
|
|
273
|
+
// 准备
|
|
274
|
+
const input = createTestInput();
|
|
275
|
+
|
|
276
|
+
// 执行
|
|
277
|
+
const result = functionName(input);
|
|
278
|
+
|
|
279
|
+
// 断言
|
|
280
|
+
expect(result).toEqual(expectedOutput);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should throw on invalid input', () => {
|
|
284
|
+
expect(() => functionName(null)).toThrow('Invalid input');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**模式:**
|
|
291
|
+
- 使用 beforeEach 进行每个测试的初始化,避免 beforeAll
|
|
292
|
+
- 使用 afterEach 恢复 mock:vi.restoreAllMocks()
|
|
293
|
+
- 复杂测试中使用显式的 arrange/act/assert 注释
|
|
294
|
+
- 每个测试聚焦一个断言(但可以有多个 expect)
|
|
295
|
+
|
|
296
|
+
## Mock
|
|
297
|
+
|
|
298
|
+
**框架:**
|
|
299
|
+
- Vitest 内置 mock(vi)
|
|
300
|
+
- 通过 vi.mock() 在测试文件顶部进行模块 mock
|
|
301
|
+
|
|
302
|
+
**模式:**
|
|
303
|
+
```typescript
|
|
304
|
+
import { vi } from 'vitest';
|
|
305
|
+
import { externalFunction } from './external';
|
|
306
|
+
|
|
307
|
+
// Mock 模块
|
|
308
|
+
vi.mock('./external', () => ({
|
|
309
|
+
externalFunction: vi.fn()
|
|
310
|
+
}));
|
|
311
|
+
|
|
312
|
+
describe('test suite', () => {
|
|
313
|
+
it('mocks function', () => {
|
|
314
|
+
const mockFn = vi.mocked(externalFunction);
|
|
315
|
+
mockFn.mockReturnValue('mocked result');
|
|
316
|
+
|
|
317
|
+
// 使用 mock 函数的测试代码
|
|
318
|
+
|
|
319
|
+
expect(mockFn).toHaveBeenCalledWith('expected arg');
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**应该 Mock 的:**
|
|
325
|
+
- 文件系统操作(fs-extra)
|
|
326
|
+
- 子进程执行(child_process.exec)
|
|
327
|
+
- 外部 API 调用
|
|
328
|
+
- 环境变量(process.env)
|
|
329
|
+
|
|
330
|
+
**不应该 Mock 的:**
|
|
331
|
+
- 内部纯函数
|
|
332
|
+
- 简单工具函数(字符串处理、数组辅助函数)
|
|
333
|
+
- TypeScript 类型
|
|
334
|
+
|
|
335
|
+
## 测试数据与工厂
|
|
336
|
+
|
|
337
|
+
**测试数据:**
|
|
338
|
+
```typescript
|
|
339
|
+
// 测试文件中的工厂函数
|
|
340
|
+
function createTestConfig(overrides?: Partial<Config>): Config {
|
|
341
|
+
return {
|
|
342
|
+
targetDir: '/tmp/test',
|
|
343
|
+
global: false,
|
|
344
|
+
...overrides
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// tests/fixtures/ 中的共享固定数据
|
|
349
|
+
// tests/fixtures/sample-command.md
|
|
350
|
+
export const sampleCommand = `---
|
|
351
|
+
description: Test command
|
|
352
|
+
---
|
|
353
|
+
Content here`;
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**位置:**
|
|
357
|
+
- 工厂函数:在测试文件中靠近使用处定义
|
|
358
|
+
- 共享固定数据:tests/fixtures/(用于多文件测试数据)
|
|
359
|
+
- Mock 数据:简单时内联在测试中,复杂时使用工厂
|
|
360
|
+
|
|
361
|
+
## 覆盖率
|
|
362
|
+
|
|
363
|
+
**要求:**
|
|
364
|
+
- 无强制覆盖率目标
|
|
365
|
+
- 覆盖率仅供参考
|
|
366
|
+
- 聚焦关键路径(解析器、服务逻辑)
|
|
367
|
+
|
|
368
|
+
**配置:**
|
|
369
|
+
- Vitest 通过 c8 提供覆盖率(内置)
|
|
370
|
+
- 排除:*.test.ts、bin/install.ts、配置文件
|
|
371
|
+
|
|
372
|
+
**查看覆盖率:**
|
|
373
|
+
```bash
|
|
374
|
+
npm run test:coverage
|
|
375
|
+
open coverage/index.html
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## 测试类型
|
|
379
|
+
|
|
380
|
+
**单元测试:**
|
|
381
|
+
- 隔离测试单个函数
|
|
382
|
+
- Mock 所有外部依赖(fs、child_process)
|
|
383
|
+
- 快速:每个测试 <100ms
|
|
384
|
+
- 示例:parser.test.ts、validator.test.ts
|
|
385
|
+
|
|
386
|
+
**集成测试:**
|
|
387
|
+
- 一起测试多个模块
|
|
388
|
+
- 仅 mock 外部边界(文件系统、进程)
|
|
389
|
+
- 示例:install-service.test.ts(测试 service + parser)
|
|
390
|
+
|
|
391
|
+
**端到端测试:**
|
|
392
|
+
- 当前未使用
|
|
393
|
+
- CLI 集成通过手动测试
|
|
394
|
+
|
|
395
|
+
## 常见模式
|
|
396
|
+
|
|
397
|
+
**异步测试:**
|
|
398
|
+
```typescript
|
|
399
|
+
it('should handle async operation', async () => {
|
|
400
|
+
const result = await asyncFunction();
|
|
401
|
+
expect(result).toBe('expected');
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**错误测试:**
|
|
406
|
+
```typescript
|
|
407
|
+
it('should throw on invalid input', () => {
|
|
408
|
+
expect(() => parse(null)).toThrow('Cannot parse null');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// 异步错误
|
|
412
|
+
it('should reject on file not found', async () => {
|
|
413
|
+
await expect(readConfig('invalid.txt')).rejects.toThrow('ENOENT');
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**文件系统 Mock:**
|
|
418
|
+
```typescript
|
|
419
|
+
import { vi } from 'vitest';
|
|
420
|
+
import * as fs from 'fs-extra';
|
|
421
|
+
|
|
422
|
+
vi.mock('fs-extra');
|
|
423
|
+
|
|
424
|
+
it('mocks file system', () => {
|
|
425
|
+
vi.mocked(fs.readFile).mockResolvedValue('file content');
|
|
426
|
+
// 测试代码
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**快照测试:**
|
|
431
|
+
- 本代码库未使用
|
|
432
|
+
- 优先使用显式断言以提高清晰度
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
*测试分析:2025-01-20*
|
|
437
|
+
*测试模式变更时更新*
|
|
438
|
+
```
|
|
439
|
+
</good_examples>
|
|
440
|
+
|
|
441
|
+
<guidelines>
|
|
442
|
+
**TESTING.md 中应包含的内容:**
|
|
443
|
+
- 测试框架和运行器配置
|
|
444
|
+
- 测试文件位置和命名模式
|
|
445
|
+
- 测试结构(describe/it、beforeEach 模式)
|
|
446
|
+
- Mock 方法和示例
|
|
447
|
+
- 固定数据/工厂模式
|
|
448
|
+
- 覆盖率要求
|
|
449
|
+
- 运行测试的命令
|
|
450
|
+
- 实际代码中的常见测试模式
|
|
451
|
+
|
|
452
|
+
**不应包含的内容:**
|
|
453
|
+
- 具体测试用例(参考实际测试文件)
|
|
454
|
+
- 技术选型(那是 STACK.md 的内容)
|
|
455
|
+
- CI/CD 配置(那是部署文档的内容)
|
|
456
|
+
|
|
457
|
+
**填写此模板时:**
|
|
458
|
+
- 检查 package.json scripts 中的测试命令
|
|
459
|
+
- 查找测试配置文件(jest.config.js、vitest.config.ts)
|
|
460
|
+
- 阅读 3-5 个现有测试文件以识别模式
|
|
461
|
+
- 查找 tests/ 或 test-utils/ 中的测试工具
|
|
462
|
+
- 检查覆盖率配置
|
|
463
|
+
- 记录实际使用的模式,而非理想模式
|
|
464
|
+
|
|
465
|
+
**在阶段规划中的用途:**
|
|
466
|
+
- 添加新功能时(编写匹配的测试)
|
|
467
|
+
- 重构时(保持测试模式)
|
|
468
|
+
- 修复 bug 时(添加回归测试)
|
|
469
|
+
- 了解验证方法
|
|
470
|
+
- 搭建测试基础设施
|
|
471
|
+
|
|
472
|
+
**分析方法:**
|
|
473
|
+
- 检查 package.json 中的测试框架和脚本
|
|
474
|
+
- 阅读测试配置文件了解覆盖率、初始化设置
|
|
475
|
+
- 检查测试文件组织方式(并列 vs 独立目录)
|
|
476
|
+
- 审查 5 个测试文件了解模式(mock、结构、断言)
|
|
477
|
+
- 查找测试工具、固定数据、工厂
|
|
478
|
+
- 记录测试类型(单元、集成、端到端)
|
|
479
|
+
- 记录运行测试的命令
|
|
480
|
+
</guidelines>
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# 阶段上下文模板
|
|
2
|
+
|
|
3
|
+
`.planning/phases/XX-name/{phase_num}-CONTEXT.md` 的模板,捕获阶段的实现决策。
|
|
4
|
+
|
|
5
|
+
**用途:** 记录下游代理需要的决策。研究者用它了解要调查什么。规划者用它了解哪些选择已锁定、哪些可灵活处理。
|
|
6
|
+
|
|
7
|
+
**关键原则:** 类别不是预定义的。它们从本阶段实际讨论的内容中涌现。CLI 阶段有 CLI 相关的部分,UI 阶段有 UI 相关的部分。
|
|
8
|
+
|
|
9
|
+
**下游消费者:**
|
|
10
|
+
- `specops-phase-researcher` — 读取决策以聚焦研究 (如 "卡片布局" → 研究卡片组件模式)
|
|
11
|
+
- `specops-planner` — 读取决策以创建具体任务 (如 "无限滚动" → 任务包含虚拟化)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 文件模板
|
|
16
|
+
|
|
17
|
+
```markdown
|
|
18
|
+
# 阶段 [X]: [名称] - 上下文
|
|
19
|
+
|
|
20
|
+
**收集时间:** [日期]
|
|
21
|
+
**状态:** 准备规划
|
|
22
|
+
|
|
23
|
+
<domain>
|
|
24
|
+
## 阶段边界
|
|
25
|
+
|
|
26
|
+
[清晰说明本阶段交付什么 — 范围锚点。来自 ROADMAP.md,是固定的。讨论在此边界内澄清实现方式。]
|
|
27
|
+
|
|
28
|
+
</domain>
|
|
29
|
+
|
|
30
|
+
<decisions>
|
|
31
|
+
## 实现决策
|
|
32
|
+
|
|
33
|
+
### [讨论的领域 1]
|
|
34
|
+
- [做出的具体决策]
|
|
35
|
+
- [另一个决策(如适用)]
|
|
36
|
+
|
|
37
|
+
### [讨论的领域 2]
|
|
38
|
+
- [做出的具体决策]
|
|
39
|
+
|
|
40
|
+
### [讨论的领域 3]
|
|
41
|
+
- [做出的具体决策]
|
|
42
|
+
|
|
43
|
+
### Claude 自行决定
|
|
44
|
+
[用户明确说 "你来决定" 的领域 — Claude 在规划/实现时有灵活性]
|
|
45
|
+
|
|
46
|
+
</decisions>
|
|
47
|
+
|
|
48
|
+
<specifics>
|
|
49
|
+
## 具体想法
|
|
50
|
+
|
|
51
|
+
[讨论中的特定参考、示例或 "我想要像 X 那样" 的时刻。产品参考、具体行为、交互模式。]
|
|
52
|
+
|
|
53
|
+
[如果没有: "无具体要求 — 接受标准方案"]
|
|
54
|
+
|
|
55
|
+
</specifics>
|
|
56
|
+
|
|
57
|
+
<deferred>
|
|
58
|
+
## 延后的想法
|
|
59
|
+
|
|
60
|
+
[讨论中出现但属于其他阶段的想法。记录在此以免丢失,但明确不在本阶段范围内。]
|
|
61
|
+
|
|
62
|
+
[如果没有: "无 — 讨论保持在阶段范围内"]
|
|
63
|
+
|
|
64
|
+
</deferred>
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
*阶段: XX-name*
|
|
69
|
+
*上下文收集时间: [日期]*
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
<good_examples>
|
|
73
|
+
|
|
74
|
+
**示例 1: 视觉功能 (帖子信息流)**
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
# 阶段 3: 帖子信息流 - 上下文
|
|
78
|
+
|
|
79
|
+
**收集时间:** 2025-01-20
|
|
80
|
+
**状态:** 准备规划
|
|
81
|
+
|
|
82
|
+
<domain>
|
|
83
|
+
## 阶段边界
|
|
84
|
+
|
|
85
|
+
展示关注用户的帖子,可滚动浏览。用户可以查看帖子和互动数据。创建帖子和互动是独立阶段。
|
|
86
|
+
|
|
87
|
+
</domain>
|
|
88
|
+
|
|
89
|
+
<decisions>
|
|
90
|
+
## 实现决策
|
|
91
|
+
|
|
92
|
+
### 布局风格
|
|
93
|
+
- 卡片式布局,不是时间线或列表
|
|
94
|
+
- 每张卡片显示: 作者头像、名称、时间戳、完整帖子内容、反应数
|
|
95
|
+
- 卡片有微妙阴影、圆角 — 现代感
|
|
96
|
+
|
|
97
|
+
### 加载行为
|
|
98
|
+
- 无限滚动,不是分页
|
|
99
|
+
- 移动端下拉刷新
|
|
100
|
+
- 顶部新帖指示器 ("3 条新帖") 而非自动插入
|
|
101
|
+
|
|
102
|
+
### 空状态
|
|
103
|
+
- 友好的插图 + "关注一些人来看帖子吧"
|
|
104
|
+
- 根据兴趣推荐 3-5 个账户关注
|
|
105
|
+
|
|
106
|
+
### Claude 自行决定
|
|
107
|
+
- 加载骨架屏设计
|
|
108
|
+
- 精确的间距和排版
|
|
109
|
+
- 错误状态处理
|
|
110
|
+
|
|
111
|
+
</decisions>
|
|
112
|
+
|
|
113
|
+
<specifics>
|
|
114
|
+
## 具体想法
|
|
115
|
+
|
|
116
|
+
- "我喜欢 Twitter 显示新帖指示器而不打断滚动位置的方式"
|
|
117
|
+
- 卡片应该像 Linear 的 issue 卡片那样 — 干净,不杂乱
|
|
118
|
+
|
|
119
|
+
</specifics>
|
|
120
|
+
|
|
121
|
+
<deferred>
|
|
122
|
+
## 延后的想法
|
|
123
|
+
|
|
124
|
+
- 帖子评论 — 阶段 5
|
|
125
|
+
- 帖子收藏 — 加入待办
|
|
126
|
+
|
|
127
|
+
</deferred>
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
*阶段: 03-post-feed*
|
|
132
|
+
*上下文收集时间: 2025-01-20*
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**示例 2: CLI 工具 (数据库备份)**
|
|
136
|
+
|
|
137
|
+
```markdown
|
|
138
|
+
# 阶段 2: 备份命令 - 上下文
|
|
139
|
+
|
|
140
|
+
**收集时间:** 2025-01-20
|
|
141
|
+
**状态:** 准备规划
|
|
142
|
+
|
|
143
|
+
<domain>
|
|
144
|
+
## 阶段边界
|
|
145
|
+
|
|
146
|
+
CLI 命令将数据库备份到本地文件或 S3。支持全量和增量备份。恢复命令是独立阶段。
|
|
147
|
+
|
|
148
|
+
</domain>
|
|
149
|
+
|
|
150
|
+
<decisions>
|
|
151
|
+
## 实现决策
|
|
152
|
+
|
|
153
|
+
### 输出格式
|
|
154
|
+
- JSON 用于程序化使用,表格格式用于人类阅读
|
|
155
|
+
- 默认表格,--json 标志切换 JSON
|
|
156
|
+
- 详细模式 (-v) 显示进度,默认静默
|
|
157
|
+
|
|
158
|
+
### 标志设计
|
|
159
|
+
- 常用选项用短标志: -o (output), -v (verbose), -f (force)
|
|
160
|
+
- 长标志保持清晰: --incremental, --compress, --encrypt
|
|
161
|
+
- 必需: 数据库连接字符串 (位置参数或 --db)
|
|
162
|
+
|
|
163
|
+
### 错误恢复
|
|
164
|
+
- 网络失败重试 3 次,然后以清晰消息失败
|
|
165
|
+
- --no-retry 标志快速失败
|
|
166
|
+
- 失败时删除部分备份 (不留损坏文件)
|
|
167
|
+
|
|
168
|
+
### Claude 自行决定
|
|
169
|
+
- 精确的进度条实现
|
|
170
|
+
- 压缩算法选择
|
|
171
|
+
- 临时文件处理
|
|
172
|
+
|
|
173
|
+
</decisions>
|
|
174
|
+
|
|
175
|
+
<specifics>
|
|
176
|
+
## 具体想法
|
|
177
|
+
|
|
178
|
+
- "我想让它感觉像 pg_dump — 数据库人员熟悉的"
|
|
179
|
+
- 应该能在 CI 管道中工作 (退出码,无交互提示)
|
|
180
|
+
|
|
181
|
+
</specifics>
|
|
182
|
+
|
|
183
|
+
<deferred>
|
|
184
|
+
## 延后的想法
|
|
185
|
+
|
|
186
|
+
- 定时备份 — 独立阶段
|
|
187
|
+
- 备份轮换/保留 — 加入待办
|
|
188
|
+
|
|
189
|
+
</deferred>
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
*阶段: 02-backup-command*
|
|
194
|
+
*上下文收集时间: 2025-01-20*
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
</good_examples>
|
|
198
|
+
|
|
199
|
+
<guidelines>
|
|
200
|
+
**此模板为下游代理捕获决策。**
|
|
201
|
+
|
|
202
|
+
输出应回答: "研究者需要调查什么?规划者的哪些选择已锁定?"
|
|
203
|
+
|
|
204
|
+
**好的内容 (具体决策):**
|
|
205
|
+
- "卡片式布局,不是时间线"
|
|
206
|
+
- "网络失败重试 3 次,然后失败"
|
|
207
|
+
- "按年分组,然后按月"
|
|
208
|
+
- "JSON 用于程序化使用,表格用于人类"
|
|
209
|
+
|
|
210
|
+
**差的内容 (太模糊):**
|
|
211
|
+
- "应该感觉现代和干净"
|
|
212
|
+
- "好的用户体验"
|
|
213
|
+
- "快速和响应式"
|
|
214
|
+
- "易于使用"
|
|
215
|
+
|
|
216
|
+
**创建后:**
|
|
217
|
+
- 文件位于阶段目录: `.planning/phases/XX-name/{phase_num}-CONTEXT.md`
|
|
218
|
+
- `specops-phase-researcher` 使用决策聚焦调查
|
|
219
|
+
- `specops-planner` 使用决策 + 研究创建可执行任务
|
|
220
|
+
- 下游代理不应需要再次向用户询问已捕获的决策
|
|
221
|
+
</guidelines>
|