import { content } from "@deadhead/content"; import type { Ship } from "@deadhead/schema"; import { checkRoute, gameToReal, realToGame } from "@deadhead/sim"; import { screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { afterEach, describe, expect, it, vi } from "vitest"; import { useClock } from "../src/clock/clock"; import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render"; const MONTH = 30.44 * 24 * 3_600_000; const gameStart = realToGame(Date.parse(content.config.epoch.realStart), content.config); const clipper = content.shipModel("clipper")!; const [mars, ceres] = [content.location("mars")!, content.location("ceres-station")!]; /** The first monthly date on which a Clipper at Mars can reach Ceres. */ const NOW = (() => { for (let i = 0; i <= 16 * 12; i++) { const t = gameStart + i * MONTH; if (checkRoute(clipper, mars, ceres, content.config).ok) return gameToReal(t, content.config); } throw new Error("no such date"); })(); const atMars: Ship = { id: "s1", modelId: "clipper", name: "Swift Heron", status: "idle", locationId: "mars", dispatchId: null, pilotId: null, lastArrivalAt: 0, ...shipExtras("clipper"), }; describe("map reachability per origin (Step 10L.8)", () => { afterEach(() => vi.useRealTimers()); it("lists the Clipper from Mars, where it is, and not from Earth", async () => { vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] }); vi.setSystemTime(NOW); useClock.setState({ offsetMs: 0, synced: true, now: NOW }); const state = samplePlayerState({ bays: [{ id: "b-earth", locationId: "earth" }], ships: [atMars], }); state.locations = state.locations.map((l) => l.id === "mars" ? { ...l, discovered: true } : l, ); renderApp(fakeApi({ getPlayerState: () => state }), "/map"); const canvas = await screen.findByTestId("map-canvas"); await userEvent.click( within(canvas).getByRole("button", { name: "Tap Ceres Station (locked)" }), ); const text = screen.getByLabelText("Reachable with").textContent ?? ""; const parts = Object.fromEntries( text.split(" ยท ").map((part) => { const [origin, list] = part.split(": "); return [origin!, list!.split(", ")]; }), ); expect(parts["From Mars"]).toContain("Clipper"); expect(parts["From Earth"] ?? []).not.toContain("Clipper"); }); });