3

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?

well
  • 647
  • 1
  • 8
  • 20
  • To address your #2: You're right that it definitely will not work by passing in the document, as TestUtils renders your component into a detached DOM node. I saw that you were using Jasmine though, so you might want to take a look at this: https://github.com/tommyh/jasmine-react It provides `jasmineReact.render` which actually does render the component into an attached DOM node then cleans it up for you afterwards. While there may be several issues causing your code to not work, I believe that this will at least solve one of them – Michael Parker Jun 25 '15 at 17:58
  • As far as simulating a mousemove event with TestUtils, you might want to try passing the same information that you're expecting on your component. I had a similar issue recently with `TestUtils.Simulate.keyDown`, as I was using the (incorrect) documentation and supplying `key` instead of `keyDown`, which is what my code was expecting. That taught me that you can supply pretty much any kind of fake data as an argument for Simulate.keyDown (mouseMove in your case) and it will take it. – Michael Parker Jun 25 '15 at 18:00

2 Answers2

4

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

0

This is an old post but I see there isn't a posted solution yet, I run into it because I am writing a similar component. I think the problem is that you are focusing on the wrong event, you should be using onDrag for dragging detection, here's an adapted version of your code that is working for me:

// Dragger.js

import React from 'react';

export default React.createClass({
    propTypes: {
        handleDrag: React.PropTypes.func // callback set by parent
    },
    getInitialState: function() {
        return {dragging: false}
    },
    onDragStart: function(e) {
        // Calls back to the parent with the drag
        this.setState({ dragging: true });
        this.props.handleDrag(e);
    },
    onDragEnd: function() {
        this.setState({ dragging: false });
    },
    render: function() {
        return <div onDragStart={this.onDragStart} onDragEnd={this.onDragEnd}></div>;
    }
});

and

// Dragger.spec.js

import React from 'react';
import Dragger from '../src/Dragger';
import {
    renderIntoDocument,
    scryRenderedDOMComponentsWithTag,
    Simulate
} from 'react-addons-test-utils';

import { expect } from 'chai';

describe('Dragger', function() {
    it('should call the callback after drag interaction', function() {
        // spy on callback
        var called = false;
        // make callback to spy on
        function callback() {
            called = true;
        };

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

        // simulate a dragStart and dragEnd
        const element = scryRenderedDOMComponentsWithTag(dragger, 'div')[0];
        Simulate.dragStart(element);
        Simulate.dragEnd(element);
        expect(called).to.equal(true);
    });
});
Javier Conde
  • 2,553
  • 17
  • 25