Setting up Jest with React and Phaser

Taylor Nodell
4 min readJul 8, 2019

After getting my Phaser set up working with React, I knew I needed to be smart about how my web app would grow and implement a testing framework.

I chose Jest as my testing engine since Jest seems to be the preferred testing framework for React apps (create-react-app ships with it). However, despite the claim on the homepage that most JavaScript projects should require zero config, I did need to tweek my package.json and resolve some dependency issues. If you’re using Phaser and React you might have a few of these too. End repo can be seen: https://github.com/nodes777/phaser3-react-template

First lets add Jest. And if you’re using React, you might be writing jsx files so we’ll need to transform those so that Jest can test those too. The Jest docs are pretty straightforward on this.

npm i --dev jest babel-jest @babel/preset-env @babel/preset-react react-test-renderer

Which gives a package.json that needs a jest config value, and a scripts config that needs a test property and value. See the docs for the full package.json and babel.config.js

If you start writing a test at this point and are using require to pull in something from your project to test, you may be getting a syntax error.

const { config } = require(“../src/index.js”);

Syntax Error for require

This is a problem I had as well and was stuck on for a while. The solution here is to npm install --save-dev @babel/plugin-proposal-class-properties @babel/plugin-transform-modules-commonjs

and include these as presets in your babelrc.js

const presets = [
[
"@babel/env",
{
targets: {
browsers: [">0.25%", "not ie 11", "not op_mini all"]
},
modules: false
}
],
"@babel/preset-react"
];
const plugins = [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-modules-commonjs"
];
module.exports = { presets, plugins };

The error occurs because require is the node environment syntax for importing variables, and Jest runs in node. More background on the differences between import/require and node can be seen in this SO question.

Because Jest isn’t a browser based test runner, it can run faster, but at the expense of lacking some features you might get out of the box with something like Karma. You’re likely getting a new error now TypeError: Cannot set property ‘fillStyle’ of null firing on the Phaser import.

Cannot set property ‘fillStyle’ of null on the Phaser import

That null is actually supposed to be the canvas element. Jest uses jsdom as a psuedo DOM, but it doesn’t include the canvas straight away. You must add in canvas support so jsdom can find it. Luckily there’s an npm package for that.

npm i --save-dev jest-canvas-mock

Per jest-canvas-mock’s docs, we also have to add the setupFiles in our package.json as well:

"jest": {
"setupFiles": ["jest-canvas-mock"]
}

At this point you may be running into syntax errors with any files that aren’t JavaScript.

An invalid token error for our png

For this, I mapped out the image and CSS files to a mock file. Anytime the test imports an image or css file, it just gets a mock file with an empty object. Now my jest config in package.json looks like:

"jest": {
"transform": {
"^.+\\.jsx?$": "babel-jest"
},
"setupFiles": [
"jest-canvas-mock"
],
"moduleFileExtensions": [
"js",
"jsx"
],
"moduleNameMapper": {
"\\.(css|less|sass|scss)$": "<rootDir>/test/mocks/styleMock.js",
"\\.(gif|ttf|eot|svg|png)$": "<rootDir>/test/mocks/fileMock.js"
}
}

And you have to add the styleMock.js and fileMock.js in the locations described above as well. They’re simple files:

styleMock.js

module.exports = {};

fileMock.js

module.exports = “test-file-stub”;

At this point you’re probably ready to just start writing some tests. If you’re inclined to start writing them on index.js, as I was, you’re rewarded with one more error!

Here’s my index.test.js

const { config } = require("../src/index.js");test("string is a string", () => {
expect(typeof "string").toBe("string");
});
test("config is an object", () => {
expect(typeof config).toBe("object");
});

Importing the config from index.js is where the error starts.

Invariant Violation: Target container is not a DOM element.

Invariant Violation for the rendered element not being attached to a DOM element

From my searching, this is a frequent error with many sources. It seems that rendering to the #root, which exists in my index.html, doesn’t exist when testing inthe jsdom. I only really found a workaround, in my index.js, I have an ||operator to create a div if it doesn’t exist.

ReactDOM.render(
<App />,
document.getElementById("root") || document.createElement("div")
);

Finally, you should be able to run some tests and start creating robust code! Let me know if you have a better solution to that Invariant Violation error, I was pulling my hair out for a while over these and a workaround was good enough for me at that point.

Full template repo: https://github.com/nodes777/phaser3-react-template

--

--

Taylor Nodell

Developer. Musician. Naturalist. Traveler. In any order. @tayloredtotaylor