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"); expect(links.map((l) => l.textContent)).toEqual(["Bridge", "Jobs", "Fleet", "More"]); 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); } }); });