【问题标题】:jsdom: dispatchEvent/addEventListener doesn't seem to workjsdom:dispatchEvent/addEventListener 似乎不起作用
【发布时间】:2016-04-22 21:41:04
【问题描述】:

总结:

我正在尝试测试一个在其componentWillMount 中侦听本机 DOM 事件的 React 组件。

我发现 jsdom (@8.4.0) 在调度事件和添加事件侦听器时无法按预期工作。

我能提取的最简单的代码:

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

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

throw new Error('failure')

这会引发“失败”。


上下文:

冒着以上是XY problem 的风险,我想提供更多上下文。

这是我要测试的组件的提取/简化版本。 You can see it working on 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>
  }
}

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

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/)
  })
})

为了完整起见,这里是我的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]
  }
})

复制案例:

我这里有一个复制案例: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

【问题讨论】:

  • 很棒的问题结构 + repo

标签: javascript testing jsdom


【解决方案1】:

您将事件发送给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);

【讨论】:

  • new window.Event("click");ermagerd。 doc.createEvent('MouseEvents').initEvent('click', true, true) 在 jsdom 中返回 undefined... 1.5 小时找到答案 O.o
【解决方案2】:

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

Enzyme 使用来自React.TestUtilsrenderIntoDocument

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);
  },
// ...
}

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

Enzyme 仅将 jsdom 提供的 document 用于静态方法,例如getElementById 等。不用于存储/操作 DOM 元素。

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

【讨论】:

    【解决方案3】:

    代码:https://github.com/LVCarnevalli/create-react-app/blob/master/src/components/datepicker

    链接:ReactTestUtils.Simulate can't trigger event bind by addEventListener?

    组件:

    componentDidMount() {   
     ReactDOM.findDOMNode(this.datePicker.refs.input).addEventListener("change", (event) => {
        const value = event.target.value;
        this.handleChange(Moment(value).toISOString(), value);
      });
    }
    

    测试:

    it('change empty value date picker', () => {
        const app = ReactTestUtils.renderIntoDocument(<Datepicker />);
        const datePicker = ReactDOM.findDOMNode(app.datePicker.refs.input);
        const value = "";
    
        const event = new Event("change");
        datePicker.value = value;
        datePicker.dispatchEvent(event);
    
        expect(app.state.formattedValue).toEqual(value);
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-29
      • 2016-02-01
      • 2020-09-23
      • 2010-12-05
      • 2011-06-14
      • 2015-01-10
      • 2016-02-24
      • 2011-01-18
      相关资源
      最近更新 更多