90 lines
3.8 KiB
TypeScript
90 lines
3.8 KiB
TypeScript
import { content } from "@deadhead/content";
|
|
import type { PlayerState, Ship } from "@deadhead/schema";
|
|
import { tripFuel } from "@deadhead/sim";
|
|
import { screen, waitFor, within } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render";
|
|
|
|
const [earth, mars] = [content.location("earth")!, content.location("mars")!];
|
|
const clipper = content.shipModel("clipper")!;
|
|
const marsLegFu = tripFuel(clipper, earth, mars, content.config);
|
|
|
|
const ship = (fuelFu: number): Ship => ({
|
|
id: "s1",
|
|
name: "Swift Heron",
|
|
modelId: "clipper",
|
|
status: "idle",
|
|
locationId: "earth",
|
|
dispatchId: null,
|
|
lastArrivalAt: 0,
|
|
...shipExtras("clipper"),
|
|
fuelFu,
|
|
fuelAvgPrice: 8,
|
|
refuelPolicy: "manual",
|
|
pilotId: null,
|
|
});
|
|
|
|
/** A Clipper at Earth with Mars discovered, `fuelFu` aboard; refuels fill the tank. */
|
|
function company(fuelFu: number, discovered = ["earth", "moon", "mars"]) {
|
|
let state: PlayerState = samplePlayerState({
|
|
tutorial: null,
|
|
company: { id: "c", name: "D", credits: 100_000, loanBalance: 0 },
|
|
bays: [{ id: "b1", locationId: "earth" }],
|
|
ships: [ship(fuelFu)],
|
|
locations: content.locations.map((l) => ({ id: l.id, discovered: discovered.includes(l.id) })),
|
|
});
|
|
const refuelShip = vi.fn(() => {
|
|
const refuelled = { ...state.ships[0]!, fuelFu: clipper.tankCapacityFu };
|
|
state = { ...state, ships: [refuelled] };
|
|
return { ship: refuelled };
|
|
});
|
|
return fakeApi({ getPlayerState: () => state, refuelShip });
|
|
}
|
|
|
|
describe("low-fuel warning (Step 18.4, decision 0.5.113)", () => {
|
|
it("warns on a Clipper at Earth that can't fly to Mars, and clears once refuelled", async () => {
|
|
renderApp(company(marsLegFu - 1), "/fleet");
|
|
const warning = await screen.findByLabelText("Swift Heron low fuel");
|
|
expect(warning).toHaveTextContent("Low fuel");
|
|
// Earth's longest leg for a Clipper among the places known here is Mars.
|
|
expect(warning).toHaveTextContent(
|
|
`Mars needs ${Math.round(marsLegFu).toLocaleString("en-US")} fu`,
|
|
);
|
|
|
|
await userEvent.click(screen.getByRole("button", { name: "Fuel for Swift Heron" }));
|
|
const sheet = screen.getByRole("dialog", { name: "Swift Heron" });
|
|
await userEvent.click(within(sheet).getByRole("button", { name: /^Fill/ }));
|
|
await waitFor(() =>
|
|
expect(screen.queryByLabelText("Swift Heron low fuel")).not.toBeInTheDocument(),
|
|
);
|
|
});
|
|
|
|
it("doesn't warn with enough for the longest leg", async () => {
|
|
renderApp(company(marsLegFu + 1), "/fleet");
|
|
await screen.findByRole("progressbar", { name: "Swift Heron fuel" });
|
|
expect(screen.queryByLabelText("Swift Heron low fuel")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("measures only legs to discovered places", async () => {
|
|
// Mars undiscovered: the Moon is the longest leg, and a little fuel covers it.
|
|
renderApp(company(marsLegFu - 1, ["earth", "moon"]), "/fleet");
|
|
await screen.findByRole("progressbar", { name: "Swift Heron fuel" });
|
|
expect(screen.queryByLabelText("Swift Heron low fuel")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows Manual selected, as the default, in a new ship's fuel sheet", async () => {
|
|
renderApp(company(clipper.tankCapacityFu), "/fleet");
|
|
await userEvent.click(await screen.findByRole("button", { name: "Fuel for Swift Heron" }));
|
|
const policy = within(screen.getByRole("dialog")).getByRole("group", { name: "Refuel policy" });
|
|
const radios = within(policy).getAllByRole("radio");
|
|
// Manual comes first and is checked.
|
|
expect(radios.map((r) => r.parentElement!.textContent)).toEqual([
|
|
"Manual refuel",
|
|
"Auto top-up",
|
|
]);
|
|
expect(within(policy).getByRole("radio", { name: "Manual refuel" })).toBeChecked();
|
|
expect(policy).toHaveTextContent("The default");
|
|
});
|
|
});
|