如何在 Jest 中为我的测试添加 <canvas> 支持?
How to add <canvas> support to my tests in Jest?
在我的Jest unit test I am rendering a component with a ColorPicker。 ColorPicker
组件创建一个 canvas 对象和 2d 上下文,但是 returns 'undefined'
会引发错误 "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"
我无法弄清楚为什么无法获得 2d 上下文。也许我的测试配置有问题?
"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
}
这是因为您的测试未在真实浏览器中 运行。 Jest 使用 jsdom
模拟 DOM 的必要部分,以便能够 运行 Node 中的测试,从而避免了浏览器通常会进行的样式计算和渲染。这很酷,因为这可以加快测试速度。
另一方面,如果您需要在组件中使用浏览器 API,则比在浏览器中更难。幸运的是,jsdom
has support for canvas。您只需配置它:
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.
或者,您可以用一些基于浏览器的测试 运行 替换 Jest,例如 Karma. Jest is pretty buggy。
我遇到了完全相同的问题。我正在部署到 gitlab ci 到 运行 我的测试,并且由于 npm canvas 需要安装 Cairo,所以使用它不是一个可行的选择。
我真正想做的就是通过 Jest 模拟实现,这样它实际上就不会尝试创建真实的上下文。这是我解决它的方法:
添加到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);
对于我的用例,我做了像这样的简单猴子修补
beforeEach(() => {
const createElement = document.createElement.bind(document);
document.createElement = (tagName) => {
if (tagName === 'canvas') {
return {
getContext: () => ({}),
measureText: () => ({})
};
}
return createElement(tagName);
};
});
无需安装 canvas-prebuilt 或 sinon。
对于那些寻找使用 create-react-app 的例子的人
安装
yarn add --dev jest-canvas-mock
用
创建一个新的${rootDir}/src/setupTests.js
import 'jest-canvas-mock';
npm install -D canvas-prebuilt@1
这为 jest.This 提供了对 canvas 的支持,即使有人因 Lottie.js.
而出错时也能正常工作
要测试 canvas 开玩笑的输出,您需要执行以下操作:
确保你至少使用 jsdom 13。你可以通过包含 jest 的 jsom 包来做到这一点,对于 14,它是:
jest-environment-jsdom-fourteen
并配置 jest 以使用此
jest --env=jest-environment-jsdom-fourteen
或在package.json
"jest": {
...
"testEnvironment": "jest-environment-jsdom-fourteen",
包括 canvas
npm 包。 (从 2.x 开始,这包括内置版本,因此不推荐使用 canvas-prebuilt)。
我设法从 canvas 开玩笑地使用 react-testing-library 和 jest-image-snapshot 创建了一个图像快照测试。这有点流行,但效果很好。
如果您能够使用 node-canvas(不是 jest-canvas-mock 或类似的)正确设置您的玩笑测试,那么您可以直接调用 toDataURL canvas 元素。
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',
})
})
我发现我需要使用低阈值而不是直接比较 image/png 数据 URL 因为有两个像素在 travis 上 运行 时随机不同CI
也可以考虑手动升级 jest environment jsdom 到 jest-environment-jsdom-13 或 jest-environment-jsdom-14(本页其他答案建议类似)并参考 https://github.com/jsdom/jsdom/issues/1782
jest-canvas-mock 会很好用。
安装 npm i --save-dev jest-canvas-mock
在你的笑话中 jest.config.js 添加 "setupFiles": ["jest-canvas-mock"]
属性。
(如果您已经拥有 setupFiles 属性,您还可以将 jest-canvas-mock 附加到数组,例如 "setupFiles": ["something-xyz.js", "jest-canvas-mock"]
)。
全部完成。
如果您使用的是 create-react-app,请使用 npm i --save-dev jest-canvas-mock
安装 jest-canvas-mock 并在测试文件的顶部放置 import 'jest-canvas-mock'
如果安装了 node-canvas 库,Jest / jsdom 可以处理 canvas 个元素。
因此卸载 jest-canvas-mock
(如果安装)并安装 canvas
:
npm uninstall jest-canvas-mock
npm i --save-dev canvas
在我的例子中,我使用的是 React。
npm uninstall jest-canvas-mock
npm i --save-dev canvas
这两个命令很有用。
在我的Jest unit test I am rendering a component with a ColorPicker。 ColorPicker
组件创建一个 canvas 对象和 2d 上下文,但是 returns 'undefined'
会引发错误 "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"
我无法弄清楚为什么无法获得 2d 上下文。也许我的测试配置有问题?
"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
}
这是因为您的测试未在真实浏览器中 运行。 Jest 使用 jsdom
模拟 DOM 的必要部分,以便能够 运行 Node 中的测试,从而避免了浏览器通常会进行的样式计算和渲染。这很酷,因为这可以加快测试速度。
另一方面,如果您需要在组件中使用浏览器 API,则比在浏览器中更难。幸运的是,jsdom
has support for canvas。您只需配置它:
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.
或者,您可以用一些基于浏览器的测试 运行 替换 Jest,例如 Karma. Jest is pretty buggy。
我遇到了完全相同的问题。我正在部署到 gitlab ci 到 运行 我的测试,并且由于 npm canvas 需要安装 Cairo,所以使用它不是一个可行的选择。
我真正想做的就是通过 Jest 模拟实现,这样它实际上就不会尝试创建真实的上下文。这是我解决它的方法:
添加到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);
对于我的用例,我做了像这样的简单猴子修补
beforeEach(() => {
const createElement = document.createElement.bind(document);
document.createElement = (tagName) => {
if (tagName === 'canvas') {
return {
getContext: () => ({}),
measureText: () => ({})
};
}
return createElement(tagName);
};
});
无需安装 canvas-prebuilt 或 sinon。
对于那些寻找使用 create-react-app 的例子的人
安装
yarn add --dev jest-canvas-mock
用
创建一个新的${rootDir}/src/setupTests.js
import 'jest-canvas-mock';
npm install -D canvas-prebuilt@1
这为 jest.This 提供了对 canvas 的支持,即使有人因 Lottie.js.
而出错时也能正常工作要测试 canvas 开玩笑的输出,您需要执行以下操作:
确保你至少使用 jsdom 13。你可以通过包含 jest 的 jsom 包来做到这一点,对于 14,它是:
jest-environment-jsdom-fourteen
并配置 jest 以使用此
jest --env=jest-environment-jsdom-fourteen
或在package.json
"jest": {
...
"testEnvironment": "jest-environment-jsdom-fourteen",
包括 canvas
npm 包。 (从 2.x 开始,这包括内置版本,因此不推荐使用 canvas-prebuilt)。
我设法从 canvas 开玩笑地使用 react-testing-library 和 jest-image-snapshot 创建了一个图像快照测试。这有点流行,但效果很好。
如果您能够使用 node-canvas(不是 jest-canvas-mock 或类似的)正确设置您的玩笑测试,那么您可以直接调用 toDataURL canvas 元素。
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',
})
})
我发现我需要使用低阈值而不是直接比较 image/png 数据 URL 因为有两个像素在 travis 上 运行 时随机不同CI
也可以考虑手动升级 jest environment jsdom 到 jest-environment-jsdom-13 或 jest-environment-jsdom-14(本页其他答案建议类似)并参考 https://github.com/jsdom/jsdom/issues/1782
jest-canvas-mock 会很好用。
安装
npm i --save-dev jest-canvas-mock
在你的笑话中 jest.config.js 添加
"setupFiles": ["jest-canvas-mock"]
属性。
(如果您已经拥有 setupFiles 属性,您还可以将 jest-canvas-mock 附加到数组,例如"setupFiles": ["something-xyz.js", "jest-canvas-mock"]
)。
全部完成。
如果您使用的是 create-react-app,请使用 npm i --save-dev jest-canvas-mock
安装 jest-canvas-mock 并在测试文件的顶部放置 import 'jest-canvas-mock'
如果安装了 node-canvas 库,Jest / jsdom 可以处理 canvas 个元素。
因此卸载 jest-canvas-mock
(如果安装)并安装 canvas
:
npm uninstall jest-canvas-mock
npm i --save-dev canvas
在我的例子中,我使用的是 React。
npm uninstall jest-canvas-mock
npm i --save-dev canvas
这两个命令很有用。