111 lines
4.1 KiB
TypeScript
111 lines
4.1 KiB
TypeScript
import type { Pilot, PlayerState, Ship } from "@deadhead/schema";
|
|
import { act, screen, within } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { useClock } from "../src/clock/clock";
|
|
import { pilotExtras } from "./fixtures";
|
|
import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render";
|
|
|
|
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
|
const MINUTE = 60_000;
|
|
|
|
const ship = (over: Partial<Ship> = {}): Ship => ({
|
|
id: "s1",
|
|
name: "Wren",
|
|
modelId: "courier",
|
|
status: "idle",
|
|
locationId: "earth",
|
|
dispatchId: null,
|
|
pilotId: "p1",
|
|
lastArrivalAt: 0,
|
|
...shipExtras("courier"),
|
|
...over,
|
|
});
|
|
const pilot = (over: Partial<Pilot> = {}): Pilot => ({
|
|
...samplePlayerState().pilots[0]!,
|
|
id: "p1",
|
|
name: "Rosa",
|
|
isAvatar: false,
|
|
assignedShipId: "s1",
|
|
locationId: null,
|
|
...pilotExtras,
|
|
...over,
|
|
});
|
|
const state = (s: Ship, pilots: Pilot[]): PlayerState =>
|
|
samplePlayerState({
|
|
tutorial: null,
|
|
company: { id: "c", name: "D", credits: 50_000, loanBalance: 0 },
|
|
ships: [s],
|
|
pilots: [samplePlayerState().pilots[0]!, ...pilots],
|
|
});
|
|
|
|
async function card(s: Ship, pilots: Pilot[] = [pilot()]) {
|
|
renderApp(fakeApi({ getPlayerState: () => state(s, pilots) }), "/fleet");
|
|
return (await screen.findByText("Wren")).closest("li")!;
|
|
}
|
|
const resting = (minutes: number) => pilot({ resting: true, restEndsAt: NOW + minutes * MINUTE });
|
|
|
|
describe('"Find work here" when the ship can\'t go (decision 0.5.164)', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers({ toFake: ["Date"] });
|
|
vi.setSystemTime(NOW);
|
|
useClock.setState({ offsetMs: 0, synced: true, now: NOW });
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it("links to the board when the ship can be sent", async () => {
|
|
const li = await card(ship());
|
|
expect(within(li).getByRole("link", { name: "Find work here" })).toHaveAttribute(
|
|
"href",
|
|
"/contracts?location=earth",
|
|
);
|
|
});
|
|
|
|
it("is disabled with the rest countdown while the pilot rests", async () => {
|
|
const li = await card(ship(), [resting(4.2)]);
|
|
const button = within(li).getByRole("button", { name: /^Find work here: Resting/ });
|
|
expect(button).toBeDisabled();
|
|
expect(button).toHaveTextContent("Resting · 04:12");
|
|
expect(within(li).queryByRole("link", { name: "Find work here" })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("is disabled with the shop countdown while the ship is in the shop", async () => {
|
|
const li = await card(ship({ inShopUntil: NOW + 12.5 * MINUTE }));
|
|
expect(
|
|
within(li).getByRole("button", { name: /^Find work here: In the shop/ }),
|
|
).toHaveTextContent("In the shop · 12:30");
|
|
});
|
|
|
|
it("counts down to the later of the two when both apply", async () => {
|
|
const shopFirst = await card(ship({ inShopUntil: NOW + 5 * MINUTE }), [resting(9)]);
|
|
expect(within(shopFirst).getByRole("button", { name: /Find work here/ })).toHaveTextContent(
|
|
"Resting · 09:00",
|
|
);
|
|
});
|
|
|
|
it("uses the shop's end when it outlasts the rest, as with service overlapping rest", async () => {
|
|
const li = await card(ship({ inShopUntil: NOW + 20 * MINUTE }), [resting(10)]);
|
|
expect(within(li).getByRole("button", { name: /Find work here/ })).toHaveTextContent(
|
|
"In the shop · 20:00",
|
|
);
|
|
});
|
|
|
|
it("says No pilot assigned for an uncrewed ship", async () => {
|
|
const li = await card(ship({ pilotId: null }), []);
|
|
const button = within(li).getByRole("button", { name: /Find work here/ });
|
|
expect(button).toBeDisabled();
|
|
expect(button).toHaveTextContent("No pilot assigned");
|
|
});
|
|
|
|
it("covers an Independent pilot's ship too", async () => {
|
|
const li = await card(ship(), [{ ...resting(3), mode: "independent" }]);
|
|
expect(within(li).getByRole("button", { name: /Find work here/ })).toBeDisabled();
|
|
});
|
|
|
|
it("re-enables on its own when the countdown reaches zero", async () => {
|
|
const li = await card(ship(), [resting(1)]);
|
|
expect(within(li).getByRole("button", { name: /Find work here/ })).toBeDisabled();
|
|
act(() => useClock.setState({ now: NOW + MINUTE }));
|
|
expect(within(li).getByRole("link", { name: "Find work here" })).toBeInTheDocument();
|
|
});
|
|
});
|