import { content } from "@deadhead/content"; import type { Contract, PlayerState } from "@deadhead/schema"; import { checkRoute, effectiveStats, wouldStrand } from "@deadhead/sim"; import { screen, 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 { pairOptions } from "../src/screens/DispatchSheet"; import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render"; const TANKS = { "fuel-tanks": 3 }; const freighter = effectiveStats(content.shipModel("freighter")!, TANKS, content); const [moon, jupiter] = [content.location("moon")!, content.location("jupiter-cloud-platform")!]; const discovered = new Set(["earth", "moon", "jupiter-cloud-platform"]); const NOW = Date.parse(content.config.epoch.realStart) + 3_600_000; const STRANDED = "Would be stranded — can't fly out of Jupiter Cloud Platform from there"; // The fixed map (CR-015): Jupiter is in range from the Moon (667 × 1.3), but from // Jupiter (667 × 1.0) neither Earth nor the Moon is, and Mars and Ceres aren't discovered. if ( !checkRoute(freighter, moon, jupiter).ok || !wouldStrand(freighter, jupiter, discovered, content) ) { throw new Error("the scenario no longer strands"); } /** A crewed Freighter with maxed tanks at the Moon, with Jupiter discovered. */ function state(): PlayerState { const base = samplePlayerState({ tutorial: null }); return { ...base, company: { ...base.company, credits: 100_000 }, bays: [{ id: "b1", locationId: "moon" }], locations: base.locations.map((l) => ({ ...l, discovered: discovered.has(l.id) })), ships: [ { id: "s1", modelId: "freighter", name: "Swift Heron", status: "idle", locationId: "moon", dispatchId: null, lastArrivalAt: 0, ...shipExtras("freighter", TANKS), pilotId: "avatar", }, ], pilots: [{ ...base.pilots[0]!, assignedShipId: "s1", locationId: "moon" }], }; } const toJupiter: Contract = { id: "c1", originId: "moon", destinationId: "jupiter-cloud-platform", cargoTypeId: "standard", goodId: "consumer-goods", shipper: "Pacific Orbital Logistics", priority: false, deliveryWindowGameHours: null, tier: 0, massTonnes: 10, payout: 50_000, requiredSkill: null, requiredLongHaulLevel: 0, offeredAt: NOW, expiresAt: NOW + 3_600_000, firstContact: false, }; describe("stranding reason (Step 10L.9)", () => { beforeEach(() => { vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] }); vi.setSystemTime(NOW); useClock.setState({ offsetMs: 0, synced: true, now: NOW }); }); afterEach(() => vi.useRealTimers()); it("marks a would-strand ship and pilot pair with the reason", () => { const [pair] = pairOptions(state(), toJupiter, NOW); expect(pair!.problems).toEqual([STRANDED]); }); it("disables a would-strand deadhead destination with the reason", async () => { renderApp(fakeApi({ getPlayerState: state }), "/fleet"); const card = (await screen.findByText("Swift Heron")).closest("li")!; await userEvent.click(within(card).getByRole("button", { name: /Deadhead/ })); const dialog = screen.getByRole("dialog", { name: "Deadhead Swift Heron" }); const option = within(dialog).getByRole("button", { name: "Jupiter Cloud Platform" }); expect(option).toBeDisabled(); expect(option).toHaveTextContent(STRANDED); expect(within(dialog).getByRole("button", { name: "Earth" })).toBeEnabled(); }); });