【问题标题】:How to add <canvas> support to my tests in Jest?如何在 Jest 中为我的测试添加 <canvas> 支持?
【发布时间】:2016-01-21 00:39:42
【问题描述】:

在我的Jest 单元测试中,我正在渲染一个带有ColorPicker 的组件。 ColorPicker 组件创建一个画布对象和 2d 上下文,但返回 '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
}

【问题讨论】:

    标签: reactjs html5-canvas jsdom jestjs


    【解决方案1】:

    对于那些使用 create-react-app 寻找示例的人

    安装

    yarn add --dev jest-canvas-mock
    

    创建一个新的${rootDir}/src/setupTests.js
    import 'jest-canvas-mock';
    

    【讨论】:

    • 就我而言,我将jest-canvas-mock 添加到我的jest.config.js 中,就像这样setupFiles: [ "jest-canvas-mock" ]
    • create react app 没有 jest.config.js 文件我们需要创建吗?以及当我们添加 setupFiles: [ "jest-canvas-mock" ] 时它应该是什么样子?
    • 是什么导入了这个神秘的新setupTests.js 文件? 如何神奇地使用它?
    【解决方案2】:

    这是因为您的测试没有在真正的浏览器中运行。 Jest 使用jsdom 模拟 DOM 的必要部分,以便能够在 Node 中运行测试,从而避免浏览器通常会执行的样式计算和呈现。这很酷,因为这可以加快测试速度。

    另一方面,如果您需要在组件中使用浏览器 API,这比在浏览器中要困难得多。幸运的是,jsdom has support for canvas。你只需要配置它:

    jsdom 支持使用 canvas 包通过 canvas API 扩展任何 &lt;canvas&gt; 元素。为了使这项工作,您需要在项目中包含 canvas 作为依赖项,作为 jsdom 的对等点。如果 jsdom 可以找到 canvas 包,它将使用它,但如果它不存在,则 &lt;canvas&gt; 元素的行为类似于 &lt;div&gt;s。

    或者,您可以将 Jest 替换为一些基于浏览器的测试运行程序,例如 Karma。开玩笑的是pretty buggy

    【讨论】:

    • 我正在切换到业力。安装 canvas 包依赖项对于其他人来说太麻烦了。谢谢!
    • 老问题/答案,但 Jest 从那时起有了很大的改进,要让画布与 jsdom(在大多数系统上)一起工作,您需要做的就是 npm install canvas-prebuilt - npmjs.com/package/canvas-prebuilt
    • @ChidG 谢谢你提供的信息。普通的画布包是不可能在 Windows 10 上构建的,至少对我来说......
    • 我也安装了画布,即使在那个玩笑未能检测到这个画布元素之后,我只能根据项目要求使用玩笑......你能帮我吗,即使我试过了画布预建...同样的问题。请帮助
    • 如果有人来寻找解决方案,Jest 提供了一个非常旧的 jsdom 版本。如果您将jsdom 添加到您的依赖项并将分辨率添加到"jest/**/jsdom": "13.0.0",这应该可以解决它。
    【解决方案3】:

    如果安装了库node-canvas,Jest / jsdom 可以处理画布元素。

    因此卸载jest-canvas-mock(如果已安装)并安装canvas

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

    【讨论】:

    • 它适用于我的 create-react-app。
    • 这是create-react-app/react-scripts 4+的最佳答案
    • 花了很长时间才找到一个干净的解决方案 - jest-canvas-mock 对我不起作用,但这个答案确实有效。使用 react-scripts 4.0.3 创建-react-app
    • 我最初尝试使用 jest-canvas-mock,但这真的很有帮助。
    【解决方案4】:

    对于我的用例,我做了这样的简单猴子修补

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

    无需安装canvas-prebuilt或sinon。

    【讨论】:

    • TypeError: ctx.measureText 不是函数
    • 哦,几个月后找到了我自己的评论
    【解决方案5】:

    我遇到了完全相同的问题。我正在部署到 gitlab ci 来运行我的测试,并且由于 npm canvas 需要安装 Cairo,因此使用它不是一个可行的选择。

    我真正想做的就是通过 Jest 模拟实现,这样它就不会真正尝试创建真实的上下文。以下是我的解决方法:

    添加到 package.json

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

    测试/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);
    

    【讨论】:

      【解决方案6】:

      jest-canvas-mock 会很好用。

      1) 由**npm i --save-dev jest-canvas-mock**安装

      2) 在你的玩笑jest.config.js 中添加"setupFiles": ["jest-canvas-mock"] 属性。 (如果您已经有 setupFiles 属性,您还可以将 jest-canvas-mock 附加到数组中,例如 "setupFiles": ["something-xyz.js", "jest-canvas-mock"])。

      全部完成。

      【讨论】:

        【解决方案7】:

        要开玩笑地测试画布输出,您需要执行以下操作:

        确保您使用的 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)。

        【讨论】:

          【解决方案8】:

          如果您使用 create-react-app,请使用 npm i --save-dev jest-canvas-mock 安装 jest-canvas-mock,并在测试文件的顶部放置 import 'jest-canvas-mock'

          【讨论】:

            【解决方案9】:
            npm install -D canvas-prebuilt@1
            

            这为 jest 提供了对画布的支持。即使有人因 Lottie.js 出现错误,这也可以工作。

            【讨论】:

            • 似乎从Jun 23, 2020开始,安装失败
            【解决方案10】:

            我设法使用 react-testing-library 和 jest-image-snapshot 从画布中创建了一个图像快照测试。这是一种流行语,但效果很好。

            如果您能够使用 node-canvas(不是 jest-canvas-mock 或类似的)正确设置您的 jest 测试,那么您可以在 canvas 元素上调用 toDataURL。

            
              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',
                })
              })
            

            我发现我需要使用低阈值而不是直接比较图像/png 数据 URL,因为在 travis-CI 上运行时有两个像素随机不同

            还可以考虑手动将 jest 环境 jsdom 升级到 jest-environment-jsdom-thirteen 或 jest-environment-jsdom-fourteen(此页面上的其他答案建议类似)并参考https://github.com/jsdom/jsdom/issues/1782

            【讨论】:

              【解决方案11】:

              就我而言,我正在使用 react。

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

              这两个命令很有帮助。

              【讨论】:

                猜你喜欢
                • 2019-12-21
                • 1970-01-01
                • 2011-12-17
                • 2015-11-17
                • 1970-01-01
                • 1970-01-01
                • 2022-01-13
                • 2017-01-04
                • 2017-12-14
                相关资源
                最近更新 更多