< Previous: Using Jest to Test Our React Components   Next: Time for Ajax: Using Jest with Asynchronous Tests >

Using Jest to Simulate User Interaction on a React Component

Now, if you cast your mind back to much earlier in this book you might remember this: "REMINDER OF IMPORTANT WARNING: Code much later in this book relies on you passing a string parameter to selectMode() rather than using the data-mode attribute approach." Well, you've finally reached the point where your choice matters.

If you remember, we looked at two ways of writing the selectMode() method: passing a string parameter or using a data-mode attribute. Both approaches have their uses in normal coding, but when it comes to testing there's a catch: Jest doesn't support the dataset property, which means when it simulates a click on a button the selectMode() method will fail if it tries to read the data-mode attribute.

Fortunately you heeded my warning and used the approach that passes a string parameter, right? Right. That means we can write our third test really easily: we just find all the buttons in the document, read the button at index 1, simulate a click on it, then make sure the Detail component's mode state is set to 'forks'.

Add this test beneath the existing two:

__tests__/Detail-test.js

it('shows forks when the button is tapped', () => {
    const rendered = TestUtils.renderIntoDocument(
        <Detail params={{repo: ''}} />
    );

    const btns = TestUtils.scryRenderedDOMComponentsWithTag(rendered, 'button');
    const forksButton = btns[1];
    TestUtils.Simulate.click(forksButton);
    expect(rendered.state.mode).toEqual('forks');
});

There are three things there that might be interesting. First, we need to use index 1 for the "forks" button because JavaScript arrays count from 0. In our detail component, button 0 is "Commits", button 1 is "Forks" and button 2 is "Pulls".

Second, the TestUtils.Simulate.click() method is new, and I hope it's pretty self-explanatory: it simulates a click on something in our document, in this case a button.

Third, this code is a bit fragile and thus not really suitable for use in production. The reason for this is the way the button is accessed: we assume the forks button is at index 1, but if a designer comes along and moves it then the test will break even though the button is there and still functioning.

The way to fix this is to give each button a unique ref property, which is React's way of identifying things that have been rendered. Please amend the end of the render() method in Detail.js to this:

src/pages/Detail.js

return (<div>
    <p>You are here:
    <IndexLink to="/" activeClassName="active">Home</IndexLink>
    > {this.props.params.repo}</p>

    <button onClick={this.selectMode.bind(this, 'commits')} ref="commits">
        Show Commits
    </button>

    <button onClick={this.selectMode.bind(this, 'forks')} ref="forks">
        Show Forks
    </button>

    <button onClick={this.selectMode.bind(this, 'pulls')} ref="pulls">
        Show Pulls
    </button>

    {content}
</div>);

Now that each button has a unique ref property we can find it directly, without even having to call scryRenderedDOMComponentsWithTag(). Update the third test to this:

__tests__/Detail-test.js

it('shows forks when the button is tapped', () => {
    const rendered = TestUtils.renderIntoDocument(
        <Detail params={{repo: ''}} />
    );

    const forksButton = rendered.refs.forks;
    TestUtils.Simulate.click(forksButton);
    expect(rendered.state.mode).toEqual('forks');
});

As you can see, we can just dip right into the refs property to find forks, and that new test will work regardless of where the button gets moved to in the future.

Buy the book for $10

Get the complete, unabridged Hacking with React e-book and take your learning to the next level - includes a 45-day no questions asked money back guarantee!

If this was helpful, please take a moment to tell others about Hacking with React by tweeting about it!

< Previous: Using Jest to Test Our React Components   Next: Time for Ajax: Using Jest with Asynchronous Tests >

Copyright ©2016 Paul Hudson. Follow me: @twostraws.