Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/native-rn-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ name: Native engine — Vitest × RN matrix
# • React Native (rows). The engine runs REAL RN JS and mocks only the ~7-module
# native boundary, so it is sensitive to RN-version drift (the boundary has
# broken before on SectionList / RendererProxy changes between minors).
# Supported range (verified): RN 0.81–0.85. 0.80 and earlier fail host-component
# Supported range (verified): RN 0.81–0.86. 0.80 and earlier fail host-component
# detection and are below the floor; latest is canaried in compat-check.yml.
# Keep the newest row in sync with the lockfile RN version — the fidelity page
# derives its published claim from THIS list (scripts/fidelity-report.mjs), so a
# stale matrix publishes an understated range.
#
# • Vitest (columns). Vitest churn is what bit-rotted the predecessor plugin
# (vitest-community/vitest-react-native) — and the hot runtime rides Vitest's
Expand Down Expand Up @@ -44,7 +47,7 @@ jobs:
# Report every cell independently rather than stopping at the first failure.
fail-fast: false
matrix:
rn: ['0.81', '0.82', '0.83', '0.84', '0.85']
rn: ['0.81', '0.82', '0.83', '0.84', '0.85', '0.86']
vitest: ['locked', 'latest-supported']

steps:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A fast pure-JS **mock** engine is available as an opt-in for RN-free unit tests.
**📖 Documentation: [danfry1.github.io/vitest-native](https://danfry1.github.io/vitest-native/)**

> **Beta.** The reproducible guarantee is a CI-gated behavioral cross-check that runs the same
> assertions under the mock engine **and** real React Native across RN 0.81–0.85, failing the build
> assertions under the mock engine **and** real React Native across RN 0.81–0.86, failing the build
> on any divergence. We've also exercised the native engine against real apps in our own testing
> (react-native-paper, the obytes template, Rocket.Chat). Some APIs may still shift before 1.0.
>
Expand Down Expand Up @@ -101,7 +101,7 @@ describe('MyComponent', () => {
14 itself requires Node >= 22.13 — on Node 20, stay on RNTL 13.
- **`engine: 'native'`** (the default) needs `@react-native/babel-preset` + `@babel/core` (these
ship with React Native projects). The opt-in mock engine needs no Babel.
- **React Native** 0.81–0.85 validated in CI (native engine).
- **React Native** 0.81–0.86 validated in CI (native engine).
- Tested against RNTL 12, 13, and 14 in CI.

## Choosing an engine
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest-native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -847,7 +847,7 @@ Every release must pass:

- Linux on Node 20.19 and 22.13, plus macOS and Windows on Node 22.13.
- The mock, native iOS, native Android, hot-runtime, isolation, and 100-file soak suites.
- React Native 0.81–0.85 against both locked and newest-supported Vitest 4.
- React Native 0.81–0.86 against both locked and newest-supported Vitest 4.
- Packed bare RN 0.83/RNTL 12, Expo 56/RNTL 13, Vite 8 monorepo/RNTL 14, and RN 0.86 consumers.
- Mock-versus-real-RN behavioral cross-checks, example-app tests, typecheck, lint, formatting, and
package export analysis.
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest-native/scripts/fidelity-report.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ generated from the corpus itself, so the numbers below are exactly what ships.

## Summary

- **${summary.matching} / ${summary.total} probes** match between the mock engine and real React Native.
- **${summary.matching} / ${summary.total} probes** match between the mock engine and real React Native${report.reactNativeVersion ? ` (this page was generated against React Native ${report.reactNativeVersion})` : ""}.
- ${ciLine}
- Reproduce it yourself: \`bun run crosscheck\`.

Expand Down
2 changes: 1 addition & 1 deletion website/guide/comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ With `engine: 'native'` and isolation on, vitest-native isn't categorically fast

## The cross-check

The mock engine is a reimplementation of React Native, so it could in principle drift from real RN behavior. vitest-native guards against that with a **CI-gated behavioral cross-check**: a corpus of probes runs the same assertions against both the mock engine and real RN across React Native 0.81–0.85, and divergences fail CI. It's reproducible — clone the repo and run `bun run crosscheck`. The full corpus and its current pass count are published in the [Fidelity Report](/guide/fidelity).
The mock engine is a reimplementation of React Native, so it could in principle drift from real RN behavior. vitest-native guards against that with a **CI-gated behavioral cross-check**: a corpus of probes runs the same assertions against both the mock engine and real RN across React Native 0.81–0.86, and divergences fail CI. It's reproducible — clone the repo and run `bun run crosscheck`. The full corpus and its current pass count are published in the [Fidelity Report](/guide/fidelity).

This is the trust mechanism for the mock — and it has already caught and fixed real mock bugs (for example, an `Animated.Text` host-name mismatch that broke `queryByText`, and `Animated.Value`-in-style not resolving for `toHaveStyle`). The native engine doesn't need this — it *is* real RN.

Expand Down
2 changes: 1 addition & 1 deletion website/guide/engines.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Both engines share the same test API. You can mix them across suites in the same

## Keeping the mock honest

Because the mock is a reimplementation, it could drift from real RN behavior. A **CI-gated behavioral cross-check** runs the same assertions against both the mock and real RN across React Native 0.81–0.85, so divergences are caught before release. See [Comparison with Jest](/guide/comparison#the-cross-check) for how that trust mechanism works.
Because the mock is a reimplementation, it could drift from real RN behavior. A **CI-gated behavioral cross-check** runs the same assertions against both the mock and real RN across React Native 0.81–0.86, so divergences are caught before release. See [Comparison with Jest](/guide/comparison#the-cross-check) for how that trust mechanism works.

## Hot runtime (experimental)

Expand Down
4 changes: 2 additions & 2 deletions website/guide/fidelity.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ generated from the corpus itself, so the numbers below are exactly what ships.

## Summary

- **75 / 75 probes** match between the mock engine and real React Native.
- CI runs the same corpus across **React Native 0.81–0.85** on every commit.
- **75 / 75 probes** match between the mock engine and real React Native (this page was generated against React Native 0.86.0).
- CI runs the same corpus across **React Native 0.81–0.86** on every commit.
- Reproduce it yourself: `bun run crosscheck`.

The `native` engine needs no cross-check — it *is* real React Native.
Expand Down
20 changes: 17 additions & 3 deletions website/guide/helpers.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
# Test Helpers

vitest-native ships a small set of helpers for controlling device state inside a test — platform, dimensions, color scheme, and native modules. Import them from `vitest-native/helpers`.
vitest-native ships a small set of helpers for controlling device state inside a test — dimensions, color scheme, safe-area insets, and native modules. Import them from `vitest-native/helpers`.

```ts
import {
setPlatform,
setDimensions,
setColorScheme,
setInsets,
mockNativeModule,
resetAllMocks,
} from 'vitest-native/helpers'
```

All helpers work under both engines, with one exception: `setPlatform` is mock-engine-only (see below).

## `setPlatform(os)`

Switch the platform for a test. Affects `Platform.OS` and platform-specific behavior.
::: warning Mock engine only
Under the default `native` engine, `setPlatform()` throws: the platform is baked in when the real React Native module graph loads (`Platform.ios`/`Platform.android` and every `.ios.ts`/`.android.ts` file have already been selected). Set it in config instead — `reactNative({ platform: 'android' })` — or run both platforms as separate Vitest projects.
:::

Switch the platform for a test (mock engine). Affects `Platform.OS` and platform-specific behavior.

```ts
setPlatform('android')
Expand All @@ -36,6 +42,14 @@ Switch the color scheme reported by `Appearance` and `useColorScheme`.
setColorScheme('dark')
```

## `setInsets(insets)`

Override the safe-area insets reported by the auto-detected `react-native-safe-area-context` preset (`useSafeAreaInsets`, `SafeAreaProvider`). No-op when the preset isn't active.

```ts
setInsets({ top: 59, bottom: 34, left: 0, right: 0 })
```

## `mockNativeModule(name, impl)`

Provide a mock implementation for a native module.
Expand Down
4 changes: 2 additions & 2 deletions website/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ Migrating a large, deeply Jest-coupled suite *wholesale* is possible but **not t
- **RNTL compatible** — works with `@testing-library/react-native` automatically.
- **Third-party presets** — auto-detected mocks for Reanimated, Gesture Handler, Safe Area, Navigation, Screens, AsyncStorage, Device Info, MMKV, SVG, WebView, and Expo.
- **jest-compat layer** — `vitest-native/jest-compat` eases migrating existing Jest suites.
- **Test helpers** — `setPlatform`, `setDimensions`, `setColorScheme`, `mockNativeModule` for easy state control.
- **Test helpers** — `setDimensions`, `setColorScheme`, `setInsets`, `mockNativeModule` for easy state control under both engines (plus mock-only `setPlatform`).
- **TypeScript first** — full type safety across the entire API.

::: tip Beta
A CI-gated behavioral cross-check runs the same assertions under the mock engine and real React Native across React Native 0.81–0.85, failing the build on any divergence. Some APIs may still shift before 1.0.
A CI-gated behavioral cross-check runs the same assertions under the mock engine and real React Native across React Native 0.81–0.86, failing the build on any divergence. Some APIs may still shift before 1.0.
:::

Next: [Installation](/guide/install) → [Quick Start](/guide/quick-start).
2 changes: 1 addition & 1 deletion website/guide/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ That's it. The plugin auto-injects its setup file, configures `@testing-library/
| **Vitest** | 4.x |
| **Vite** | ^6.4.2, ^7.3.2, or ^8.0.5 |
| **React** | >= 18 |
| **React Native** | 0.81–0.85 validated in CI (native engine) |
| **React Native** | 0.81–0.86 validated in CI (native engine) |

The default **`engine: 'native'`** needs `@react-native/babel-preset` and `@babel/core` — these already ship with React Native projects. The plugin uses them to Flow-strip real React Native, the same toolchain RN already uses.

Expand Down
6 changes: 5 additions & 1 deletion website/guide/plugin-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Which engine to use. See [Choosing an Engine](/guide/engines) for the full break

## `platform`

`'ios'` (default) or `'android'`. Controls which platform-specific file extension wins during resolution (`.ios.ts` vs `.android.ts`) and the value of `Platform.OS`. Switch per-test at runtime with [`setPlatform`](/guide/helpers).
`'ios'` (default) or `'android'`. Controls which platform-specific file extension wins during resolution (`.ios.ts` vs `.android.ts`) and the value of `Platform.OS`. To test both platforms, run them as separate Vitest projects with one plugin instance each. (Under the mock engine only, [`setPlatform`](/guide/helpers#setplatform-os) can also switch per-test at runtime; the native engine fixes the platform when the module graph loads.)

## `diagnostics`

Expand All @@ -45,6 +45,10 @@ reactNative({

## `mocks`

::: warning Mock engine only
`mocks` merges overrides into the mock's export table, so it only applies with `engine: 'mock'` — the native engine runs the real `react-native` module and throws a configuration error if `mocks` is set. Use `vi.mock()` in a setup file or [`mockNativeModule`](/guide/helpers#mocknativemodule-name-impl) instead.
:::

Custom mock overrides, keyed by export name. Useful for the handful of [unstable/private RN exports](/api/coverage#not-covered) that aren't mocked:

```ts
Expand Down
12 changes: 7 additions & 5 deletions website/guide/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ it('handles a press', () => {

## Control device state

Use the [test helpers](/guide/helpers) to switch platform, dimensions, or color scheme inside a test:
Use the [test helpers](/guide/helpers) to control dimensions or color scheme inside a test — these work under both engines:

```tsx
import { setPlatform, setColorScheme, resetAllMocks } from 'vitest-native/helpers'
import { setDimensions, setColorScheme, resetAllMocks } from 'vitest-native/helpers'
import { afterEach } from 'vitest'

afterEach(() => resetAllMocks())

it('renders Android branch', () => {
setPlatform('android')
// … assertions for the Android path
it('renders the tablet layout', () => {
setDimensions({ width: 768, height: 1024 })
// … assertions for the tablet path
})

it('renders in dark mode', () => {
Expand All @@ -86,6 +86,8 @@ it('renders in dark mode', () => {
})
```

To test Android-specific behavior, set the platform in config — `reactNative({ platform: 'android' })` — or run both platforms as separate Vitest projects. Under the default `native` engine the platform is fixed when the module graph loads, so there is no per-test switch (`setPlatform()` is [mock-engine-only](/guide/helpers#setplatform-os)).

## Next steps

- [Choosing an Engine](/guide/engines) — when to use `native` vs `mock`.
Expand Down
2 changes: 1 addition & 1 deletion website/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ It is **not** primarily a speed play — choose it for the fidelity option and D

## How it's verified

The reproducible guarantee is a **CI-gated behavioral cross-check**: a corpus of probes runs the same assertions under the mock engine **and** real React Native across React Native 0.81–0.85, and any divergence fails the build. Anyone can run it (`bun run crosscheck`), and the full corpus with its current pass count is published in the [Fidelity Report](/guide/fidelity). On top of that, the full CI gate runs lint, typecheck, build, and the mock + native + hot suites across an OS × Node matrix.
The reproducible guarantee is a **CI-gated behavioral cross-check**: a corpus of probes runs the same assertions under the mock engine **and** real React Native across React Native 0.81–0.86, and any divergence fails the build. Anyone can run it (`bun run crosscheck`), and the full corpus with its current pass count is published in the [Fidelity Report](/guide/fidelity). On top of that, the full CI gate runs lint, typecheck, build, and the mock + native + hot suites across an OS × Node matrix.

We've also run real apps' own test suites under the native engine. **react-native-paper**'s suite passes **625 of 734 tests (~85%)** — no paper source changed, just an RNTL bump and the test config/setup. The remaining failures are tests coupled to Jest's RN-mock internals (e.g. `vi.spyOn(View.prototype, 'measure')`, deep `jest.mock('react-native/…')` of Appearance/Dimensions), not vitest-native bugs. The Expo-based **obytes template** runs **34 of 40 (~85%)** — a more deeply-coupled case that needed a few library mocks. Both are reproducible: see [**vitest-native-bakeoffs**](https://github.com/danfry1/vitest-native-bakeoffs). We've also migrated a Rocket.Chat suite in local testing.

Expand Down
2 changes: 1 addition & 1 deletion website/migration/from-vitest-react-native.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ npm rm vitest-react-native @vitejs/plugin-react
npm i -D vitest-native @react-native/babel-preset @babel/core
```

Keep your existing `vitest`, `vite`, `react`, `react-native`, and `@testing-library/react-native`. Requirements: **Vitest 4.x, Vite ^6.4.2, ^7.3.2, or ^8.0.5, the React version required by your RN release, React Native 0.81–0.85 (validated in CI), and RNTL 12–14.** RNTL 14 uses async rendering APIs and (per RNTL's own requirements) needs Node 22.13 or 24+.
Keep your existing `vitest`, `vite`, `react`, `react-native`, and `@testing-library/react-native`. Requirements: **Vitest 4.x, Vite ^6.4.2, ^7.3.2, or ^8.0.5, the React version required by your RN release, React Native 0.81–0.86 (validated in CI), and RNTL 12–14.** RNTL 14 uses async rendering APIs and (per RNTL's own requirements) needs Node 22.13 or 24+.

## 2. Update vitest.config

Expand Down
Loading