jsdom:dispatchEvent/addEventListener似乎不起作用

Jes*_*nan 8 javascript testing jsdom

摘要:

我试图测试一个侦听其中的本机DOM事件的React组件componentWillMount.

我发现jsdom(@8.4.0)在调度事件和添加事件监听器时没有按预期工作.

我可以提取的最简单的代码:

window.addEventListener('click', () => {
  throw new Error("success")
})

const event = new Event('click')
document.dispatchEvent(event)

throw new Error('failure')
Run Code Online (Sandbox Code Playgroud)

这引发了"失败".


语境:

如果上述问题存在XY问题,我想提供更多背景信息.

这是我试图测试的组件的提取/简化版本.你可以在Webpackbin上看到它.

import React from 'react'

export default class Example extends React.Component {
  constructor() {
    super()
    this._onDocumentClick = this._onDocumentClick.bind(this)
  }

  componentWillMount() {
    this.setState({ clicked: false })
    window.addEventListener('click', this._onDocumentClick)
  }

  _onDocumentClick() {
    const clicked = this.state.clicked || false
    this.setState({ clicked: !clicked })
  }


  render() {
    return <p>{JSON.stringify(this.state.clicked)}</p>
  }
}
Run Code Online (Sandbox Code Playgroud)

这是我正在尝试编写的测试.

import React from 'react'
import ReactDOM from 'react-dom'
import { mount } from 'enzyme'

import Example from '../src/example'

describe('test', () => {
  it('test', () => {
    const wrapper = mount(<Example />)

    const event = new Event('click')
    document.dispatchEvent(event)

    // at this point, I expect the component to re-render,
    // with updated state.

    expect(wrapper.text()).to.match(/true/)
  })
})
Run Code Online (Sandbox Code Playgroud)

只是为了完整性,这是我的test_helper.js初始化jsdom:

import { jsdom } from 'jsdom'
import chai from 'chai'

const doc = jsdom('<!doctype html><html><body></body></html>')
const win = doc.defaultView

global.document = doc
global.window = win

Object.keys(window).forEach((key) => {
  if (!(key in global)) {
    global[key] = window[key]
  }
})
Run Code Online (Sandbox Code Playgroud)

复制案例:

我在这里有一个repro案例:https://github.com/jbinto/repro-jsdom-events-not-firing

git clone https://github.com/jbinto/repro-jsdom-events-not-firing.git cd repro-jsdom-events-not-firing npm install npm test

Lou*_*uis 7

您正在调度该事件,document因此window不会看到它,因为默认情况下它不会冒泡.您需要创建bubbles设置为的事件true.例:

var jsdom = require("jsdom");

var document = jsdom.jsdom("");
var window = document.defaultView;

window.addEventListener('click', function (ev) {
  console.log('window click', ev.target.constructor.name,
              ev.currentTarget.constructor.name);
});

document.addEventListener('click', function (ev) {
  console.log('document click', ev.target.constructor.name,
              ev.currentTarget.constructor.name);
});

console.log("not bubbling");

var event = new window.Event("click");
document.dispatchEvent(event);

console.log("bubbling");

event = new window.Event("click", {bubbles: true});
document.dispatchEvent(event);
Run Code Online (Sandbox Code Playgroud)

  • `new window.Event("click");`ermagerd.`doc.createEvent('MouseEvents').initEvent('click',true,true)`在jsdom中返回undefined ... 1.5小时找到你的答案Oo (2认同)

Jes*_*nan 6

这里的问题是提供的jsdom document实际上并没有被酶测试使用.

酶利用renderIntoDocumentReact.TestUtils.

https://github.com/facebook/react/blob/510155e027d56ce3cf5c890c9939d894528cf007/src/test/ReactTestUtils.js#L85

{
  renderIntoDocument: function(instance) {
    var div = document.createElement('div');
    // None of our tests actually require attaching the container to the
    // DOM, and doing so creates a mess that we rely on test isolation to
    // clean up, so we're going to stop honoring the name of this method
    // (and probably rename it eventually) if no problems arise.
    // document.documentElement.appendChild(div);
    return ReactDOM.render(instance, div);
  },
// ...
}
Run Code Online (Sandbox Code Playgroud)

这意味着我们所有的酶测试都不会针对提供的jsdom执行document,而是执行与任何文档分离的div节点.

酶仅使用document为静态方法提供的jsdom ,例如getElementById等.它不用于存储/操作DOM元素.

为了进行这些类型的测试,我求助于实际调用ReactDOM.render,并使用DOM方法在输出上断言.