108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
import type { Contract } from "@deadhead/schema";
|
|
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 { fakeApi, renderApp, samplePlayerState } from "./render";
|
|
|
|
const NOW = Date.parse("2100-01-01T00:00:00Z");
|
|
|
|
const contract = (overrides: Partial<Contract>): Contract => ({
|
|
id: "c",
|
|
originId: "earth",
|
|
destinationId: "moon",
|
|
cargoTypeId: "standard",
|
|
tier: 0,
|
|
massTonnes: 50,
|
|
payout: 2000,
|
|
requiredSkill: null,
|
|
requiredLongHaulLevel: 0,
|
|
offeredAt: NOW,
|
|
expiresAt: NOW + 30 * 60_000,
|
|
firstContact: false,
|
|
...overrides,
|
|
});
|
|
|
|
const boards: Record<string, Contract[]> = {
|
|
earth: [
|
|
contract({ id: "e1", payout: 1999 }),
|
|
contract({
|
|
id: "e2",
|
|
destinationId: "mars",
|
|
cargoTypeId: "heavy",
|
|
tier: 2,
|
|
requiredSkill: { skillId: "heavy-cargo", level: 2 },
|
|
requiredLongHaulLevel: 1,
|
|
firstContact: true,
|
|
payout: 88_000,
|
|
}),
|
|
],
|
|
moon: [contract({ id: "m1", originId: "moon", destinationId: "earth", payout: 1234 })],
|
|
};
|
|
|
|
const owner = () =>
|
|
samplePlayerState({
|
|
tutorial: null,
|
|
bays: [
|
|
{ id: "b1", locationId: "earth" },
|
|
{ id: "b2", locationId: "moon" },
|
|
],
|
|
});
|
|
|
|
describe("bay contract boards", () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers({ toFake: ["Date", "setInterval", "clearInterval"] });
|
|
vi.setSystemTime(NOW);
|
|
useClock.setState({ offsetMs: 0, synced: true, now: NOW });
|
|
});
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
it("shows the first bay's board, and switching bays shows that bay's board", async () => {
|
|
const api = fakeApi({
|
|
getPlayerState: owner,
|
|
getContractBoard: ({ locationId }) => ({
|
|
contracts: boards[locationId] ?? [],
|
|
periodEndsAt: NOW + 30 * 60_000,
|
|
}),
|
|
});
|
|
renderApp(api, "/contracts");
|
|
|
|
expect(await screen.findByText("1,999 cr")).toBeInTheDocument();
|
|
expect(screen.queryByText("1,234 cr")).not.toBeInTheDocument();
|
|
|
|
await userEvent.selectOptions(screen.getByLabelText("Bay"), "moon");
|
|
expect(await screen.findByText("1,234 cr")).toBeInTheDocument();
|
|
expect(screen.getByText("Moon → Earth")).toBeInTheDocument();
|
|
expect(screen.queryByText("1,999 cr")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("shows contract details, requirements, expiry and the first-contact badge", async () => {
|
|
const api = fakeApi({
|
|
getPlayerState: owner,
|
|
getContractBoard: () => ({ contracts: boards.earth!, periodEndsAt: NOW + 30 * 60_000 }),
|
|
});
|
|
renderApp(api, "/contracts");
|
|
|
|
const items = await screen.findAllByRole("listitem");
|
|
const [plain, special] = items;
|
|
expect(plain).toHaveTextContent("Standard · 50 t");
|
|
expect(plain).toHaveTextContent("No skills needed");
|
|
expect(plain).toHaveTextContent("expires in 30m 00s");
|
|
expect(within(plain!).queryByText("First contact")).not.toBeInTheDocument();
|
|
|
|
expect(special).toHaveTextContent("Earth → Mars");
|
|
expect(special).toHaveTextContent("Heavy (tier 2)");
|
|
expect(special).toHaveTextContent("Needs Heavy Cargo 2, Long Haul 1");
|
|
expect(special).toHaveTextContent(/M km/);
|
|
expect(within(special!).getByText("First contact")).toBeInTheDocument();
|
|
});
|
|
|
|
it("explains how to get contracts when the player has no bays", async () => {
|
|
renderApp(
|
|
fakeApi({ getPlayerState: () => samplePlayerState({ tutorial: null }) }),
|
|
"/contracts",
|
|
);
|
|
expect(await screen.findByText(/You have no bays yet/)).toBeInTheDocument();
|
|
});
|
|
});
|