Skip to content

Commit

Permalink
Skia Jest Environnement (#1943)
Browse files Browse the repository at this point in the history
Co-authored-by: Simen Bekkhus <[email protected]>
  • Loading branch information
wcandillon and SimenB authored Oct 31, 2023
1 parent 31d092d commit c3b3f90
Show file tree
Hide file tree
Showing 10 changed files with 599 additions and 446 deletions.
49 changes: 44 additions & 5 deletions docs/docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,52 @@ There is also an [React Native VSCode extension](https://marketplace.visualstudi

## Testing with Jest

React Native Skia test mocks use web implementation which depends on loading CanvasKit. Before you begin using the mocks you need some setup actions.
React Native Skia test mocks use a web implementation that depends on loading CanvasKit. Before using the mocks, some setup actions are required.

In order to load CanvasKit and in turn loading the React Native Skia mock, you need to add the following your jest config:
We recommend using [ESM](https://jestjs.io/docs/ecmascript-modules). To enable ESM support, you need to update your `jest` command to `node --experimental-vm-modules node_modules/.bin/jest`.

### ESM Setup

To load CanvasKit and subsequently the React Native Skia mock, add the following setup file to your Jest configuration:

```js
// notice the extension: .mjs
"setupFiles": ["@shopify/react-native-skia/jestSetup.mjs"]
```

### CommonJS Setup

We also offer a version of the setup file without ECMAScript modules support. To use this version, add the following setup file to your Jest configuration:

```js
// This is needed to load CanvasKit
"globalSetup": "@shopify/react-native-skia/globalJestSetup.js",
// This is needed to load the mock
// notice the extension: .js
"setupFiles": ["@shopify/react-native-skia/jestSetup.js"]
```

With this setup, you need to load the Skia environment in your test. Include the following at the top of your test file:

```js
/**
* @jest-environment @shopify/react-native-skia/jestEnv.mjs
*/
```

For instance:

```js
/**
* @jest-environment @shopify/react-native-skia/jestEnv.mjs
*/
import "react-native";
import React from "react";
import { cleanup, render } from "@testing-library/react-native";
import App from "./App";

it("renders correctly", () => {
render(<App />);
});

afterEach(cleanup);
```

With this configuration, you will have properly set up Jest to work with React Native Skia mocks using either ECMAScript Modules or CommonJS.
7 changes: 0 additions & 7 deletions example/globalJestSetup.js

This file was deleted.

13 changes: 13 additions & 0 deletions example/jestEnv.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable import/no-default-export */
/* eslint-disable import/no-extraneous-dependencies */
import CanvasKitInit from "canvaskit-wasm/bin/full/canvaskit";
import { TestEnvironment } from "jest-environment-node";

const CanvasKit = await CanvasKitInit({});

export default class SkiaEnvironment extends TestEnvironment {
constructor(...args) {
super(...args);
this.global.CanvasKit = CanvasKit;
}
}
16 changes: 12 additions & 4 deletions example/jestSetup.js → example/jestSetup.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
require("react-native-reanimated/lib/module/reanimated2/jestUtils").setUpTests();
/* eslint-disable import/no-extraneous-dependencies */
import { jest } from "@jest/globals";
import CanvasKitInit from "canvaskit-wasm/bin/full/canvaskit";
import JestUtils from "react-native-reanimated/lib/module/reanimated2/jestUtils";
import Reanimated from "react-native-reanimated/mock";

import Mock from "../package/src/mock";

JestUtils.setUpTests();
global.__reanimatedWorkletInit = () => {};
jest.mock("react-native-reanimated", () => {
const Reanimated = require("react-native-reanimated/mock");
global.CanvasKit = await CanvasKitInit({});

jest.mock("react-native-reanimated", () => {
// The mock for `call` immediately calls the callback which is incorrect
// So we override it with a no-op
Reanimated.default.call = () => {};
Expand Down Expand Up @@ -30,7 +38,7 @@ jest.mock("@shopify/react-native-skia", () => {
View: Noop,
};
});
return require("../package/src/mock").Mock(global.CanvasKit);
return Mock.Mock(global.CanvasKit);
});

const mockedNavigate = jest.fn();
Expand Down
5 changes: 2 additions & 3 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"start": "react-native start",
"web": "webpack serve --mode=development --config webpack.config.js",
"build:web": "rm -rf dist/ && webpack --mode=production --config webpack.config.js",
"test": "jest",
"test": "node --experimental-vm-modules node_modules/.bin/jest",
"lint": "eslint . --ext .ts,.tsx --max-warnings 0 --cache",
"pod-install": "npx pod-install ios",
"android-reverse-tcp": "adb devices | grep '\t' | awk '{print $1}' | sed 's/\\s//g' | xargs -I {} adb -s {} reverse tcp:8081 tcp:8081"
Expand Down Expand Up @@ -81,10 +81,9 @@
"<rootDir>/lib/typescript",
"setup.(ts|tsx)$"
],
"globalSetup": "<rootDir>/globalJestSetup.js",
"setupFilesAfterEnv": [
"<rootDir>/node_modules/react-native-gesture-handler/jestSetup.js",
"<rootDir>/jestSetup.js"
"<rootDir>/jestSetup.mjs"
],
"preset": "react-native",
"transformIgnorePatterns": [
Expand Down
6 changes: 0 additions & 6 deletions package/globalJestSetup.js

This file was deleted.

23 changes: 23 additions & 0 deletions package/jestEnv.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable import/no-default-export */
// eslint-disable-next-line import/no-extraneous-dependencies
import { TestEnvironment } from "jest-environment-node";
import { LoadSkiaWeb } from "@shopify/react-native-skia/lib/commonjs/web/LoadSkiaWeb";

const CanvasKit = await LoadSkiaWeb();

export default class SkiaEnvironment extends TestEnvironment {
constructor(config, context) {
super(config, context);
}

async setup() {
await super.setup();
this.global.CanvasKit = CanvasKit;
}

async teardown() {}

getVmContext() {
return super.getVmContext();
}
}
23 changes: 23 additions & 0 deletions package/jestSetup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable import/no-extraneous-dependencies */
import { jest } from "@jest/globals";
import CanvasKitInit from "canvaskit-wasm/bin/full/canvaskit";

import Mock from "./src/mock";

global.CanvasKit = await CanvasKitInit({});

jest.mock("@shopify/react-native-skia", () => {
jest.mock("@shopify/react-native-skia/lib/commonjs/Platform", () => {
const Noop = () => undefined;
return {
OS: "web",
PixelRatio: 1,
requireNativeComponent: Noop,
resolveAsset: Noop,
findNodeHandle: Noop,
NativeModules: Noop,
View: Noop,
};
});
return Mock.Mock(global.CanvasKit);
});
4 changes: 2 additions & 2 deletions package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@types/jest": "^28.1.6",
"@types/jest": "29.5.6",
"@types/pixelmatch": "^5.2.4",
"@types/pngjs": "^6.0.1",
"@types/react-native": "0.70.6",
Expand All @@ -92,7 +92,7 @@
"eslint": "8.21.0",
"eslint-config-react-native-wcandillon": "3.9.0",
"eslint-plugin-reanimated": "2.0.0",
"jest": "28.1.3",
"jest": "29.6.4",
"merge-dirs": "^0.2.1",
"pixelmatch": "^5.3.0",
"pngjs": "^6.0.0",
Expand Down
Loading

0 comments on commit c3b3f90

Please sign in to comment.