user1778856 user1778856 - 4 years ago 248
React JSX Question

Testing a React Modal component

I'm sorry, but I've been having the toughest time trying to test closing my React Modal by clicking a button. The Modal is as simple as can be, and I've tried everything I can think of or find, but I still can't query its children.

The Modal component:

var React = require('react');
var Modal = require('react-bootstrap').Modal;
var Button = require('react-bootstrap').Button;

var MyModal = React.createClass({
...
render: function() {
return (
<Modal className="my-modal-class" show={this.props.show}>
<Modal.Header>
<Modal.Title>My Modal</Modal.Title>
</Modal.Header>
<Modal.Body>
Hello, World!
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
</Modal>
);
}
});


My goal is to test if that Close button fires the
onHide()
function when it is clicked.

My test file:

describe('MyModal.jsx', function() {
it('tests the Close Button', function() {
var spy = sinon.spy();
var MyModalComponent = TestUtils.renderIntoDocument(
<MyModal show onHide={spy}/>
);

// This passes
TestUtils.findRenderedComponentWithType(MyModalComponent, MyModal);

// This fails
var CloseButton = TestUtils.findRenderedDOMComponentWithTag(MyModalComponent, 'button');

// Never gets here
TestUtils.Simulate.click(CloseButton);
expect(spy.calledOnce).to.be.true;
});
});


No matter what I try, I can't seem to find the Close Button.

Answer Source

I wrote a jsFiddle using the React Base Fiddle (JSX) to find out what was going on in your test (I created my own 'spy' that simply logged to the console when called).

I found out that the reason you can't find your button is because it doesn't exist where you might expect it to.

The Bootstrap Modal Component (<Modal/>) is actually contained within a React-Overlays modal component (called BaseModal in the code, which is from here). This in turn renders a component called Portal whose render method simply returns null. It is this null value you are trying to find rendered components on.

Due to the modal not being rendered the traditional React way, React cannot see the modal in order to use TestUtils on. An entirely seperate <div/> child node is placed in the document body and this new <div/> is used to build the modal.

So, in order to allow you to simulate a click using React's TestUtils (the click handler on the button is still bound to the button's click event), you can use standard JS methods to search the DOM instead. Set up your test as follows:

describe('MyModal.jsx', function() {
  it('tests the Close Button', function() {
    var spy = sinon.spy();
    var MyModalComponent = TestUtils.renderIntoDocument(
      <MyModal show onHide={spy}/>
    );

    // This passes
    TestUtils.findRenderedComponentWithType(MyModalComponent, MyModal);

    // This will get the actual DOM node of the button
    var closeButton = document.body.getElementsByClassName("my-modal-class")[0].getElementsByClassName("btn btn-default")[0];

    // Will now get here
    TestUtils.Simulate.click(CloseButton);
    expect(spy.calledOnce).to.be.true;
  });
});

The function getElementsByClassName returns a collection of elements with that class so you must take the first one (and in your test case, your only one) from each collection.

Your test should now pass ^_^

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download