Watch mode
`mandu test --watch` keeps a persistent chokidar watcher over `app/`, `src/`, and `packages/` and re-runs only the tests affected by each change — a ~500ms red-green-refactor cycle.
On this page
Watch mode
--watch keeps a persistent chokidar watcher over app/, src/, and
packages/ and re-runs only the tests affected by each change. This
makes the red-green-refactor cycle a ~500ms round-trip even on large
projects.
Quick start
# Watch everything, re-run affected tests on change
mandu test --watch
# Preview what would be watched (exits 0)
mandu test --watch --dry-run
# Watch + coverage — re-runs emit fresh LCOV per iteration
mandu test --watch --coverage
Stop with Ctrl+C.
How it works
- Chokidar watcher — recursively watches
app/,src/,packages/; ignoresnode_modules/,.git/,.mandu/,dist/. - 200ms debounce — batches bursts of changes (IDE save-all) into a single re-run.
- Affected-file mapping — for each change, pick tests that:
- Directly match — the changed file is a
*.test.ts/*.test.tsx. - Import the changed file — tests whose source references the basename (or extension-stripped stem).
- Directly match — the changed file is a
- Re-run — invoke
bun testwith the narrowed file list. The full config (timeouts, snapshots, coverage) is preserved.
Concurrency is guarded: if a re-run is already in flight when new changes arrive, we coalesce them into a single follow-up batch instead of piling up subprocesses.
Flag compatibility
| Flag | Behavior under --watch |
|---|---|
--filter |
Forwarded to every re-run invocation. |
--coverage |
Coverage emitted per re-run. Merged LCOV is NOT automatic — run --coverage without --watch for a final merged report. |
--bail |
Stops the current re-run on first failure. |
--update-snapshots |
Forwarded — useful when reviewing a new UI. |
--e2e |
Incompatible — use mandu test:watch for E2E watch (ATE pipeline). |
--dry-run |
Prints the plan and exits 0. |
Dry-run example
$ mandu test --watch --dry-run
mandu test --watch plan
debounce: 200ms
targets: unit, integration
test files: 126
watch dirs: 3
- /repo/app
- /repo/src
- /repo/packages
Affected-file heuristic
We use a cheap grep-equivalent rather than the full ATE dep-graph. For a sub-second watch loop, substring import scanning hits the sweet spot:
- Speed: O(changed_files × test_files) string-contains — under 100ms even at 500+ changes.
- Precision: matches both
.ts-suffixed and extension-stripped imports. - Recall: may over-include tests with coincidental basename matches — acceptable, because false positives cost a test run, not correctness.
For exact transitive impact, use mandu test:watch which wraps
the full ATE computeImpact() → dep-graph pipeline at the cost of
~500ms startup.
Transitive vs substring — when to pick which
| Scenario | Pick |
|---|---|
| Most of the day. Save file → see tests rerun fast. | mandu test --watch |
| Final pre-commit gate — you care that every transitively-impacted test runs. | mandu test:watch |
You have generic basenames (types.ts, utils.ts) triggering false positives. |
mandu test:watch |
| You want E2E watch (regenerate Playwright specs on change). | mandu test:watch |
Regression tests to be aware of
- No watch dirs — if
app/,src/, andpackages/are all missing, the watcher refuses to start and emits CLI_E066. - Concurrent changes — 500 saves in 200ms produce a single re-run.
- Unreadable test files — a temporarily inaccessible test file is dropped from the affected list without crashing the loop.
Exit codes
| Code | Meaning |
|---|---|
0 |
--dry-run planning OR graceful Ctrl+C |
2 |
Watcher infra error (fs.watch EACCES etc.) |
4 |
No watch dirs (CLI_E066) |
Troubleshooting
No reaction to my save — check the file path is inside one of the
watched dirs (app/, src/, packages/). Changes to docs/ or
scripts/ are intentionally ignored.
Too many re-runs — increase the debounce window by editing
packages/cli/src/commands/test.ts::DEBOUNCE (currently 200ms). A
future release will expose this as a flag.
Tests I expected to run didn't re-run — the basename heuristic
matches on substring. If your source file name is very common (e.g.
types.ts), consider renaming it, or use mandu test:watch which
uses the full dep-graph.
🤖 Agent Prompt
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/testing/watch to my project.
Summary of the page:
`--watch` uses a 200ms debounced chokidar watcher. Change → compute affected tests (direct match OR substring import scan) → `bun test <narrowed>`. Concurrency guarded: no pile-ups. For transitive-impact precision use `mandu test:watch` instead.
Required invariants — must hold after your changes:
- Watch dirs: `app/`, `src/`, `packages/` — ignores `node_modules/`, `.git/`, `.mandu/`, `dist/`
- Debounce window: 200ms — bursts of saves coalesce into a single re-run
- Concurrency guarded — re-runs already in flight absorb new changes into a follow-up batch
- Affected-test heuristic: direct file match OR substring import scan (NOT full dep-graph)
- `--e2e` is incompatible with `--watch` — use `mandu test:watch` for E2E watch
Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.
Related
- Unit tests —
--watchpairs best with pure unit tests (no integration spin-up per iteration). - CLI Reference —
mandu test:watch— the ATE-powered alternative with exact transitive impact.
For Agents
{
"schema": "mandu.testing.watch/v0.24",
"command": "mandu test --watch",
"debounce_ms": 200,
"watch_dirs": ["app/", "src/", "packages/"],
"ignore_dirs": ["node_modules/", ".git/", ".mandu/", "dist/"],
"affected_heuristic": "direct match OR substring import scan",
"e2e_alternative": "mandu test:watch",
"exit_codes": {
"0": "graceful exit or --dry-run",
"2": "fs.watch error",
"4": "no watch dirs (CLI_E066)"
},
"rules": [
"`--watch` is incompatible with `--e2e` — use `mandu test:watch` for E2E",
"Re-runs coalesce under concurrency — no pile-ups",
"Merged LCOV does NOT auto-generate in watch mode"
]
}For Agents
`--watch` uses a 200ms debounced chokidar watcher. Change → compute affected tests (direct match OR substring import scan) → `bun test <narrowed>`. Concurrency guarded: no pile-ups. For transitive-impact precision use `mandu test:watch` instead.
- Watch dirs: `app/`, `src/`, `packages/` — ignores `node_modules/`, `.git/`, `.mandu/`, `dist/`
- Debounce window: 200ms — bursts of saves coalesce into a single re-run
- Concurrency guarded — re-runs already in flight absorb new changes into a follow-up batch
- Affected-test heuristic: direct file match OR substring import scan (NOT full dep-graph)
- `--e2e` is incompatible with `--watch` — use `mandu test:watch` for E2E watch