Screens with a <Scene> can be tested with Jest (jest-expo) and React Native Testing Library. Tests run in Node, with no GPU, so mock Apricot with @apricot/react/mock:
<Scene>is a plainViewwith the testIDapricot-scene.- 3D components draw nothing but render their children, so your own components and the text in a
<Label>are still there to query. - Hooks return values that do nothing (
useFramedoes nothing,useModelreturns no object). useLayoutandfileStorageare the real ones, so layout logic (add, move, undo, saving) can be tested for real.
Setup#
Apricot ships TypeScript sources, so let Jest compile it:
jest.config.js
module.exports = {
preset: "jest-expo",
transformIgnorePatterns: [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@apricot/.*)",
],
};Mock the entry points your screens use, in each test file or once in Jest's setup file:
jest.setup.js
jest.mock("@apricot/react", () => require("@apricot/react/mock"));
jest.mock("@apricot/react/model", () => require("@apricot/react/mock"));
jest.mock("@apricot/react/capture", () => require("@apricot/react/mock"));A test#
Showroom.test.tsx
import { fireEvent, render, screen } from "@testing-library/react-native";
import { Showroom } from "./Showroom";
jest.mock("@apricot/react", () => require("@apricot/react/mock"));
jest.mock("@apricot/react/model", () => require("@apricot/react/mock"));
test("shows the scene and the colour picker", () => {
render(<Showroom />);
expect(screen.getByTestId("apricot-scene")).toBeTruthy();
fireEvent.press(screen.getByText("Red"));
expect(screen.getByText("Red")).toBeTruthy();
});What the mock returns#
| Export | In the mock |
|---|---|
<Scene> |
A View (testID="apricot-scene") with its style and children |
| Every other component | Renders its children only. A ref gets a handle whose methods do nothing |
useFrame, useSceneTouches, useAttach, useTransform |
Do nothing |
useScene, useParent, usePath, useTrip, useWaves, useShore |
Objects whose properties and methods do nothing |
useAnimatable(value) |
Returns a reader that gives value |
useDaylight() |
Returns a reader that gives null |
useModel |
{ object: null, animations: [], error: null } |
useTexture, useTextureState |
null, { texture: null, error: null } |
captureScene |
Resolves { uri: "file:///apricot/capture.png", width: 1, height: 1 } |
download, isDownloaded, measureModel |
Resolve at once; isDownloaded is true; measureModel is a 1 m box |
useLayout, fileStorage |
The real ones |
Taps on 3D objects can't be simulated: test what your handlers do by calling them, or by pressing the React Native buttons that drive the scene.
Related#
- Placing things by hand:
useLayout. - Installation.