@grimoire-cc/cli 0.13.3 → 0.14.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/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +14 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/enforce.d.ts +3 -1
- package/dist/enforce.d.ts.map +1 -1
- package/dist/enforce.js +18 -6
- package/dist/enforce.js.map +1 -1
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +47 -0
- package/dist/setup.js.map +1 -1
- package/dist/summary.d.ts.map +1 -1
- package/dist/summary.js +9 -0
- package/dist/summary.js.map +1 -1
- package/package.json +1 -1
- package/packs/dev-pack/agents/grimoire.tdd-specialist.md +194 -27
- package/packs/dev-pack/grimoire.json +0 -38
- package/packs/dev-pack/skills/grimoire.conventional-commit/SKILL.md +69 -65
- package/packs/dotnet-pack/agents/grimoire.csharp-coder.md +110 -113
- package/packs/dotnet-pack/grimoire.json +23 -5
- package/packs/dotnet-pack/skills/grimoire.unit-testing-dotnet/SKILL.md +252 -0
- package/packs/{dev-pack/skills/grimoire.tdd-specialist → dotnet-pack/skills/grimoire.unit-testing-dotnet}/reference/anti-patterns.md +78 -0
- package/packs/dotnet-pack/skills/grimoire.unit-testing-dotnet/reference/tdd-workflow-patterns.md +259 -0
- package/packs/go-pack/grimoire.json +19 -0
- package/packs/go-pack/skills/grimoire.unit-testing-go/SKILL.md +256 -0
- package/packs/go-pack/skills/grimoire.unit-testing-go/reference/anti-patterns.md +244 -0
- package/packs/go-pack/skills/grimoire.unit-testing-go/reference/tdd-workflow-patterns.md +259 -0
- package/packs/python-pack/grimoire.json +19 -0
- package/packs/python-pack/skills/grimoire.unit-testing-python/SKILL.md +239 -0
- package/packs/python-pack/skills/grimoire.unit-testing-python/reference/anti-patterns.md +244 -0
- package/packs/python-pack/skills/grimoire.unit-testing-python/reference/tdd-workflow-patterns.md +259 -0
- package/packs/rust-pack/grimoire.json +29 -0
- package/packs/rust-pack/skills/grimoire.unit-testing-rust/SKILL.md +243 -0
- package/packs/rust-pack/skills/grimoire.unit-testing-rust/reference/anti-patterns.md +244 -0
- package/packs/rust-pack/skills/grimoire.unit-testing-rust/reference/tdd-workflow-patterns.md +259 -0
- package/packs/ts-pack/agents/grimoire.typescript-coder.md +36 -1
- package/packs/ts-pack/grimoire.json +27 -1
- package/packs/ts-pack/skills/grimoire.unit-testing-typescript/SKILL.md +255 -0
- package/packs/ts-pack/skills/grimoire.unit-testing-typescript/reference/anti-patterns.md +244 -0
- package/packs/ts-pack/skills/grimoire.unit-testing-typescript/reference/tdd-workflow-patterns.md +259 -0
- package/packs/dev-pack/skills/grimoire.tdd-specialist/SKILL.md +0 -248
- package/packs/dev-pack/skills/grimoire.tdd-specialist/reference/language-frameworks.md +0 -388
- package/packs/dev-pack/skills/grimoire.tdd-specialist/reference/tdd-workflow-patterns.md +0 -135
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/SKILL.md +0 -293
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/anti-patterns.md +0 -329
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/framework-guidelines.md +0 -361
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/parameterized-testing.md +0 -378
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-organization.md +0 -476
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/reference/test-performance.md +0 -576
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/tunit-template.md +0 -438
- package/packs/dotnet-pack/skills/grimoire.dotnet-unit-testing/templates/xunit-template.md +0 -303
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# TDD Workflow Patterns
|
|
2
|
+
|
|
3
|
+
Guidance on the test-driven development process, when to apply it, and advanced techniques.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Canon TDD — Start with a Test List](#canon-tdd--start-with-a-test-list)
|
|
8
|
+
- [Red-Green-Refactor](#red-green-refactor)
|
|
9
|
+
- [Transformation Priority Premise](#transformation-priority-premise)
|
|
10
|
+
- [F.I.R.S.T. Principles](#first-principles)
|
|
11
|
+
- [London School vs Detroit School](#london-school-vs-detroit-school)
|
|
12
|
+
- [When to Use TDD](#when-to-use-tdd)
|
|
13
|
+
- [When TDD Is Less Effective](#when-tdd-is-less-effective)
|
|
14
|
+
- [BDD and ATDD Extensions](#bdd-and-atdd-extensions)
|
|
15
|
+
- [Advanced Techniques](#advanced-techniques)
|
|
16
|
+
|
|
17
|
+
## Canon TDD — Start with a Test List
|
|
18
|
+
|
|
19
|
+
> Source: https://tidyfirst.substack.com/p/canon-tdd
|
|
20
|
+
|
|
21
|
+
Kent Beck's recommended starting point is not a single test but a **test list** — a written enumeration of all behaviors you intend to verify. This separates the creative work (what to test) from the mechanical work (write, make pass, refactor).
|
|
22
|
+
|
|
23
|
+
**Process:**
|
|
24
|
+
1. Write down all behaviors the code needs — a flat list, not tests
|
|
25
|
+
2. Pick the simplest item on the list
|
|
26
|
+
3. Write one failing test for it
|
|
27
|
+
4. Make it pass with the minimum code
|
|
28
|
+
5. Refactor
|
|
29
|
+
6. Cross off the item; repeat
|
|
30
|
+
|
|
31
|
+
**Why test order matters:** Starting with simpler behaviors forces simpler transformations (see TPP below) and lets the design emerge naturally. Jumping to complex cases early leads to over-engineered solutions. The test list keeps you focused and prevents scope creep.
|
|
32
|
+
|
|
33
|
+
## Red-Green-Refactor
|
|
34
|
+
|
|
35
|
+
> Source: https://martinfowler.com/bliki/TestDrivenDevelopment.html
|
|
36
|
+
|
|
37
|
+
The core TDD cycle, repeated in small increments:
|
|
38
|
+
|
|
39
|
+
### 1. Red — Write a Failing Test
|
|
40
|
+
|
|
41
|
+
Write the smallest test that describes the next piece of behavior. The test MUST fail before you write any production code. A test that passes immediately provides no confidence.
|
|
42
|
+
|
|
43
|
+
**Rules:**
|
|
44
|
+
- Write only ONE test at a time
|
|
45
|
+
- The test should compile/parse but fail at the assertion
|
|
46
|
+
- If the test passes immediately, it's either trivial or testing existing behavior
|
|
47
|
+
|
|
48
|
+
### 2. Green — Make It Pass
|
|
49
|
+
|
|
50
|
+
Write the MINIMUM code to make the failing test pass. Do not add extra logic, handle cases not yet tested, or optimize.
|
|
51
|
+
|
|
52
|
+
**Rules:**
|
|
53
|
+
- Write the simplest code that makes the test pass
|
|
54
|
+
- It's OK to hardcode values initially — the next test will force generalization
|
|
55
|
+
- Do not add code for future tests
|
|
56
|
+
- All existing tests must still pass
|
|
57
|
+
|
|
58
|
+
### 3. Refactor — Clean Up
|
|
59
|
+
|
|
60
|
+
With all tests green, improve the code structure without changing behavior. Tests give you the safety net.
|
|
61
|
+
|
|
62
|
+
**Rules:**
|
|
63
|
+
- No new functionality during refactoring
|
|
64
|
+
- All tests must remain green after each refactoring step
|
|
65
|
+
- Remove duplication, improve naming, extract methods
|
|
66
|
+
- Refactor both production code AND test code
|
|
67
|
+
|
|
68
|
+
### Cycle Length
|
|
69
|
+
|
|
70
|
+
Each Red-Green-Refactor cycle should take 1–10 minutes. If you're spending more than 10 minutes in the Red or Green phase, the step is too large — break it down.
|
|
71
|
+
|
|
72
|
+
## Transformation Priority Premise
|
|
73
|
+
|
|
74
|
+
> Source: http://blog.cleancoder.com/uncle-bob/2013/05/27/TheTransformationPriorityPremise.html
|
|
75
|
+
|
|
76
|
+
When going from Red to Green, prefer simpler transformations over complex ones. Listed from simplest to most complex:
|
|
77
|
+
|
|
78
|
+
1. **Constant** — return a hardcoded value
|
|
79
|
+
2. **Scalar** — replace constant with a variable
|
|
80
|
+
3. **Direct** — replace unconditional with conditional (if/else)
|
|
81
|
+
4. **Collection** — operate on a collection instead of a scalar
|
|
82
|
+
5. **Iteration** — add a loop
|
|
83
|
+
6. **Recursion** — add recursive call
|
|
84
|
+
7. **Assignment** — replace computed value with mutation
|
|
85
|
+
|
|
86
|
+
**Example — building FizzBuzz with TDD:**
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Test 1: input 1 → "1" Transformation: Constant
|
|
90
|
+
Test 2: input 2 → "2" Transformation: Scalar (use the input)
|
|
91
|
+
Test 3: input 3 → "Fizz" Transformation: Direct (add if)
|
|
92
|
+
Test 4: input 5 → "Buzz" Transformation: Direct (add another if)
|
|
93
|
+
Test 5: input 15 → "FizzBuzz" Transformation: Direct (add combined if)
|
|
94
|
+
Test 6: input 1-15 → full list Transformation: Iteration (generalize)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
By following this priority, you avoid over-engineering early and let the design emerge naturally from the tests.
|
|
98
|
+
|
|
99
|
+
## F.I.R.S.T. Principles
|
|
100
|
+
|
|
101
|
+
Every unit test must satisfy these five properties:
|
|
102
|
+
|
|
103
|
+
| Principle | Definition | Violation Signal |
|
|
104
|
+
|-----------|------------|-----------------|
|
|
105
|
+
| **Fast** | Runs in milliseconds | Real I/O, network calls, `sleep()` |
|
|
106
|
+
| **Independent** | No dependency on other tests | Shared mutable state, ordered execution |
|
|
107
|
+
| **Repeatable** | Same result every run | System clock, random data without seed, race conditions |
|
|
108
|
+
| **Self-Validating** | Pass or fail without manual interpretation | Tests that print output for a human to read |
|
|
109
|
+
| **Timely** | Written before or alongside production code | Tests added weeks after a feature shipped |
|
|
110
|
+
|
|
111
|
+
F.I.R.S.T. is a diagnostic checklist: if a test violates any property, it will erode team trust and reduce the value of the suite.
|
|
112
|
+
|
|
113
|
+
## London School vs Detroit School
|
|
114
|
+
|
|
115
|
+
> Source: https://martinfowler.com/articles/mocksArentStubs.html
|
|
116
|
+
|
|
117
|
+
Two schools of TDD with different philosophies on test doubles. Most teams use a hybrid.
|
|
118
|
+
|
|
119
|
+
### Detroit School (Classicist, Inside-Out)
|
|
120
|
+
|
|
121
|
+
- **Unit definition**: A module of any size — can span multiple classes
|
|
122
|
+
- **Approach**: Bottom-up; start from domain logic, build outward
|
|
123
|
+
- **Test doubles**: Avoid mocks; use real objects when feasible
|
|
124
|
+
- **Verification**: State verification — examine the result after execution
|
|
125
|
+
- **Testing style**: Black-box; test through public API
|
|
126
|
+
- **Refactoring**: Safe — tests aren't coupled to implementation details
|
|
127
|
+
- **Best for**: Building confidence in real interactions; reducing brittleness
|
|
128
|
+
|
|
129
|
+
### London School (Mockist, Outside-In)
|
|
130
|
+
|
|
131
|
+
- **Unit definition**: A single class in isolation
|
|
132
|
+
- **Approach**: Top-down; start from the API, work inward
|
|
133
|
+
- **Test doubles**: Mock all collaborators
|
|
134
|
+
- **Verification**: Behavior verification — confirm correct method calls occurred
|
|
135
|
+
- **Testing style**: White-box; tests know about internals
|
|
136
|
+
- **Refactoring**: Can be brittle — tests break when implementation changes
|
|
137
|
+
- **Best for**: Designing interactions upfront; driving architecture decisions
|
|
138
|
+
|
|
139
|
+
### Recommended: Hybrid Approach
|
|
140
|
+
|
|
141
|
+
Apply Detroit discipline as the default — use real objects, verify state. Apply London mocking only at architectural boundaries (external APIs, databases, clocks). Never mock value objects, pure functions, or in-process helpers.
|
|
142
|
+
|
|
143
|
+
The most important rule: if you're mocking to make a test easy to write, that's often a design smell (see The Hard Test in anti-patterns). If you're mocking because the dependency is genuinely external or slow, that's the right use.
|
|
144
|
+
|
|
145
|
+
## When to Use TDD
|
|
146
|
+
|
|
147
|
+
TDD is most valuable when:
|
|
148
|
+
|
|
149
|
+
- **Business logic** — Complex rules, calculations, state machines. TDD forces you to think through all cases before implementing.
|
|
150
|
+
- **Algorithm development** — Sorting, parsing, validation, transformation logic. Tests serve as a specification.
|
|
151
|
+
- **Bug fixes** — Write a test that reproduces the bug first (Red), then fix it (Green). This prevents regressions.
|
|
152
|
+
- **API/interface design** — Writing tests first helps you design interfaces from the consumer's perspective.
|
|
153
|
+
- **Refactoring** — Ensure tests exist before refactoring. If they don't, write characterization tests first, then refactor.
|
|
154
|
+
|
|
155
|
+
## When TDD Is Less Effective
|
|
156
|
+
|
|
157
|
+
TDD is not universally optimal. Use judgment:
|
|
158
|
+
|
|
159
|
+
- **UI/visual components** — Layout, styling, animations are hard to express as unit tests. Use visual regression testing or snapshot tests instead.
|
|
160
|
+
- **Exploratory/prototype code** — When you don't know what to build yet, writing tests first slows exploration. Spike first, then write tests.
|
|
161
|
+
- **Thin integration layers** — Simple pass-through code (e.g., a controller that calls a service) may not benefit from test-first approach. Integration tests are more valuable here.
|
|
162
|
+
- **Infrastructure/glue code** — Database migrations, config files, build scripts. Test these with integration or end-to-end tests.
|
|
163
|
+
- **External API wrappers** — Thin clients wrapping external APIs are better tested with integration tests against the real (or sandboxed) API.
|
|
164
|
+
|
|
165
|
+
For these cases, write tests AFTER the implementation (test-last), but still write them.
|
|
166
|
+
|
|
167
|
+
## BDD and ATDD Extensions
|
|
168
|
+
|
|
169
|
+
### Behavior-Driven Development (BDD)
|
|
170
|
+
|
|
171
|
+
> Source: https://martinfowler.com/bliki/GivenWhenThen.html
|
|
172
|
+
|
|
173
|
+
BDD extends TDD by using natural language to describe behavior. Useful when tests need to be readable by non-developers.
|
|
174
|
+
|
|
175
|
+
**Given-When-Then** structure:
|
|
176
|
+
|
|
177
|
+
```gherkin
|
|
178
|
+
Given a cart with items totaling $100
|
|
179
|
+
When a 10% discount is applied
|
|
180
|
+
Then the total should be $90
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Maps to test code:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
def test_cart_with_10_percent_discount_totals_90():
|
|
187
|
+
# Given
|
|
188
|
+
cart = Cart(items=[Item(price=100)])
|
|
189
|
+
|
|
190
|
+
# When
|
|
191
|
+
cart.apply_discount(PercentageDiscount(10))
|
|
192
|
+
|
|
193
|
+
# Then
|
|
194
|
+
assert cart.total == 90.0
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Acceptance TDD (ATDD)
|
|
198
|
+
|
|
199
|
+
Write high-level acceptance tests before implementing a feature. These tests describe the feature from the user's perspective and drive the overall design. Unit tests (via TDD) then drive the implementation of each component.
|
|
200
|
+
|
|
201
|
+
**Flow:**
|
|
202
|
+
1. Write acceptance test (fails — Red)
|
|
203
|
+
2. Use TDD to implement components needed to pass it
|
|
204
|
+
3. Acceptance test passes (Green)
|
|
205
|
+
4. Refactor
|
|
206
|
+
|
|
207
|
+
ATDD is most valuable for features with clear acceptance criteria and when working with product owners or stakeholders.
|
|
208
|
+
|
|
209
|
+
## Advanced Techniques
|
|
210
|
+
|
|
211
|
+
### Property-Based Testing
|
|
212
|
+
|
|
213
|
+
Instead of writing individual input/output pairs, define **properties** that should always hold true and let a framework generate hundreds of test cases automatically.
|
|
214
|
+
|
|
215
|
+
**Best for:** Pure functions, algorithms, data transformations, serialization round-trips.
|
|
216
|
+
|
|
217
|
+
**Tools:**
|
|
218
|
+
- Python: [Hypothesis](https://hypothesis.readthedocs.io)
|
|
219
|
+
- JavaScript/TypeScript: [fast-check](https://fast-check.dev)
|
|
220
|
+
- Go: `testing/quick` (stdlib), [gopter](https://github.com/leanovate/gopter)
|
|
221
|
+
- Rust: [proptest](https://github.com/proptest-rs/proptest)
|
|
222
|
+
- Java: [jqwik](https://jqwik.net)
|
|
223
|
+
- Elixir: [StreamData](https://hexdocs.pm/stream_data)
|
|
224
|
+
|
|
225
|
+
**Example property** (Python/Hypothesis):
|
|
226
|
+
```python
|
|
227
|
+
from hypothesis import given, strategies as st
|
|
228
|
+
|
|
229
|
+
@given(st.lists(st.integers()))
|
|
230
|
+
def test_sort_is_idempotent(lst):
|
|
231
|
+
assert sorted(sorted(lst)) == sorted(lst)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Mutation Testing
|
|
235
|
+
|
|
236
|
+
Mutation testing introduces small code changes (mutations) and checks whether your tests catch them. A test suite that lets mutations survive has gaps in its coverage.
|
|
237
|
+
|
|
238
|
+
**Metric:** Mutation score = % of mutations killed. Target 80%+.
|
|
239
|
+
|
|
240
|
+
**Tools:**
|
|
241
|
+
- JavaScript/TypeScript/C#: [Stryker](https://stryker-mutator.io)
|
|
242
|
+
- Java: [PITest](https://pitest.org)
|
|
243
|
+
- Python: [mutmut](https://mutmut.readthedocs.io)
|
|
244
|
+
- Go: [go-mutesting](https://github.com/zimmski/go-mutesting)
|
|
245
|
+
|
|
246
|
+
Run mutation testing periodically (not on every commit) to identify weak spots in the test suite.
|
|
247
|
+
|
|
248
|
+
### Contract Testing
|
|
249
|
+
|
|
250
|
+
In microservice or distributed architectures, contract tests verify that services communicate correctly without running full integration tests.
|
|
251
|
+
|
|
252
|
+
**How it works:**
|
|
253
|
+
1. Consumer defines a contract (expected interactions)
|
|
254
|
+
2. Provider verifies it can fulfill the contract
|
|
255
|
+
3. Both test independently — no need to spin up the full system
|
|
256
|
+
|
|
257
|
+
**Tool:** [Pact](https://pact.io) — supports most major languages.
|
|
258
|
+
|
|
259
|
+
Contract tests replace the expensive integration test layer for inter-service communication while still catching breaking API changes early.
|
|
@@ -2,12 +2,23 @@
|
|
|
2
2
|
name: grimoire.typescript-coder
|
|
3
3
|
description: "Use this agent when the user needs to write, refactor, debug, review, or understand TypeScript code in any environment — Node.js backends, React, Angular, Vue, Svelte, or any other TypeScript-compatible platform. This includes tasks like designing type-safe data models, implementing utility types, handling errors with discriminated unions, refactoring JavaScript to TypeScript, or reviewing TypeScript code for type safety and correctness.\\n\\nExamples:\\n<example>\\nContext: User wants to write a type-safe API client.\\nuser: \"Write a type-safe fetch wrapper that handles errors without throwing\"\\nassistant: \"I'll use the grimoire.typescript-coder agent to implement this with a Result type pattern.\"\\n<commentary>\\nThe user needs TypeScript code involving error handling and type safety — a perfect fit for the grimoire.typescript-coder agent.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: User is refactoring an existing function.\\nuser: \"Refactor this function to be more type-safe and remove the use of `any`\"\\nassistant: \"Let me hand this off to the grimoire.typescript-coder agent to refactor it properly.\"\\n<commentary>\\nRemoving `any` and improving type safety is core to what this agent does.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: User is building a Vue 3 composable.\\nuser: \"Create a reusable composable for paginated data fetching with TypeScript\"\\nassistant: \"I'll use the grimoire.typescript-coder agent to design this composable with proper types.\"\\n<commentary>\\nFramework-specific TypeScript (Vue Composition API here) is within scope for this agent.\\n</commentary>\\n</example>\\n\\n<example>\\nContext: User asks about discriminated unions.\\nuser: \"How do I model a payment result that can succeed or fail with different error types?\"\\nassistant: \"I'll use the grimoire.typescript-coder agent to model this with a discriminated union Result type.\"\\n<commentary>\\nType design questions are core responsibilities of this agent.\\n</commentary>\\n</example>"
|
|
4
4
|
tools: Glob, Grep, Read, Edit, Write, Skill, TaskCreate, TaskGet, TaskUpdate, TaskList, mcp__plugin_context7_context7__resolve-library-id, mcp__plugin_context7_context7__query-docs
|
|
5
|
-
model: sonnet
|
|
6
5
|
memory: project
|
|
7
6
|
---
|
|
8
7
|
|
|
9
8
|
You are an expert TypeScript developer with deep mastery of the TypeScript type system, modern language features, and best practices across all major frameworks and runtimes. Your role is to write, refactor, debug, and review high-quality, production-ready TypeScript code. You are platform-agnostic and framework-agnostic — equally fluent in Node.js backends, React, Angular, Vue, Svelte, and any other TypeScript-compatible environment.
|
|
10
9
|
|
|
10
|
+
You own the implementation end-to-end. You receive a task, read the codebase, make design decisions, and deliver working TypeScript code that fits the project.
|
|
11
|
+
|
|
12
|
+
## How You Work
|
|
13
|
+
|
|
14
|
+
1. **Read the task** — understand what needs to be built or changed
|
|
15
|
+
2. **Look up docs when needed** — use Context7 for API reference when working with unfamiliar libraries or APIs
|
|
16
|
+
3. **Break down complex work** — use tasks to track progress on multi-file implementations
|
|
17
|
+
4. **Implement** — write clean, working code that fits the existing codebase
|
|
18
|
+
5. **Verify** — ensure code compiles logically, follows existing patterns, handles edge cases
|
|
19
|
+
|
|
20
|
+
When the task specifies an approach, follow it. When it doesn't, choose the best one yourself. Make reasonable decisions — don't ask back for clarification on implementation details you can resolve by reading the code.
|
|
21
|
+
|
|
11
22
|
## Core Principles
|
|
12
23
|
|
|
13
24
|
### Type Safety
|
|
@@ -108,3 +119,27 @@ Before presenting code, verify:
|
|
|
108
119
|
- [ ] Error cases are modeled explicitly
|
|
109
120
|
- [ ] Code compiles cleanly under `strict: true` assumptions
|
|
110
121
|
- [ ] Logic is idiomatic for the target framework (if applicable)
|
|
122
|
+
|
|
123
|
+
# Persistent Agent Memory
|
|
124
|
+
|
|
125
|
+
Your `memory: project` setting gives you a persistent memory directory (under `.claude/agent-memory/grimoire.typescript-coder/`). Contents persist across conversations.
|
|
126
|
+
|
|
127
|
+
Consult your memory files to build on previous experience. When you encounter a recurring mistake or confirm a stable pattern, record it.
|
|
128
|
+
|
|
129
|
+
Guidelines:
|
|
130
|
+
- `MEMORY.md` is always loaded into your system prompt — keep it under 200 lines
|
|
131
|
+
- Create separate topic files (e.g., `debugging.md`, `patterns.md`) for details and link from MEMORY.md
|
|
132
|
+
- Update or remove memories that turn out to be wrong or outdated
|
|
133
|
+
- Organize by topic, not chronologically
|
|
134
|
+
|
|
135
|
+
What to save:
|
|
136
|
+
- Stable patterns and conventions confirmed across multiple interactions
|
|
137
|
+
- Key architectural decisions, important file paths, and project structure
|
|
138
|
+
- User preferences for workflow, tools, and communication style
|
|
139
|
+
- Solutions to recurring problems and debugging insights
|
|
140
|
+
|
|
141
|
+
What NOT to save:
|
|
142
|
+
- Session-specific context (current task details, in-progress work)
|
|
143
|
+
- Information that might be incomplete — verify before writing
|
|
144
|
+
- Anything that duplicates existing CLAUDE.md instructions
|
|
145
|
+
- Speculative conclusions from reading a single file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"name": "grimoire.typescript-coder",
|
|
7
7
|
"path": "agents/grimoire.typescript-coder.md",
|
|
8
8
|
"description": "Use this agent when the user needs to write, refactor, debug, review, or understand TypeScript code in any environment — Node.js backends, React, Angular, Vue, Svelte, or any other TypeScript-compatible platform. This includes tasks like designing type-safe data models, implementing utility types, handling errors with discriminated unions, refactoring JavaScript to TypeScript, or reviewing TypeScript code for type safety and correctness.",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "2.0.0",
|
|
10
10
|
"file_patterns": ["*.ts", "*.tsx", "*.mts", "*.cts", "*.js", "*.mjs", "*.cjs"]
|
|
11
11
|
}
|
|
12
12
|
],
|
|
@@ -37,6 +37,32 @@
|
|
|
37
37
|
],
|
|
38
38
|
"file_paths": ["**/tsconfig*", "**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"]
|
|
39
39
|
}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "grimoire.unit-testing-typescript",
|
|
43
|
+
"path": "skills/grimoire.unit-testing-typescript",
|
|
44
|
+
"description": "TypeScript/JavaScript unit testing specialist. Framework selection, patterns, and best practices for Vitest, Jest, Mocha, and Node test runner. Use when writing tests for .ts/.tsx/.js files, configuring test frameworks, or asking about TypeScript testing patterns, mocking, assertions, async testing.",
|
|
45
|
+
"version": "1.0.0",
|
|
46
|
+
"triggers": {
|
|
47
|
+
"keywords": ["vitest", "jest", "mocha"],
|
|
48
|
+
"file_extensions": [".ts", ".tsx", ".js", ".jsx"],
|
|
49
|
+
"patterns": [
|
|
50
|
+
"write.*test",
|
|
51
|
+
"add.*test",
|
|
52
|
+
"create.*test",
|
|
53
|
+
"test.*coverage",
|
|
54
|
+
"typescript.*test"
|
|
55
|
+
],
|
|
56
|
+
"file_paths": [
|
|
57
|
+
"__tests__/**",
|
|
58
|
+
"**/*.test.ts",
|
|
59
|
+
"**/*.spec.ts",
|
|
60
|
+
"**/*.test.js",
|
|
61
|
+
"**/*.spec.js",
|
|
62
|
+
"**/vitest.config.*",
|
|
63
|
+
"**/jest.config.*"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
40
66
|
}
|
|
41
67
|
]
|
|
42
68
|
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grimoire.unit-testing-typescript
|
|
3
|
+
description: "TypeScript/JavaScript unit testing specialist. Framework selection, patterns, and best practices for Vitest, Jest, Mocha, and Node test runner. Use when writing tests for .ts/.tsx/.js files, configuring test frameworks, or asking about TypeScript testing patterns, mocking, assertions, async testing."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TypeScript Unit Testing
|
|
7
|
+
|
|
8
|
+
Focused guidance for writing clean, type-safe unit tests in TypeScript and JavaScript projects.
|
|
9
|
+
|
|
10
|
+
## Framework Selection
|
|
11
|
+
|
|
12
|
+
### Detection
|
|
13
|
+
|
|
14
|
+
1. Check existing test files first — always match what the project uses
|
|
15
|
+
2. Check `package.json` devDependencies for `vitest`, `jest`, `mocha`, `@types/mocha`
|
|
16
|
+
3. Check for config files: `vitest.config.ts`, `jest.config.ts`, `.mocharc.*`
|
|
17
|
+
|
|
18
|
+
### Decision Table
|
|
19
|
+
|
|
20
|
+
| Condition | Use | Reason |
|
|
21
|
+
|-----------|-----|--------|
|
|
22
|
+
| Project has existing tests | **Match existing** | Consistency is paramount |
|
|
23
|
+
| Vite-based project | **Vitest** | Native integration, fastest |
|
|
24
|
+
| New TypeScript project | **Vitest** | ESM-native, Jest-compatible API, fast |
|
|
25
|
+
| React (CRA) or existing Jest | **Jest** | Mature ecosystem, wide adoption |
|
|
26
|
+
| Legacy project, custom setup | **Mocha + Chai** | Flexible, pluggable |
|
|
27
|
+
| Node.js 20+, minimal deps | **Node test runner** | Built-in, zero dependencies |
|
|
28
|
+
| User explicitly requests | **Requested** | Respect user preference |
|
|
29
|
+
|
|
30
|
+
## Naming Conventions
|
|
31
|
+
|
|
32
|
+
Use descriptive strings in `test()` or `it()`:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// Pattern: 'methodName scenario expected behavior'
|
|
36
|
+
test('getUser with invalid id throws NotFound', () => { ... });
|
|
37
|
+
test('calculateTotal with discount applies percentage', () => { ... });
|
|
38
|
+
test('parseConfig with missing required fields throws ValidationError', () => { ... });
|
|
39
|
+
|
|
40
|
+
// describe blocks for grouping
|
|
41
|
+
describe('OrderService', () => {
|
|
42
|
+
describe('processOrder', () => {
|
|
43
|
+
test('with valid order saves and returns id', async () => { ... });
|
|
44
|
+
test('with invalid order throws ValidationError', async () => { ... });
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Patterns
|
|
50
|
+
|
|
51
|
+
### AAA with Vitest/Jest
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { describe, test, expect, vi, beforeEach } from 'vitest';
|
|
55
|
+
|
|
56
|
+
describe('OrderService', () => {
|
|
57
|
+
let mockRepo: MockedObject<OrderRepository>;
|
|
58
|
+
let service: OrderService;
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
mockRepo = { save: vi.fn(), findById: vi.fn() };
|
|
62
|
+
service = new OrderService(mockRepo);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('processOrder with valid order saves and returns id', async () => {
|
|
66
|
+
// Arrange
|
|
67
|
+
const order = createValidOrder();
|
|
68
|
+
mockRepo.save.mockResolvedValue({ id: '123' });
|
|
69
|
+
|
|
70
|
+
// Act
|
|
71
|
+
const result = await service.processOrder(order);
|
|
72
|
+
|
|
73
|
+
// Assert
|
|
74
|
+
expect(result.id).toBe('123');
|
|
75
|
+
expect(mockRepo.save).toHaveBeenCalledWith(order);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('processOrder with invalid order throws ValidationError', async () => {
|
|
79
|
+
// Arrange
|
|
80
|
+
const order = createInvalidOrder();
|
|
81
|
+
|
|
82
|
+
// Act & Assert
|
|
83
|
+
await expect(service.processOrder(order)).rejects.toThrow(ValidationError);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Parameterized Tests
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Vitest/Jest
|
|
92
|
+
test.each([
|
|
93
|
+
{ discount: 0, expected: 100.0 },
|
|
94
|
+
{ discount: 10, expected: 90.0 },
|
|
95
|
+
{ discount: 50, expected: 50.0 },
|
|
96
|
+
])('applyDiscount with $discount% returns $expected', ({ discount, expected }) => {
|
|
97
|
+
expect(applyDiscount(100, discount)).toBe(expected);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// With descriptive names via tagged template
|
|
101
|
+
test.each`
|
|
102
|
+
input | expected
|
|
103
|
+
${''} | ${false}
|
|
104
|
+
${'abc'} | ${true}
|
|
105
|
+
${'a'} | ${true}
|
|
106
|
+
`('isNonEmpty("$input") returns $expected', ({ input, expected }) => {
|
|
107
|
+
expect(isNonEmpty(input)).toBe(expected);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Async Testing
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Async/await (preferred)
|
|
115
|
+
test('fetchUser resolves with user data', async () => {
|
|
116
|
+
const user = await fetchUser('123');
|
|
117
|
+
expect(user.name).toBe('Alice');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Promise rejection
|
|
121
|
+
test('fetchUser with bad id rejects with NotFound', async () => {
|
|
122
|
+
await expect(fetchUser('bad')).rejects.toThrow(NotFoundError);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Callback-based (rare, legacy)
|
|
126
|
+
test('legacyFetch calls back with data', (done) => {
|
|
127
|
+
legacyFetch('123', (err, data) => {
|
|
128
|
+
expect(err).toBeNull();
|
|
129
|
+
expect(data.name).toBe('Alice');
|
|
130
|
+
done();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Error Testing
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Sync errors
|
|
139
|
+
test('divide by zero throws', () => {
|
|
140
|
+
expect(() => divide(1, 0)).toThrow('Cannot divide by zero');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Async errors
|
|
144
|
+
test('save invalid order rejects with ValidationError', async () => {
|
|
145
|
+
await expect(service.save(invalidOrder)).rejects.toThrow(ValidationError);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Error properties
|
|
149
|
+
test('validation error includes field name', async () => {
|
|
150
|
+
try {
|
|
151
|
+
await service.save(invalidOrder);
|
|
152
|
+
expect.fail('should have thrown');
|
|
153
|
+
} catch (err) {
|
|
154
|
+
expect(err).toBeInstanceOf(ValidationError);
|
|
155
|
+
expect((err as ValidationError).field).toBe('items');
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Mocking
|
|
161
|
+
|
|
162
|
+
### Vitest (`vi`)
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
// Function mock
|
|
166
|
+
const mockFn = vi.fn().mockReturnValue(42);
|
|
167
|
+
|
|
168
|
+
// Module mock
|
|
169
|
+
vi.mock('./emailService', () => ({
|
|
170
|
+
sendEmail: vi.fn().mockResolvedValue(true),
|
|
171
|
+
}));
|
|
172
|
+
|
|
173
|
+
// Spy on object method
|
|
174
|
+
const spy = vi.spyOn(console, 'log');
|
|
175
|
+
|
|
176
|
+
// Timer mocking
|
|
177
|
+
vi.useFakeTimers();
|
|
178
|
+
vi.advanceTimersByTime(1000);
|
|
179
|
+
vi.useRealTimers();
|
|
180
|
+
|
|
181
|
+
// Mock reset
|
|
182
|
+
beforeEach(() => vi.clearAllMocks());
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Jest (`jest`)
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// Same API, replace `vi` with `jest`
|
|
189
|
+
const mockFn = jest.fn().mockReturnValue(42);
|
|
190
|
+
jest.mock('./emailService');
|
|
191
|
+
jest.spyOn(console, 'log');
|
|
192
|
+
jest.useFakeTimers();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Type-safe mocking
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Use MockedObject for full type safety
|
|
199
|
+
import type { MockedObject } from 'vitest';
|
|
200
|
+
|
|
201
|
+
let mockRepo: MockedObject<OrderRepository>;
|
|
202
|
+
|
|
203
|
+
// Or create typed mock factories
|
|
204
|
+
function createMockRepo(overrides?: Partial<OrderRepository>): MockedObject<OrderRepository> {
|
|
205
|
+
return {
|
|
206
|
+
save: vi.fn(),
|
|
207
|
+
findById: vi.fn(),
|
|
208
|
+
delete: vi.fn(),
|
|
209
|
+
...overrides,
|
|
210
|
+
} as MockedObject<OrderRepository>;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### What NOT to mock
|
|
215
|
+
|
|
216
|
+
- Value objects, DTOs, plain data structures
|
|
217
|
+
- Pure functions with no side effects
|
|
218
|
+
- The class/module under test itself
|
|
219
|
+
- Simple utility functions
|
|
220
|
+
|
|
221
|
+
Mock only at system boundaries: APIs, databases, file system, timers, randomness.
|
|
222
|
+
|
|
223
|
+
## File Conventions
|
|
224
|
+
|
|
225
|
+
- `*.test.ts` / `*.spec.ts` (co-located or in `__tests__/`)
|
|
226
|
+
- `vitest.config.ts` or `jest.config.ts` for configuration
|
|
227
|
+
- `vitest` or `jest` in `package.json` scripts
|
|
228
|
+
- Shared test utilities in `test/helpers/` or `__tests__/helpers/`
|
|
229
|
+
|
|
230
|
+
## Package Setup
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Vitest
|
|
234
|
+
npm install -D vitest @vitest/coverage-v8
|
|
235
|
+
|
|
236
|
+
# Jest with TypeScript
|
|
237
|
+
npm install -D jest ts-jest @types/jest
|
|
238
|
+
npx ts-jest config:init
|
|
239
|
+
|
|
240
|
+
# Optional: testing-library for DOM
|
|
241
|
+
npm install -D @testing-library/react @testing-library/jest-dom
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Authoritative Sources
|
|
245
|
+
|
|
246
|
+
- Vitest: https://vitest.dev
|
|
247
|
+
- Jest: https://jestjs.io
|
|
248
|
+
- Mocha: https://mochajs.org
|
|
249
|
+
- Kent Beck — Canon TDD: https://tidyfirst.substack.com/p/canon-tdd
|
|
250
|
+
- Martin Fowler — Mocks Aren't Stubs: https://martinfowler.com/articles/mocksArentStubs.html
|
|
251
|
+
|
|
252
|
+
## Reference Materials
|
|
253
|
+
|
|
254
|
+
- **[Anti-Patterns](reference/anti-patterns.md)** — Common testing mistakes and how to fix them
|
|
255
|
+
- **[TDD Workflow Patterns](reference/tdd-workflow-patterns.md)** — Red-Green-Refactor, Transformation Priority Premise, when to use TDD
|