Skip to content
This repository has been archived by the owner on Sep 8, 2021. It is now read-only.

Commit

Permalink
✨ Added ability to join different rooms (#54)
Browse files Browse the repository at this point in the history
* ✨ Added room name to local state

* ✨ Added useSharedStore factory

* 💄 Make sure that control panel sits at grid start

* 🚧 Fleshing out join room flow

* ✨ Gave join form an input field for room anme

* ✨ Added Join! button

* ✨ Set room name on form submit

* 🔧 VSCode workspace is now relative

* ✏️ All files end in LF

* ✨ Added join form

* 🔥 Removed debugging for JoinForm

* ✅ ActorList uses shared state context

* ✅ ActorList and AddActor use shared store context

* ✅ Added act around setState call

* ✅ Wrapped useLocalStore in act

* ✅ Removed unneeded act call

* ✅ Updated token tests to use new shared state

* ✅ Updated tabletop to use new shared store

* 🔥 Removed old shared store

* 🐛 Fixed creating duplicate useShareStores on every render

* ✅ Passing all e2e tests

* ✅ Disabled headed mode and slow-mo

* 💄 Styled JoinForm

* 📝 Added note about flaky test
  • Loading branch information
joebobmiles authored Jul 27, 2021
1 parent db91f64 commit 4efc265
Show file tree
Hide file tree
Showing 20 changed files with 427 additions and 77 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* eol=lf
2 changes: 1 addition & 1 deletion .vscode/TableTop.code-workspace
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"folders": [
{
"path": "C:\\Users\\josep\\Documents\\Programming\\TypeScript\\TableTop"
"path": ".."
}
]
}
26 changes: 26 additions & 0 deletions e2e/select-theme.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { firefox, chromium, webkit, Browser, Page } from "playwright";
import { v4 as uuidv4} from "uuid";

jest.setTimeout(10000);

