deadhead-space-freight-tycoon/apps/web/test/states.test.tsx

137 lines
4.9 KiB
TypeScript

import { focusManager } from "@tanstack/react-query";
import { act, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it } from "vitest";
import { SCREENS } from "../src/routes";
import { fakeApi, renderApp, samplePlayerState } from "./render";
const networkDown = () => {
throw new TypeError("Failed to fetch");
};
const afterTutorial = samplePlayerState({ tutorial: null });
describe("load errors", () => {
afterEach(() => focusManager.setFocused(undefined));
it.each(SCREENS)(
"$title: a failed first load offers Retry, which recovers",
async ({ path, title }) => {
let online = false;
renderApp(
fakeApi({
getPlayerState: () => (online ? afterTutorial : networkDown()),
getContractBoard: () => ({ contracts: [], periodEndsAt: 0 }),
}),
path,
);
const alert = await screen.findByRole("alert");
expect(alert).toHaveTextContent("Could not load the game.");
online = true;
await userEvent.click(within(alert).getByRole("button", { name: "Retry" }));
expect(await screen.findByRole("heading", { level: 1, name: title })).toBeInTheDocument();
},
);
it.each(SCREENS)(
"$title: a failed refresh keeps the screen and shows an error banner",
async ({ path, title }) => {
let online = true;
renderApp(
fakeApi({
getPlayerState: () => (online ? afterTutorial : networkDown()),
getContractBoard: () => ({ contracts: [], periodEndsAt: 0 }),
}),
path,
);
await screen.findByRole("heading", { level: 1, name: title });
online = false;
act(() => {
focusManager.setFocused(false);
focusManager.setFocused(true);
});
const banner = await screen.findByText("Couldn't refresh the game.");
expect(screen.getByRole("heading", { level: 1, name: title })).toBeInTheDocument();
online = true;
await userEvent.click(within(banner.parentElement!).getByRole("button", { name: "Retry" }));
await act(async () => {});
expect(screen.queryByText("Couldn't refresh the game.")).not.toBeInTheDocument();
},
);
it("Contracts: a failed board load offers Retry", async () => {
let online = false;
renderApp(
fakeApi({
getPlayerState: () => ({ ...afterTutorial, bays: [{ id: "b", locationId: "earth" }] }),
getContractBoard: () => (online ? { contracts: [], periodEndsAt: 0 } : networkDown()),
}),
"/contracts",
);
const alert = await screen.findByText("Could not load contracts.", { exact: false });
online = true;
await userEvent.click(within(alert).getByRole("button", { name: "Retry" }));
expect(
await screen.findByText(/No jobs here your fleet can take right now/),
).toBeInTheDocument();
});
});
describe("empty states", () => {
const show = (path: string, state = afterTutorial, extra = {}) =>
renderApp(fakeApi({ getPlayerState: () => state, ...extra }), path);
it("HQ: no ships points to the Market", async () => {
show("/");
expect(await screen.findByText("No ships yet — buy one in the Market.")).toBeInTheDocument();
expect(screen.getByText("Nothing in flight.")).toBeInTheDocument();
});
it("Fleet: no ships links to the Market", async () => {
show("/fleet");
expect(await screen.findByRole("link", { name: "visit the Market" })).toHaveAttribute(
"href",
"/market",
);
});
it("Contracts: no idle ships explains how to take the jobs", async () => {
show("/contracts");
expect(await screen.findByText(/No idle ships here/)).toBeInTheDocument();
});
it("Contracts: an empty board says no job fits the fleet, and when new ones come (CR-019)", async () => {
show(
"/contracts",
{ ...afterTutorial, bays: [{ id: "b", locationId: "earth" }] },
{ getContractBoard: () => ({ contracts: [], periodEndsAt: 0 }) },
);
expect(
await screen.findByText(/No jobs here your fleet can take right now/),
).toBeInTheDocument();
});
it("Contracts: an empty tutorial board says to check back", async () => {
show("/contracts", samplePlayerState(), {
getTutorialBoard: () => ({ contracts: [], periodEndsAt: 0 }),
});
expect(await screen.findByText(/No company runs left this period/)).toBeInTheDocument();
});
it("Pilots: no hired pilots suggests hiring", async () => {
show("/pilots");
expect(await screen.findByText(/No hired pilots yet/)).toBeInTheDocument();
expect(screen.getByText("Lease a bay to hire pilots there.")).toBeInTheDocument();
});
it("Market and Locations always have content for a new player", async () => {
show("/market");
expect(await screen.findByRole("button", { name: "Buy Courier" })).toBeInTheDocument();
show("/locations");
expect((await screen.findAllByText("Earth")).length).toBeGreaterThan(0);
});
});