diff --git a/apps/docs/content/docs/getting-started/installation.mdx b/apps/docs/content/docs/getting-started/installation.mdx index d5ca240c4..cb5ed7fa4 100644 --- a/apps/docs/content/docs/getting-started/installation.mdx +++ b/apps/docs/content/docs/getting-started/installation.mdx @@ -39,6 +39,22 @@ Rendering happens inside a [`Canvas`](/api/canvas) view. The typical loop is: Start with the [Canvas](/docs/getting-started/canvas) view once installation is done. +## Testing with Jest + +`react-native-webgpu` wraps a native module, which isn't available under Jest. Add its mock to `setupFilesAfterEnv` in your `jest.config.js`: + +```js +// jest.config.js +module.exports = { + // Other values + setupFilesAfterEnv: [ + "react-native-webgpu/jestSetup.js", + ], +}; +``` + +This replaces the package with a JS-only stand-in: `` renders as a plain `View` so screens built around it can still be rendered in tests, while GPU-dependent APIs (`importDevice`, `adoptTexture`, `useDevice`, a real canvas context) throw a clear error if a test actually exercises them. + ## Troubleshooting diff --git a/packages/webgpu/jestSetup.js b/packages/webgpu/jestSetup.js new file mode 100644 index 000000000..15068b811 --- /dev/null +++ b/packages/webgpu/jestSetup.js @@ -0,0 +1,11 @@ +/* globals jest */ + +// `react-native-webgpu` wraps a native TurboModule (`NativeWebGPUModule.ts` +// calls `TurboModuleRegistry.getEnforcing("WebGPUModule")` at import time), +// which throws under Jest since there is no native binary to register +// against. Swap the whole package for a JS-only stand-in (see `src/mock.ts`) +// so screens built around `` / `importDevice` / etc. can still be +// imported and rendered in tests. +jest.mock("react-native-webgpu", () => + require("react-native-webgpu/lib/module/mock"), +); diff --git a/packages/webgpu/package.json b/packages/webgpu/package.json index f3b2b765f..0f3475580 100644 --- a/packages/webgpu/package.json +++ b/packages/webgpu/package.json @@ -11,6 +11,7 @@ "src/**", "lib/**", "!**/__tests__/**", + "jestSetup.js", "android/build.gradle", "android/CMakeLists.txt", "android/cpp/**", diff --git a/packages/webgpu/src/mock.ts b/packages/webgpu/src/mock.ts new file mode 100644 index 000000000..7652d9e06 --- /dev/null +++ b/packages/webgpu/src/mock.ts @@ -0,0 +1,72 @@ +/// +import React from "react"; +import type { ReactNode } from "react"; +import { View } from "react-native"; +import type { ViewProps } from "react-native"; + +import type { NativeCanvas, RNCanvasContext } from "./Canvas"; + +// Jest mock for `react-native-webgpu`. +// +// There is no native WebGPU implementation under Jest, so this never talks to +// `NativeWebGPUModule` or the native `RNWebGPU` global. `Canvas` renders as a +// plain `View` so screens built around it can still be rendered in tests; +// anything that requires an actual GPU (`importDevice`, `adoptTexture`, +// `useDevice`, a real canvas context) fails loudly instead of silently +// returning fake data, mirroring how the real module already reports a +// missing native install (see `importDevice`/`adoptTexture` in +// `./importDevice.ts`). + +export * from "./constants"; +export * from "./install"; +export * from "./formats"; + +const NOT_AVAILABLE_UNDER_JEST = + "is not available under Jest - react-native-webgpu has no native implementation in tests (see react-native-webgpu/jestSetup.js)"; + +export type { CanvasRef, NativeCanvas, RNCanvasContext } from "./Canvas"; + +interface CanvasProps extends ViewProps { + transparent?: boolean; + ref?: React.Ref; +} + +export const Canvas = ({ + transparent: _transparent, + ref, + ...props +}: CanvasProps) => { + React.useImperativeHandle(ref, () => ({ + getContextId: () => -1, + getContext: (_contextName: "webgpu"): RNCanvasContext | null => null, + getNativeSurface: (): NativeCanvas => { + throw new Error(`Canvas.getNativeSurface() ${NOT_AVAILABLE_UNDER_JEST}`); + }, + })); + return React.createElement(View, props); +}; + +export const useCanvasRef = () => React.useRef(null); +export const useSurface = () => ({ ref: React.useRef(null), surface: null }); +export const warnIfNotHardwareAccelerated = () => undefined; + +export const useDevice = () => ({ adapter: null, device: null }); + +export const GPUDeviceProvider = ({ children }: { children?: ReactNode }) => + children ?? null; + +export const useMainDevice = (): never => { + throw new Error(`useMainDevice() ${NOT_AVAILABLE_UNDER_JEST}`); +}; + +export const importDevice = (_pointer: bigint): GPUDevice => { + throw new Error(`importDevice() ${NOT_AVAILABLE_UNDER_JEST}`); +}; + +export const adoptTexture = (_pointer: bigint): GPUTexture => { + throw new Error(`adoptTexture() ${NOT_AVAILABLE_UNDER_JEST}`); +}; + +export const installWebGPU = () => undefined; + +export const WebGPUModule = { install: () => false };