Expand Down Expand Up @@ -42,6 +43,14 @@ describe.each([
{
page = await browser.newPage({ colorScheme: preferredTheme });
await page.goto("http://localhost:8080");

const roomNameInput = await page.waitForSelector(
"input[type=text]:near(:text('Room Name'))"
);

await roomNameInput.type(uuidv4());

await (await page.$("input[type=submit]:text('Join!')")).click();
});

afterEach(async () =>
Expand Down Expand Up @@ -73,13 +82,22 @@ describe.each([
{
page = await browser.newPage({ colorScheme: "light" });
await page.goto("http://localhost:8080");

const roomNameInput = await page.waitForSelector(
"input[type=text]:near(:text('Room Name'))"
);

await roomNameInput.type(uuidv4());

await (await page.$("input[type=submit]:text('Join!')")).click();
});

afterEach(async () =>
{
await page.close();
});

// Flaky test, fails on random expects in random browsers.
it("Selects that theme regardless of browser preference.", async () =>
{
const selector = `input[type=radio]:left-of(:text("Dark"))`;
Expand All @@ -94,6 +112,14 @@ describe.each([

await page.reload();

const roomNameInput = await page.waitForSelector(
"input[type=text]:near(:text('Room Name'))"
);

await roomNameInput.type(uuidv4());

await (await page.$("input[type=submit]:text('Join!')")).click();

const reloadedOption = await page.waitForSelector(selector);

expect(await reloadedOption.isChecked()).toBe(true);
Expand Down
26 changes: 26 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@semantic-release/git": "^9.0.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^26.0.24",
"@types/react": "^16.14.2",
"@types/react-dom": "^16.9.10",
Expand Down
47 changes: 41 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,51 @@
import * as React from "react"
import { v4 as uuidv4 } from "uuid";

import { useLocalStore } from "./store/local";
import { useSharedStoreFactory } from "./store/shared";

import Tabletop from "~/components/tabletop/Tabletop";
import { RectangularGrid } from "~/components/tabletop/RectangularGrid";

import ControlPanel from "./components/control-panel/ControlPanel";
import JoinForm from "./components/join-form/JoinForm";

export const App = () =>
(
<main style={{ width: "100%", height: "100%" }}>
<ControlPanel />
<Tabletop grid={<RectangularGrid />} />
</main>
export const SharedStoreContext = React.createContext(
useSharedStoreFactory(uuidv4())
);

export const App = () =>
{
const { room } = useLocalStore();

const useSharedStore = React.useMemo(
() => useSharedStoreFactory(room),
[ room ]
);

return (
<main
style={{
width: "100vw",
height: "100vh",
display: "grid",
placeItems: "center",
}}
>
{
room === ""
? (
<JoinForm />
)
: (
<SharedStoreContext.Provider value={useSharedStore}>
<ControlPanel />
<Tabletop grid={<RectangularGrid />} />
</SharedStoreContext.Provider>
)
}
</main>
)
}

export default App;
52 changes: 45 additions & 7 deletions src/components/control-panel/ActorList.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
import React from "react";
import { UseStore } from "zustand";

import { fireEvent, render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";

import { useSharedStore } from "~/store/shared";
import { useLocalStore } from "~/store/local";
import { SharedState, useSharedStoreFactory } from "~/store/shared";
import { Actor } from "~/core/Actor";

import ActorList, { actorListItemTestId, } from "./ActorList";
import { SharedStoreContext } from "~/App";

import { v4 as uuidv4 } from "uuid";

describe("Connected ActorList", () =>
{
let useSharedStore: UseStore<SharedState>;

beforeEach(() =>
{
useLocalStore.setState(() => ({ room: uuidv4() }));
useSharedStore = useSharedStoreFactory(useLocalStore.getState().room);
});

afterEach(() =>
{
useLocalStore.setState(() => ({ room: "" }));
});

it("Displays 'no actors' when there are no actors in state.", () =>
{
useSharedStore.setState(() => ({ actors: [] }));

const { getByText } = render(<ActorList />);
const { getByText } = render(
<SharedStoreContext.Provider value={useSharedStore}>
<ActorList />
</SharedStoreContext.Provider>
);

expect(getByText(/no actors/i)).toBeInTheDocument();
});
Expand All @@ -36,7 +58,11 @@ describe("Connected ActorList", () =>
for (let actorNumber of actorList)
useSharedStore.getState().addActor({ id: `${actorNumber}` } as Actor);

const { getAllByTestId } = render(<ActorList />);
const { getAllByTestId } = render(
<SharedStoreContext.Provider value={useSharedStore}>
<ActorList />
</SharedStoreContext.Provider>
);

expect(getAllByTestId(actorListItemTestId)).toHaveLength(actorList.length);
});
Expand All @@ -46,9 +72,13 @@ describe("Connected ActorList", () =>
useSharedStore.setState(() => ({ actors: [] }));
useSharedStore.getState().addActor({ id: `1`, name: `Actor 1` } as Actor);

const { getByText } = render(<ActorList />);
const { queryByText } = render(
<SharedStoreContext.Provider value={useSharedStore}>
<ActorList />
</SharedStoreContext.Provider>
);

expect(getByText("Actor 1")).toBeInTheDocument();
expect(queryByText("Actor 1")).toBeInTheDocument();
});

it("Displays Actors sorted from highest initiative to lowest.", () =>
Expand All @@ -57,7 +87,11 @@ describe("Connected ActorList", () =>
useSharedStore.getState().addActor({ id: `1`, name: `Actor 1`, initiative: 1 } as Actor);
useSharedStore.getState().addActor({ id: `2`, name: `Actor 2`, initiative: 2 } as Actor);

const { getAllByTestId } = render(<ActorList />);
const { getAllByTestId } = render(
<SharedStoreContext.Provider value={useSharedStore}>
<ActorList />
</SharedStoreContext.Provider>
);

expect(getAllByTestId(actorListItemTestId)[0]).toHaveTextContent("Actor 2");
expect(getAllByTestId(actorListItemTestId)[1]).toHaveTextContent("Actor 1");
Expand All @@ -70,7 +104,11 @@ describe("Connected ActorList", () =>
useSharedStore.getState().addActor({ id: "1", name: "Actor 1", initiative: 2 } as Actor);
useSharedStore.getState().addActor({ id: "2", name: "Actor 2", initiative: 1 } as Actor);

const { getAllByTestId } = render(<ActorList />);
const { getAllByTestId } = render(
<SharedStoreContext.Provider value={useSharedStore}>
<ActorList />
</SharedStoreContext.Provider>
);

fireEvent.click(getAllByTestId(actorListItemTestId)[0]);

Expand Down
5 changes: 4 additions & 1 deletion src/components/control-panel/ActorList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { useSharedStore } from "~/store/shared";
import { SharedStoreContext } from "~/App";
// import { useSharedStore } from "~/store/shared";

import "./ActorList.module.css";
import styles from "./ActorList.module.css";
Expand All @@ -9,6 +10,8 @@ export const actorListItemTestId = "actor-list-item";

export const ActorList = () =>
{
const useSharedStore = React.useContext(SharedStoreContext);

const { actors, removeActor } = useSharedStore(({ actors, removeActor }) =>
({
actors,
Expand Down
15 changes: 12 additions & 3 deletions src/components/control-panel/AddActor.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from "react";
import { fireEvent, render } from "@testing-library/react";
import { act, fireEvent, render } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";

import { useSharedStore } from "~/store/shared";
import { v4 as uuidv4 } from "uuid";
import { UseStore } from "zustand";

import { SharedState, useSharedStoreFactory } from "~/store/shared";
import { SharedStoreContext } from "~/App";

import AddActor, { AddActor as DisconnectedAddActor} from "./AddActor";

Expand All @@ -23,10 +27,15 @@ describe("Connected AddActor", () =>
{
it("Adds an actor when clicked.", () =>
{
let useSharedStore: UseStore<SharedState>;

useSharedStore = useSharedStoreFactory(uuidv4());
useSharedStore.setState(() => ({ actors: [] }));

const { getByText } = render(
<AddActor />
<SharedStoreContext.Provider value={useSharedStore}>
<AddActor />
</SharedStoreContext.Provider>
);

const button = getByText("Add Actor");
Expand Down
4 changes: 3 additions & 1 deletion src/components/control-panel/AddActor.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React from "react";
import { useSharedStore } from "~/store/shared";
import { v4 as uuid } from "uuid";

import { SharedStoreContext } from "~/App";
import { Button } from "~/components/util/Button";

import "./AddActor.module.css";

export const AddActor = () =>
{
const useSharedStore = React.useContext(SharedStoreContext);

const addActor = useSharedStore((state) => state.addActor);

return (
Expand Down
Loading

0 comments on commit 4efc265

Please sign in to comment.