Welcome back!
There's a whole lot of automated interface testing to be done here. There are four components to be tested -
App,
HangedMan,
Computer and
Player. While the entire app on its own is workable, we may have to enter some
test-id attributes at certain parts to facilitate testing.
Testing App
We first create this file. Some standard testing libraries will need to be imported, as well as the
App component.
App is pretty straightforward; not that much testing required.
src/App.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import App from "./App";
The main test for this is a rendering test, where the word "Loading" should appear, due to the app loading the API for retrieving the word list.
src/App.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import App from "./App";
describe("App", () => {
it('renders', () => {
render(<App />);
expect(screen.queryByText('Loading...')).toBeInTheDocument();
});
});
Testing HangedMan
This one will be a little more complicated. We begin by creating the file which will do the same imports.
src/components/HangedMan/HangedMan.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import HangedMan from "./HangedMan";
Declare
stage. This variable will be important because we'll need it to test the ways the hanged man will appear at different stages of the app. Remember how we tested in Part 2 of this web tutorial? Well, this will be an
automated version of that.
src/components/HangedMan/HangedMan.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import HangedMan from "./HangedMan";
let stage;
This will describe the
HangedMan suite of tests.
src/components/HangedMan/HangedMan.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import HangedMan from "./HangedMan";
let stage;
describe("HangedMan", () => {
});
Now this first test will test for the value of 0 for
stage.
src/components/HangedMan/HangedMan.test.jsdescribe("HangedMan", () => {
it("should render with no hanged man parts according to stage 0", () => {
});
});
Here, we set
stage to 0 and render
HangedMan with that value.
src/components/HangedMan/HangedMan.test.jsdescribe("HangedMan", () => {
it("should render with no hanged man parts according to stage 0", () => {
stage = 0;
render(
<HangedMan
stage={ stage }
/>
);
});
});
And then we grab all the testing ids from the various SVG tags making up the hanged man. All of them should have
hidden as part of their CSS class. Because
stage is 0. But this won't work because the testing ids have not been set yet, so...
src/components/HangedMan/HangedMan.test.jsdescribe("HangedMan", () => {
it("should render with no hanged man parts according to stage 0", () => {
stage = 0;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
});
...let's add those in.
src/components/HangedMan/HangedMan.js<g className={ props.stage >= 6 ? 'swing' : '' }>
<line
className="rope"
x1="300"
y1="20"
x2="300"
y2="60"
></line>
<circle
className={props.stage >= 1 ? 'man' : 'man hidden'}
cx="300"
cy="80"
r="20"
data-testid="hangedMan_head"
></circle>
<line
className={props.stage >= 2 ? 'man' : 'man hidden'}
x1="300"
y1="100"
x2="280"
y2="160"
data-testid="hangedMan_leftArm"
></line>
<line
className={props.stage >= 3 ? 'man' : 'man hidden'}
x1="300"
y1="100"
x2="320"
y2="160"
data-testid="hangedMan_rightArm"
></line>
<line
className={props.stage >= 4 ? 'man' : 'man hidden'}
x1="300"
y1="100"
x2="300"
y2="180"
data-testid="hangedMan_torso"
></line>
<line
className={props.stage >= 5 ? 'man' : 'man hidden'}
x1="300"
y1="180"
x2="280"
y2="250"
data-testid="hangedMan_leftLeg"
></line>
<line
className={props.stage >= 6 ? 'man' : 'man hidden'}
x1="300"
y1="180"
x2="320"
y2="250"
data-testid="hangedMan_rightLeg"
></line>
</g>
<text
x="350"
y="100"
className={props.stage >= 6 ? 'gameover' : 'gameover hidden'}
data-testid="txtGameOver"
>
<tspan x="350" y="100">GAME</tspan>
<tspan x="350" y="145">OVER</tspan>
</text>
The rest of these tests are basically the same - just with a different value for
stage. And those elements that have
false where expected, will vary according to that value. For instance, if
stage is 1, then only the head will not have the class
hidden.
src/components/HangedMan/HangedMan.test.jsdescribe("HangedMan", () => {
it("should render with no hanged man parts according to stage 0", () => {
stage = 0;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
it("should render with only head according to stage 1", () => {
stage = 1;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
it("should render with only head and left arm according to stage 2", () => {
stage = 2;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
it("should render with only head, left arm and right arm according to stage 3", () => {
stage = 3;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
it("should render with only head, left arm, right arm and torso according to stage 4", () => {
stage = 4;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
it("should render with only head, left arm, right arm, torso and left leg according to stage 5", () => {
stage = 5;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(true);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(true);
});
it("should render with full hanged man and game over text according to stage 6", () => {
stage = 6;
render(
<HangedMan
stage={ stage }
/>
);
expect(screen.queryByTestId("hangedMan_head").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightArm").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_torso").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_leftLeg").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("hangedMan_rightLeg").classList.contains("hidden")).toBe(false);
expect(screen.queryByTestId("txtGameOver").classList.contains("hidden")).toBe(false);
});
});
That was actually pretty easy. Let's move on to something slightly more complex.
Testing Computer
Yep you guessed it. Create the file in the appropriate directory and import the files required.
src/components/Computer/Computer.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import Computer from "./Computer";
Declare
guessedLetters and
mysteryWord. Those are our testing variables.
src/components/Computer/Computer.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import Computer from "./Computer";
let guessedLetters;
let mysteryWord;
Again, we describe the test group for this.
src/components/Computer/Computer.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import Computer from "./Computer";
let guessedLetters;
let mysteryWord;
describe("Computer", () => {
});
There is only one test. And here, we test that the display component shows the letters correctly.
src/components/Computer/Computer.test.jsdescribe("Computer", () => {
it("should render with Computer display component when game in progress", () => {
});
});
So here, we set "friend" as the mystery word, and declare that the letters "i" and "e" have been guessed. Then we render
Computer and pass in these values.
src/components/Computer/Computer.test.jsdescribe("Computer", () => {
it("should render with Computer display component when game in progress", () => {
mysteryWord = "friend";
guessedLetters = ["i", "e"];
render(
<Computer
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
/>
);
});
});
Now we check the screen for all elements containing these exact letters making up "friend". Since "i" and "e" have been guessed, only these two should be visible.
src/components/Computer/Computer.test.jsdescribe("Computer", () => {
it("should render with Computer display component when game in progress", () => {
mysteryWord = "friend";
guessedLetters = ["i", "e"];
render(
<Computer
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
/>
);
expect(screen.queryByText("f")).not.toBeInTheDocument();
expect(screen.queryByText("r")).not.toBeInTheDocument();
expect(screen.queryByText("i")).toBeInTheDocument();
expect(screen.queryByText("e")).toBeInTheDocument();
expect(screen.queryByText("n")).not.toBeInTheDocument();
expect(screen.queryByText("d")).not.toBeInTheDocument();
});
});
Testing Player
Now this is a big one. That's where all the user input is.
Create the file and do the imports. In this case, we also import
userEvent because we'll be testing using simulated button clicks and text inputs.
src/components/Player/Player.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Player from "./Player";
Declare
error,
isPending and
mysteryWord. Those are testing variables.
src/components/Player/Player.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Player from "./Player";
let error;
let isPending;
let mysteryWord;
Also declare
guessedLetters and create a simple mutator,
setGuessedLetters(). Do the same for
stage and
setStage(). Declare
setMessageAndContext() but we won't bother to put anything in it because we won't be testing that output. In fact, we won't be using
setMessageAndContext() except to pass it down to the
Player component in
props.
src/components/Player/Player.test.jsimport React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Player from "./Player";
let guessedLetters;
let setGuessedLetters = (arr)=> {
guessedLetters = arr;
};
let stage;
let setStage = (num)=> {
stage = num;
};
let setMessageAndContext = (x)=> {
};
let error;
let isPending;
let mysteryWord;
We also set
window.alert as a kind of dummy placeholder so that if it's ever called by any of our simulated events, no errors will be thrown. Remember one of our event handlers runs an
alert() function?
src/components/Player/Player.test.jslet error;
let isPending;
let mysteryWord;
window.alert = ()=> {};
And finally, we begin the test suite. There are going to be quite a few tests.
src/components/Player/Player.test.jslet error;
let isPending;
let mysteryWord;
window.alert = ()=> {};
describe("Player", () => {
});
The first test ensures that the correct button is shown when the game has not started. So we set
error to
undefined,
isPending to
false, set
mysteryWord to a random word and
stage is set to -1. We can set
stage directly instead of using a mutator, but since we already went to the trouble of defining the mutator, what the heck, right?
src/components/Player/Player.test.jsdescribe("Player", () => {
it("should render with Begin button when game not started", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
});
});
Then render
Player with the appropriate arguments.
src/components/Player/Player.test.jsdescribe("Player", () => {
it("should render with Begin button when game not started", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
});
After that, we should check if there is any element with "Begin" is in the document.
src/components/Player/Player.test.jsdescribe("Player", () => {
it("should render with Begin button when game not started", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Begin")).toBeInTheDocument();
});
});
Similar logic is deployed for the "Replay" button. This time,
stage is set to 6.
src/components/Player/Player.test.jsdescribe("Player", () => {
it("should render with Begin button when game not started", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Begin")).toBeInTheDocument();
});
it("should render with Replay button when game over", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(6);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Replay")).toBeInTheDocument();
});
});
The next test is for when the game is in progress, so set
stage to any value between 0 and 4 inclusive. And render
Player.
src/components/Player/Player.test.jsit("should render with Replay button when game over", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(6);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Replay")).toBeInTheDocument();
});
it("should render with Player dashboard component when game in progress", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
We should test for the presence of "Select A Letter", "Guess The Word" and all letters of the alphabet. Seems like overkill, but yeah, let's do that.
src/components/Player/Player.test.jsit("should render with Replay button when game over", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(6);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Replay")).toBeInTheDocument();
});
it("should render with Player dashboard component when game in progress", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Select A Letter")).toBeInTheDocument();
expect(screen.queryByText("Guess The Word")).toBeInTheDocument();
expect(screen.queryByText("a")).toBeInTheDocument();
expect(screen.queryByText("b")).toBeInTheDocument();
expect(screen.queryByText("c")).toBeInTheDocument();
expect(screen.queryByText("d")).toBeInTheDocument();
expect(screen.queryByText("e")).toBeInTheDocument();
expect(screen.queryByText("f")).toBeInTheDocument();
expect(screen.queryByText("g")).toBeInTheDocument();
expect(screen.queryByText("h")).toBeInTheDocument();
expect(screen.queryByText("i")).toBeInTheDocument();
expect(screen.queryByText("j")).toBeInTheDocument();
expect(screen.queryByText("k")).toBeInTheDocument();
expect(screen.queryByText("l")).toBeInTheDocument();
expect(screen.queryByText("m")).toBeInTheDocument();
expect(screen.queryByText("n")).toBeInTheDocument();
expect(screen.queryByText("o")).toBeInTheDocument();
expect(screen.queryByText("p")).toBeInTheDocument();
expect(screen.queryByText("q")).toBeInTheDocument();
expect(screen.queryByText("r")).toBeInTheDocument();
expect(screen.queryByText("s")).toBeInTheDocument();
expect(screen.queryByText("t")).toBeInTheDocument();
expect(screen.queryByText("u")).toBeInTheDocument();
expect(screen.queryByText("v")).toBeInTheDocument();
expect(screen.queryByText("w")).toBeInTheDocument();
expect(screen.queryByText("x")).toBeInTheDocument();
expect(screen.queryByText("y")).toBeInTheDocument();
expect(screen.queryByText("z")).toBeInTheDocument();
});
Next thing to do is to ensure that if error is true, clicking on "Begin" does nothing. So set
error to an
Error object, set the other variables, and render
Player.
src/components/Player/Player.test.jsit("should render with Player dashboard component when game in progress", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
expect(screen.queryByText("Select A Letter")).toBeInTheDocument();
expect(screen.queryByText("Guess The Word")).toBeInTheDocument();
expect(screen.queryByText("a")).toBeInTheDocument();
expect(screen.queryByText("b")).toBeInTheDocument();
expect(screen.queryByText("c")).toBeInTheDocument();
expect(screen.queryByText("d")).toBeInTheDocument();
expect(screen.queryByText("e")).toBeInTheDocument();
expect(screen.queryByText("f")).toBeInTheDocument();
expect(screen.queryByText("g")).toBeInTheDocument();
expect(screen.queryByText("h")).toBeInTheDocument();
expect(screen.queryByText("i")).toBeInTheDocument();
expect(screen.queryByText("j")).toBeInTheDocument();
expect(screen.queryByText("k")).toBeInTheDocument();
expect(screen.queryByText("l")).toBeInTheDocument();
expect(screen.queryByText("m")).toBeInTheDocument();
expect(screen.queryByText("n")).toBeInTheDocument();
expect(screen.queryByText("o")).toBeInTheDocument();
expect(screen.queryByText("p")).toBeInTheDocument();
expect(screen.queryByText("q")).toBeInTheDocument();
expect(screen.queryByText("r")).toBeInTheDocument();
expect(screen.queryByText("s")).toBeInTheDocument();
expect(screen.queryByText("t")).toBeInTheDocument();
expect(screen.queryByText("u")).toBeInTheDocument();
expect(screen.queryByText("v")).toBeInTheDocument();
expect(screen.queryByText("w")).toBeInTheDocument();
expect(screen.queryByText("x")).toBeInTheDocument();
expect(screen.queryByText("y")).toBeInTheDocument();
expect(screen.queryByText("z")).toBeInTheDocument();
});
it("should not react to button clicks if error", () => {
error = new Error("");
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
Then simulate an event by using the
userEvent object and the
click() method on the "Begin" button. "Begin" should still be in the document after that.
src/components/Player/Player.test.jsit("should not react to button clicks if error", () => {
error = new Error("");
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByText("Begin"));
expect(screen.queryByText("Begin")).toBeInTheDocument();
});
Similar logic if
pending is
true.
src/components/Player/Player.test.jsit("should not react to button clicks if error", () => {
error = new Error("");
isPending = false;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByText("Begin"));
expect(screen.queryByText("Begin")).toBeInTheDocument();
});
it("should not react to button clicks if pending", () => {
error = undefined;
isPending = true;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByText("Begin"));
expect(screen.queryByText("Begin")).toBeInTheDocument();
});
Next, we test for correct input. This is to ensure only valid characters are entered. Set the variables to ensure game is in progress, and render
Player.
src/components/Player/Player.test.jsit("should not react to button clicks if pending", () => {
error = undefined;
isPending = true;
mysteryWord = "evergreen";
setStage(-1);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByText("Begin"));
expect(screen.queryByText("Begin")).toBeInTheDocument();
});
it("should only allow letters to be input when guessing word", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
Then simulate tying into the input by using the
type() method of the
userEvent object. Enter something obviously invalid, like a number. And in the next line, ensure that the value in the text box is that very string we used, but with only alphabetical characters remaining!
src/components/Player/Player.test.jsit("should only allow letters to be input when guessing word", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.type(screen.getByTestId("txtGuessWord"), "xyz123-abc");
expect(screen.getByTestId("txtGuessWord").value).toBe("xyzabc");
});
OK, but there's no component known as
txtGuessWord. We'll need to insert the testing id here before this will test correctly.
src/components/Player/Player.js
<input
type="text"
maxLength="13"
value={ guessedWord }
onChange={ (e)=>{ setGuessedWord(RemoveIllegalCharacters(e.target.value)); }}
data-testid="txtGuessWord"
/>
And now... we will test the game by correctly "guessing" the word.
src/components/Player/Player.test.jsit("should only allow letters to be input when guessing word", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.type(screen.getByTestId("txtGuessWord"), "xyz123-abc");
expect(screen.getByTestId("txtGuessWord").value).toBe("xyzabc");
});
it("should end game if guessed word is correct", () => {
});
Here, we repeat what we did for the last test. Note that
stage is 3, which means that the game is in progress.
src/components/Player/Player.test.jsit("should end game if guessed word is correct", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
Then simulate input with text "evergreen", and simulate a click on the Confirm button. Since the input matches
mysteryWord,
stage should now be -1.
src/components/Player/Player.test.jsit("should end game if guessed word is correct", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.type(screen.getByTestId("txtGuessWord"), "evergreen");
userEvent.click(screen.getByText("Confirm"));
expect(stage).toBe(-1);
});
The next test is similar, expect that we deliberately test with an incorrect value. Instead of being set to -1,
stage should be incremented by 1.
src/components/Player/Player.test.jsit("should end game if guessed word is correct", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.type(screen.getByTestId("txtGuessWord"), "evergreen");
userEvent.click(screen.getByText("Confirm"));
expect(stage).toBe(-1);
});
it("should increment stage if guessed word is incorrect", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.type(screen.getByTestId("txtGuessWord"), "notevergreen");
userEvent.click(screen.getByText("Confirm"));
expect(stage).toBe(4);
});
Next test is for letter input.
src/components/Player/Player.test.jsit("should increment stage if guessed word is incorrect", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.type(screen.getByTestId("txtGuessWord"), "notevergreen");
userEvent.click(screen.getByText("Confirm"));
expect(stage).toBe(4);
});
it("should handle if guessed letter is correct", () => {
});
The testing variables are the same, but here we will include
guessedLetters and ensure it is an empty array.
src/components/Player/Player.test.jsit("should handle if guessed letter is correct", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
Simulate a click on the letter "e".
stage should still be 3, and there should be an element inserted into
guessedLetters.
src/components/Player/Player.test.jsit("should handle if guessed letter is correct", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByTestId("btnLetter_e"));
expect(stage).toBe(3);
expect(guessedLetters.length).toBe(1);
});
This test, of cours, will be useless if we don't insert testing ids...
src/components/Player/Player.jsconst keyboard = playerLetters.map((item, index) => (
<div
key={'letter_' + index}
className={usedLetters.indexOf(item) === -1 ? 'Key' : 'Key hidden'}
onClick={()=>{LetterClick(item);}}
data-testid={'btnLetter_' + item}
>
{item}
</div>
)
);
This next test is for incorrect letter input. The letter "y" is not in "evergreen", so
state is incremented to 4, and
guessedLetters is still an empty array.
src/components/Player/Player.test.jsit("should handle if guessed letter is correct", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByTestId("btnLetter_e"));
expect(stage).toBe(3);
expect(guessedLetters.length).toBe(1);
});
it("should handle if guessed letter is incorrect", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByTestId("btnLetter_y"));
expect(stage).toBe(4);
expect(guessedLetters.length).toBe(0);
});
Now let's take the test for correct input further.
src/components/Player/Player.test.jsit("should handle if guessed letter is incorrect", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByTestId("btnLetter_y"));
expect(stage).toBe(4);
expect(guessedLetters.length).toBe(0);
});
it("should handle if guessed letters win the game", () => {
});
Here, we repeat what we did what we did for guessing correct letters.
src/components/Player/Player.test.jsit("should handle if guessed letters win the game", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
});
We simulate clicks on all the letters within the word "evergreen".
stage should now be set to -1.
src/components/Player/Player.test.jsit("should handle if guessed letters win the game", () => {
error = undefined;
isPending = false;
mysteryWord = "evergreen";
guessedLetters = [];
setStage(3);
render(
<Player
stage={ stage }
setStage={ setStage }
mysteryWord={ mysteryWord }
guessedLetters={ guessedLetters }
setGuessedLetters={ setGuessedLetters }
setMessageAndContext={ setMessageAndContext }
error={ error }
isPending={ isPending }
/>
);
userEvent.click(screen.getByTestId("btnLetter_e"));
userEvent.click(screen.getByTestId("btnLetter_v"));
userEvent.click(screen.getByTestId("btnLetter_r"));
userEvent.click(screen.getByTestId("btnLetter_g"));
userEvent.click(screen.getByTestId("btnLetter_n"));
expect(stage).toBe(-1);
});
And when you finally run the tests, you should see this.
We're done!
That was a whole lot of testing, certainly. Hopefully, you see what we did mostly was automate tests that we were already carrying out during the game. Certainly the cverage of this testing can be increased if you can think of more tests.
This web tutorial was possible only from the tutelege I received from Red Airship. In fact, it was the first app I wrote using what I had learned from the experience. It was fun to actually build something, no matter how insignificant. Hope you had fun too!
Hang in there,
T___T