vitest-mobile@0.2.0
Minor Changes
-
fd223b8: Restructure plugin options and rewrite tye device-side runtime.
Breaking: plugin options
nativePlugin(...)options are now nested intoharness/device/metrogroups:// Before nativePlugin({ platform: 'ios', headless: true, bundle: true, metro: customizerFn }); // After nativePlugin({ platform: 'ios', device: { headless: true }, metro: { bundle: true, customize: customizerFn } });
Removed
promptForNewDeviceandskipIfUnavailable. Missing environments now throw instead of soft-skipping.Runtime rewrite
setup.ts,ControlBridge,TestRegistry,TestRunnerService,run.ts,state.tscollapsed into a singleHarnessRuntimeroot that owns connection, state, registry diff, and RPC forwarding.- Generated test-registry module replaced by
require.contextintest-context.ts(backed by newinline-app-root-plugin). - Explorer UI walks Vitest's native task tree directly; old
TestTreeNodeparallel tree deleted. ReactNativeRunnersimplified to(config, runtime)— delegatesonCollected/onTaskUpdateto the runtime.- Task state (
collectedFiles,taskState) lives on the runtime instance; entries are append-only across reruns.
Other
- New
vitest-compat-plugin.tsrewrites Vitest dist for Hermes/Metro. Hermes-safenode:*stubs added. - Pool-side:
PauseController, Metro log tailing, bundle server, and pool message types extracted into dedicated modules.
-
1434ccd: Per-project device ownership, interactive device picker, and a big CLI UX polish pass.
Project-scoped devices (iOS + Android).
- Each project now owns a specific simulator / AVD, stored in
~/.cache/vitest-mobile/devices.jsonkeyed by project path. Running Expo, Android Studio, or another vitest-mobile project on the same machine no longer collides with your tests. vitest-mobile bootstrapalways shows an interactive picker: your existing simulators/AVDs plus a "Create new dedicated device" option. Current mapping is pre-selected so hitting Enter keeps your choice.--device <name>skips the picker (for CI and scripts). Non-TTY bootstrap auto-creates the project-scoped device.- On Android, "Create new" is annotated as unavailable and refused unless the Android cmdline-tools (
sdkmanager+avdmanager) are installed — with a pointer to install them or pick an existing AVD instead. reset-devicenow respects whether vitest-mobile created the device: deletes + clears the mapping if we created it, only clears the mapping if the user picked their own device.- iOS: existing
VitestMobile-<hash>simulators are auto-registered into the mapping on first run (no re-prompt on upgrade). - Concurrent runs on the same project get a per-instance secondary simulator (iOS) / emulator instance (Android).
CLI UX polish.
- Consistent
--platformflag (breaking). Every command takes--platform <ios|android>(previously some were positional, some--platform). The oldvitest-mobile build iosform prints a clear migration hint and exits non-zero. Most commands prompt (TTY) or error (non-TTY) when--platformis omitted;trim-cache/clean-devices/bundledefault to both platforms;cache-keystill requires an explicit platform. - Spinners with live step messages for
build,install,bootstrap,bundle,boot-device— instead of a wall of xcodebuild / gradle / pod-install output. Spinner now animates during long builds (the underlying spawn is async; previously a syncexecSyncblocked the event loop). - Child-process output is tee'd to
~/.cache/vitest-mobile/logs/<timestamp>-<command>-<platform>.log. On failure the log path is printed; nothing is silently swallowed. - Ctrl+C now works during long builds (same async-spawn fix — SIGINT previously went to the blocked child instead of Node).
- Unknown commands exit 1 with a help dump (previously exited 0 silently).
Metro + native modules config.
- New
nativePlugin({ metro })customizer and exportedMetroConfigCustomizertype. Layers on top of the auto-generated harness-anchored base Metro config (runs before internal test transforms, so the vitest shim and test-registry stay authoritative). Composes across multiple plugin instances. build,install,bootstrap, andbundleCLIs readnativeModulesfrom the vitest config automatically;--native-modulesoverrides when passed.
Setup diagnostics.
- Simulator creation failures now report the real cause — unaccepted Xcode license,
xcode-selectpointing at Command Line Tools, missing iOS runtime. Preflight checks beforexcodebuildcatch SDK/runtime mismatch upfront.
Build system.
- Xcode 26.4 compat: bundled
fmtpod pinned to C++17 so RCT-Folly compiles under Apple Clang 21. Harness build format bumped to v5 — existing cached binaries rebuild once.
Fixes.
promptConfirmno longer leaks a stdin resume that keptbootstrapalive after accepting the prompt.
- Each project now owns a specific simulator / AVD, stored in
Patch Changes
-
01ce6fe: Collapse the pool worker lifecycle to fire once per user-initiated run instead of once per file, by setting
test.isolate = falsein the plugin's config hook.Why. With
isolate: true(the previous default), Vitest's scheduler created onePoolRunnerper test file, meaningworker.start()andworker.stop()— and the 60s / 90s handshake timeouts guarding them — fired N times per run. The React Native harness shares a single JS VM across files anyway, so the per-file isolation was a fiction maintained by singleton idempotency flags. Underisolate: false+maxWorkers: 1, Vitest bundles every file into one task withcontext.files = [all], and the handshake timers fire exactly once per user-initiated run (initial or HMR-driven rerun). Timer scope now matches reality.Changes.
test.isolate = falseis applied by the plugin, guarded so user-level overrides win.canReuse: () => trueadded to the pool worker.- Device-side
handleRunrefactored into a per-file loop so explorer file-start/file-done UI events still fire per file; test execution insidestartTestsis unchanged. - Removed dead code: the fallback
__rerunreplay path (Vitest ^4'srerunFilesis guaranteed), the_lastRunMessagesmap,_sessionCount,countTestsInSpecs, and the triple-keying of file identifiers. Reporter shrunk to a pass-through for__native_run_start/__native_run_endplus therun-modeteardown()await.
No user-visible behavior change. Custom Vitest pools, test configs that explicitly set
test.isolate, and the device-side test execution path are all unaffected.