# hmr-tester _(no submodules)_ ## Purpose `withHMR` round-trips a single Vite HMR edit through an injectable `HmrServerLike` adapter and asserts that the expected events fire in order. It is designed for tests that exercise HMR-driven UI logic without spinning up a real Vite dev server — the adapter can wrap an in-memory event bus, a Playwright page, or a real `ws` connection depending on the testing strategy. ## Features - Injectable server adapter (`send` + `onEvent`). - Ordered event matching — events fire in the order specified in `expectEvents`. - Configurable `timeoutMs` with `TestViteError(TEST_VITE.HMR_TIMEOUT)` on expiry. - Injectable `Clock` for deterministic duration accounting. - OTel `test_vite_hmr_round_trip_ms` histogram recorded on every resolution. ## Usage: Basic ```ts import { withHMR } from "@suaveplan/test-vite/hmr-tester"; const events = await withHMR({ server: viteServerAdapter, edit: { file: "src/widget.ts", content: "..." }, expectEvents: ["vite:beforeUpdate", "vite:afterUpdate"], timeoutMs: 5000, }); ``` ## Usage: Advanced Drive a synthetic server in tests: ```ts import { withHMR } from "@suaveplan/test-vite/hmr-tester"; let listener: ((event: string) => void) | undefined; const server = { send: () => setTimeout(() => listener?.("vite:afterUpdate"), 1), onEvent: (cb: (event: string) => void) => { listener = cb; return () => { listener = undefined; }; }, }; await withHMR({ server, edit: { file: "a.ts", content: "1" }, expectEvents: ["vite:afterUpdate"], timeoutMs: 100, clock: () => Date.now(), }); ``` ## API Reference - `withHMR(opts: WithHmrOptions): Promise`. - `WithHmrOptions` — `{ server, edit: { file, content }, expectEvents, timeoutMs?, clock? }`. - Throws `TestViteError` with `TEST_VITE.INVALID_OPTIONS` or `TEST_VITE.HMR_TIMEOUT`. ## Implementation Notes `withHMR` matches expected events in order — non-matching events are captured but do not advance the cursor. Once the final expected event fires the promise resolves with the complete capture array, including any non-matching events that arrived along the way. The timer is cleared and the `onEvent` disposer invoked before resolution so the caller does not have to wire teardown. Test consumers can swap the clock to record deterministic durations and exercise edge cases without real wall-clock delays.