deadhead-space-freight-tycoon/apps/web/test/market-reach.test.tsx

65 lines
2.6 KiB
TypeScript

import { content } from "@deadhead/content";
import { checkRoute, gameToReal, realToGame } from "@deadhead/sim";
import { screen, within } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useClock } from "../src/clock/clock";
import { fakeApi, renderApp, samplePlayerState } from "./render";
const MONTH = 30.44 * 24 * 3_600_000;
const gameStart = realToGame(Date.parse(content.config.epoch.realStart), content.config);
const clipper = content.shipModel("clipper")!;
const [earth, mars] = [content.location("earth")!, content.location("mars")!];
/** The first monthly date on which Earth→Mars is (or isn't) in a Clipper's range. */
function dateWhenMars(inRange: boolean): number {
for (let i = 0; i <= 16 * 12; i++) {
const t = gameStart + i * MONTH;
if (checkRoute(clipper, earth, mars, t).ok === inRange) return gameToReal(t, content.config);
}
throw new Error("no such date");
}
async function reachOf(modelName: string, now: number) {
vi.useFakeTimers({ toFake: ["Date"] });
vi.setSystemTime(now);
useClock.setState({ offsetMs: 0, synced: true, now });
renderApp(
fakeApi({
getPlayerState: () => samplePlayerState({ bays: [{ id: "b-earth", locationId: "earth" }] }),
}),
"/market",
);
const card = (await screen.findByRole("button", { name: `Buy ${modelName}` })).closest("li")!;
return within(card).getByLabelText("Reach").textContent ?? "";
}
describe("market roles and reach (Step 10L.7)", () => {
afterEach(() => vi.useRealTimers());
it("shows each model's role", async () => {
renderApp(fakeApi({ getPlayerState: () => samplePlayerState() }), "/market");
for (const model of content.purchasableShipModels) {
expect(await screen.findByText(model.role)).toBeInTheDocument();
}
});
it("lists Mars from Earth for a Clipper only when Mars is in range, and Ceres by hopping", async () => {
const near = await reachOf("Clipper", dateWhenMars(true));
expect(near).toContain("From Earth now: Moon, Mars");
expect(near).toContain("By hopping: Ceres Station");
});
it("puts Mars under other dates for a Clipper when it is out of range", async () => {
const far = await reachOf("Clipper", dateWhenMars(false));
expect(far).toContain("From Earth now: Moon");
expect(far).not.toContain("From Earth now: Moon, Mars");
expect(far).toContain("Other dates: Mars");
expect(far).toContain("By hopping: Ceres Station");
});
it("lists only the Moon for a Lighter", async () => {
const reach = await reachOf("Lighter", dateWhenMars(true));
expect(reach).toBe("Reach From Earth now: Moon");
});
});