【问题标题】:Jest with jsdom, document is undefined inside Promise resolve用 jsdom 开玩笑,文档在 Promise 解析中未定义
【发布时间】:2018-07-16 11:59:06
【问题描述】:

场景

尝试使用 Jest(和 Enzyme)测试一个简单的 React 组件。这个组件使用react-dropzone,我想测试一些涉及DOM的操作,所以我使用jsdom(已经由create-react-app配置)

问题

document 对象虽然在我的测试代码中可用并且在组件内部也可用,但在 dropzone onDrop 回调内部是 undefined,它会阻止测试运行。

代码

MyDropzone

import React from 'react'
import Dropzone from 'react-dropzone'

const MyDropzone = () => {
    const onDrop = ( files ) =>{
        fileToBase64({file: files[0]})
            .then(base64Url => {
                return resizeBase64Img({base64Url})
            })
            .then( resizedURL => {
                console.log(resizedURL.substr(0, 50))
            })
    }
    return (
        <div>
            <Dropzone onDrop={onDrop}>
                Some text
            </Dropzone>
        </div>
    );
};

const fileToBase64 = ({file}) => {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = () => {
            return resolve(reader.result)
        }
        reader.onerror = (error) => {
            return reject(error)
        }
        reader.readAsDataURL(file)
    })
}

/**
 * Resize base64 image to width and height,
 * keeping the original image proportions
 * with the width winning over the height
 *
 */
const resizeBase64Img = ({base64Url, width = 50}) => {
    const canvas = document.createElement('canvas')
    canvas.width = width
    const context = canvas.getContext('2d')
    const img = new Image()

    return new Promise((resolve, reject) => {
        img.onload = () => {
            const imgH = img.height
            const imgW = img.width
            const ratio = imgW / imgH
            canvas.height = width / ratio
            context.scale(canvas.width / imgW, canvas.height / imgH)
            context.drawImage(img, 0, 0)
            resolve(canvas.toDataURL())
        }

        img.onerror = (error) => {
            reject(error)
        }

        img.src = base64Url
    })
}

export default MyDropzone;

MyDropzone.test.jsx

import React from 'react'
import { mount } from 'enzyme'
import Dropzone from 'react-dropzone'

import MyDropzone from '../MyDropzone'

describe('DropzoneInput component', () => {
    it('Mounts', () => {
        const comp = mount(<MyDropzone />)
        const dz = comp.find(Dropzone)
        const file = new File([''], 'testfile.jpg')
        console.log(document)
        dz.props().onDrop([file])
    })
})

setupJest.js

import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

configure({ adapter: new Adapter() })

配置

  • 默认create-react-app开玩笑配置setupJest.js添加到setupFiles
  • 运行:纱线测试

错误

TypeError: Cannot read property 'createElement' of undefined
    at resizeBase64Img (C:\dev\html\sandbox\src\MyDropzone.jsx:44:29)
    at fileToBase64.then.base64Url (C:\dev\html\sandbox\src\MyDropzone.jsx:8:20)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)

更多信息

如果在浏览器中运行该代码,则始终定义 document,所以对我来说,这个问题似乎与 jsdom 或 Jest 有关。

我不确定它是否与 Promises、FileReaded 或一般的 JS 范围有关。

也许是 Jest 方面的错误?

【问题讨论】:

  • 我在使用 jest 读取磁盘上的文件时遇到了同样的问题。
  • 开玩笑设置中的一切都是正确的。在测试环境中使用 jsdom 作为文档。除了在其他地方定义文档的地方文档为 null 的内部承诺。
  • @Zword,你能提供一个最小的调试仓库吗?它会更快
  • @TarunLalwani here ya go。有趣的是,我发现这很容易用 JS 应用程序重现,但不能用 TS app

标签: javascript reactjs jestjs enzyme jsdom


【解决方案1】:

所以我能够解决这个问题。假设它在没有任何配置更改的情况下工作是错误的。首先,您需要添加更多的软件包。以下是我更新的package.json

{
  "name": "js-cra",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "react-dropzone": "^4.2.9",
    "react-scripts": "1.1.4",
    "react-test-renderer": "^16.3.2"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "devDependencies": {
    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1",
    "jest-enzyme": "^6.0.0",
    "jsdom": "11.10.0",
    "jsdom-global": "3.0.2"
  }
}

我还从测试脚本中删除了--env=jsdom。因为我无法使其与该组合一起使用

之后,您需要创建一个src/setupTests.js,它是为您的测试加载全局变量。这里你需要加载jsdomenzyme

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'jest-enzyme';
import 'jsdom-global/register'; //at the top of file , even  , before importing react

configure({ adapter: new Adapter() });

之后你的测试会出现以下错误

/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jsdom-js-demo/node_modules/react-scripts/scripts/test.js:20
  throw err;
  ^

ReferenceError: FileReader is not defined

问题似乎是FileReader 应该使用window 范围来引用。所以你需要像下面这样更新它

const reader = new window.FileReader()

然后再次运行测试

现在测试工作正常

【讨论】:

  • 这可能对某些人有所帮助,这就是我奖励赏金的原因,但在导入 jsdom-global 时我收到此错误Failed to execute 'dispatchEvent' on 'EventTarget'
  • 你能更新异常吗?当我忘记从 package.json 中删除 --env=jsdom 时,我也遇到了类似的情况
  • 我删除了 --env=jsdom 并且它给了我其他错误,例如 window.scrollTo 失败
  • 我在你分享的 repo 上运行了测试,这就是为什么我首先想要 repo
  • 这是我的实际代码:github.com/JesselJohn/react-components。你可以试试这个
猜你喜欢
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2019-11-28
  • 2022-07-12
  • 2019-09-25
  • 2017-09-11
  • 2018-06-02
  • 2021-09-17
相关资源
最近更新 更多