95 lines
3.6 KiB
TypeScript
95 lines
3.6 KiB
TypeScript
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();
|
||
});
|
||
});
|