React TestUtils, how can I simulate document mouseMove?
Asked Answered
S

2

5

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?

Seriocomic answered 25/6, 2015 at 16:23 Comment(2)
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: 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 themCordalia
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.Cordalia
H
6

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

Hateful answered 7/9, 2016 at 17:8 Comment(1)
How did you import Event?Huntington
U
1

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);
    });
});
Uribe answered 5/8, 2016 at 17:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.