How to add <canvas> support to my tests in Jest?

早过忘川 提交于 2019-12-03 23:32:04

问题


In my Jest unit test I am rendering a component with a ColorPicker. The ColorPicker component creates a canvas object and 2d context but returns 'undefined' which throws an error "Cannot set property 'fillStyle' of undefined"

if (typeof document == 'undefined') return null; // Dont Render On Server
var canvas = document.createElement('canvas'); 
canvas.width = canvas.height = size * 2;
var ctx = canvas.getContext('2d'); // returns 'undefined'
ctx.fillStyle = c1; // "Cannot set property 'fillStyle' of undefined"

I'm having troubles figuring out why I can't get a 2d context. Maybe there an issue with my test config?

"jest": {
  "scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
  "unmockedModulePathPatterns": [
    "<rootDir>/node_modules/react",
    "<rootDir>/node_modules/react-dom",
    "<rootDir>/node_modules/react-addons-test-utils",
    "<rootDir>/node_modules/react-tools"
  ],
  "moduleFileExtensions": [
    "jsx",
    "js",
    "json",
    "es6"
  ],
  "testFileExtensions": [
    "jsx"
  ],
  "collectCoverage": true
}

回答1:


It's because your test doesn't run in a real browser. Jest uses jsdom for mocking the necessary parts of the DOM to be able to run the tests in Node, thus avoiding style calculation and rendering that a browser would normally do. This is cool because this makes tests fast.

On the other hand, if you need browser APIs in your components, it's more difficult then in browser. Luckily, jsdom has support for canvas. You just have to configure it:

jsdom includes support for using the canvas package to extend any <canvas> elements with the canvas API. To make this work, you need to include canvas as a dependency in your project, as a peer of jsdom. If jsdom can find the canvas package, it will use it, but if it's not present, then <canvas> elements will behave like <div>s.

Alternatively, you could replace Jest with some browser-based test runner like Karma. Jest is pretty buggy anyway.




回答2:


For those looking for examples using create-react-app

Install

yarn add --dev jest-canvas-mock

Create a new ${rootDir}/src/setupTests.js with

import 'jest-canvas-mock';



回答3:


I had the exact same issue. I'm deploying to gitlab ci to run my tests, and since npm canvas requires an installation of Cairo, using that wasn't a viable option.

All I really wanted to do was mock the implementation through Jest so that it doesn't actually try to create a real context. Here's how I solved it:

added to package.json

"jest": {
  "setupFiles": ["./tests/setup.js"],
}

tests/setup.js

import sinon from 'sinon';

const createElement = global.document.createElement;
const FAKECanvasElement = {
  getContext: jest.fn(() => {
    return {
      fillStyle: null,
      fillRect: jest.fn(),
      drawImage: jest.fn(),
      getImageData: jest.fn(),
    };
  }),
};

/**
 * Using Sinon to stub the createElement function call with the original method
 * unless we match the 'canvas' argument.  If that's the case, return the Fake 
 * Canvas object.
 */
sinon.stub(global.document, 'createElement')
  .callsFake(createElement)
  .withArgs('canvas')
  .returns(FAKECanvasElement);



回答4:


For my use case I did simple monkey patching like this

beforeEach(() => {
    const createElement = document.createElement.bind(document);
    document.createElement = (tagName) => {
        if (tagName === 'canvas') {
            return {
                getContext: () => ({}),
                measureText: () => ({})
            };
        }
        return createElement(tagName);
    };
});

No need to install canvas-prebuilt or sinon.




回答5:


npm install -D canvas-prebuilt@1

This provides support for canvas for jest.This even works if someone is getting error due to Lottie.js.




回答6:


To test canvas output in jest you need to do the following:

Make sure you are using at least jsdom 13. You can do this by including the jsom package for jest, for 14 it is:

jest-environment-jsdom-fourteen

And configuring jest to use this

jest --env=jest-environment-jsdom-fourteen

or in the package.json

"jest": {
   ...
   "testEnvironment": "jest-environment-jsdom-fourteen",

Include the canvas npm package. (as of 2.x this includes built version so canvas-prebuilt is deprecated).




回答7:


I managed to create an image snapshot test from a canvas in jest with react-testing-library and jest-image-snapshot. That's sort of buzzwordy but it worked nicely.

If you are able to properly setup your jest testing with node-canvas (not jest-canvas-mock or similar) then you can literally call toDataURL on the canvas element.


  import {render, waitForElement} from 'react-testing-library'
  import React from 'react'
  import { toMatchImageSnapshot } from 'jest-image-snapshot'

  expect.extend({ toMatchImageSnapshot })

  test('open a canvas', async () => {
    const { getByTestId } = render(
      <YourCanvasContainer />,
    )
    const canvas = await waitForElement(() =>
      getByTestId('your_canvas'),
    )
    const img = canvas.toDataURL()
    const data = img.replace(/^data:image\/\w+;base64,/, '')
    const buf = Buffer.from(data, 'base64')
    expect(buf).toMatchImageSnapshot({
      failureThreshold: 0.001,
      failureThresholdType: 'percent',
    })
  })

I found that I needed to use a low threshold rather than directly comparing the image/png data URL because there were two pixels that were just randomly different when running on travis-CI

Also consider manually upgrading jest environment jsdom to jest-environment-jsdom-thirteen or jest-environment-jsdom-fourteen (other answers on this page suggest similar) and refer to https://github.com/jsdom/jsdom/issues/1782



来源:https://stackoverflow.com/questions/33269093/how-to-add-canvas-support-to-my-tests-in-jest

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!