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.
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 ^_^
This is the solution I ended up going with. It stubs the way React renders a Modal to just render a <div>
instead.
test-utils.js
:
var sinon = require('sinon');
var React = require('react');
var Modal = require('react-bootstrap').Modal;
module.exports = {
stubModal: function() {
var createElement = React.createElement;
modalStub = sinon.stub(React, 'createElement', function(elem) {
return elem === Modal ?
React.DOM.div.apply(this, [].slice.call(arguments, 1)) :
createElement.apply(this, arguments);
});
return modalStub;
},
stubModalRestore: function() {
if (modalStub) {
modalStub.restore();
modalStub = undefined;
} else {
console.error('Cannot restore nonexistent modalStub');
}
}
};
modal-test.jsx
var testUtils = require('./test-utils');
describe('test a Modal!', function() {
before(testUtils.stubModal);
after(testUtils.stubModalRestore);
it('renders stuff', function() {
var MyModalComponent = TestUtils.renderIntoDocument(
<MyModal show/>
);
// Do more stuff you normally expect you can do
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With