Browse Source

Add unit tests for Lobby

Kirk Trombley 4 years ago
parent
commit
b26b1ed2b7

+ 3 - 3
client/src/components/screens/Lobby/Lobby.jsx

@@ -15,7 +15,7 @@ import {
   COUNTRY_RACE,
 } from "../../../domain/ruleSets";
 
-const GameInfo = () => {
+export const GameInfo = () => {
   const { rounds, timer, countryLock, ruleSet } = useGameConfig();
 
   if (!rounds || !timer) {
@@ -69,7 +69,7 @@ const getUrl = gameId => {
   return u.href;
 };
 
-const PlayerList = ({ playerNames }) => (
+export const PlayerList = ({ playerNames }) => (
   <div className={styles.players}>
     <span className={styles.playersTitle}>Players</span>
     <ul>
@@ -106,7 +106,7 @@ const Lobby = ({ onStart }) => {
           </ClickToCopy>
         </span>
       </div>
-      <PlayerList playerNames={players?.map(({ name }) => name) ?? []} />
+      <PlayerList playerNames={players.map(({ name }) => name)} />
     </div>
   );
 };

+ 79 - 0
client/src/tests/JoinForm.test.js

@@ -0,0 +1,79 @@
+import React from "react";
+import { shallow } from "enzyme";
+import JoinForm from "../components/screens/Lobby/JoinForm";
+
+jest.mock("../domain/gameStore");
+
+import { usePlayerName, dispatch } from "../domain/gameStore";
+
+describe("JoinForm", () => {
+  it("renders", () => {
+    const onJoined = jest.fn();
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    expect(rendered).toMatchSnapshot();
+  });
+
+  it("blocks joining with empty player name", () => {
+    const onJoined = jest.fn();
+    usePlayerName.mockReturnValue("");
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    expect(rendered).toMatchSnapshot();
+  });
+
+  it("sets player name", () => {
+    const onJoined = jest.fn();
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    rendered
+      .find("input")
+      .first()
+      .simulate("change", { target: { value: "test" } });
+    expect(dispatch.setPlayerName).toHaveBeenCalledWith("test");
+  });
+
+  it("joins game", async () => {
+    const onJoined = jest.fn();
+    usePlayerName.mockReturnValue("test-player");
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    expect(rendered).toMatchSnapshot();
+    await rendered.find("button").first().simulate("click");
+    expect(rendered).toMatchSnapshot();
+    expect(dispatch.joinGame).toHaveBeenCalled();
+    expect(onJoined).toHaveBeenCalled();
+  });
+
+  it("joins game if enter pressed in input", async () => {
+    const onJoined = jest.fn();
+    usePlayerName.mockReturnValue("test-player");
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    expect(rendered).toMatchSnapshot();
+    await rendered.find("input").first().simulate("keydown", { key: "Enter" });
+    expect(rendered).toMatchSnapshot();
+    expect(dispatch.joinGame).toHaveBeenCalled();
+    expect(onJoined).toHaveBeenCalled();
+  });
+
+  it("does not join game if other keys pressed in input", async () => {
+    const onJoined = jest.fn();
+    usePlayerName.mockReturnValue("test-player");
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    expect(rendered).toMatchSnapshot();
+    await rendered.find("input").first().simulate("keydown", { key: "Escape" });
+    expect(rendered).toMatchSnapshot();
+    expect(dispatch.joinGame).not.toHaveBeenCalled();
+    expect(onJoined).not.toHaveBeenCalled();
+  });
+
+  it("handles failure", async () => {
+    dispatch.joinGame.mockImplementation(() => {
+      throw new Error();
+    });
+    const onJoined = jest.fn();
+    usePlayerName.mockReturnValue("test-player");
+    const rendered = shallow(<JoinForm onJoined={onJoined} />);
+    expect(rendered).toMatchSnapshot();
+    await rendered.find("button").first().simulate("click");
+    expect(rendered).toMatchSnapshot();
+    expect(dispatch.joinGame).toHaveBeenCalled();
+    expect(onJoined).not.toHaveBeenCalled();
+  });
+});

+ 170 - 0
client/src/tests/Lobby.test.js

@@ -0,0 +1,170 @@
+import React from "react";
+import { shallow } from "enzyme";
+import Lobby, { GameInfo, PlayerList } from "../components/screens/Lobby/Lobby";
+import {
+  COUNTRY_RACE,
+  FROZEN,
+  NORMAL,
+  RACE,
+  TIME_BANK,
+} from "../domain/ruleSets";
+
+jest.mock("iso-3166-1");
+jest.mock("../domain/gameStore");
+jest.mock("../hooks/useGameInfo");
+
+import iso from "iso-3166-1";
+import { useGameId } from "../domain/gameStore";
+import { useGameConfig, usePlayers } from "../hooks/useGameInfo";
+
+describe("Lobby", () => {
+  it("renders while loading", () => {
+    useGameId.mockReturnValue("test-game-id");
+    usePlayers.mockReturnValue(null);
+    const onStart = jest.fn();
+    const rendered = shallow(<Lobby onStart={onStart} />);
+    expect(rendered).toMatchSnapshot();
+  });
+
+  it("renders with no players", () => {
+    useGameId.mockReturnValue("test-game-id");
+    usePlayers.mockReturnValue([]);
+    const onStart = jest.fn();
+    const rendered = shallow(<Lobby onStart={onStart} />);
+    expect(rendered).toMatchSnapshot();
+  });
+
+  it("renders", () => {
+    useGameId.mockReturnValue("test-game-id");
+    usePlayers.mockReturnValue([{ name: "foo", name: "bar" }]);
+    const onStart = jest.fn();
+    const rendered = shallow(<Lobby onStart={onStart} />);
+    expect(rendered).toMatchSnapshot();
+  });
+
+  it("properly handles joining and starting", () => {
+    useGameId.mockReturnValue("test-game-id");
+    usePlayers.mockReturnValue([]);
+    const onStart = jest.fn();
+    const rendered = shallow(<Lobby onStart={onStart} />);
+    rendered.find("JoinForm").first().prop("onJoined")();
+    expect(rendered).toMatchSnapshot();
+    rendered.find("StartGame").first().prop("onStart")();
+    expect(onStart).toHaveBeenCalled();
+  });
+
+  describe("GameInfo", () => {
+    it("renders while loading", () => {
+      useGameConfig.mockReturnValue({});
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for single round", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 1,
+        timer: 300,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for COUNTRY_RACE", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+        ruleSet: COUNTRY_RACE,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for FROZEN", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+        ruleSet: FROZEN,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for single round COUNTRY_RACE", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 1,
+        timer: 300,
+        ruleSet: COUNTRY_RACE,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for single round FROZEN", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 1,
+        timer: 300,
+        ruleSet: FROZEN,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for TIME_BANK", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+        ruleSet: TIME_BANK,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for RACE", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+        ruleSet: RACE,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders for NORMAL", () => {
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+        ruleSet: NORMAL,
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+
+    it("renders with country lock", () => {
+      iso.whereAlpha2.mockReturnValue({ country: "test-country" });
+      useGameConfig.mockReturnValue({
+        rounds: 5,
+        timer: 300,
+        countryLock: "country",
+      });
+      const rendered = shallow(<GameInfo />);
+      expect(rendered).toMatchSnapshot();
+    });
+  });
+
+  describe("PlayerList", () => {
+    it("renders", () => {
+      const playerNames = ["foo", "bar"];
+      const rendered = shallow(<PlayerList playerNames={playerNames} />);
+      expect(rendered).toMatchSnapshot();
+    });
+  });
+});

+ 313 - 0
client/src/tests/__snapshots__/JoinForm.test.js.snap

@@ -0,0 +1,313 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`JoinForm blocks joining with empty player name 1`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value=""
+    />
+    <button
+      disabled={true}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm does not join game if other keys pressed in input 1`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={false}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm does not join game if other keys pressed in input 2`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={false}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm handles failure 1`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={false}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm handles failure 2`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={false}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  >
+    Failed to join the game! Maybe try a different name?
+  </div>
+</Fragment>
+`;
+
+exports[`JoinForm joins game 1`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={false}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm joins game 2`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={true}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm joins game if enter pressed in input 1`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={false}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm joins game if enter pressed in input 2`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value="test-player"
+    />
+    <button
+      disabled={true}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;
+
+exports[`JoinForm renders 1`] = `
+<Fragment>
+  <div
+    className="join"
+  >
+    <span
+      className="label"
+    >
+      Enter your name to join:
+    </span>
+    <input
+      className="name"
+      onChange={[Function]}
+      onKeyDown={[Function]}
+      type="text"
+      value=""
+    />
+    <button
+      disabled={true}
+      onClick={[Function]}
+      type="button"
+    >
+      Join Game
+    </button>
+  </div>
+  <div
+    className="error"
+  />
+</Fragment>
+`;

+ 270 - 0
client/src/tests/__snapshots__/Lobby.test.js.snap

@@ -0,0 +1,270 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Lobby GameInfo renders 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    , each with a 5m time limit
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for COUNTRY_RACE 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    , each with a 5m time limit, where you must be the fastest to select the right country
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for FROZEN 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    , each with a 5m time limit, and you will not be able to adjust your view
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for NORMAL 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    , each with a 5m time limit
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for RACE 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    , each with a 5m time limit
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for TIME_BANK 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    with a 25m time bank across all rounds
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for single round 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    1
+     round
+     with a 5m time limit
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for single round COUNTRY_RACE 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    1
+     round
+     with a 5m time limit, where you must be the fastest to select the right country
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders for single round FROZEN 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    1
+     round
+     with a 5m time limit, and you will not be able to adjust your view
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby GameInfo renders while loading 1`] = `<Loading />`;
+
+exports[`Lobby GameInfo renders with country lock 1`] = `
+<Fragment>
+  <span
+    className="label"
+  >
+    Game will run for 
+    5
+     round
+    s
+    , each with a 5m time limit
+  </span>
+  <span
+    className="label"
+  >
+    This game will only use locations within:
+     
+    test-country
+  </span>
+</Fragment>
+`;
+
+exports[`Lobby PlayerList renders 1`] = `
+<div
+  className="players"
+>
+  <span
+    className="playersTitle"
+  >
+    Players
+  </span>
+  <ul>
+    <li
+      key="foo"
+    >
+      foo
+    </li>
+    <li
+      key="bar"
+    >
+      bar
+    </li>
+  </ul>
+</div>
+`;
+
+exports[`Lobby properly handles joining and starting 1`] = `
+<div
+  className="page"
+>
+  <div
+    className="info"
+  >
+    <GameInfo />
+    <div
+      className="form"
+    >
+      <StartGame
+        onStart={[MockFunction]}
+      />
+    </div>
+    <span
+      className="label"
+    >
+      <ClickToCopy
+        text="http://localhost/?join=test-game-id"
+      >
+        Click here to copy an invite link!
+      </ClickToCopy>
+    </span>
+  </div>
+  <PlayerList
+    playerNames={Array []}
+  />
+</div>
+`;
+
+exports[`Lobby renders 1`] = `
+<div
+  className="page"
+>
+  <div
+    className="info"
+  >
+    <GameInfo />
+    <div
+      className="form"
+    >
+      <JoinForm
+        onJoined={[Function]}
+      />
+    </div>
+    <span
+      className="label"
+    >
+      <ClickToCopy
+        text="http://localhost/?join=test-game-id"
+      >
+        Click here to copy an invite link!
+      </ClickToCopy>
+    </span>
+  </div>
+  <PlayerList
+    playerNames={
+      Array [
+        "bar",
+      ]
+    }
+  />
+</div>
+`;
+
+exports[`Lobby renders while loading 1`] = `<Loading />`;
+
+exports[`Lobby renders with no players 1`] = `
+<div
+  className="page"
+>
+  <div
+    className="info"
+  >
+    <GameInfo />
+    <div
+      className="form"
+    >
+      <JoinForm
+        onJoined={[Function]}
+      />
+    </div>
+    <span
+      className="label"
+    >
+      <ClickToCopy
+        text="http://localhost/?join=test-game-id"
+      >
+        Click here to copy an invite link!
+      </ClickToCopy>
+    </span>
+  </div>
+  <PlayerList
+    playerNames={Array []}
+  />
+</div>
+`;