import type { Ship } from "@deadhead/schema"; import { screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useClock } from "../src/clock/clock"; import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render"; const NOW = Date.parse("2100-01-01T00:00:00Z"); const ship = (id: string, name: string, overrides: Partial): Ship => ({ id, name, modelId: "freighter", status: "idle", locationId: "earth", dispatchId: null, pilotId: null, lastArrivalAt: 0, ...shipExtras("freighter"), ...overrides, }); describe("the shop in summaries and on HQ (Step 10I.8)", () => { beforeEach(() => { vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] }); vi.setSystemTime(NOW); useClock.setState({ offsetMs: 0, synced: true, now: NOW }); }); afterEach(() => vi.useRealTimers()); it("says a ship is out of the shop for an upgrade that finished, and acknowledges it", async () => { const api = fakeApi({ getPlayerState: () => samplePlayerState({ tutorial: null, ships: [ ship("s1", "Slow Ox", { upgrades: { engines: 2 }, shop: { upgradeId: "engines", level: 2, until: NOW - 60_000 }, }), ship("s2", "Busy Ox", { upgrades: { "cargo-bay": 1 }, inShopUntil: NOW + 60_000, shop: { upgradeId: "cargo-bay", level: 1, until: NOW + 60_000 }, }), ], }), }); renderApp(api, "/"); const dialog = await screen.findByRole("dialog", { name: "While you were away" }); expect(dialog).toHaveTextContent("Slow Ox is out of the shop: Engine Tuning 2"); expect(dialog).not.toHaveTextContent("Busy Ox"); await userEvent.click(within(dialog).getByRole("button", { name: "Got it" })); await waitFor(() => expect(api.call).toHaveBeenCalledWith("acknowledgeSummaries", { dispatchIds: [], shipIds: ["s1"], }), ); }); it("lists ships in the shop on HQ, soonest first, with countdowns", async () => { renderApp( fakeApi({ getPlayerState: () => samplePlayerState({ tutorial: null, ships: [ ship("s1", "Late Ox", { inShopUntil: NOW + 25 * 60_000, shop: { upgradeId: "engines", level: 2, until: NOW + 25 * 60_000 }, }), ship("s2", "Soon Ox", { inShopUntil: NOW + 4 * 60_000, shop: { upgradeId: "cargo-bay", level: 1, until: NOW + 4 * 60_000 }, }), ship("s3", "Free Ox", {}), ], }), }), "/", ); const card = await screen.findByLabelText("In the shop"); expect(card).toHaveTextContent("In the shop: 2"); const [first, second] = within(card).getAllByRole("listitem"); expect(first).toHaveTextContent("Soon Ox · Cargo Bay Expansion 1 · ready in 4m 00s"); expect(second).toHaveTextContent("Late Ox · Engine Tuning 2 · ready in 25m 00s"); expect(card).not.toHaveTextContent("Free Ox"); }); });