2016-04-22 13 views
7

Zusammenfassung zu arbeiten:jsdom: dispatch/addEventListener scheint nicht

ich ein Reagieren Komponente zu testen, bin versucht, das in seiner componentWillMount zu nativen DOM-Ereignisse lauscht.

Ich finde, dass jsdom (@8.4.0) funktioniert nicht wie erwartet, wenn es darum geht, Ereignisse zu versenden und Ereignislistener hinzuzufügen.

Die einfachste Stück Code ich extrahieren kann:

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

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

throw new Error('failure') 

Dieses "Versagen" wirft.


Kontext:

Gefährdet der oben ist ein XY problem, ich will mehr Kontext.

Hier ist eine extrahierte/vereinfachte Version der Komponente, die ich versuche zu testen. 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> 
    } 
} 

Hier ist der Test, den ich zu schreiben bin versucht.

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

Nur der Vollständigkeit halber, hier ist meine test_helper.js die jsdom initialisiert:

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

Reproduktion Fall:

Ich habe einen repro Fall hier: 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

Antwort

3

Das Problem war hier, dass die jsdom bereitgestellten document nicht tatsächlich durch Enzymtests verwendet zu werden.

Enzym verwendet renderIntoDocument von React.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); 
    }, 
// ... 
} 

Das bedeutet, alle unsere Enzymtests sind nicht gegen die jsdom bereitgestellten document ausgeführt, sondern in einen div Knoten aus jedem Dokument abgelöst.

Enzym verwendet nur die von jsdom bereitgestellten document für statische Methoden, z. getElementById usw. Es wird nicht zum Speichern/Bearbeiten von DOM-Elementen verwendet.

Um diese Arten von Tests zu tun, ich tatsächlich ReactDOM.render, und behauptet auf den Ausgabemethoden unter Verwendung von DOM zurückgegriffen zu nennen.

6

Sie verschicken das Ereignis an document so window wird es nicht sehen, weil es standardmäßig nicht aufbläht. Sie müssen das Ereignis mit bubbles auf true erstellen. Beispiel:

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

'neue window.event ("Klick");' ermagerd. 'doc.createEvent ('MouseEvents'). initEvent ('click', true, true)' gibt undefined in jsdom zurück ... 1.5 Stunden, um deine Antwort zu finden O.o – Larry

0

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

-Link: ReactTestUtils.Simulate can't trigger event bind by addEventListener?

Komponente:

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

Test:

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