Эх сурвалжийг харах

Refactor out some hooks for better component testing

Kirk Trombley 4 жил өмнө
parent
commit
f4c19774f1

+ 2 - 8
client/src/components/screens/GamePanel/GamePanel.jsx

@@ -1,5 +1,3 @@
-import { useEffect } from "react";
-import { dispatch, useCurrentRound } from "../../../domain/gameStore";
 import { FROZEN, RACE } from "../../../domain/ruleSets";
 import { useGameConfig } from "../../../hooks/useGameInfo";
 import usePreventNavigation from "../../../hooks/usePreventNavigation";
@@ -8,17 +6,13 @@ import styles from "./GamePanel.module.css";
 import GuessPane from "./GuessPane";
 import PositionedStreetView from "./PositionedStreetView";
 import RaceMode from "./RaceMode";
+import useIsFinished from "./useIsFinished";
 
 const GamePanel = () => {
   // warn the user if they navigate away
   usePreventNavigation();
   const { ruleSet } = useGameConfig();
-  const finished = useCurrentRound() === null;
-  useEffect(() => {
-    if (finished) {
-      dispatch.goToSummary();
-    }
-  }, [finished]);
+  const finished = useIsFinished();
 
   return finished ? (
     <Loading />

+ 3 - 18
client/src/components/screens/GamePanel/PositionedStreetView.jsx

@@ -1,15 +1,12 @@
-import { useEffect, useRef } from "react";
+import { useRef } from "react";
 import {
   usePanoStartPosition,
   usePanoStartPov,
   useTargetPoint,
 } from "../../../domain/gameStore";
-import {
-  savePanoPositionToLocalStorage,
-  savePanoPovToLocalStorage,
-} from "../../../domain/localStorageMethods";
 import styles from "./GamePanel.module.css";
 import usePano from "./usePano";
+import useSavePanoSettings from "./useSavePanoSettings";
 
 const PositionedStreetView = () => {
   const startPosition = usePanoStartPosition();
@@ -17,19 +14,7 @@ const PositionedStreetView = () => {
   const resetPosition = useTargetPoint();
   const panoDivRef = useRef(null);
   const panoRef = usePano(panoDivRef, startPosition, startPov);
-  useEffect(() => {
-    if (panoRef.current) {
-      panoRef.current.addListener("position_changed", () => {
-        const { lat, lng } = panoRef.current.getPosition();
-        savePanoPositionToLocalStorage(lat(), lng());
-      });
-      panoRef.current.addListener("pov_changed", () => {
-        const { heading, pitch } = panoRef.current.getPov();
-        savePanoPovToLocalStorage(heading, pitch);
-      });
-    }
-  }, [panoRef]);
-
+  useSavePanoSettings(panoRef);
   const reset = () => panoRef.current.setPosition(resetPosition);
 
   return (

+ 2 - 34
client/src/components/screens/GamePanel/RaceMode.jsx

@@ -1,41 +1,9 @@
 import ms from "pretty-ms";
-import { useEffect, useState } from "react";
-import {
-  useGameId,
-  useCurrentRound,
-  dispatch,
-} from "../../../domain/gameStore";
-import { getFirstSubmitter } from "../../../domain/apiMethods";
 import styles from "./GamePanel.module.css";
+import useRaceModeCheck from "./useRaceModeCheck";
 
 const RaceMode = ({ rate, cutoffTime }) => {
-  const [first, setFirst] = useState(null);
-  const gameId = useGameId();
-  const round = useCurrentRound();
-  useEffect(() => {
-    if (first !== null) {
-      return;
-    }
-    const update = async () => {
-      const firstRes = await getFirstSubmitter(gameId, round);
-      if (firstRes !== null) {
-        dispatch.updateRoundSeconds(r => Math.min(r, cutoffTime));
-        setFirst(firstRes);
-      }
-    };
-    update();
-    const interval = setInterval(update, rate);
-    // eslint-disable-next-line consistent-return
-    return () => clearInterval(interval);
-  }, [first, gameId, round, rate, cutoffTime]);
-  const [faded, setFaded] = useState(false);
-  // eslint-disable-next-line consistent-return
-  useEffect(() => {
-    if (first !== null) {
-      const timeout = setTimeout(() => setFaded(true), 2000);
-      return () => clearTimeout(timeout);
-    }
-  }, [first]);
+  const [first, faded] = useRaceModeCheck(rate, cutoffTime);
 
   if (first === null) {
     return <></>;

+ 14 - 0
client/src/components/screens/GamePanel/useIsFinished.jsx

@@ -0,0 +1,14 @@
+import { useEffect } from "react";
+import { dispatch, useCurrentRound } from "../../../domain/gameStore";
+
+const useIsFinished = () => {
+  const finished = useCurrentRound() === null;
+  useEffect(() => {
+    if (finished) {
+      dispatch.goToSummary();
+    }
+  }, [finished]);
+  return finished;
+};
+
+export default useIsFinished;

+ 41 - 0
client/src/components/screens/GamePanel/useRaceModeCheck.jsx

@@ -0,0 +1,41 @@
+import { useEffect, useState } from "react";
+import {
+  useGameId,
+  useCurrentRound,
+  dispatch,
+} from "../../../domain/gameStore";
+import { getFirstSubmitter } from "../../../domain/apiMethods";
+
+const useRaceModeCheck = (rate, cutoffTime) => {
+  const [first, setFirst] = useState(null);
+  const gameId = useGameId();
+  const round = useCurrentRound();
+  useEffect(() => {
+    if (first !== null) {
+      return;
+    }
+    const update = async () => {
+      const firstRes = await getFirstSubmitter(gameId, round);
+      if (firstRes !== null) {
+        dispatch.updateRoundSeconds(r => Math.min(r, cutoffTime));
+        setFirst(firstRes);
+      }
+    };
+    update();
+    const interval = setInterval(update, rate);
+    // eslint-disable-next-line consistent-return
+    return () => clearInterval(interval);
+  }, [first, gameId, round, rate, cutoffTime]);
+  const [faded, setFaded] = useState(false);
+  // eslint-disable-next-line consistent-return
+  useEffect(() => {
+    if (first !== null) {
+      const timeout = setTimeout(() => setFaded(true), 2000);
+      return () => clearTimeout(timeout);
+    }
+  }, [first]);
+
+  return [first, faded];
+};
+
+export default useRaceModeCheck;

+ 22 - 0
client/src/components/screens/GamePanel/useSavePanoSettings.jsx

@@ -0,0 +1,22 @@
+import { useEffect } from "react";
+import {
+  savePanoPositionToLocalStorage,
+  savePanoPovToLocalStorage,
+} from "../../../domain/localStorageMethods";
+
+const useSavePanoSettings = panoRef => {
+  useEffect(() => {
+    if (panoRef.current) {
+      panoRef.current.addListener("position_changed", () => {
+        const { lat, lng } = panoRef.current.getPosition();
+        savePanoPositionToLocalStorage(lat(), lng());
+      });
+      panoRef.current.addListener("pov_changed", () => {
+        const { heading, pitch } = panoRef.current.getPov();
+        savePanoPovToLocalStorage(heading, pitch);
+      });
+    }
+  }, [panoRef]);
+};
+
+export default useSavePanoSettings;

+ 6 - 6
client/src/tests/GamePanel.test.js

@@ -3,17 +3,17 @@ import { shallow } from "enzyme";
 import { FROZEN, NORMAL, RACE } from "../domain/ruleSets";
 import GamePanel from "../components/screens/GamePanel";
 
-jest.mock("../domain/gameStore");
 jest.mock("../hooks/usePreventNavigation");
 jest.mock("../hooks/useGameInfo");
+jest.mock("../components/screens/GamePanel/useIsFinished");
 
 import usePreventNavigation from "../hooks/usePreventNavigation";
 import { useGameConfig } from "../hooks/useGameInfo";
-import { useCurrentRound } from "../domain/gameStore";
+import useIsFinished from "../components/screens/GamePanel/useIsFinished";
 
 describe("GamePanel", () => {
   it("renders for NORMAL game", () => {
-    useCurrentRound.mockReturnValue("");
+    useIsFinished.mockReturnValue(false);
     useGameConfig.mockReturnValue({ ruleSet: NORMAL });
     const rendered = shallow(<GamePanel />);
     expect(rendered).toMatchSnapshot();
@@ -22,7 +22,7 @@ describe("GamePanel", () => {
   });
 
   it("renders for end of game", () => {
-    useCurrentRound.mockReturnValue(null);
+    useIsFinished.mockReturnValue(true);
     useGameConfig.mockReturnValue({ ruleSet: NORMAL });
     const rendered = shallow(<GamePanel />);
     expect(rendered).toMatchSnapshot();
@@ -31,7 +31,7 @@ describe("GamePanel", () => {
   });
 
   it("renders for FROZEN game", () => {
-    useCurrentRound.mockReturnValue("");
+    useIsFinished.mockReturnValue(false);
     useGameConfig.mockReturnValue({ ruleSet: FROZEN });
     const rendered = shallow(<GamePanel />);
     expect(rendered).toMatchSnapshot();
@@ -40,7 +40,7 @@ describe("GamePanel", () => {
   });
 
   it("renders for RACE game", () => {
-    useCurrentRound.mockReturnValue("");
+    useIsFinished.mockReturnValue(false);
     useGameConfig.mockReturnValue({ ruleSet: RACE });
     const rendered = shallow(<GamePanel />);
     expect(rendered).toMatchSnapshot();

+ 17 - 0
client/src/tests/PositionedStreetView.test.js

@@ -4,6 +4,7 @@ import PositionedStreetView from "../components/screens/GamePanel/PositionedStre
 
 jest.mock("../domain/gameStore");
 jest.mock("../components/screens/GamePanel/usePano");
+jest.mock("../components/screens/GamePanel/useSavePanoSettings");
 
 import {
   usePanoStartPosition,
@@ -51,4 +52,20 @@ describe("PositionedStreetView", () => {
       .simulate("keydown", { key: "Enter" });
     expect(setPosition).toHaveBeenCalledWith("point");
   });
+
+  it("does not reset on other keys on button", () => {
+    usePanoStartPosition.mockReturnValue();
+    usePanoStartPov.mockReturnValue();
+    useTargetPoint.mockReturnValue("point");
+    const setPosition = jest.fn();
+    usePano.mockReturnValue({ current: { setPosition } });
+
+    const handle = shallow(<PositionedStreetView />);
+    expect(handle).toMatchSnapshot();
+    handle
+      .find("div.resetButton")
+      .first()
+      .simulate("keydown", { key: "Escape" });
+    expect(setPosition).not.toHaveBeenCalled();
+  });
 });

+ 14 - 0
client/src/tests/RaceMode.test.js

@@ -4,10 +4,24 @@ import RaceMode from "../components/screens/GamePanel/RaceMode";
 
 jest.mock("../domain/gameStore");
 jest.mock("../domain/apiMethods");
+jest.mock("../components/screens/GamePanel/useRaceModeCheck");
+
+import useRaceModeCheck from "../components/screens/GamePanel/useRaceModeCheck";
 
 describe("PositionedStreetView", () => {
   it("renders", () => {
+    useRaceModeCheck.mockReturnValue([null, false]);
     const rendered = shallow(<RaceMode />);
     expect(rendered).toMatchSnapshot();
   });
+  it("renders when cut off", () => {
+    useRaceModeCheck.mockReturnValue(["cutoff-player", false]);
+    const rendered = shallow(<RaceMode cutoffTime={10} />);
+    expect(rendered).toMatchSnapshot();
+  });
+  it("renders when faded", () => {
+    useRaceModeCheck.mockReturnValue(["cutoff-player", true]);
+    const rendered = shallow(<RaceMode cutoffTime={10} />);
+    expect(rendered).toMatchSnapshot();
+  });
 });

+ 17 - 0
client/src/tests/__snapshots__/PositionedStreetView.test.js.snap

@@ -1,5 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`PositionedStreetView does not reset on other keys on button 1`] = `
+<Fragment>
+  <div
+    className="fullsize"
+  />
+  <div
+    className="resetButton"
+    onClick={[Function]}
+    onKeyDown={[Function]}
+    role="button"
+    tabIndex="0"
+  >
+    Reset
+  </div>
+</Fragment>
+`;
+
 exports[`PositionedStreetView renders 1`] = `
 <Fragment>
   <div

+ 24 - 0
client/src/tests/__snapshots__/RaceMode.test.js.snap

@@ -1,3 +1,27 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`PositionedStreetView renders 1`] = `<Fragment />`;
+
+exports[`PositionedStreetView renders when cut off 1`] = `
+<div
+  className="cutoff "
+>
+  You were cut off by 
+  cutoff-player
+  ! Only 
+  10s
+   left!
+</div>
+`;
+
+exports[`PositionedStreetView renders when faded 1`] = `
+<div
+  className="cutoff hidden"
+>
+  You were cut off by 
+  cutoff-player
+  ! Only 
+  10s
+   left!
+</div>
+`;