deadhead-space-freight-tycoon/apps/web/test/stranding-reason.test.tsx

95 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, content.config).ok ||
!wouldStrand(freighter, jupiter, 0, 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();
});
});