deadhead-space-freight-tycoon/apps/web/test/layout.test.tsx

88 lines
3.6 KiB
TypeScript

import { screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it } from "vitest";
import { SCREENS } from "../src/routes";
import { fakeApi, renderApp, samplePlayerState } from "./render";
const signedIn = () =>
fakeApi({
getPlayerState: () =>
samplePlayerState({ company: { id: "c", name: "D", credits: 1234, loanBalance: 0 } }),
});
describe("app layout", () => {
it("renders the header with credits and game clock, and the bottom navigation", async () => {
renderApp(signedIn());
expect(await screen.findByRole("banner")).toBeInTheDocument();
expect(screen.getByLabelText("Credits")).toHaveTextContent("1,234 cr");
expect(screen.getByLabelText("Game clock")).toHaveTextContent(
// Device-locale clock by default (CR-011): 24-hour or 12-hour.
/^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}( [AP]M)?$/,
);
const nav = screen.getByRole("navigation", { name: "Main" });
expect(within(nav).getAllByRole("link")).toHaveLength(SCREENS.length);
});
it.each(SCREENS)("shows the $title placeholder at $path", async ({ path, title }) => {
renderApp(signedIn(), path);
expect(await screen.findByRole("heading", { level: 1, name: title })).toBeInTheDocument();
});
it("navigates between screens with the bottom navigation", async () => {
renderApp(signedIn());
const nav = await screen.findByRole("navigation", { name: "Main" });
await userEvent.click(within(nav).getByRole("link", { name: "Fleet" }));
expect(screen.getByRole("heading", { level: 1, name: "Fleet" })).toBeInTheDocument();
expect(within(nav).getByRole("link", { name: "Fleet" })).toHaveAttribute(
"aria-current",
"page",
);
});
it("opens on the Bridge among four tabs (CR-029)", async () => {
renderApp(signedIn());
const nav = await screen.findByRole("navigation", { name: "Main" });
const links = within(nav).getAllByRole("link");
["Bridge", "Jobs", "Fleet", "More"].forEach((name, i) =>
expect(links[i]).toHaveAccessibleName(name),
);
expect(links[0]).toHaveAttribute("aria-current", "page");
expect(screen.getByRole("heading", { level: 1, name: "Bridge" })).toBeInTheDocument();
});
it.each([
["/map", "Map", "Bridge"],
["/pilots", "Crew", "Fleet"],
["/market", "Shipyard", "More"],
["/locations", "Places", "More"],
["/hqs", "HQs", "More"],
["/logbook", "Logbook", "More"],
["/settings", "Settings", "More"],
])("still opens %s, as %s under %s", async (path, title, tab) => {
renderApp(signedIn(), path);
expect(await screen.findByRole("heading", { level: 1, name: title })).toBeInTheDocument();
const nav = screen.getByRole("navigation", { name: "Main" });
expect(within(nav).getByRole("link", { name: tab })).toHaveAttribute("aria-current", "page");
});
it("switches between Ships and Crew inside Fleet", async () => {
renderApp(signedIn(), "/fleet");
const views = await screen.findByRole("navigation", { name: "Fleet views" });
await userEvent.click(within(views).getByRole("link", { name: /^Crew/ }));
expect(await screen.findByRole("heading", { level: 1, name: "Crew" })).toBeInTheDocument();
});
it("More reaches every screen without a tab", async () => {
renderApp(signedIn(), "/more");
await screen.findByRole("heading", { level: 1, name: "More" });
for (const [name, href] of [
[/^Shipyard/, "/market"],
[/^Places/, "/locations"],
[/^HQs/, "/hqs"],
[/^Logbook/, "/logbook"],
] as const) {
expect(screen.getByRole("link", { name })).toHaveAttribute("href", href);
}
});
});