import type { Contract, Ship } from "@deadhead/schema"; import { screen, within } from "@testing-library/react"; import { describe, expect, it } from "vitest"; import { realTimeOf } from "../src/components/Goods"; import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render"; const NOW = Date.now(); const job = (overrides: Partial): Contract => ({ id: "j", originId: "earth", destinationId: "moon", cargoTypeId: "high-value", goodId: "medical-supplies", shipper: "Meridian Health", priority: false, deliveryWindowGameHours: null, tier: 1, massTonnes: 6, payout: 3_210, requiredSkill: { skillId: "expensive-cargo", level: 1 }, requiredLongHaulLevel: 0, offeredAt: NOW, expiresAt: NOW + 600_000, firstContact: false, ...overrides, }); const ship = (modelId: string): Ship => ({ id: modelId, modelId, name: `My ${modelId}`, status: "idle", locationId: "earth", dispatchId: null, pilotId: null, lastArrivalAt: 0, ...shipExtras(modelId), }); function render(contracts: Contract[], ships: Ship[]) { renderApp( fakeApi({ getPlayerState: () => samplePlayerState({ tutorial: null, bays: [{ id: "b", locationId: "earth" }], ships }), getContractBoard: () => ({ contracts, periodEndsAt: NOW + 600_000 }), }), "/contracts?location=earth", ); } const card = async (payout: string) => (await screen.findByText(payout)).closest("li")!; describe("contract card (CR-007)", () => { it("shows the good, its class and tier, the shipper, mass and requirement", async () => { render([job({})], [ship("courier")]); const c = await card("3,210 cr"); expect(within(c).getByRole("heading", { name: "Medical Supplies" })).toBeInTheDocument(); expect(c).toHaveTextContent("High-Value · T1"); expect(c).toHaveTextContent("Meridian Health · 6 t"); expect(c).toHaveTextContent("Needs Expensive Cargo 1"); expect(c).not.toHaveTextContent("Priority"); }); it("shows a Priority window in game hours, and real time on tap", async () => { render( [ job({ goodId: "machine-parts", cargoTypeId: "heavy", priority: true, deliveryWindowGameHours: 5, }), ], [ship("courier")], ); const c = await card("3,210 cr"); // A Priority good can have its own name. expect(within(c).getByRole("heading", { name: "Emergency Parts" })).toBeInTheDocument(); // The window follows the countdown preference: real time by default (CR-011). expect(c).toHaveTextContent(`Priority · within ${realTimeOf(5)}`); expect(c).not.toHaveTextContent("Too slow"); }); it("marks a Priority job too slow when no idle ship here can make the window", async () => { const tight = job({ priority: true, deliveryWindowGameHours: 0.5 }); render([tight], [ship("bulk-hauler")]); expect(await card("3,210 cr")).toHaveTextContent("Too slow"); }); });