Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
264 views
in Technique[技术] by (71.8m points)

javascript - jsdom: dispatchEvent/addEventListener doesn't seem to work

Summary:

I am attempting to test a React component that listens to native DOM events in its componentWillMount.

I'm finding that jsdom (@8.4.0) doesn't work as expected when it comes to dispatching events and adding event listeners.

The simplest bit of code I can extract:

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

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

throw new Error('failure')

This throws "failure".


Context:

At risk of the above being an XY problem, I want to provide more context.

Here is an extracted/simplified version of the component I'm trying to test. 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>
  }
}

Here is the test I'm trying to write.

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

Just for completeness, here is my test_helper.js which initializes 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]
  }
})

Reproduction case:

I have a repro case here: 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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You're dispatching the event to document so window won't see it because by default it won't bubble up. You need to create the event with bubbles set to true. Example:

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);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...