diff --git a/.github/workflows/native-rn-matrix.yml b/.github/workflows/native-rn-matrix.yml index d7d2925..655bdeb 100644 --- a/.github/workflows/native-rn-matrix.yml +++ b/.github/workflows/native-rn-matrix.yml @@ -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 @@ -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: diff --git a/README.md b/README.md index df770c3..105bd9e 100644 --- a/README.md +++ b/README.md @@ -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. > @@ -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 diff --git a/packages/vitest-native/README.md b/packages/vitest-native/README.md index 30b6ba5..d647a73 100644 --- a/packages/vitest-native/README.md +++ b/packages/vitest-native/README.md @@ -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. diff --git a/packages/vitest-native/scripts/fidelity-report.mjs b/packages/vitest-native/scripts/fidelity-report.mjs index 4fc0232..46562ed 100644 --- a/packages/vitest-native/scripts/fidelity-report.mjs +++ b/packages/vitest-native/scripts/fidelity-report.mjs @@ -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\`. diff --git a/website/guide/comparison.md b/website/guide/comparison.md index 0bb9e4e..ba8126a 100644 --- a/website/guide/comparison.md +++ b/website/guide/comparison.md @@ -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. diff --git a/website/guide/engines.md b/website/guide/engines.md index 0fa3e04..6d00904 100644 --- a/website/guide/engines.md +++ b/website/guide/engines.md @@ -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) diff --git a/website/guide/fidelity.md b/website/guide/fidelity.md index 1b4c60e..3014884 100644 --- a/website/guide/fidelity.md +++ b/website/guide/fidelity.md @@ -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. diff --git a/website/guide/helpers.md b/website/guide/helpers.md index ddd8b80..fbddb41 100644 --- a/website/guide/helpers.md +++ b/website/guide/helpers.md @@ -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') @@ -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. diff --git a/website/guide/index.md b/website/guide/index.md index 0b4220b..e02d4b6 100644 --- a/website/guide/index.md +++ b/website/guide/index.md @@ -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). diff --git a/website/guide/install.md b/website/guide/install.md index a21ca5d..a47082f 100644 --- a/website/guide/install.md +++ b/website/guide/install.md @@ -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. diff --git a/website/guide/plugin-options.md b/website/guide/plugin-options.md index 9e72a4a..9b79c0e 100644 --- a/website/guide/plugin-options.md +++ b/website/guide/plugin-options.md @@ -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` @@ -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 diff --git a/website/guide/quick-start.md b/website/guide/quick-start.md index 3651677..c14f748 100644 --- a/website/guide/quick-start.md +++ b/website/guide/quick-start.md @@ -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', () => { @@ -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`. diff --git a/website/index.md b/website/index.md index f4e4600..41da668 100644 --- a/website/index.md +++ b/website/index.md @@ -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. diff --git a/website/migration/from-vitest-react-native.md b/website/migration/from-vitest-react-native.md index 751de0c..369db41 100644 --- a/website/migration/from-vitest-react-native.md +++ b/website/migration/from-vitest-react-native.md @@ -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