Implementing Jest and RTL for beginners (2/3)
Tags: webdev, jest, javascript, beginners
Published: 2022-06-28 08:28:24

## Table Of Contents
[1. Introduction](#chapter-1)
[2. Examples and Methods](#chapter-2)
[3. Conclusion](#chapter-3)

Hi everyone. It is another week and another post from yours truly. This post will be a continuation from the "Implementing Jest and RTL for beginners" series. 

<a name="chapter-1">Introduction</a>
For this post, we will cover a few more testing methods with the TicTacToe game I created with React framework just to familiarize with Jest and RTL. More details of this game can be found in the previous post of this series.

<a name="chapter-2">Examples and Methods</a>
In the previous post, we looked at setting up Jest and RTL in a React Application, went thorough some definitions and did a basic render test to confirm the game board is render on the screen. For today, we will cover two more types of test we can do for the game. 

__Test: Click Action__

A click test is simulating a click event to test if the logic works by confirming what is rendered on the screen. This means the only way to confirm the click is working as expected is through confirmation on what is "expected" to render on the screen when it is clicked. For example in the `Board` component, nine `Square` components were components were rendered as follows:

```Javascript
  const [arr, setArr] = useState<any[]>(
    Array(9)
      .fill("")
      .map((a) => a),
  );
  const [select, setSelect] = useState<String>("X"); // start with X
  const [winner, setWinner] = useState<String>("");
  const [disable, setDisable] = useState<boolean>(false);

  const onHandleSelect = (i: any) => {
    if (arr[i] === "X" || arr[i] === "O") return;
    if (select === "X") {
      setSelect("O");
      arr[i] = "X";
    } else {
      setSelect("X");
      arr[i] = "O";
    }
    const result = decideIfThereIsWinner(arr, select);
    if (result === true) {
      setWinner(select);
      setDisable(true);
    }
    if (drawCombi.find((a) => arr.toString() === a.toString())) {
      setWinner("Draw");
      setDisable(true);
    }
  };

  return (
    <div className={classes.container} data-testid="board">
    <span data-testid="playerTurn">Player {select} Turn</span> 
      <div className={classes.gridContainer}>
        {arr.map((a, i) => (
          <Square
            key={Math.random()}
            index={i}
            onHandleSelect={onHandleSelect}
            moveName={a}
            disable={disable}
          />
        ))}
      </div>
      {winner ? (
        <h2 data-testid="winnerMessage">
          {winner === "Draw"
            ? "Round Draw. Restart Round."
            : `Player ${winner} is the Winner!`}
        </h2>
      ) : (
        ""
      )}
      <button
        onClick={onHandleReset}
        type="button"
        className={classes.buttonReset}
      >
        reset
      </button>
    </div>
)
```
and each `Square` component is a button as shown:
```Javascript
import React from "react";
import classes from "./Square.module.scss";

interface SelectProps {
  onHandleSelect: (event: React.MouseEvent<HTMLButtonElement>) => void;
  moveName: String;
  index: any;
  disable: boolean;
}

function Square({ moveName, onHandleSelect, index, disable }: SelectProps) {
  return (
    <div className={classes.container} data-testid="board">
    <button
      data-testid={`squareButton${index}`}
      onClick={() => {
        onHandleSelect(index);
      }}
      key={Math.random()}
      type="button"
      disabled={disable}
      className={classes.square}
    >
      {moveName}
    </button>
  );
}

export default Square;
```
For this test, we are going to test when we click the square button, will an "X" show up in the `Square` box. Dirst we have to confirm that X is the current turn. For that, we will run this test:

```Javascript
describe(Board, () => {
  test("First player turn is X", () => {
    const { getByTestId } = render(<Board />);
    const playerTurnMsg = getByTestId("playerTurn");
    expect(playerTurnMsg).toHaveTextContent("Player X Turn");
  });
}
```
which means we will find the HTML element which has a data_testid of "playerTurn" (line 2 in the JSX portion of the `board` component) and we expect the "player turn message" rendered on the screen to be "Player X turn". Once we confirm this, we will do a click test on the square itself as follows:
```Javascript
  test("Next player turn is O after click", () => {
    const { getByTestId } = render(<Board />);
    const button = getByTestId("squareButton0");
    const playerTurnMsg = getByTestId("playerTurn");
    fireEvent.click(button);
    expect(playerTurnMsg).toHaveTextContent("Player O Turn");
    expect(getByTestId("squareButton0")).toHaveTextContent("X"); // confirm click produce X on button
  });
```
In this test, we use a new method from React Testing Library called `fireEvent`. This method acts like a click action. Also in this test we have two expected result to happen
1. The player turn message (data_testid= `playerTurn`) to change to "Player O Turn". 
2. The square that we click to have a string "X". In this case we will use the `.toHaveTextContent` matcher from Jest to confirm that. Note that I selected the data_testid of `squareButton0` for the first square in the board. If the test is shown in the board, this is how it will render after the click event happens.

![This is what the test is trying to simulate when the click event happens in the first square of the board](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/681by0jljv04l7tl4s26.png)
 
Because the component rendered as expected, the test was successful. 

__Test: Test number of components rendered__

The next test is to check the number of `Square` components rendered. On the board, we expect nine squares to be rendered to form the game board. We can do a basic test as follows:

```javascript
  test("Game Rendered nine squares", () => {
    const { container } = render(<Board />);
    const renderSquares = container.getElementsByClassName("square");
    expect(renderSquares).toHaveLength(9);
  });
```

What is different about this test is, since we have specific data-testid for each `Square` component (squareButton0, squareButton1, etc), we cannot use the data-testid to find out the number of `Square` components being rendered. We need to find a common identifier in the `Square` component which Jest and RTL is able to search and count the number of component. 

Hence we will not be using the query `getByTestId` and instead use the query `getElementsByClassName`. However before that, we must destructure the `container` object from the `render` method. This is because the `container` object consist of DOM node of your rendered React Element. These methods enable us to search for specific attributes in the DOM (eg `getAttribute`, `getElementByTagName`, etc). Then, we can use the method to call on the className (which is `square`) for the `Square` component `button`. From there, we will call a matcher `toHaveLength` and `expect` that the number of renderSquares to be 9. If everything goes well, the test should be successful.

Just a note: According to RTL official docs, it is not advisable to use `container` frequently to query for rendered elements in our app as it maybe not be resilient to change as compared to other built in queries in RTL such as `getByTestId`. But for the sake for demonstration, we will use this. If we want to use RTL built in queries, we can add in `placeholders` into the `Square` component buttons and use `getByPlaceHolderText` query to find the html element.

<a name="chapter-3">Conclusion</a>
Well, this conclude the second part of this series. In the last part, we will dive into how do we test the winning combination of the game and how to test for conditional conditions (such as the game win or draw scenario) that renders specific content/elements.

Thank you!