deadhead-space-freight-tycoon/apps/web/test/bay-boards.test.tsx

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();
});
});