LangENKO

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.

since v0.24
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

  1. Chokidar watcher — recursively watches app/, src/, packages/; ignores node_modules/, .git/, .mandu/, dist/.
  2. 200ms debounce — batches bursts of changes (IDE save-all) into a single re-run.
  3. 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).
  4. Re-run — invoke bun test with 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/, and packages/ 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

🤖 Agent Prompt — Watch mode
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.

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

AI hint

`--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.

Invariants
  • 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
Guard scope
testing