add-test
Skill50 starsChanged 20 days agoApache-2.0View on GitHub
Add vitest tests and a human-readable cahier de recette (docs/recette.md) to a T3 project, with a pre-push hook and CI that refuse to publish a feature without its verification.
- Reads credentials
- Installs packages
18 other projects use a version of this.
Adopted by
Most recently taken up by
- microsoft/veronatheir own version, first seen Nov 8, 2026
- TidalPaladin/skillstheir own version, first seen Nov 8, 2026
- flavien-ia/hypervibe-craftword for word, first seen Oct 25, 2026
- WhatIfWeDigDeeper/claude-code-courseratheir own version, first seen Oct 20, 2026
- nativewind/react-native-csstheir own version, first seen Oct 9, 2026
- osmandapp/webtheir own version, first seen Oct 9, 2026
Used with
Projects with it also use review-pr, triage and 1 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
DockerSkillreview-pr
Review one or more incoming Docker documentation pull requests as a maintainer. Independently validate technical claims, assess editorial fit and information architecture, choose a verdict, and draft exact inline or PR-wide feedback behind a confirmation gate. Use for requests such as "review PR 123", "is this PR correct?", "does this information belong here?", "validate this PR", or "help review backlog PRs". Do not use to maintain or fix a PR you own; use maintain-pr for that.
Also in 3 projects that use this
Google CloudSkilltriage
Gather context on a change or bug and plan the implementation.
Also in 3 projects that use this
SentrySkillreview
Fixture review skill.
Also in 3 projects that use this
Other versions18 projects use a version of this.
Word for word · 1
- flavien-ia/hypervibe-craft★ 50100% same
Edited · 0
None found.
Wrote their own version · 17
- gitkraken/vscode-gitlens★ 10ksame name
- nativewind/nativewind★ 8.1ksame name
- microsoft/verona★ 3.7ksame name
- The-OpenROAD-Project/OpenROAD★ 3.3ksame name
- OpenAEV-Platform/openaev★ 1.8ksame name
- DexForce/EmbodiChain★ 240same name
- fpindej/netrock★ 234same name
- nativewind/react-native-css★ 167same name
- osmandapp/web★ 63same name
- KhaledSaeed18/node-express-boilerplate★ 36same name
7 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository89 other files it gives its agents.
- accessibilitySkill
- add-2faSkill
- add-agent-dashboardSkill
- add-aiSkill
- add-analyticsSkill
- add-authSkill
- add-automationSkill
- add-backup-dbSkill
- add-collabSkill
- add-cronSkill
- add-dark-modeSkill
- add-dbSkill
- add-domainSkill
- add-emailSkill
- add-github-authSkill
- add-google-authSkill
- add-i18nSkill
- _add-keyringSkill
- add-mapSkill
- add-notification-centerSkill
- add-push-notificationSkill
- add-pwaSkill
- add-roleSkill
- add-routineSkill
- add-storageSkill
- add-stripeSkill
- bootstrapSkill
- _check-depsSkill
- cleanSkill
- _collect-secretSkill
- _convert-to-turborepoSkill
- _create-agentSkill
- _create-cloudflare-workerSkill
- _create-contact-pageSkill
- _create-render-workerSkill
- _create-routineSkill
- _create-workflowSkill
- delete-projectSkill
- _detect-project-rootSkill
- _dns-brevoSkill
- _dns-cloudflareSkill
- _dns-gandiSkill
- _dns-godaddy-manualSkill
- _dns-hostingerSkill
- _dns-infomaniak-manualSkill
- _dns-ionos-manualSkill
- _dns-lwsSkill
- _dns-namecheapSkill
- _dns-ovhSkill
- _dns-porkbunSkill
- _dns-resendSkill
- _dns-squarespace-manualSkill
- eco-auditSkill
- _ensure-aiSkill
- _ensure-vaultSkill
- _generate-secretSkill
- geoSkill
- _get-secretSkill
- gscSkill
- _hash-passwordSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
6 rules. 0 are common, 5 are distinctive, and 1 is very specific to this project.
0
Common
Most of the 16 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 12
Use plain, non-technical business language.
- Distinctive: line 13
When generating user-facing content for the scaffolded project (UI labels, emails, copy), write it in the user's language too.
- Distinctive: line 49
Wait for the answer.
- Distinctive: line 101
Read each template before copying: they carry the explanations the user will read later (in the files themselves), so keep them intact …
- Very specific: line 126
Write <WEB_DIR>/docs/recette.md from the recette.md template: keep its header (it teaches the word "recette" and explains the columns to a …
- Distinctive: line 182
Test files are TypeScript like the rest of the project: they go through the same checks.
“Common” is measured: kept by at least 6 in 10 of the 16 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Add Test - tests automatisés + cahier de recette
Communication
- Detect the user's language from the conversation (the user's own messages, anywhere in the session - not just this invocation: a bare slash command like
/bootstrapcarries no language signal by itself). If nothing in the conversation gives a signal, fall back to the OS locale (node -e "console.log(Intl.DateTimeFormat().resolvedOptions().locale)") before defaulting to English. ALWAYS reply in that language for every user-facing message: questions, progress, confirmations, summaries, errors - including any example text quoted in this skill, which is illustrative and must be translated, never sent verbatim. - Use plain, non-technical business language. Never expose internal script names (*.mjs) or jargon; describe actions in human terms.
- When generating user-facing content for the scaffolded project (UI labels, emails, copy), write it in the user's language too.
- Show progress as a short natural-language checklist (in-progress and done states).
Gives the project two things that answer the same question, "does it work, and will it still work after the next change?", for two audiences:
docs/recette.md, the cahier de recette: one line per feature, readable by someone who has never opened the code. What the feature does, how it is checked, what is expected, who validates.- Automated tests (vitest): one per tRPC procedure at least, each tagged with the line of the cahier it covers (
// recette: R-07).
And it wires the guard that keeps both alive: a pre-push hook and a GitHub Action that run the tests and refuse to publish while a procedure has no test or a page is missing from the cahier. From then on, "add a feature" means "add its line and its test", and the project's CLAUDE.md says so.
What this skill does NOT do: browser journey tests (Playwright). Pages are covered by a manual scenario in the cahier, written for a human. Journey tests may come in a later version; the cahier is designed so they can slot in without changing anything.
Step 0 - Preflight: is the project already equipped?
First of all, invoke _check-deps tests:
result=$(node "${CLAUDE_SKILL_DIR}/../../scripts/check-deps.mjs" tests)
tests_ok=$(echo "$result" | node -e "console.log(JSON.parse(require('fs').readFileSync(0,'utf8')).tests.ok)")If tests_ok = true -> maintenance mode
The project already has vitest, a cahier de recette and the checker. Do NOT reinstall anything. Show a menu:
✅ Les tests et le cahier de recette sont déjà en place
Que veux-tu faire ?
- Compléter la recette et les tests (après une nouvelle fonctionnalité, ou parce que la publication a été refusée)
- Lancer la recette et me montrer le résultat
- Réinstaller le garde-fou (hook avant publication, action GitHub) s'il a été supprimé
- Autre chose - dis-moi
Wait for the answer.
| Choice | Action |
| 1 | Jump to Step 5 (inventory), then Steps 6 to 8. Only ADD what is missing: never rewrite existing lines or tests. |
| 2 | Run pnpm test then node scripts/check-recette.mjs (in WEB_DIR), and explain the result in plain words: what is verified, what is missing, what it means for the next publication. Then stop. |
| 3 | Re-run Step 3 for .hooks/pre-push and .github/workflows/tests.yml only, then Step 9. Then stop. |
| 4 | Ask for details. Do not launch the full flow by default. |
If tests_ok = false -> installation
Continue to Step 1.
Step 1 - Detect the project structure
Invoke _detect-project-root. You get PROJECT_NAME, WEB_DIR, IS_MONOREPO, IS_NEXTJS. If IS_NEXTJS=no, stop: this skill is for a Next.js project (typically initialized by /bootstrap).
Everything below runs inside WEB_DIR (. for a single app, apps/web in a monorepo). The hook and the GitHub Action are the only files written at the repository root.
Step 2 - Install vitest
cd <WEB_DIR>
pnpm add -D vitest
node -e "
const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));
p.scripts??={};
p.scripts.test??='vitest run';
p.scripts['test:watch']??='vitest';
p.scripts.recette??='node scripts/check-recette.mjs';
fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n');"Idempotent: existing scripts are kept.
Step 3 - Copy the templates
From ${CLAUDE_SKILL_DIR}/../../templates/tests/:
| Template | Destination | Note |
vitest.config.ts | <WEB_DIR>/vitest.config.ts | Do not overwrite an existing one: merge the test.env and setupFiles entries instead. |
setup.ts | <WEB_DIR>/tests/setup.ts | Placeholder env values, so server modules load without real services. |
caller.ts | <WEB_DIR>/tests/helpers/caller.ts | The tRPC caller used by every test. It puts the project's db (from ~/server/db) in the context, so a test's vi.mock("~/server/db") is what the procedures receive. Project without a database (no src/server/db/index.ts): remove the db import and the db property from the helper. |
healthcheck.test.ts | <WEB_DIR>/tests/healthcheck.test.ts | Only if src/server/api/routers/healthcheck.ts exists (created by /bootstrap). Otherwise skip it, and R-01 in the cahier will be the first real feature instead. |
check-recette.mjs | <WEB_DIR>/scripts/check-recette.mjs | The checker. Versioned in the project: it runs on every machine and in CI. |
pre-push | <repo root>/.hooks/pre-push | Replace {{WEB_DIR}} with the detected value (. or apps/web). Then git update-index --add --chmod=+x .hooks/pre-push after staging, so the executable bit travels with the repository. |
tests.yml | <repo root>/.github/workflows/tests.yml | Replace {{WEB_DIR}}. |
recette.md | <WEB_DIR>/docs/recette.md | Written in Step 6, not copied as is: it is the template of the cahier. |
Read each template before copying: they carry the explanations the user will read later (in the files themselves), so keep them intact apart from the placeholders.
Step 4 - First run
cd <WEB_DIR> && pnpm testThe healthcheck test must pass. If it fails at import time (a server module refuses to load), the cause is almost always a variable that ~/env demands: add it to tests/setup.ts with a placeholder value of the right shape. Never put a real key there. If it fails because the project has no healthcheck router, delete tests/healthcheck.test.ts and continue: Step 7 writes the real tests.
Two import-time failures are already handled by the templates, so you recognize them rather than fix them: Cannot find module '.../next/server' imported from next-auth/lib/env.js (vitest cannot resolve next-auth's own imports in a pnpm layout: vitest.config.ts inlines next-auth), and This module cannot be imported from a Client Component module (the server-only marker: tests/setup.ts neutralizes it, and simulates next/cache for the same reason). If either still appears, the project's vitest.config.ts or tests/setup.ts was not replaced by the template: compare and merge.
Step 5 - Inventory: what must be covered
cd <WEB_DIR> && node scripts/check-recette.mjs --inventaireYou get, as JSON: the routers with their procedures, the pages with their routes, the existing tests, and (if a cahier exists) what is still uncovered. Also read:
cahier-des-charges.mdat the project root, if/specproduced one: it holds the business rules the cahier must state (a rule the spec writes is a line of the recette).CLAUDE.md: the conventions and the stack, to describe features with the right words.
Step 6 - Write the cahier de recette
Write <WEB_DIR>/docs/recette.md from the recette.md template: keep its header (it teaches the word "recette" and explains the columns to a human), keep the R-01 line only if the healthcheck test exists, and generate the lines:
- One line per tRPC procedure (
Vérifiée par: test). Describe the feature in business terms, not the procedure name: "Envoi du formulaire de contact", notcontact.send. Put the procedure name in the "Comment on vérifie" column, so the reader can find the test. - One line per page (
Vérifiée par: manuel), with a scenario a person can follow in a browser and an expected result they can observe. The route (/contact) MUST appear in the line: that is how the checker matches a page to the cahier. - One line per business rule from the spec that is not obviously a procedure or a page (a pricing rule, a deadline, a quota). Mark it
testif it lives in code you can call,manuelotherwise.
Identifiers R-01, R-02... in order, never reused. Write the lines in the user's language. Statut: ok for lines you will cover with a passing test in Step 7, à faire for manual scenarios nobody has run yet.
The "Procès-verbal" section is the enterprise half of this document, and it is not for everyone. Keep it when the project is delivered to someone (a client, an employer, an IT department) - the template already says it can be removed otherwise. Drop the section entirely when the context makes it plainly useless: a personal project, a portfolio, a tool the user built for themselves alone. A signature table nobody will ever sign makes the whole document look like paperwork, and the rest of the cahier is what earns its keep.
In maintenance mode (Step 0, choice 1): append the missing lines with the next free identifiers, do not renumber.
Step 7 - Write the tests
One file per router: <WEB_DIR>/tests/<router>.test.ts. Rules:
- Meaningful assertions. Each test checks a real output or a real refusal. Never
expect(true).toBe(true), never a test that only checks "it did not throw". - The literal
<router>.<procedure>call through the caller (await caller().contact.send({...})): the checker recognizes coverage by that exact text. - Tag every test with the cahier line:
// recette: R-07above theit, or in the file header for a whole file (// recette: R-07, R-08). - Never a real database, never the network. A procedure that reads or writes
ctx.dbgets a simulated module:
Shape the fake after what the procedure actually calls (read the router first). For Drizzle query builders (vi.mock("~/server/db", () => ({ db: { query: { posts: { findMany: async () => [{ id: 1, title: "Bonjour" }] } } }, }));db.select().from()...), return a chainable fake whose terminal call resolves to the fixture. Same for an email module (~/lib/mail,~/server/mail), a payment client, a storage client: simulate the module the router imports, then assert on what it received. vi.mockis hoisted. vitest moves everyvi.mock(...)to the top of the file, before theconstdeclarations: a factory that references a top-level variable throwsCannot access 'x' before initialization. Any fake the test needs to inspect or mutate later (avi.fn()you assert on, an env object you toggle) MUST be created invi.hoisted:
Giveconst { sendMail, envSimule } = vi.hoisted(() => ({ sendMail: vi.fn<(envoi: { to: { email: string }[]; subject: string }) => Promise<void>>(async () => undefined), envSimule: { BREVO_API_KEY: "cle-de-test" as string | undefined }, })); vi.mock("~/lib/mail", () => ({ sendMail })); vi.mock("~/env", () => ({ env: envSimule }));vi.fnits signature (vi.fn<(arg: T) => R>()): without it,mock.calls[0][0]is typed as never andpnpm tsc --noEmitfails on the test.- Rate-limited procedures (
rateLimitedProcedure, keyed by IP): give each test its ownx-forwarded-forheader through the caller, or the fifth call of the file trips the limit meant for the sixth. - Patterns to cover, per procedure:
- a public query: the happy path, with the exact shape of the result;
- a protected procedure called anonymously:
await expect(caller().x.y()).rejects.toMatchObject({ code: "UNAUTHORIZED" }); - an input validated by zod: one invalid input rejected with
BAD_REQUEST, one valid input accepted; - a mutation: the fake records what was written, and the test checks it (a
vi.fn()oninsert/update).
- A large project (more than 30 procedures): write and run router by router, so a failing fake is found while it is still small.
Step 8 - Green, then complete
cd <WEB_DIR> && pnpm test && node scripts/check-recette.mjsBoth must be clean. The checker lists what is still missing, in plain language: a line marked test without a tagged test, a procedure no test exercises, a page absent from the cahier. Fix, re-run. Then:
pnpm tsc --noEmit && pnpm lintTest files are TypeScript like the rest of the project: they go through the same checks.
Step 9 - The hook on this machine, and the opt-in of this checkout
The repository's .hooks/pre-push only runs if two things hold. The machine's global git hooks must hand over to it (Hypervibe sets core.hooksPath globally for the secret scan, which makes .git/hooks inert). And THIS checkout must have opted in: .hooks/ files are versioned, so they arrive with any clone, and git does not version its hooks precisely so that cloning can never execute code. The opt-in is a local git config value (hypervibe.hooks = true), which a clone never carries. Both in one command, run from the repository:
node "${CLAUDE_SKILL_DIR}/../../scripts/ensure-hooks-chain.mjs" --trust| Output | Meaning |
OK / INSTALLED / REFRESHED (followed by TRUSTED) | The global chain hook is in place (refreshed if an older, ungated block was found), and this checkout is trusted: its .hooks/pre-push now runs before every push. |
LOCAL TRUSTED | No global hooks path (the secret scan was never installed): the chain was written into this repository's .git/hooks/pre-push instead, and the checkout is trusted. Suggest /start later, which installs the global setup. |
FOREIGN | A pre-push that is not Hypervibe's already exists. Do not overwrite it. Tell the user where it is (~/.git-hooks/pre-push) and what to add to it (the chain block of scripts/ensure-hooks-chain.mjs). |
Other people who clone the project do not get the guard by cloning, and that is the point: on their machine the hook announces itself at the first commit or push and stays inert until they opt in themselves, once, in their clone (git config hypervibe.hooks true). The GitHub Action covers the case where they have not.
Step 10 - Update CLAUDE.md
Invoke _update-claude-md with:
commands:- \pnpm test` - lance les tests (vitest)`- \pnpm recette` - vérifie que chaque fonctionnalité a sa vérification dans `docs/recette.md``
conventions:- **Tests et recette** : \docs/recette.md` est le cahier de recette, une ligne par fonctionnalité (identifiant `R-xx`). Toute fonctionnalité nouvelle ou modifiée ajoute ou relit sa ligne ET son test (`tests/<routeur>.test.ts`, tagué `// recette: R-xx`, appelant `<routeur>.<procédure>` via `tests/helpers/caller.ts`, base simulée par `vi.mock("~/server/db")`, jamais de vraie base). Toute nouvelle page ajoute sa ligne (scénario manuel, avec sa route). `pnpm test` avant tout commit : le hook pre-push et l'action GitHub refusent de publier tant qu'une procédure n'a pas de test ou qu'une page manque au cahier. Ne jamais contourner avec `--no-verify` : compléter la recette à la place.`
Translate these lines into the user's language if the project's CLAUDE.md is not in French.
Step 11 - Commit (no push)
Stage nominatively (the guardrail refuses git add -A), then commit:
git add docs/recette.md tests scripts/check-recette.mjs vitest.config.ts package.json pnpm-lock.yaml CLAUDE.md .hooks/pre-push .github/workflows/tests.yml
git update-index --chmod=+x .hooks/pre-push
git commit -m "feat(tests): cahier de recette et tests automatisés"(Adapt the paths with <WEB_DIR>/ in a monorepo.) Do NOT push: the user publishes when they want to, and that push will be the first one the hook checks.
Step 12 - Summary
Tell the user, in plain words:
- What now exists: the cahier (
docs/recette.md, N lines, so many by test, so many manual), the tests (N files, all green), the guard (before every publication on this machine, and on GitHub for everyone). - How it works from now on: adding a feature adds its line and its test; that is written in
CLAUDE.md, so Claude Code does it on its own. If a publication is refused, the message says exactly what is missing, and "complète la recette et les tests manquants" fixes it. - What the cahier is for: explain the word in one sentence (it comes from recevoir: the moment the person who ordered the software accepts it), then say what it does for them. If someone validates the application, it is the document to hand over: the manual lines are their checklist, the "Procès-verbal" table is where they sign off. If nobody does, it is their own memory of what the app must do, six months from now, and it is already written the day someone asks.
- What is not covered yet: browser journeys (a page is checked by a person following the scenario), and anything the fakes do not reproduce (a real database constraint, a third-party service). Say it plainly: a green suite means "the logic we wrote behaves as written", not "nothing can go wrong".
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

