Docs that run in your test suite

Mark a TypeScript code block in any .md file. Testdocs lifts it into a real vitest or jest test — no separate spec file, no boilerplate.

Doctests, the way you wished they worked

Lean defaults, no setup beyond the plugin, full control when you need it.

Zero boilerplate

describe, it, and expect are injected for you. Doctest bodies are pure assertions — no framework imports.

Multi-runner

One vite plugin works for vitest and vite-plus. A sibling jest transformer covers jest projects.

AST-aware imports

Multi-line and type-only imports inside a block are hoisted to module scope via ts-morph — no regex foot-guns.

Markdown stays markdown

The test marker lives in the fence info string, invisible to readers. Your docs site renders normal code blocks.

Per-block options

Use name="...", skip, or only inside the fence to override naming and focus or skip individual doctests.

Docs that cannot lie

If a snippet stops compiling or asserting, your CI breaks. Docs and code stay in sync by construction.

Write a doctest. Get a test.

Tag a fence with test and the block's body becomes the body of an it().

docs/sum.md
## Adds two numbers

```ts test
import { sum } from "./sum.ts"
expect(sum(1, 2)).toBe(3)
```
pnpm test
$ pnpm test

  docs/sum.md (1)
    Adds two numbers

 Test Files 1 passed (1)
      Tests 1 passed (1)

Stop writing docs that lie. Make them tests.

Install, mark a fence, run your usual test command. That's it.