Skip to content

vitest-mobile@0.2.0

Choose a tag to compare

@github-actions github-actions released this 28 Apr 18:52
383cab9

Minor Changes

  • fd223b8: Restructure plugin options and rewrite tye device-side runtime.

    Breaking: plugin options

    nativePlugin(...) options are now nested into harness / device / metro groups:

    // Before
    nativePlugin({ platform: 'ios', headless: true, bundle: true, metro: customizerFn });
    
    // After
    nativePlugin({ platform: 'ios', device: { headless: true }, metro: { bundle: true, customize: customizerFn } });

    Removed promptForNewDevice and skipIfUnavailable. Missing environments now throw instead of soft-skipping.

    Runtime rewrite

    • setup.ts, ControlBridge, TestRegistry, TestRunnerService, run.ts, state.ts collapsed into a single HarnessRuntime root that owns connection, state, registry diff, and RPC forwarding.
    • Generated test-registry module replaced by require.context in test-context.ts (backed by new inline-app-root-plugin).
    • Explorer UI walks Vitest's native task tree directly; old TestTreeNode parallel tree deleted.
    • ReactNativeRunner simplified to (config, runtime) — delegates onCollected/onTaskUpdate to the runtime.
    • Task state (collectedFiles, taskState) lives on the runtime instance; entries are append-only across reruns.

    Other

    • New vitest-compat-plugin.ts rewrites Vitest dist for Hermes/Metro. Hermes-safe node:* 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.json keyed by project path. Running Expo, Android Studio, or another vitest-mobile project on the same machine no longer collides with your tests.
    • vitest-mobile bootstrap always 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-device now 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 --platform flag (breaking). Every command takes --platform <ios|android> (previously some were positional, some --platform). The old vitest-mobile build ios form prints a clear migration hint and exits non-zero. Most commands prompt (TTY) or error (non-TTY) when --platform is omitted; trim-cache / clean-devices / bundle default to both platforms; cache-key still 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 sync execSync blocked 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 exported MetroConfigCustomizer type. 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, and bundle CLIs read nativeModules from the vitest config automatically; --native-modules overrides when passed.

    Setup diagnostics.

    • Simulator creation failures now report the real cause — unaccepted Xcode license, xcode-select pointing at Command Line Tools, missing iOS runtime. Preflight checks before xcodebuild catch SDK/runtime mismatch upfront.

    Build system.

    • Xcode 26.4 compat: bundled fmt pod pinned to C++17 so RCT-Folly compiles under Apple Clang 21. Harness build format bumped to v5 — existing cached binaries rebuild once.

    Fixes.

    • promptConfirm no longer leaks a stdin resume that kept bootstrap alive after accepting the prompt.

Patch Changes

  • 01ce6fe: Collapse the pool worker lifecycle to fire once per user-initiated run instead of once per file, by setting test.isolate = false in the plugin's config hook.

    Why. With isolate: true (the previous default), Vitest's scheduler created one PoolRunner per test file, meaning worker.start() and worker.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. Under isolate: false + maxWorkers: 1, Vitest bundles every file into one task with context.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 = false is applied by the plugin, guarded so user-level overrides win.
    • canReuse: () => true added to the pool worker.
    • Device-side handleRun refactored into a per-file loop so explorer file-start/file-done UI events still fire per file; test execution inside startTests is unchanged.
    • Removed dead code: the fallback __rerun replay path (Vitest ^4's rerunFiles is guaranteed), the _lastRunMessages map, _sessionCount, countTestsInSpecs, and the triple-keying of file identifiers. Reporter shrunk to a pass-through for __native_run_start / __native_run_end plus the run-mode teardown() 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.