Skip to content

Releases

v0.15.1

Choose a tag to compare

@github-actions github-actions released this 15 Nov 10:56
2430a92

Patch Changes

  • 51d5899: import * as X from 'lib' now carries every part of a component library built by esbuild or tsup with JSX preserved. The library's index re-exports a module the engine compiles, and Node read that module's names from the published file, whose JSX it could not read: the namespace held only default, so X.Root was undefined and rendering it failed with "Element type is invalid". The engine now lists the names a module re-exports from files it compiles. No action needed.

  • 9def928: A snapshot of an Expo component that passes a ref as a prop, like expo-image's Image, is stable again under the native engine. expo-modules-core's createSnapshotFriendlyRef() is React's createRef, so a snapshot printed the view and React's fiber tree behind each ref: thousands of lines with render timings that changed on every run. It now returns the ref jest-expo serves, which prints as "[React.ref]" and still holds its view. Run vitest -u once to rewrite snapshots written with the old output.

  • fbcebbc: Gesture Handler 3's Touchable, RectButton and BorderlessButton can be pressed from Testing Library under the native engine. userEvent.press and fireEvent.press now call onPress once, as a tap on a device does, and a disabled button takes the press without calling it. Before, userEvent found nothing to press and fireEvent missed RectButton. The engine now mocks the native button host beside the package's own jestSetup.js, following its testOnly_onPress convention. Delete a vi.mock that replaced Touchable with Pressable for this. A Touchable is found by role only with accessible, as on iOS; pass it in your wrapper.

    Thanks @believer for reporting this (#351).

  • 21b99dd: hangTimeout no longer stops a run because the machine slept. On macOS, Node's timers count time asleep, so after a sleep mid-run every running test looked past its budget and the run was stopped on wake. Time the machine spends asleep now extends each limit instead, while a test stuck in synchronous code is still caught once its awake time runs out. No action needed.

  • ff4dba7: Under the hot runtime on Node 22, a React Native library whose ES index is an .mjs file beside CommonJS files now loads. No action needed. Builds from esbuild and tsup publish this layout. vitest-native compiles the .mjs index, and its require() of a CommonJS sibling was served the ES wrapper meant for imports, which Node 22 failed to link (ERR_VM_MODULE_LINK_FAILURE: "is from a module not been linked"). Such a require() now returns the sibling's module.exports, the same instance the test file's own import of it reaches, and a new one in each file.

  • 7569bcb: With diagnostics: true, the hot runtime's per-file reset lines now print. No action needed. Lines such as hot reset: dropped N modules were written while Vitest was starting the next file, which drops console output, so they never appeared. Hot-runtime diagnostics from the worker now go to stderr.

  • 2f36bc5: Under the hot runtime, a library that defines a non-configurable global no longer fails every later file in the worker. No action needed. The reset before each file could not delete such a property (the openai SDK defines one for its provider definitions), so it threw hotRuntime state restoration failed before every file after the first. A property no reset can delete or replace now stays for the worker's life, as Node's own process-wide state does, and diagnostics: true names it. Every other global a file adds is still deleted before the next file.

  • 628e964: jest.mock through a renamed jest from @jest/globals now applies. jestMockTransform() recognised only calls on an identifier named jest, so import { jest as mockJest } from '@jest/globals' followed by mockJest.mock(…) was neither hoisted nor registered, and the module stayed real. A jest imported, required or reached as <namespace>.jest under any name is now handled like the global. A call through a further name (const j = mockJest; j.mock(…)) fails with JEST_BINDING_NOT_FOLLOWED instead of being skipped. No action needed.

  • 63d6efa: vitest-native migrate keeps a suite that ran the real Reanimated on the real library. A Jest setup that calls Reanimated's setUpTests() and mocks Reanimated nowhere ran the real library, but migrate called that setup file covered and suggested deleting it, which moved the suite onto the preset's mock, where getAnimatedStyle returns {}. Migrate now writes presets: { reanimated: false } for such a suite, when react-native-reanimated is a declared dependency, and keeps its setup file; "delete" stays for suites that also mock Reanimated. The presets guide shows the two modes side by side (#834).

    Add presets: { reanimated: false } where your Jest setup ran setUpTests() without mocking Reanimated, and keep that setup file.

  • 14a0b13: vitest-native migrate lists the library mocks in your own Jest setup file that the presets cover. A jest.mock('react-native-reanimated', …), a Safe Area mock or a require('react-native-gesture-handler/jestSetup') in a setupFiles or setupFilesAfterEnv file now appears under "Covered by presets — delete", as the migration guide says. A mock that keeps part of the library is listed for review instead. The report also stops calling jest-react-native, which only React Native's (jest-)?react-native pattern produces, not installed, and lists a preset-shadowed transformIgnorePatterns entry under "Mapped automatically" (#795, #796, #797).

    Delete the setup-file lines the report now lists, once the suite passes without them.

  • bca755b: Reanimated's interpolateColor works under the mock engine. It threw PRESET_UNAVAILABLE there with reanimated 4.x, because its file imports React Native, which the mock engine does not transform. When interpolateColor is first called, the preset now compiles reanimated's own file with the project's @babel/core and @babel/plugin-transform-modules-commonjs, which a React Native app has through its Babel preset. It returns what reanimated returns: RGB, HSV and LAB, with colour strings or numbers. A project without that Babel keeps the error, which now names the packages to install. No action needed.

  • 1ad8a8d: The navigation preset now has the React Navigation 7 API it lacked, so tests calling it no longer fail with "is not a function".

    • Container refs gain resetRoot, setParams, setOptions, isFocused, getId, getParent, removeListener and emit; they and useNavigation() gain preload, replaceParams and navigateDeprecated, as mock functions.
    • StackActions.popTo and CommonActions.preload, replaceParams and navigateDeprecated return React Navigation's action objects. StackRouter, TabRouter, DrawerRouter, NavigationProvider, createScreenFactory, createStandardNavigationFactories, NativeStackView and createNativeStackScreen are exported.
    • Existing exports behave as before. No action needed.
  • 17ab16a: A package's .js file with JSX no longer stops the run with Vite's "Failed to parse source for import analysis" error under the native engine. A package that declares no react-native dependency or peer isn't detected, so Vite serves it uncompiled, and Vite skips JSX in .js files. vitest-native now compiles JSX in such a file when Vite's parser rejects it; every file that parses is served unchanged. No action needed. A package that also ships Metro-only source, such as react-native-calendars, still needs reactNative({ transform: ['<package>'] }), and the error now says so.

  • daac3a3: hotRuntime.freshRuntime now works with vitest-native's presets, so it no longer declines when one that uses React is active. No action needed for vitest-native's presets. Each preset builds its mock with the React react-native uses, the test file's own, so hooks and contexts work in every file. A preset module's factory now receives { React } (PresetFactoryContext). Change a custom preset whose factory makes components, contexts or hooks to use it: factory: ({ React }) => …. Under the native engine, a project that resolves a second copy of React now fails with PRESET_REACT_MISMATCH.

  • 0b18493: vitest-native migrate and doctor no longer say components render the same without React Compiler. The compiler is still not needed for tests to run, but it applies to the files projectBabel compiles with Babel. There a memoized child skips re-rendering, so a test that reads a value React does not track during render can see an earlier value. Other files run without it, so a test that counts renders can see more than under babel-jest (#755).

    Set projectBabel: 'always' where a suite depends on React Compiler running on every file, as under babel-jest.

  • 705f7ae: A Reanimated derived value from a render React discards no longer keeps running, and an updater that writes a value it reads no longer recurses. useDerivedValue started following its inputs during render, so a render React discarded (a Suspense throw, an error boundary) kept its updater running on every later write. It now follows them once the component commits, as Reanimated starts its mapper in an effect. An updater that wrote a value it reads overflowed the stack; the write now waits for the next change to one of its inputs. No action needed.

  • ae8bfea: A cancelled run no longer waits for a stopped background build before it exits. No action needed. On Windows, a cold run cancelled with --bail or Ctrl-C could take several more seconds to exit on a busy machine. The background process that precompiles React Native got its input on stdin, and a stop before it had read that input left ...

Read more

v0.15.0

Choose a tag to compare

@github-actions github-actions released this 14 Nov 13:07
515d0b5

Upgrading from 0.14

  • Node 22.22.3+, 24.12+ or 26+. An older Node stops at startup with UNSUPPORTED_NODE.
  • Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER.
  • React 19, React Native Testing Library 13 or 14, and React Native 0.86+ (Expo SDK 57+) for the
    native engine.
    An older React or React Native stops at startup with UNSUPPORTED_PEER.
  • Snapshots print in Jest's format. Run vitest -u once to re-record existing snapshots,
    then review the diff.
  • jestMockTransform() applies Jest's manual mocks of installed packages (__mocks__/<pkg>),
    as Jest does. Pass jestMockTransform({ rootMocks: false }) to keep the previous behaviour.
  • Run npx vitest-native doctor to check all of these. Each entry below has the details.

Minor Changes

  • 48fc91d: vitest-native compare --hot shows whether hotRuntime: 'auto' changes any test's result in your suite. It runs your Vitest config per file and hot, three times each (one warm-up and two timed runs), plus one hot run with shuffled files and one at --maxWorkers=2. It matches every test by file, name and occurrence, compares failures by kind, and checks the engine's banner, so a declined 'auto' gives no verdict. It lists each test whose result differs. With no differences it prints each mode's median and range, and calls hot faster or slower only when each mode has at least two timed runs and their ranges don't overlap. --runs <n> changes the run count; 2 or fewer give no speed verdict, and --json records each run's role. See the CLI guide. No action needed.

  • 98c62f4: The hot runtime can run React, its renderers, scheduler and RNTL fresh in every file (experimental). Set hotRuntime: { freshRuntime: true } so useId values, React's once-per-process warnings, an act() scope an earlier file left open, the scheduler's timers and RNTL's fake-timer reinstall no longer carry from one file to the next. It costs about 5 ms per file at four workers, and about 17 ms at one. It needs esmGeneration, which is on by default.

  • e4473ae: A test stuck in synchronous code no longer hangs a hot-runtime run forever: the run fails and names the file and test. Vitest's testTimeout can't interrupt a test that never yields, such as a loop that never ends, so such a run used to wait with nothing on screen. The new hangTimeout option fails a run when nothing has made progress for 60 s (or twice the longest test or hook timeout) and every running test is past its own timeout, retries included. Every module a file loads through Vite counts as progress, so a slow cold start is not taken for a hang. The report gives each file's running test and when it started, or how long ago a worker took the file, and when the last module loaded. It's on by default under the hot runtime and off otherwise. Add reactNative({ hangTimeout: <ms> }) to turn it on everywhere, or hangTimeout: false to turn it off.

  • 1cb74f0: Behaviour change: jestMockTransform() applies Jest's manual mocks of node_modules packages. To keep the previous behaviour, pass jestMockTransform({ rootMocks: false }). A file such as __mocks__/moti.ts in a __mocks__ folder under Jest's roots now replaces that package with no jest.mock() call, as under Jest, for imports and require() alike, __mocks__/react-native.js included. Before, the real package ran. A setup file's or test file's own jest.mock() or jest.unmock() still wins.

    • migrate lists each such file and carries over Jest's roots and modulePathIgnorePatterns.
    • A package a preset covers keeps its preset; the run warns that its manual mock is not applied.
  • 41959da: Under jest-compat, View.prototype has the native methods, as React Native's Jest preset gives it. jest.spyOn(View.prototype, 'measure') now reaches every View ref. It used to fail because React Native's View has no prototype. Under the native engine, each native view name now has one host class, so every View ref shares the methods a spy replaces.

    No action needed. Text and ScrollView get no prototype.

  • 23275f8: jest-compat supports jest.replaceProperty(). jest.replaceProperty(Platform, 'OS', 'android') replaces the value in place, as Jest does. It used to throw. The returned handle has replaceValue() and restore(). jest.restoreAllMocks(), the restoreMocks setting and the end of the test file also restore it; vi.restoreAllMocks() does not. It throws Jest's errors for a getter, a function or a missing property.

    No action needed.

  • ece53b3: Files that need a plugin from your project's Babel config now compile with that config, as Metro and babel-jest compile them. Legacy decorators (WatermelonDB models), macros, react-native-dotenv, the worklets plugin, custom plugins and Flow used to fail or behave differently. Remove any babel() entry you added only to run them; set projectBabel: 'never' for the previous behaviour.

    • Only a file that uses such a plugin compiles with Babel; other files keep Oxc.
    • migrate and doctor list these plugins as applied, and doctor calls a babel() entry that repeats one redundant.
    • It needs @babel/core in the project. Without it, migrate, doctor and the mock engine's startup say so.
  • e517388: Behaviour change: snapshots of React Native trees print in Jest's format. Run vitest -u once to re-record existing vitest-native snapshots, then review the diff. vitest-native no longer registers its own serializer. Jest-written snapshots share the format, but ones inside a describe, or of components Jest's preset stubs, still need one re-record (Snapshots).

    • No host prints ref. A host ref, such as a Text ref under the native engine or any host ref under the mock engine, is now an instance with measure, focus and the other native methods, where it used to be the test renderer's node or null.
    • vitest-native/serializer keeps the old format for one release; it is deprecated.
  • a107e3a: New vitest-native compare runs your suite under your own Jest and under Vitest and lists every test whose result differs. Use it while both runners are in place: npx vitest-native compare names each test that passes under Jest but not under Vitest, grouped by its first error, and exits 0 only when every one of them passes and vitest run exits 0. Neither run writes snapshots or changes project files, and --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See the CLI reference.

  • 4271252: Under the native engine, react-native-gesture-handler and @shopify/flash-list run as themselves, with the jestSetup.js each ships before the project's setup files, as a Jest suite lists them. No action needed; to keep a preset, set presets: { gestureHandler: true } or { flashList: true }.

    • Gesture Handler 3's hook API, builder methods and test helpers work, and a Pressable press fires onPress.
    • A FlashList renders a window of rows, as under Jest, where the preset rendered every row.
    • react-native-gesture-handler/Swipeable and the other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • 4271252: Under the native engine, a React Native library's own Jest setup and mocks get a jest with Jest's semantics. No action needed; a suite that imports react-native-gesture-handler/jestSetup or react-native-safe-area-context/jest/mock gets what Jest gave it.

    • Their jest.mock reaches every require of the module, and jest.requireActual and @jest/globals work.
    • Implementations a library installs while building its mocks survive vi.resetAllMocks() and mockReset: true.
    • jest.requireActual of a package a preset shadows returns the package, as under Jest.
  • ebdda39: Breaking: vitest-native now requires Node 22.22.3+, 24.12+ or 26+, for both engines. An unsupported Node stops at startup with UNSUPPORTED_NODE, naming the release to install; vitest-native doctor reports the same.

    Update Node within your major, or move from Node 20, 23 or 25 to a supported line, including CI images and .nvmrc or engines pins. Remove VITEST_NATIVE_LOADER_THREAD from scripts and CI; it no longer has an effect.

    • Under the native engine, a runtime without module.registerHooks (Bun) gets UNSUPPORTED_NODE too.
    • The support policy is in docs/versioning.md.
  • dae3119: Native engine: a file that Vite and Node both load, with both copies in use, is now reported by name, SOURCE_EVALUATED_TWICE, instead of silently existing twice. No action needed. To fail on it, set reactNative({ ownership: 'error' }).

    • The report names the file, how each side loaded it, and the fix. Exempt a deliberate case with ownership: { allow: [{ path, reason }] }.
    • require() of a CommonJS project file a test already imported now returns that same module.exports.
    • The warning, its end-of-run summary and vitest-native doctor link the error reference.
  • 5cad255: Native engine: a new opt-in flag makes Vite and Node resolve a package to the same file, so one package is one module. Add experimental: { ownershipModel: 'resolver' } to try it. Nothing changes without it.

    • Any package with separate import and require builds, or a react-native field, loads one file on both sides; a JSX or Flow react-native field is compiled, as Metro does.
    • RegExp aliases and tsconfig paths also apply to Node's require, including jest.requireActual. NODE_PATH applies to require only, as in Node.
    • Not covered: an alias with a...
Read more

v0.15.0-rc.5

v0.15.0-rc.5 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 11 Nov 23:18
e4f01db

Upgrading from 0.14

  • Node 22.22.3+, 24.12+ or 26+. An older Node stops at startup with UNSUPPORTED_NODE.
  • Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER.
  • React 19, React Native Testing Library 13 or 14, and React Native 0.86+ (Expo SDK 57+) for the
    native engine.
    An older React or React Native stops at startup with UNSUPPORTED_PEER.
  • Snapshots print in Jest's format. Run vitest -u once to re-record existing snapshots,
    then review the diff.
  • jestMockTransform() applies Jest's manual mocks of installed packages (__mocks__/<pkg>),
    as Jest does. Pass jestMockTransform({ rootMocks: false }) to keep the previous behaviour.
  • Run npx vitest-native doctor to check all of these. Each entry below has the details.

Minor Changes

  • 48fc91d: vitest-native compare --hot shows whether hotRuntime: 'auto' changes any test's result in your suite. It runs your Vitest config per file and hot, three times each (one warm-up and two timed runs), plus one hot run with shuffled files and one at --maxWorkers=2. It matches every test by file, name and occurrence, compares failures by kind, and checks the engine's banner, so a declined 'auto' gives no verdict. It lists each test whose result differs. With no differences it prints each mode's median and range, and calls hot faster or slower only when each mode has at least two timed runs and their ranges don't overlap. --runs <n> changes the run count; 2 or fewer give no speed verdict, and --json records each run's role. See the CLI guide. No action needed.

  • 98c62f4: The hot runtime can run React, its renderers, scheduler and RNTL fresh in every file (experimental). Set hotRuntime: { freshRuntime: true } so useId values, React's once-per-process warnings, an act() scope an earlier file left open, the scheduler's timers and RNTL's fake-timer reinstall no longer carry from one file to the next. It costs about 5 ms per file at four workers, and about 17 ms at one. It needs esmGeneration, which is on by default.

  • e4473ae: A test stuck in synchronous code no longer hangs a hot-runtime run forever: the run fails and names the file and test. Vitest's testTimeout can't interrupt a test that never yields, such as a loop that never ends, so such a run used to wait with nothing on screen. The new hangTimeout option fails a run when nothing has made progress for 60 s (or twice the longest test or hook timeout) and every running test is past its own timeout, retries included. Every module a file loads through Vite counts as progress, so a slow cold start is not taken for a hang. The report gives each file's running test and when it started, or how long ago a worker took the file, and when the last module loaded. It's on by default under the hot runtime and off otherwise. Add reactNative({ hangTimeout: <ms> }) to turn it on everywhere, or hangTimeout: false to turn it off.

  • 1cb74f0: Behaviour change: jestMockTransform() applies Jest's manual mocks of node_modules packages. To keep the previous behaviour, pass jestMockTransform({ rootMocks: false }). A file such as __mocks__/moti.ts in a __mocks__ folder under Jest's roots now replaces that package with no jest.mock() call, as under Jest, for imports and require() alike, __mocks__/react-native.js included. Before, the real package ran. A setup file's or test file's own jest.mock() or jest.unmock() still wins.

    • migrate lists each such file and carries over Jest's roots and modulePathIgnorePatterns.
    • A package a preset covers keeps its preset; the run warns that its manual mock is not applied.
  • 41959da: Under jest-compat, View.prototype has the native methods, as React Native's Jest preset gives it. jest.spyOn(View.prototype, 'measure') now reaches every View ref. It used to fail because React Native's View has no prototype. Under the native engine, each native view name now has one host class, so every View ref shares the methods a spy replaces.

    No action needed. Text and ScrollView get no prototype.

  • 23275f8: jest-compat supports jest.replaceProperty(). jest.replaceProperty(Platform, 'OS', 'android') replaces the value in place, as Jest does. It used to throw. The returned handle has replaceValue() and restore(). jest.restoreAllMocks(), the restoreMocks setting and the end of the test file also restore it; vi.restoreAllMocks() does not. It throws Jest's errors for a getter, a function or a missing property.

    No action needed.

  • ece53b3: Files that need a plugin from your project's Babel config now compile with that config, as Metro and babel-jest compile them. Legacy decorators (WatermelonDB models), macros, react-native-dotenv, the worklets plugin, custom plugins and Flow used to fail or behave differently. Remove any babel() entry you added only to run them; set projectBabel: 'never' for the previous behaviour.

    • Only a file that uses such a plugin compiles with Babel; other files keep Oxc.
    • migrate and doctor list these plugins as applied, and doctor calls a babel() entry that repeats one redundant.
    • It needs @babel/core in the project. Without it, migrate, doctor and the mock engine's startup say so.
  • e517388: Behaviour change: snapshots of React Native trees print in Jest's format. Run vitest -u once to re-record existing vitest-native snapshots, then review the diff. vitest-native no longer registers its own serializer. Jest-written snapshots share the format, but ones inside a describe, or of components Jest's preset stubs, still need one re-record (Snapshots).

    • No host prints ref. A host ref, such as a Text ref under the native engine or any host ref under the mock engine, is now an instance with measure, focus and the other native methods, where it used to be the test renderer's node or null.
    • vitest-native/serializer keeps the old format for one release; it is deprecated.
  • a107e3a: New vitest-native compare runs your suite under your own Jest and under Vitest and lists every test whose result differs. Use it while both runners are in place: npx vitest-native compare names each test that passes under Jest but not under Vitest, grouped by its first error, and exits 0 only when every one of them passes and vitest run exits 0. Neither run writes snapshots or changes project files, and --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See the CLI reference.

  • 4271252: Under the native engine, react-native-gesture-handler and @shopify/flash-list run as themselves, with the jestSetup.js each ships before the project's setup files, as a Jest suite lists them. No action needed; to keep a preset, set presets: { gestureHandler: true } or { flashList: true }.

    • Gesture Handler 3's hook API, builder methods and test helpers work, and a Pressable press fires onPress.
    • A FlashList renders a window of rows, as under Jest, where the preset rendered every row.
    • react-native-gesture-handler/Swipeable and the other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • 4271252: Under the native engine, a React Native library's own Jest setup and mocks get a jest with Jest's semantics. No action needed; a suite that imports react-native-gesture-handler/jestSetup or react-native-safe-area-context/jest/mock gets what Jest gave it.

    • Their jest.mock reaches every require of the module, and jest.requireActual and @jest/globals work.
    • Implementations a library installs while building its mocks survive vi.resetAllMocks() and mockReset: true.
    • jest.requireActual of a package a preset shadows returns the package, as under Jest.
  • ebdda39: Breaking: vitest-native now requires Node 22.22.3+, 24.12+ or 26+, for both engines. An unsupported Node stops at startup with UNSUPPORTED_NODE, naming the release to install; vitest-native doctor reports the same.

    Update Node within your major, or move from Node 20, 23 or 25 to a supported line, including CI images and .nvmrc or engines pins. Remove VITEST_NATIVE_LOADER_THREAD from scripts and CI; it no longer has an effect.

    • Under the native engine, a runtime without module.registerHooks (Bun) gets UNSUPPORTED_NODE too.
    • The support policy is in docs/versioning.md.
  • dae3119: Native engine: a file that Vite and Node both load, with both copies in use, is now reported by name, SOURCE_EVALUATED_TWICE, instead of silently existing twice. No action needed. To fail on it, set reactNative({ ownership: 'error' }).

    • The report names the file, how each side loaded it, and the fix. Exempt a deliberate case with ownership: { allow: [{ path, reason }] }.
    • require() of a CommonJS project file a test already imported now returns that same module.exports.
    • The warning, its end-of-run summary and vitest-native doctor link the error reference.
  • 5cad255: Native engine: a new opt-in flag makes Vite and Node resolve a package to the same file, so one package is one module. Add experimental: { ownershipModel: 'resolver' } to try it. Nothing changes without it.

    • Any package with separate import and require builds, or a react-native field, loads one file on both sides; a JSX or Flow react-native field is compiled, as Metro does.
    • RegExp aliases and tsconfig paths also apply to Node's require, including jest.requireActual. NODE_PATH applies to require only, as in Node.
    • Not covered: an alias with a...
Read more

v0.15.0-rc.4

v0.15.0-rc.4 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 10 Nov 13:19
3bfe689

Upgrading from 0.14

  • Node 22.22.3+, 24.12+ or 26+. An older Node stops at startup with UNSUPPORTED_NODE.
  • Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER.
  • React 19, React Native Testing Library 13 or 14, and React Native 0.86+ (Expo SDK 57+) for the
    native engine.
    An older React or React Native stops at startup with UNSUPPORTED_PEER.
  • Snapshots print in Jest's format. Run vitest -u once to re-record existing snapshots,
    then review the diff.
  • jestMockTransform() applies Jest's manual mocks of installed packages (__mocks__/<pkg>),
    as Jest does. Pass jestMockTransform({ rootMocks: false }) to keep the previous behaviour.
  • Run npx vitest-native doctor to check all of these. Each entry below has the details.

Minor Changes

  • 48fc91d: vitest-native compare --hot shows whether hotRuntime: 'auto' changes any test's result in your suite. It runs your Vitest config per file and hot, three times each (one warm-up and two timed runs), plus one hot run with shuffled files and one at --maxWorkers=2. It matches every test by file, name and occurrence, compares failures by kind, and checks the engine's banner, so a declined 'auto' gives no verdict. It lists each test whose result differs. With no differences it prints each mode's median and range, and calls hot faster or slower only when each mode has at least two timed runs and their ranges don't overlap. --runs <n> changes the run count; 2 or fewer give no speed verdict, and --json records each run's role. See the CLI guide. No action needed.

  • 98c62f4: The hot runtime can run React, its renderers, scheduler and RNTL fresh in every file (experimental). Set hotRuntime: { freshRuntime: true } so useId values, React's once-per-process warnings, an act() scope an earlier file left open, the scheduler's timers and RNTL's fake-timer reinstall no longer carry from one file to the next. It costs about 5 ms per file at four workers, and about 17 ms at one. It needs esmGeneration, which is on by default.

  • e4473ae: A test stuck in synchronous code no longer hangs a hot-runtime run forever: the run fails and names the file and test. Vitest's testTimeout can't interrupt a test that never yields, such as a loop that never ends, so such a run used to wait with nothing on screen. The new hangTimeout option fails a run when nothing has made progress for 60 s (or twice the longest test or hook timeout) and every running test is past its own timeout, retries included. Every module a file loads through Vite counts as progress, so a slow cold start is not taken for a hang. The report gives each file's running test and when it started, or how long ago a worker took the file, and when the last module loaded. It's on by default under the hot runtime and off otherwise. Add reactNative({ hangTimeout: <ms> }) to turn it on everywhere, or hangTimeout: false to turn it off.

  • 1cb74f0: Behaviour change: jestMockTransform() applies Jest's manual mocks of node_modules packages. To keep the previous behaviour, pass jestMockTransform({ rootMocks: false }). A file such as __mocks__/moti.ts in a __mocks__ folder under Jest's roots now replaces that package with no jest.mock() call, as under Jest, for imports and require() alike, __mocks__/react-native.js included. Before, the real package ran. A setup file's or test file's own jest.mock() or jest.unmock() still wins.

    • migrate lists each such file and carries over Jest's roots and modulePathIgnorePatterns.
    • A package a preset covers keeps its preset; the run warns that its manual mock is not applied.
  • 41959da: Under jest-compat, View.prototype has the native methods, as React Native's Jest preset gives it. jest.spyOn(View.prototype, 'measure') now reaches every View ref. It used to fail because React Native's View has no prototype. Under the native engine, each native view name now has one host class, so every View ref shares the methods a spy replaces.

    No action needed. Text and ScrollView get no prototype.

  • 23275f8: jest-compat supports jest.replaceProperty(). jest.replaceProperty(Platform, 'OS', 'android') replaces the value in place, as Jest does. It used to throw. The returned handle has replaceValue() and restore(). jest.restoreAllMocks(), the restoreMocks setting and the end of the test file also restore it; vi.restoreAllMocks() does not. It throws Jest's errors for a getter, a function or a missing property.

    No action needed.

  • ece53b3: Files that need a plugin from your project's Babel config now compile with that config, as Metro and babel-jest compile them. Legacy decorators (WatermelonDB models), macros, react-native-dotenv, the worklets plugin, custom plugins and Flow used to fail or behave differently. Remove any babel() entry you added only to run them; set projectBabel: 'never' for the previous behaviour.

    • Only a file that uses such a plugin compiles with Babel; other files keep Oxc.
    • migrate and doctor list these plugins as applied, and doctor calls a babel() entry that repeats one redundant.
    • It needs @babel/core in the project. Without it, migrate, doctor and the mock engine's startup say so.
  • e517388: Behaviour change: snapshots of React Native trees print in Jest's format. Run vitest -u once to re-record existing vitest-native snapshots, then review the diff. vitest-native no longer registers its own serializer. Jest-written snapshots share the format, but ones inside a describe, or of components Jest's preset stubs, still need one re-record (Snapshots).

    • No host prints ref. A host ref, such as a Text ref under the native engine or any host ref under the mock engine, is now an instance with measure, focus and the other native methods, where it used to be the test renderer's node or null.
    • vitest-native/serializer keeps the old format for one release; it is deprecated.
  • a107e3a: New vitest-native compare runs your suite under your own Jest and under Vitest and lists every test whose result differs. Use it while both runners are in place: npx vitest-native compare names each test that passes under Jest but not under Vitest, grouped by its first error, and exits 0 only when every one of them passes and vitest run exits 0. Neither run writes snapshots or changes project files, and --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See the CLI reference.

  • 4271252: Under the native engine, react-native-gesture-handler and @shopify/flash-list run as themselves, with the jestSetup.js each ships before the project's setup files, as a Jest suite lists them. No action needed; to keep a preset, set presets: { gestureHandler: true } or { flashList: true }.

    • Gesture Handler 3's hook API, builder methods and test helpers work, and a Pressable press fires onPress.
    • A FlashList renders a window of rows, as under Jest, where the preset rendered every row.
    • react-native-gesture-handler/Swipeable and the other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • 4271252: Under the native engine, a React Native library's own Jest setup and mocks get a jest with Jest's semantics. No action needed; a suite that imports react-native-gesture-handler/jestSetup or react-native-safe-area-context/jest/mock gets what Jest gave it.

    • Their jest.mock reaches every require of the module, and jest.requireActual and @jest/globals work.
    • Implementations a library installs while building its mocks survive vi.resetAllMocks() and mockReset: true.
    • jest.requireActual of a package a preset shadows returns the package, as under Jest.
  • ebdda39: Breaking: vitest-native now requires Node 22.22.3+, 24.12+ or 26+, for both engines. An unsupported Node stops at startup with UNSUPPORTED_NODE, naming the release to install; vitest-native doctor reports the same.

    Update Node within your major, or move from Node 20, 23 or 25 to a supported line, including CI images and .nvmrc or engines pins. Remove VITEST_NATIVE_LOADER_THREAD from scripts and CI; it no longer has an effect.

    • Under the native engine, a runtime without module.registerHooks (Bun) gets UNSUPPORTED_NODE too.
    • The support policy is in docs/versioning.md.
  • dae3119: Native engine: a file that Vite and Node both load, with both copies in use, is now reported by name, SOURCE_EVALUATED_TWICE, instead of silently existing twice. No action needed. To fail on it, set reactNative({ ownership: 'error' }).

    • The report names the file, how each side loaded it, and the fix. Exempt a deliberate case with ownership: { allow: [{ path, reason }] }.
    • require() of a CommonJS project file a test already imported now returns that same module.exports.
    • The warning, its end-of-run summary and vitest-native doctor link the error reference.
  • 5cad255: Native engine: a new opt-in flag makes Vite and Node resolve a package to the same file, so one package is one module. Add experimental: { ownershipModel: 'resolver' } to try it. Nothing changes without it.

    • Any package with separate import and require builds, or a react-native field, loads one file on both sides; a JSX or Flow react-native field is compiled, as Metro does.
    • RegExp aliases and tsconfig paths also apply to Node's require, including jest.requireActual. NODE_PATH applies to require only, as in Node.
    • Not covered: an alias with a...
Read more

v0.15.0-rc.3

v0.15.0-rc.3 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Nov 22:17
2ff9fee

Upgrading from 0.14

  • Node 22.22.3+, 24.12+ or 26+. An older Node stops at startup with UNSUPPORTED_NODE.
  • Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER.
  • React 19, React Native Testing Library 13 or 14, and React Native 0.86+ (Expo SDK 57+) for the
    native engine.
    An older React or React Native stops at startup with UNSUPPORTED_PEER.
  • Snapshots print in Jest's format. Run vitest -u once to re-record existing snapshots,
    then review the diff.
  • jestMockTransform() applies Jest's manual mocks of installed packages (__mocks__/<pkg>),
    as Jest does. Pass jestMockTransform({ rootMocks: false }) to keep the previous behaviour.
  • Run npx vitest-native doctor to check all of these. Each entry below has the details.

Minor Changes

  • 48fc91d: vitest-native compare --hot shows whether hotRuntime: 'auto' changes any test's result in your suite. It runs your Vitest config per file and hot: twice each, plus one hot run with shuffled files and one at --maxWorkers=2. It matches every test by file, name and occurrence, compares failures by kind, and checks the engine's banner, so a declined 'auto' gives no verdict. When nothing differs it prints the median speed-up; otherwise it lists each differing test. See the CLI guide. No action needed.

  • 98c62f4: The hot runtime can run React, its renderers, scheduler and RNTL fresh in every file (experimental). Set hotRuntime: { freshRuntime: true } so useId values, React's once-per-process warnings, an act() scope an earlier file left open, the scheduler's timers and RNTL's fake-timer reinstall no longer carry from one file to the next. It costs about 5 ms per file at four workers, and about 17 ms at one. It needs esmGeneration, which is on by default.

  • e4473ae: A test stuck in synchronous code no longer hangs a hot-runtime run forever: the run fails and names the file and test. Vitest's testTimeout can't interrupt a test that never yields, such as a loop that never ends, so such a run used to wait with nothing on screen. The new hangTimeout option fails a run when nothing has made progress for 60 s (or twice the longest test or hook timeout) and every running test is past its own timeout, retries included. It's on by default under the hot runtime and off otherwise. Add reactNative({ hangTimeout: <ms> }) to turn it on everywhere, or hangTimeout: false to turn it off.

  • 1cb74f0: Behaviour change: jestMockTransform() applies Jest's manual mocks of node_modules packages. To keep the previous behaviour, pass jestMockTransform({ rootMocks: false }). A file such as __mocks__/moti.ts in a __mocks__ folder under Jest's roots now replaces that package with no jest.mock() call, as under Jest, for imports and require() alike, __mocks__/react-native.js included. Before, the real package ran. A setup file's or test file's own jest.mock() or jest.unmock() still wins.

    • migrate lists each such file and carries over Jest's roots and modulePathIgnorePatterns.
    • A package a preset covers keeps its preset; the run warns that its manual mock is not applied.
  • 41959da: Under jest-compat, View.prototype has the native methods, as React Native's Jest preset gives it. jest.spyOn(View.prototype, 'measure') now reaches every View ref. It used to fail because React Native's View has no prototype. Under the native engine, each native view name now has one host class, so every View ref shares the methods a spy replaces.

    No action needed. Text and ScrollView get no prototype.

  • 23275f8: jest-compat supports jest.replaceProperty(). jest.replaceProperty(Platform, 'OS', 'android') replaces the value in place, as Jest does. It used to throw. The returned handle has replaceValue() and restore(). jest.restoreAllMocks(), the restoreMocks setting and the end of the test file also restore it; vi.restoreAllMocks() does not. It throws Jest's errors for a getter, a function or a missing property.

    No action needed.

  • ece53b3: Files that need a plugin from your project's Babel config now compile with that config, as Metro and babel-jest compile them. Legacy decorators (WatermelonDB models), macros, react-native-dotenv, the worklets plugin, custom plugins and Flow used to fail or behave differently. Remove any babel() entry you added only to run them; set projectBabel: 'never' for the previous behaviour.

    • Only a file that uses such a plugin compiles with Babel; other files keep Oxc.
    • migrate and doctor list these plugins as applied, and doctor calls a babel() entry that repeats one redundant.
    • It needs @babel/core in the project. Without it, migrate, doctor and the mock engine's startup say so.
  • e517388: Behaviour change: snapshots of React Native trees print in Jest's format. Run vitest -u once to re-record existing vitest-native snapshots, then review the diff. vitest-native no longer registers its own serializer. Jest-written snapshots share the format, but ones inside a describe, or of components Jest's preset stubs, still need one re-record (Snapshots).

    • No host prints ref. A host ref, such as a Text ref under the native engine or any host ref under the mock engine, is now an instance with measure, focus and the other native methods, where it used to be the test renderer's node or null.
    • vitest-native/serializer keeps the old format for one release; it is deprecated.
  • a107e3a: New vitest-native compare runs your suite under your own Jest and under Vitest and lists every test whose result differs. Use it while both runners are in place: npx vitest-native compare names each test that passes under Jest but not under Vitest, grouped by its first error, and exits 0 only when every one of them passes and vitest run exits 0. Neither run writes snapshots or changes project files, and --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See the CLI reference.

  • 4271252: Under the native engine, react-native-gesture-handler and @shopify/flash-list run as themselves, with the jestSetup.js each ships before the project's setup files, as a Jest suite lists them. No action needed; to keep a preset, set presets: { gestureHandler: true } or { flashList: true }.

    • Gesture Handler 3's hook API, builder methods and test helpers work, and a Pressable press fires onPress.
    • A FlashList renders a window of rows, as under Jest, where the preset rendered every row.
    • react-native-gesture-handler/Swipeable and the other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • 4271252: Under the native engine, a React Native library's own Jest setup and mocks get a jest with Jest's semantics. No action needed; a suite that imports react-native-gesture-handler/jestSetup or react-native-safe-area-context/jest/mock gets what Jest gave it.

    • Their jest.mock reaches every require of the module, and jest.requireActual and @jest/globals work.
    • Implementations a library installs while building its mocks survive vi.resetAllMocks() and mockReset: true.
    • jest.requireActual of a package a preset shadows returns the package, as under Jest.
  • ebdda39: Breaking: vitest-native now requires Node 22.22.3+, 24.12+ or 26+, for both engines. An unsupported Node stops at startup with UNSUPPORTED_NODE, naming the release to install; vitest-native doctor reports the same.

    Update Node within your major, or move from Node 20, 23 or 25 to a supported line, including CI images and .nvmrc or engines pins. Remove VITEST_NATIVE_LOADER_THREAD from scripts and CI; it no longer has an effect.

    • Under the native engine, a runtime without module.registerHooks (Bun) gets UNSUPPORTED_NODE too.
    • The support policy is in docs/versioning.md.
  • dae3119: Native engine: a file that Vite and Node both load, with both copies in use, is now reported by name, SOURCE_EVALUATED_TWICE, instead of silently existing twice. No action needed. To fail on it, set reactNative({ ownership: 'error' }).

    • The report names the file, how each side loaded it, and the fix. Exempt a deliberate case with ownership: { allow: [{ path, reason }] }.
    • require() of a CommonJS project file a test already imported now returns that same module.exports.
    • The warning, its end-of-run summary and vitest-native doctor link the error reference.
  • 5cad255: Native engine: a new opt-in flag makes Vite and Node resolve a package to the same file, so one package is one module. Add experimental: { ownershipModel: 'resolver' } to try it. Nothing changes without it.

    • Any package with separate import and require builds, or a react-native field, loads one file on both sides; a JSX or Flow react-native field is compiled, as Metro does.
    • RegExp aliases and tsconfig paths also apply to Node's require, including jest.requireActual. NODE_PATH applies to require only, as in Node.
    • Not covered: an alias with a custom resolver or a function replacement. It is refused at startup by name. See experimental.
  • 45154cf: Native engine: experimental: { ownershipModel: 'resolution' } gives Node's require of a project file the same instance a test's import gets, whichever loads it first. To try it, set the flag; it also turns on nodeMocks, and refuses nodeMocks: false.

    • A file not yet evaluated is evaluated then. A `requi...
Read more

v0.15.0-rc.2

v0.15.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Nov 17:41
109727a

Upgrading from 0.14

  • Node 22.22.3+, 24.12+ or 26+. An older Node stops at startup with UNSUPPORTED_NODE.
  • Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER.
  • React 19, React Native Testing Library 13 or 14, and React Native 0.86+ (Expo SDK 57+) for the
    native engine.
    An older React or React Native stops at startup with UNSUPPORTED_PEER.
  • Snapshots print in Jest's format. Run vitest -u once to re-record existing snapshots,
    then review the diff.
  • jestMockTransform() applies Jest's manual mocks of installed packages (__mocks__/<pkg>),
    as Jest does. Pass jestMockTransform({ rootMocks: false }) to keep the previous behaviour.
  • Run npx vitest-native doctor to check all of these. Each entry below has the details.

Minor Changes

  • e4473ae: A test stuck in synchronous code no longer hangs a hot-runtime run forever: the run fails and names the file and test. Vitest's testTimeout can't interrupt a test that never yields, such as a loop that never ends, so such a run used to wait with nothing on screen. The new hangTimeout option fails a run when nothing has made progress for 60 s (or twice the longest test or hook timeout) and every running test is past its own timeout, retries included. It's on by default under the hot runtime and off otherwise. Add reactNative({ hangTimeout: <ms> }) to turn it on everywhere, or hangTimeout: false to turn it off.

  • 1cb74f0: Behaviour change: jestMockTransform() applies Jest's manual mocks of node_modules packages. To keep the previous behaviour, pass jestMockTransform({ rootMocks: false }). A file such as __mocks__/moti.ts in a __mocks__ folder under Jest's roots now replaces that package with no jest.mock() call, as under Jest, for imports and require() alike, __mocks__/react-native.js included. Before, the real package ran. A setup file's or test file's own jest.mock() or jest.unmock() still wins.

    • migrate lists each such file and carries over Jest's roots and modulePathIgnorePatterns.
    • A package a preset covers keeps its preset; the run warns that its manual mock is not applied.
  • 41959da: Under jest-compat, View.prototype has the native methods, as React Native's Jest preset gives it. jest.spyOn(View.prototype, 'measure') now reaches every View ref. It used to fail because React Native's View has no prototype. Under the native engine, each native view name now has one host class, so every View ref shares the methods a spy replaces.

    No action needed. Text and ScrollView get no prototype.

  • 23275f8: jest-compat supports jest.replaceProperty(). jest.replaceProperty(Platform, 'OS', 'android') replaces the value in place, as Jest does. It used to throw. The returned handle has replaceValue() and restore(). jest.restoreAllMocks(), the restoreMocks setting and the end of the test file also restore it; vi.restoreAllMocks() does not. It throws Jest's errors for a getter, a function or a missing property.

    No action needed.

  • ece53b3: Files that need a plugin from your project's Babel config now compile with that config, as Metro and babel-jest compile them. Legacy decorators (WatermelonDB models), macros, react-native-dotenv, the worklets plugin, custom plugins and Flow used to fail or behave differently. Remove any babel() entry you added only to run them; set projectBabel: 'never' for the previous behaviour.

    • Only a file that uses such a plugin compiles with Babel; other files keep Oxc.
    • migrate and doctor list these plugins as applied, and doctor calls a babel() entry that repeats one redundant.
    • It needs @babel/core in the project. Without it, migrate, doctor and the mock engine's startup say so.
  • e517388: Behaviour change: snapshots of React Native trees print in Jest's format. Run vitest -u once to re-record existing vitest-native snapshots, then review the diff. vitest-native no longer registers its own serializer. Jest-written snapshots share the format, but ones inside a describe, or of components Jest's preset stubs, still need one re-record (Snapshots).

    • No host prints ref. A host ref, such as a Text ref under the native engine or any host ref under the mock engine, is now an instance with measure, focus and the other native methods, where it used to be the test renderer's node or null.
    • vitest-native/serializer keeps the old format for one release; it is deprecated.
  • a107e3a: New vitest-native compare runs your suite under your own Jest and under Vitest and lists every test whose result differs. Use it while both runners are in place: npx vitest-native compare names each test that passes under Jest but not under Vitest, grouped by its first error, and exits 0 only when every one of them passes and vitest run exits 0. Neither run writes snapshots or changes project files, and --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See the CLI reference.

  • 4271252: Under the native engine, react-native-gesture-handler and @shopify/flash-list run as themselves, with the jestSetup.js each ships before the project's setup files, as a Jest suite lists them. No action needed; to keep a preset, set presets: { gestureHandler: true } or { flashList: true }.

    • Gesture Handler 3's hook API, builder methods and test helpers work, and a Pressable press fires onPress.
    • A FlashList renders a window of rows, as under Jest, where the preset rendered every row.
    • react-native-gesture-handler/Swipeable and the other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • 4271252: Under the native engine, a React Native library's own Jest setup and mocks get a jest with Jest's semantics. No action needed; a suite that imports react-native-gesture-handler/jestSetup or react-native-safe-area-context/jest/mock gets what Jest gave it.

    • Their jest.mock reaches every require of the module, and jest.requireActual and @jest/globals work.
    • Implementations a library installs while building its mocks survive vi.resetAllMocks() and mockReset: true.
    • jest.requireActual of a package a preset shadows returns the package, as under Jest.
  • ebdda39: Breaking: vitest-native now requires Node 22.22.3+, 24.12+ or 26+, for both engines. An unsupported Node stops at startup with UNSUPPORTED_NODE, naming the release to install; vitest-native doctor reports the same.

    Update Node within your major, or move from Node 20, 23 or 25 to a supported line, including CI images and .nvmrc or engines pins. Remove VITEST_NATIVE_LOADER_THREAD from scripts and CI; it no longer has an effect.

    • Under the native engine, a runtime without module.registerHooks (Bun) gets UNSUPPORTED_NODE too.
    • The support policy is in docs/versioning.md.
  • dae3119: Native engine: a file that Vite and Node both load, with both copies in use, is now reported by name, SOURCE_EVALUATED_TWICE, instead of silently existing twice. No action needed. To fail on it, set reactNative({ ownership: 'error' }).

    • The report names the file, how each side loaded it, and the fix. Exempt a deliberate case with ownership: { allow: [{ path, reason }] }.
    • require() of a CommonJS project file a test already imported now returns that same module.exports.
    • The warning, its end-of-run summary and vitest-native doctor link the error reference.
  • 5cad255: Native engine: a new opt-in flag makes Vite and Node resolve a package to the same file, so one package is one module. Add experimental: { ownershipModel: 'resolver' } to try it. Nothing changes without it.

    • Any package with separate import and require builds, or a react-native field, loads one file on both sides; a JSX or Flow react-native field is compiled, as Metro does.
    • RegExp aliases and tsconfig paths also apply to Node's require, including jest.requireActual. NODE_PATH applies to require only, as in Node.
    • Not covered: an alias with a custom resolver or a function replacement. It is refused at startup by name. See experimental.
  • 45154cf: Native engine: experimental: { ownershipModel: 'resolution' } gives Node's require of a project file the same instance a test's import gets, whichever loads it first. To try it, set the flag; it also turns on nodeMocks, and refuses nodeMocks: false.

    • A file not yet evaluated is evaluated then. A require back into a module from inside its own evaluation gets its exports so far, as in Node and Jest.
    • Mocks apply to what Node's require reaches, as to the test's imports.
    • What cannot be served fails by name: top-level await, a module that throws, one still evaluating when required from elsewhere, or a mock not available synchronously (MOCK_NOT_SEEN_BY_NODE).
  • 4271252: Under the native engine, the worklets, safe-area-context, keyboard-controller, bottom-sheet and netinfo presets are each library's own Jest mock, as a Jest suite installs it. No action needed; snapshots of the presets' own output may change, and the mock engine keeps the hand-written presets.

    • Safe-area insets are zero unless <SafeAreaProvider initialMetrics> sets them; setInsets() still sets them until resetAllMocks().
    • Bottom-sheet's views render only their children, and netinfo reports a connected cellular network.
    • Names a preset served that a mock lacks stay: bottom-sheet's *_STATUS enums and footer components, netinfo's `useNetInfoInstan...
Read more

v0.15.0-rc.1

v0.15.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 06 Nov 16:26
2434083

Upgrading from 0.14

  • Node 22.22.3+, 24.12+ or 26+. An older Node stops at startup with UNSUPPORTED_NODE.
  • Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER.
  • React 19, React Native Testing Library 13 or 14, and React Native 0.86+ (Expo SDK 57+) for the
    native engine.
    An older React or React Native stops at startup with UNSUPPORTED_PEER.
  • Run npx vitest-native doctor to check all of these. Each entry below has the details.

Minor Changes

  • a107e3a: New vitest-native compare runs your suite under your own Jest and under Vitest and lists every test whose result differs. Use it while both runners are in place: npx vitest-native compare names each test that passes under Jest but not under Vitest, grouped by its first error, and exits 0 only when every one of them passes and vitest run exits 0. Neither run writes snapshots or changes project files, and --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See the CLI reference.

  • 4271252: Under the native engine, react-native-gesture-handler and @shopify/flash-list run as themselves, with the jestSetup.js each ships before the project's setup files, as a Jest suite lists them. No action needed; to keep a preset, set presets: { gestureHandler: true } or { flashList: true }.

    • Gesture Handler 3's hook API, builder methods and test helpers work, and a Pressable press fires onPress.
    • A FlashList renders a window of rows, as under Jest, where the preset rendered every row.
    • react-native-gesture-handler/Swipeable and the other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • 4271252: Under the native engine, a React Native library's own Jest setup and mocks get a jest with Jest's semantics. No action needed; a suite that imports react-native-gesture-handler/jestSetup or react-native-safe-area-context/jest/mock gets what Jest gave it.

    • Their jest.mock reaches every require of the module, and jest.requireActual and @jest/globals work.
    • Implementations a library installs while building its mocks survive vi.resetAllMocks() and mockReset: true.
    • jest.requireActual of a package a preset shadows returns the package, as under Jest.
  • ebdda39: Breaking: vitest-native now requires Node 22.22.3+, 24.12+ or 26+, for both engines. An unsupported Node stops at startup with UNSUPPORTED_NODE, naming the release to install; vitest-native doctor reports the same.

    Update Node within your major, or move from Node 20, 23 or 25 to a supported line, including CI images and .nvmrc or engines pins. Remove VITEST_NATIVE_LOADER_THREAD from scripts and CI; it no longer has an effect.

    • Under the native engine, a runtime without module.registerHooks (Bun) gets UNSUPPORTED_NODE too.
    • The support policy is in docs/versioning.md.
  • dae3119: Native engine: a file that Vite and Node both load, with both copies in use, is now reported by name, SOURCE_EVALUATED_TWICE, instead of silently existing twice. No action needed. To fail on it, set reactNative({ ownership: 'error' }).

    • The report names the file, how each side loaded it, and the fix. Exempt a deliberate case with ownership: { allow: [{ path, reason }] }.
    • require() of a CommonJS project file a test already imported now returns that same module.exports.
    • The warning, its end-of-run summary and vitest-native doctor link the error reference.
  • 5cad255: Native engine: a new opt-in flag makes Vite and Node resolve a package to the same file, so one package is one module. Add experimental: { ownershipModel: 'resolver' } to try it. Nothing changes without it.

    • Any package with separate import and require builds, or a react-native field, loads one file on both sides; a JSX or Flow react-native field is compiled, as Metro does.
    • RegExp aliases and tsconfig paths also apply to Node's require, including jest.requireActual. NODE_PATH applies to require only, as in Node.
    • Not covered: an alias with a custom resolver or a function replacement. It is refused at startup by name. See experimental.
  • 45154cf: Native engine: experimental: { ownershipModel: 'resolution' } gives Node's require of a project file the same instance a test's import gets, whichever loads it first. To try it, set the flag; it also turns on nodeMocks, and refuses nodeMocks: false.

    • A file not yet evaluated is evaluated then. A require back into a module from inside its own evaluation gets its exports so far, as in Node and Jest.
    • Mocks apply to what Node's require reaches, as to the test's imports.
    • What cannot be served fails by name: top-level await, a module that throws, one still evaluating when required from elsewhere, or a mock not available synchronously (MOCK_NOT_SEEN_BY_NODE).
  • 4271252: Under the native engine, the worklets, safe-area-context, keyboard-controller, bottom-sheet and netinfo presets are each library's own Jest mock, as a Jest suite installs it. No action needed; snapshots of the presets' own output may change, and the mock engine keeps the hand-written presets.

    • Safe-area insets are zero unless <SafeAreaProvider initialMetrics> sets them; setInsets() still sets them until resetAllMocks().
    • Bottom-sheet's views render only their children, and netinfo reports a connected cellular network.
    • Names a preset served that a mock lacks stay: bottom-sheet's *_STATUS enums and footer components, netinfo's useNetInfoInstance.
  • 4271252: Reanimated's interpolate, interpolateColor and clamp return real values, and its scroll and event hooks run their handlers. No action needed; snapshots of interpolated styles may change.

    • Of Reanimated's own mock, only what still answers with a no-op is replaced, so a later Reanimated whose mock does better keeps its own.
    • Shared values take listeners, so a Gesture Handler 3 gesture configured with a shared value works.
    • Reanimated 3.17 to 4.6 get the same fixes in the hand-written preset.
  • 4271252: Under the native engine, Reanimated 4.7+ (with Worklets 0.9+) is Reanimated's own test mock, as a Jest suite installs it with jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')). No action needed; presets: { reanimated: false } turns it off, and older versions keep the hand-written preset.

    • The hand-written preset lacked 104 of the mock's names, among them interpolateColor, Keyframe, useScrollOffset and most layout animations.
    • Worklets is its own test mock too, so a test and Reanimated share one instance; its runOnJS and scheduleOnRN run a tick later, as under Jest.
    • A setup file that calls Reanimated's setUpTests() keeps toHaveAnimatedStyle and toHaveAnimatedProps working; keep or delete the call.
  • 0a71dff: Native engine: a cold run now starts at once instead of waiting for React Native's precompiled modules to build. They compile in a low-priority background process.

    To opt out, set reactNative({ registryBuild: 'wait' }) to build them before the run, as before.

    • Test files that start before the build lands load React Native file by file, which is slower; column numbers in React Native's stack frames can differ.
    • CI runs and hotRuntime still wait. On close, a running build gets half of teardownTimeout to finish.
    • Not covered: on Windows, a Vitest process killed outright can leave the compiler running for about 130 s.
  • 5a7f226: Breaking: vitest-native now requires React 19 and supports React Native Testing Library 13 or 14, and the native engine requires React Native 0.86+. An older React or React Native stops at startup with UNSUPPORTED_PEER, for example react-native 0.85.3 is not supported: the native engine supports react-native 0.86–0.88; vitest-native doctor reports the same.

    Upgrade to React Native 0.86 or later (Expo SDK 57 or later) and React 19, and move from React Native Testing Library 12 to 13 or 14.

    • Testing Library 14 makes render, fireEvent and act async: await render(<App />).
    • The mock engine runs on any React Native.
    • Testing Library outside 13–14, React Native above 0.88, or an Expo SDK other than 57 and 58, warns rather than fails.
  • 25f0147: Under the native engine, a test file that imports react-native now loads only the React Native modules it uses. No action needed.

    • Each export is read when a test uses it, as under Jest, instead of all at once on import, which loaded React Native's whole module graph.
    • A change made to an export on the real react-native module is now visible through import { … } from "react-native" too.
  • 2b764f1: Breaking: vitest-native now requires Vitest 5 and Vite 8. Vitest 4, or Vite 6 or 7, stops at startup with UNSUPPORTED_PEER, naming the supported range; vitest-native doctor reports the same.

    Install vitest@^5 with matching @vitest/* packages, and vite@^8.0.5.

    • Vitest 5 clears mocks before every test (clearMocks: true). A suite that asserts on calls recorded before the test started sets test.clearMocks: false.
    • The plugin turns on Vitest 5's fsModuleCache unless the config sets test.fsModuleCache or test.experimental.fsModuleCache.
    • Babel plugins your tests need go through @rolldown/plugin-babel; @vitejs/plugin-react 6 removed its babel option.

Patch Changes

  • a107e3a: vitest-native init, doctor and migrate now refuse a flag they do not take, instead of ignoring it. A typo such as migrate --wirte used to run a dry report without saying so; it now exits 1 with `✗ unknown option --wirte for migrate. Va...
Read more

v0.14.1

Choose a tag to compare

@github-actions github-actions released this 05 Nov 19:33
df91408

Patch Changes

  • 0dd12ca: Native engine: a React Native package's required peer that your package manager installs for you (pnpm, npm 7+ and bun do), such as expo-router's expo-linking, is now compiled. No action needed. Remove transform: ['expo-linking'] if you added it for this.

    • Before, a required peer that no package.json declared stayed uncompiled. It then failed with "Cannot require() ES Module … not yet fully loaded" or "Cannot find module …/Linking.types".
    • Optional peers, and peers a package names only in its config plugin, scripts or types, are still not followed.

    Thanks @AntSplash360 for reporting this (#296).

  • 967d3cf: Under the native engine, a relative import whose name has a dot, such as ./Linking.types, now resolves. A package that Node loads as an ES module failed with "Cannot find module …/Linking.types" because the part after the dot was read as a file extension. Only a known source or asset extension counts now, as in Metro, so ./Linking.types finds Linking.types.js, and a platform variant such as Theme.config.native.js wins over Theme.config.js. No action needed.

    Thanks @AntSplash360 for reporting this (#296).

  • 8ea7273: vitest-native migrate now reads jest.config.ts, .mjs, .mts and .cts, in Jest's own lookup order. TypeScript configs load through Node's type stripping (Node 22.18 or later). A config that still can't be loaded, such as one that relies on ts-node (an extensionless relative import, an enum), uses top-level await or exports a function, is now listed under "Needs your attention" with the reason, where before the report silently left out its settings. No action needed.

  • e5ff183: vitest-native migrate now warns when the project's Babel config makes imports lazy under Jest. With babel-preset-expo's lazyImports, @react-native/babel-preset's lazyImportExportTransform, the CommonJS transform's lazy option or an inline-requires plugin, Jest ran a package import only when a test first used it, while vitest-native runs every import when its file loads. A module a test never touched is then evaluated, and a partial jest.mock of something it needs can fail. The report now lists the setting under "Needs your attention". Babel plugin names under the @babel/ scope are also now read the way Babel reads them.

    Use jest.requireActual to give such a mock the rest of its module: jest.mock('x', () => ({ ...jest.requireActual('x'), ... })).

What's Changed

  • fix(migrate): read jest.config.ts, .mjs, .mts and .cts in Jest's lookup order by @danfry1 in #512
  • test: start the Babel worker before the tests that transform by @danfry1 in #514
  • fix(migrate): flag Babel settings that made imports lazy under Jest by @danfry1 in #515
  • docs(readme): one short README for GitHub and npm by @danfry1 in #518
  • docs(site): make the getting-started snippets run under the default engine by @danfry1 in #520
  • fix(native): compile a required React Native peer a compiled package loads, though nothing declares it by @danfry1 in #519
  • docs(site): one Mocking page for vi.mock reach, untranspiled packages and fake timers by @danfry1 in #521
  • fix(native): a relative import with a dot in its name resolves, as in Metro by @danfry1 in #509
  • test(museum): pfs-hot-unawaited-act fails by name with one worker, its accepted outcome by @danfry1 in #517
  • test(docs): run the README and quick-start snippets against the packed package by @danfry1 in #522
  • chore(release): 0.14.1 by @danfry1 in #524

Full Changelog: v0.14.0...v0.14.1

v0.14.0-rc.14

v0.14.0-rc.14 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 05 Nov 14:01
7a6e3d2

Upgrading from 0.13

  • Node 22.13 or later on Node 22, or Node 24.3 or later. engines.node is
    >=22.13.0 <23 || >=24.3.0 (it was >=20.19.0): Node 20 reached end of life in April 2026, and
    on Node 24.0–24.2 jest.mock, jest.isolateModules and jest.resetModules do not reach modules
    Node loads. vitest-native doctor checks the range.
  • Per-file isolation stays the default. hotRuntime defaults to false, as in 0.13.0: under
    the native engine each test file runs in a fresh worker. The hot runtime, which reuses workers
    across files and is faster on most suites, is available with hotRuntime: 'auto'. Five known
    differences from per-file isolation are listed in
    troubleshooting.
    Early 0.14.0 release candidates defaulted to 'auto'; set hotRuntime: 'auto' to keep the hot runtime.
  • How to tell. When the hot runtime is active, the startup banner names it, as in
    (platform ios, hot runtime). With 'auto', it keeps Vitest's per-file isolation whenever the run
    cannot be bounded, or when you set a pool or test.isolate yourself, and prints the reason.
  • Hot workers leave Node's compile cache off when they re-evaluate ES modules, so the cache no
    longer grows with every test file. It costs about 15–20 ms per worker boot;
    hotRuntime: { esmGeneration: false } keeps the cache on.
  • Hot workers no longer keep a copy of every CommonJS package each file imports, on Node
    22.22.3+, 24.12+ and 26, so a suite importing large packages no longer recycles workers every few
    files.
  • hotRuntime: { mode: 'auto', memoryLimit } runs hot with your recycling options where the run
    can be bounded and keeps per-file isolation where it cannot. Without mode, the object form still
    requires the hot runtime.
  • A test that times out inside act() retires its hot worker, so later files on that worker no
    longer fail with Unable to find an element with text: ….
  • Coverage works under the native engine. With provider: 'v8', files Node compiles, such as a
    React Native workspace package, are reported against their source lines.
  • A project file imported and required in one test is one module. Under the native engine, with
    or without jest-compat, vi.spyOn, export assignments and instanceof reach both sides, as under
    Jest.
  • Vite+ projects start. The Vite and Vitest versions are read from the running instances, so
    Vite+ no longer fails with UNSUPPORTED_PEER. Two physical copies of Vitest stop the run at startup
    with VITEST_TWIN_INSTANCE, naming both.
  • Expo SDK 57 and 58 are the supported SDKs. On an older or newer SDK the plugin prints one
    warning per run naming them, and vitest-native doctor reports it; tests that load Expo modules
    can fail there. A test that loads expo or expo-modules-core from another SDK, for example through
    a library, also warns, naming the version and path. Upgrade to Expo SDK 57 or 58.
  • The package is about 30% smaller and no longer ships CHANGELOG.md; the changelog is
    on GitHub.
  • RNTL works without jest-compat or globals. findBy*, waitFor and userEvent advance
    vi.useFakeTimers(), and RNTL's auto-cleanup runs without test.globals: true. A suite that relied
    on trees staying mounted between tests imports @testing-library/react-native/pure.
  • Expo path aliases resolve. In an Expo project on Vite 8, imports through tsconfig paths (the
    template's @/…) now resolve as Metro resolves them; a vite-tsconfig-paths plugin or a hand-written
    alias for them can go.
  • Vitest 5 keeps transformed modules on disk. The plugin turns on Vitest's fsModuleCache unless
    your config sets it, keyed so that upgrading vitest-native or any dependency invalidates it. Set
    test.fsModuleCache: false to opt out.
  • react-native-mmkv 3 and later run as themselves. They detect Vitest and use their own in-memory
    backend, so the mmkv preset now applies to mmkv 2 only. Tests get mmkv's real API (v4's remove)
    and change listeners instead of the preset's approximation.
  • jest-compat follows Jest more closely. A name a jest.mock factory leaves out is undefined
    instead of an error, new on a jest.fn with an arrow implementation works, and
    process.env.JEST_WORKER_ID is set, so code that checks for Jest takes its test branch.
  • hotRuntime: true is stricter. Combined with a configured pool, test.isolate, or a conflicting
    CLI flag, it now fails at startup with HOT_RUNTIME_OVERRIDDEN instead of warning and overriding.
  • Native modules answer as on a device. Under the native engine, NativeModules[name] and
    TurboModuleRegistry.get(name) are undefined/null for a module no app registers, and a React
    Native module's stub has only the methods its codegen spec declares. Register a module a test needs
    with mockNativeModule(name, impl); nativeModules: 'permissive' restores the earlier behaviour.
  • Assets are registered ids. require('./logo.png') evaluates to the id React Native assigned
    when registering the asset, as with Metro, instead of the file name. Snapshots that contained an asset's name
    change; re-record them with -u.
  • jest.mock reaches require(). With jest-compat, a jest.mock factory now applies to
    require(), jest.requireActual's dependencies and React Native's own requires of a mocked internal;
    jest.isolateModules and jest.isolateModulesAsync work. TypeScript app files that Node loads are
    compiled with the project's Babel config, as babel-jest compiles them.
  • Unistyles and Skia are mocked automatically. The new unistyles and skia presets are
    auto-detected. Remove Unistyles' react-native-unistyles/mocks setup file, whose jest.mock() calls
    would replace the preset (vitest-native migrate removes it), and a hand-written Skia mock, which the
    preset replaces. Skia's preset loads CanvasKit before each test file, about 20–30 ms;
    presets: { skia: false } turns it off.
  • Presets of your own are listed by path. A preset object of a project's own passed to presets
    used to shadow its packages with undefined exports; it is now refused at startup. List the module that
    exports it instead: presets: { './test/presets/my-lib.ts': true }.
  • A deep import of a preset package loads its real file. Under the native engine,
    react-native-reanimated/src/… and similar subpaths are the package's own files, as under Jest,
    not the root mock; import from the package root if a test relied on the mock. The
    safeAreaContext preset's hooks read their contexts, so call them inside a component or
    renderHook.
  • Expo SDK 57 and 58 projects run real React Native with nothing extra installed. When
    @react-native/babel-preset does not resolve (babel-preset-expo no longer depends on it), the
    native engine compiles with Expo's preset, as Metro does. reactNative() used the mock engine there
    before.
  • Expo's globals are installed before your setup files, and fetch stays Node's. A fetch mocked
    in a setup file, an MSW server, nock and undici's MockAgent keep working whether or not a test
    imports expo. Every test file in an Expo project now sees Expo's URL, structuredClone and
    DOMException polyfills, as under jest-expo.
  • Expo's globals need expo declared. The native engine installs Expo's runtime (URL,
    structuredClone and the other globals jest-expo installs) only when the project, or a manifest
    above it, declares expo or a package that depends on it, or transform names it. A project where
    expo merely resolves, for example beside an Expo app in a hoisted workspace, no longer gets them;
    declare expo or add it to transform if it relies on them.
  • Your Babel config runs on its own thread. Under the native engine, Babel runs apart from the
    tests, as under Jest. A Babel config or plugin sees the Babel worker's process.argv,
    isMainThread is false, and the tests' require.cache and globals are not visible.
    process.env is shared. A transform slower than 60 s plus 15 s per MB of source fails with
    TRANSFORM_FAILED.
  • Expo modules' own native-module mocks are served. Values that were undefined are the mock's
    (digestStringAsync resolves ""), and a member a mock leaves out is undefined where the generic
    stub gave a function.
  • An optional native module that is not installed reads as absent. requireOptionalNativeModule
    returns null for a module a package only probes for and no installed package defines, as on a
    device.
  • Expo modules run as their installed packages. Under the native engine, the expo preset no
    longer replaces expo-constants, expo-status-bar, expo-font, expo-asset, expo-splash-screen
    and expo-linking, so their whole API is the installed SDK's (hide(), setOptions(),
    useAssets …) and their values are jest-expo's: Constants.expoConfig is {},
    Linking.createURL() needs a scheme mocked into expo-constants, and Font.isLoaded() is false
    until a font loads. The Minor Changes entry lists each value; presets: { expo: true } keeps the
    preset while a suite moves off it.

Thank you

Thank you to @AntSplash360, @arvidtorbjoernsen, @believer, @doutatsu, @eshvatskyi, @Joozty, @OpenDeernet, @sincraianul and @swey for the issues and pull requests in this release.

Minor Changes

  • 271c149: **hotRuntime: 'auto' can take recyclin...
Read more

v0.14.0-rc.13

v0.14.0-rc.13 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Nov 23:10
439eaf5

Upgrading from 0.13

  • Node 22.13 or later on Node 22, or Node 24.3 or later. engines.node is
    >=22.13.0 <23 || >=24.3.0 (it was >=20.19.0): Node 20 reached end of life in April 2026, and
    on Node 24.0–24.2 jest.mock, jest.isolateModules and jest.resetModules do not reach modules
    Node loads. vitest-native doctor checks the range.
  • Per-file isolation stays the default. hotRuntime defaults to false, as in 0.13.0: under
    the native engine each test file runs in a fresh worker. The hot runtime, which reuses workers
    across files and is faster on most suites, is available with hotRuntime: 'auto'. Five known
    differences from per-file isolation are listed in
    troubleshooting.
    Early 0.14.0 release candidates defaulted to 'auto'; set hotRuntime: 'auto' to keep the hot runtime.
  • How to tell. When the hot runtime is active, the startup banner names it, as in
    (platform ios, hot runtime). With 'auto', it keeps Vitest's per-file isolation whenever the run
    cannot be bounded, or when you set a pool or test.isolate yourself, and prints the reason.
  • Hot workers leave Node's compile cache off when they re-evaluate ES modules, so the cache no
    longer grows with every test file. It costs about 15–20 ms per worker boot;
    hotRuntime: { esmGeneration: false } keeps the cache on.
  • Hot workers no longer keep a copy of every CommonJS package each file imports, on Node
    22.22.3+, 24.12+ and 26, so a suite importing large packages no longer recycles workers every few
    files.
  • hotRuntime: { mode: 'auto', memoryLimit } runs hot with your recycling options where the run
    can be bounded and keeps per-file isolation where it cannot. Without mode, the object form still
    requires the hot runtime.
  • A test that times out inside act() retires its hot worker, so later files on that worker no
    longer fail with Unable to find an element with text: ….
  • Coverage works under the native engine. With provider: 'v8', files Node compiles, such as a
    React Native workspace package, are reported against their source lines.
  • A project file imported and required in one test is one module. Under the native engine, with
    or without jest-compat, vi.spyOn, export assignments and instanceof reach both sides, as under
    Jest.
  • Vite+ projects start. The Vite and Vitest versions are read from the running instances, so
    Vite+ no longer fails with UNSUPPORTED_PEER. Two physical copies of Vitest stop the run at startup
    with VITEST_TWIN_INSTANCE, naming both.
  • Expo SDK 57 and 58 are the supported SDKs. On an older or newer SDK the plugin prints one
    warning per run naming them, and vitest-native doctor reports it; tests that load Expo modules
    can fail there. A test that loads expo or expo-modules-core from another SDK, for example through
    a library, also warns, naming the version and path. Upgrade to Expo SDK 57 or 58.
  • The package is about 30% smaller and no longer ships CHANGELOG.md; the changelog is
    on GitHub.
  • RNTL works without jest-compat or globals. findBy*, waitFor and userEvent advance
    vi.useFakeTimers(), and RNTL's auto-cleanup runs without test.globals: true. A suite that relied
    on trees staying mounted between tests imports @testing-library/react-native/pure.
  • Expo path aliases resolve. In an Expo project on Vite 8, imports through tsconfig paths (the
    template's @/…) now resolve as Metro resolves them; a vite-tsconfig-paths plugin or a hand-written
    alias for them can go.
  • Vitest 5 keeps transformed modules on disk. The plugin turns on Vitest's fsModuleCache unless
    your config sets it, keyed so that upgrading vitest-native or any dependency invalidates it. Set
    test.fsModuleCache: false to opt out.
  • react-native-mmkv 3 and later run as themselves. They detect Vitest and use their own in-memory
    backend, so the mmkv preset now applies to mmkv 2 only. Tests get mmkv's real API (v4's remove)
    and change listeners instead of the preset's approximation.
  • jest-compat follows Jest more closely. A name a jest.mock factory leaves out is undefined
    instead of an error, new on a jest.fn with an arrow implementation works, and
    process.env.JEST_WORKER_ID is set, so code that checks for Jest takes its test branch.
  • hotRuntime: true is stricter. Combined with a configured pool, test.isolate, or a conflicting
    CLI flag, it now fails at startup with HOT_RUNTIME_OVERRIDDEN instead of warning and overriding.
  • Native modules answer as on a device. Under the native engine, NativeModules[name] and
    TurboModuleRegistry.get(name) are undefined/null for a module no app registers, and a React
    Native module's stub has only the methods its codegen spec declares. Register a module a test needs
    with mockNativeModule(name, impl); nativeModules: 'permissive' restores the earlier behaviour.
  • Assets are registered ids. require('./logo.png') evaluates to the id React Native assigned
    when registering the asset, as with Metro, instead of the file name. Snapshots that contained an asset's name
    change; re-record them with -u.
  • jest.mock reaches require(). With jest-compat, a jest.mock factory now applies to
    require(), jest.requireActual's dependencies and React Native's own requires of a mocked internal;
    jest.isolateModules and jest.isolateModulesAsync work. TypeScript app files that Node loads are
    compiled with the project's Babel config, as babel-jest compiles them.
  • Unistyles and Skia are mocked automatically. The new unistyles and skia presets are
    auto-detected. Remove Unistyles' react-native-unistyles/mocks setup file, whose jest.mock() calls
    would replace the preset (vitest-native migrate removes it), and a hand-written Skia mock, which the
    preset replaces. Skia's preset loads CanvasKit before each test file, about 20–30 ms;
    presets: { skia: false } turns it off.
  • Presets of your own are listed by path. A preset object of a project's own passed to presets
    used to shadow its packages with undefined exports; it is now refused at startup. List the module that
    exports it instead: presets: { './test/presets/my-lib.ts': true }.
  • A deep import of a preset package loads its real file. Under the native engine,
    react-native-reanimated/src/… and similar subpaths are the package's own files, as under Jest,
    not the root mock; import from the package root if a test relied on the mock. The
    safeAreaContext preset's hooks read their contexts, so call them inside a component or
    renderHook.
  • Expo SDK 57 and 58 projects run real React Native with nothing extra installed. When
    @react-native/babel-preset does not resolve (babel-preset-expo no longer depends on it), the
    native engine compiles with Expo's preset, as Metro does. reactNative() used the mock engine there
    before.
  • Expo's globals are installed before your setup files, and fetch stays Node's. A fetch mocked
    in a setup file, an MSW server, nock and undici's MockAgent keep working whether or not a test
    imports expo. Every test file in an Expo project now sees Expo's URL, structuredClone and
    DOMException polyfills, as under jest-expo.
  • Expo's globals need expo declared. The native engine installs Expo's runtime (URL,
    structuredClone and the other globals jest-expo installs) only when the project, or a manifest
    above it, declares expo or a package that depends on it, or transform names it. A project where
    expo merely resolves, for example beside an Expo app in a hoisted workspace, no longer gets them;
    declare expo or add it to transform if it relies on them.
  • Your Babel config runs on its own thread. Under the native engine, Babel runs apart from the
    tests, as under Jest. A Babel config or plugin sees the Babel worker's process.argv,
    isMainThread is false, and the tests' require.cache and globals are not visible.
    process.env is shared. A transform slower than 60 s plus 15 s per MB of source fails with
    TRANSFORM_FAILED.
  • Expo modules' own native-module mocks are served. Values that were undefined are the mock's
    (digestStringAsync resolves ""), and a member a mock leaves out is undefined where the generic
    stub gave a function.
  • An optional native module that is not installed reads as absent. requireOptionalNativeModule
    returns null for a module a package only probes for and no installed package defines, as on a
    device.
  • Expo modules run as their installed packages. Under the native engine, the expo preset no
    longer replaces expo-constants, expo-status-bar, expo-font, expo-asset, expo-splash-screen
    and expo-linking, so their whole API is the installed SDK's (hide(), setOptions(),
    useAssets …) and their values are jest-expo's: Constants.expoConfig is {},
    Linking.createURL() needs a scheme mocked into expo-constants, and Font.isLoaded() is false
    until a font loads. The Minor Changes entry lists each value; presets: { expo: true } keeps the
    preset while a suite moves off it.

Thank you

Thank you to @AntSplash360, @arvidtorbjoernsen, @believer, @doutatsu, @eshvatskyi, @Joozty, @OpenDeernet, @sincraianul and @swey for the issues and pull requests in this release.

Minor Changes

  • 271c149: **hotRuntime: 'auto' can take recyclin...
Read more