102 lines
3.3 KiB
TypeScript
102 lines
3.3 KiB
TypeScript
import type { Contract, PlayerState, Ship } from "@deadhead/schema";
|
|
import { screen, within } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { useClock } from "../src/clock/clock";
|
|
import { fakeApi, renderApp, samplePlayerState, shipExtras } from "./render";
|
|
|
|
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
|
const PERIOD_ENDS = NOW + 4 * 60_000 + 5_000;
|
|
|
|
const job = (overrides: Partial<Contract>): Contract => ({
|
|
id: "j",
|
|
originId: "earth",
|
|
destinationId: "moon",
|
|
cargoTypeId: "standard",
|
|
goodId: "consumer-goods",
|
|
shipper: "Pacific Orbital Logistics",
|
|
priority: false,
|
|
deliveryWindowGameHours: null,
|
|
tier: 0,
|
|
massTonnes: 6,
|
|
payout: 1_000,
|
|
requiredSkill: null,
|
|
requiredLongHaulLevel: 0,
|
|
offeredAt: NOW,
|
|
expiresAt: PERIOD_ENDS,
|
|
firstContact: false,
|
|
...overrides,
|
|
});
|
|
|
|
const courier: Ship = {
|
|
id: "s1",
|
|
modelId: "courier",
|
|
name: "Dusty Mule",
|
|
status: "idle",
|
|
locationId: "earth",
|
|
dispatchId: null,
|
|
pilotId: "avatar",
|
|
lastArrivalAt: 0,
|
|
...shipExtras("courier"),
|
|
};
|
|
|
|
function owner(): PlayerState {
|
|
const base = samplePlayerState({ tutorial: null });
|
|
return {
|
|
...base,
|
|
bays: [{ id: "b", locationId: "earth" }],
|
|
ships: [courier],
|
|
pilots: [{ ...base.pilots[0]!, assignedShipId: "s1", locationId: null }],
|
|
};
|
|
}
|
|
|
|
function show(contracts: Contract[]) {
|
|
renderApp(
|
|
fakeApi({
|
|
getPlayerState: owner,
|
|
getContractBoard: () => ({ contracts, periodEndsAt: PERIOD_ENDS }),
|
|
}),
|
|
"/contracts?location=earth",
|
|
);
|
|
}
|
|
|
|
describe("board cards and the empty board (Step 19.4, decision 0.5.114)", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] });
|
|
vi.setSystemTime(NOW);
|
|
useClock.setState({ offsetMs: 0, synced: true, now: NOW });
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it("shows no permanent-problem badge on any card", async () => {
|
|
// Jobs a lone level-0 Courier couldn't take: the engine filters them out,
|
|
// and even if one reached a card it carries no problem badge.
|
|
show([
|
|
job({ id: "heavy", massTonnes: 300, payout: 9_001 }),
|
|
job({ id: "rushed", priority: true, deliveryWindowGameHours: 0.01, payout: 9_002 }),
|
|
job({
|
|
id: "skilled",
|
|
cargoTypeId: "heavy",
|
|
goodId: "machine-parts",
|
|
tier: 3,
|
|
requiredSkill: { skillId: "heavy-cargo", level: 3 },
|
|
payout: 9_003,
|
|
}),
|
|
]);
|
|
for (const payout of ["9,001 cr", "9,002 cr", "9,003 cr"]) {
|
|
const card = (await screen.findByText(payout)).closest("tr")!;
|
|
expect(card).not.toHaveTextContent(/too heavy/i);
|
|
expect(card).not.toHaveTextContent(/too slow/i);
|
|
expect(card).not.toHaveTextContent(/skill too low/i);
|
|
}
|
|
});
|
|
|
|
it("says no job fits the fleet, with the refresh countdown, Rest and Deadhead", async () => {
|
|
show([]);
|
|
const empty = await screen.findByText(/No jobs here your fleet can take right now/);
|
|
expect(empty).toHaveTextContent("New contracts in 04:05.");
|
|
const stuck = screen.getByRole("group", { name: "Nothing for Dusty Mule" });
|
|
expect(within(stuck).getByRole("button", { name: /^Rest / })).toBeInTheDocument();
|
|
expect(within(stuck).getByRole("button", { name: "Deadhead (fly empty)" })).toBeInTheDocument();
|
|
});
|
|
});
|