Igor Lacik Igor Lacik - 1 year ago 128
React JSX Question

React unit testing -> simulate click on child to invoke method

Say I implemented a simple pexeso game in react (for those who do not know pexeso - this is a random pexeso example - not mine).

For now it is important, that the game consists of one GameBoard component, in which display a large number of Card components.

The GameBoard component has as a state an array of currently flipped cards - this.state.flipped_cards.

Once a card is clicked, it invokes an inner function (forcing the card to flip). In this function, the GameBoard's handleCardFlip function is involved to determine the resulting game state.

The resulting JSX returned from the GameBoard looks something like this:

<div>
<Card onClick={this.handleFlippedCard} name="MistyRose" />
<Card onClick={this.handleFlippedCard} name="Olive" />
<Card onClick={this.handleFlippedCard} name="MistyRose" />
<Card onClick={this.handleFlippedCard} name="Olive" />
</div>

handleFlippedCard( flipped_card_text ) {
if ( this.state.flipped_cards.length < MAX_FLIPPED_CARDS ) {
this.setState(
{
flipped_cards:
this.state.flipped_cards.concat( [flipped_card_text] )
},
() => {
__resolveGameState.bind( this )()
}
)
}
} // handleFlippedCard


Now I want to write unit tests for my code. Currently I am using jest, enzyme and chai (I am getting a bit overwhelmed and am honestly getting really confused with all the crazy test engine names :)) ).

How do I simulate a click on the cards? I read something about spies and stubs, but did not really understand how/when to use them.

it.only( 'Removes flipped cards if they match', () => {
const board = shallow( <GameBoard cardsInput={ cardsInput } /> )
// reveal first card
board.setState( {
flipped_cards:
board.state( 'flipped_cards' ).concat( cardsInput[1] )
} )

// reveal second card
board.setState( {
flipped_cards:
board.state( 'flipped_cards' ).concat( cardsInput[3] )
} )

// thought the handleFlipped cards would get invoked here..

expect( board.state( 'visible_cards' ).length ).to
.equal( cardsInput.length-2 )
} )


I will welcome any comments, I am very new to react.

Answer Source

Based on your jsx

<div>
  <Card onClick={this.handleFlippedCard} name="MistyRose" />
  <Card onClick={this.handleFlippedCard} name="Olive" />
  <Card onClick={this.handleFlippedCard} name="MistyRose" />
  <Card onClick={this.handleFlippedCard} name="Olive" />
</div>

You can simulate click events on each <Card> component by

const wrapper = shallow(
  // Pass the required props to GameBoard component if any
  <GameBoard />
);

wrapper.find('Card').forEach(function (card) {
    let cardName = card.prop('name');
    card.simulate('click', {
        preventDefault: () => {
        },
        target: [
          {
            value: cardName,
          }
        ]
      });
});

If you want to select each component individually via prop that also you can do via -

let card = wrapper.find('[name="Olive"]').first();

and then simulate click event similarly on card component shallow wrapper returned.

Also keep in mind that simulating click event will call handleFlippedCard but it will pass the associated event object to your handler and you have to get the required value from the received event object.

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