React TestUtils, how can I simulate document mouseMove?

well picture well · Jun 25, 2015 · Viewed 10.2k times · Source

I want to use TestUtils.Simulate.mouseMove on the document. I have a component Dragger that adds a mouseMove event listener to the document. Here is an incomplete version:

// Dragger.js
'use strict';

var React = require('react');

export default React.createClass({
    propTypes: {
        handleDrag: React.PropTypes.func // callback set by parent
    },
    getInitialState: function() {
        return {dragging: false}
    },
    componentDidUpdate: function(props, state) {
        // 
        if (this.state.dragging && !state.dragging) {
            document.addEventListener('mousemove', this.onMouseMove)
        } else if (!this.state.dragging && state.dragging) {
            document.removeEventListener('mousemove', this.onMouseMove)
        }
    },
    onMouseDown: function(e) {
        this.setState({dragging: true})
    },
    onMouseMove: function(e) {
        // Calls back to the parent with the drag
        this.props.handleDrag(e);
    },
    render: function() {
        return <div onMouseDown={this.onMouseDown} ></div>
    }
});

I'm using jasmine, and I want to make sure my handleDrag callback is called after a mouseDown followed by a mouseMove.

// Dragger.spec.js

var React = require('react/addons');
import Dragger from './Dragger';

var TestUtils = React.addons.TestUtils;

describe('Dragger', function() {
    it('should call the callback after drag interaction', function() {
        // make callback to spy on
        var f = {callback: function(e){return}};

        // render Dragger
        var dragger = TestUtils.renderIntoDocument(<Dragger handleDrag={f.callback} />);

        // spy on callback
        spyOn(f, 'callback');

        // simulate a mouseDown and mouseMove
        TestUtils.Simulate.mouseDown(dragger.getDOMNode(), {button: 0});
        TestUtils.Simulate.mouseMove(document);

        expect(f.callback).toHaveBeenCalled(); // FAILS!
    }
}

But the mouseMove event is not being properly simulated. I see 2 problems

  1. I might need to pass event data to TestUtils.Simulate.mouseMove. For example, the call TestUtils.Simulate.mouseDown(dragger.getDOMNode()) did not work until I changed it to TestUtils.Simulate.mouseDown(dragger.getDOMNode(), {button: 0}). What event data should I pass to TestUtils.Simulate.mouseMove?
  2. The document is not part of the detached DOM that the test component is rendered into. This could be another reason the Simulate.mouseMove doesn't work. What can I use in the test instead of document?

How can I use TestUtils.Simulate.mouseMove?

Answer

Jonathan Modell picture Jonathan Modell · Sep 7, 2016

After hours of trying various methods with enzyme and react's TestUtils I finally came upon just creating and dispatching events in pure JS, which works in my jest tests like this

it('calls handler on mouseDown on element, mouseMove on document', () => {
  const handler = jest.fn();
  const props = {
    foo: {
      uid: '1',
      resizable: true,
    },
    resizeHandler,
  };

  const instance = mount(<Header {...props} />);
  const resizer = instance.find('.resizer');
  const top = window.document.documentElement;  // target the documentElement
  resizer.simulate('mouseDown', { preventDefault: () => true });   // uses enzyme to simulate this event, adding listener to documentElement on mousemove
  const mouseMove = new Event('mousemove');  // creates a new event
  top.dispatchEvent(mouseMove);              // dispatches it
  const mouseUp = new Event('mouseup');
  top.dispatchEvent(mouseUp);
  expect(resizeHandler).toBeCalled();        // the passed in handler is called on mousemove
});

Basically, you can find document.documentElement with window.document.documentElement and dispatch events from it like any other element