Imagine a simple contentEditable
component with an oninput
event listener.
export default React.createClass({
render() {
return React.createElement('p', {
contentEditable: true,
onInput: this.emitChange
});
},
emitChange(e) {
console.log("Input event occurred", e);
}
});
In my test, I simulate an input
event on the component (like the user typing the letter a in the contentEditable
tag).
This works in the browser, I can click the component, press the a key and I will see an 'a' in the tag and the console.log
will trigger. I just can't get it to work in this test:
// Require the code from the block above.
var CE = require('content-editable');
describe('Component', function() {
it('updates state when we type', function() {
// Render a component instance and get a handle on it.
let ce = TestUtils.renderIntoDocument(<CE/>);
let _ce = ReactDOM.findDOMNode(ce);
TestUtils.Simulate.input(_ce, {
key: 'a'
});
expect(_ce.textContent).to.equal('a');
});
});
This test fails because _ce.textContent
is an empty string. I've tried simulating a click on the _ce
before simulating the input and it doesn't fix the problem.
How can I get my test to pass?
The main issue is that TestUtils.Simulate does not actually send an event to the DOM, it creates a fake event and sends it through React's event handling system. You can mitigate this by propagating the change event back up to the parent component via a callback, which you'll likely need to do anyway to retrieve the value of the editable:
export default React.createClass({
propTypes: {
onChange: React.PropTypes.func
},
render() {
return React.createElement('p', {
contentEditable: true,
onInput: this.emitChange
});
},
emitChange(e) {
if (this.props.onChange) {
this.props.onChange(e);
}
}
});
Now we can test that onChange was called:
describe('Component', function() {
it('updates state when we type', function() {
let changeFired = false;
let ce = TestUtils.renderIntoDocument(<CE onChange={verify} />);
let _ce = ReactDOM.findDOMNode(ce);
TestUtils.Simulate.input(_ce, {
key: 'a'
});
function verify(e) {
changeFired = true;
expect(e.key).to.equal('a');
}
expect(changeFired).to.equal(true);
});
});
This will unit test the code in your emitChange
function without any actual interaction with the rest of the environment. This is probably what you want in a unit test because your component doesn't care what the browser does to the DOM when the user types a key, all it cares about is that it is able to do something when the browser tells it that something has happened.
Generally in a React unit test, you won't see the textContent
and innerHTML
DOM properties change without causing the component to re-render. If you need to test browser interactions more thoroughly, you may need to pursue an integration (end-to-end) testing solution.