docs(storybook): render MDX tables + add i18n & testing-strategy docs/skill

- fix: wire remark-gfm into addon-docs so GFM pipe tables in *.mdx render
  (previously raw text in cibg-gaps/layers/atomic-design docs)
- add src/docs/i18n.mdx (Foundations/Internationalization): the $localize
  locale seam + how to test languages without coupling to copy
- add src/docs/testing.mdx (Foundations/Testing strategy): per-layer spec
  matrix, house style, Storybook a11y gate, GREEN gate
- add .claude/skills/test-strategy skill

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
eho
2026-07-20 19:26:34 +02:00
co-authored by Claude Opus 4.8
parent ba32e3dd9f
commit 0edfbba2a9
6 changed files with 1400 additions and 1 deletions
+67
View File
@@ -0,0 +1,67 @@
---
name: test-strategy
description: Place tests the house way — Vitest specs co-located by layer (pure domain, no TestBed; parse* trust boundaries; thin UI via Storybook a11y). Use whenever adding a spec or deciding what to test.
---
# Test strategy (test where it's pure)
Push logic down to where it's pure, test it there directly, keep the layers above thin.
No `TestBed` for domain. Never assert on user-facing copy.
## Rules
- **`domain/` + any pure logic → required spec.** Reducers, combinators, `visibleSteps`,
policies, parsers. Import the function and call it — no Angular, no `TestBed`.
- **Value-object parser → happy path + normalisation + each rejection.** Assert on the
`Result` discriminant (`.ok`) and the parsed value, **not** the error message.
- **`infrastructure/` `parse*` (trust boundary) → required spec.** Accept a valid DTO;
reject `null`, `{}`, and malformed shapes. Name it `describe('… (trust boundary)')`.
- **`application/` stores/commands → spec** the pure reduce + optimistic
begin→confirm/rollback + the command `Result`.
- **`ui/` → Storybook story, not a component test.** Axe runs on every story; add a `play`
only for wiring axe can't see.
- **Never assert on `$localize` copy.** It changes per locale/edit — assert on the
`Result`, the value object, or the message id.
## Skeleton
Co-locate `*.spec.ts` next to the unit, in the same layer folder:
```
<context>/domain/<thing>.spec.ts # pure — no TestBed
<context>/domain/value-objects/<vo>.spec.ts # parser: ok + normalise + each reject
<context>/infrastructure/<x>.adapter.spec.ts# parse* trust boundary
<context>/application/<store|command>.spec.ts
```
Minimal parser spec:
```ts
import { describe, it, expect } from 'vitest';
import { parseThing } from './thing';
describe('parseThing', () => {
it('accepts + normalises', () => {
const r = parseThing(' raw ');
expect(r.ok).toBe(true);
if (r.ok) expect(r.value).toBe('RAW');
});
it('rejects malformed', () => {
expect(parseThing('').ok).toBe(false); // asserts the tag, not the copy
});
});
```
## Worked examples
- `src/app/registratie/domain/value-objects/postcode.spec.ts` — parser style.
- `src/app/registratie/infrastructure/brp.adapter.spec.ts` — trust boundary (`null`/`{}`).
- `src/app/registratie/domain/registratie-wizard.machine.spec.ts` — pure reducer.
## Verify
```bash
npm test # Vitest (ng test — no vitest.config)
npm run test-storybook # axe over every story (UI a11y gate)
cd backend && dotnet test # backend rule + endpoint + golden tests
```