133 lines
4.8 KiB
TypeScript
133 lines
4.8 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("network 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 reach the server.");
|
|
|
|
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 offline 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("Offline — showing last update.");
|
|
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("Offline — showing last update.")).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 contracts left this period/)).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 bays explains how to get contracts", async () => {
|
|
show("/contracts");
|
|
expect(await screen.findByText(/You have no bays yet/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("Contracts: an empty bay board says to check back", async () => {
|
|
show(
|
|
"/contracts",
|
|
{ ...afterTutorial, bays: [{ id: "b", locationId: "earth" }] },
|
|
{ getContractBoard: () => ({ contracts: [], periodEndsAt: 0 }) },
|
|
);
|
|
expect(await screen.findByText(/No contracts left this period/)).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);
|
|
});
|
|
});
|