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

74 lines
2.7 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import { act } from "react";
import { describe, expect, it } from "vitest";
import { ApiError } from "../src/api/client";
import { loadLastState, saveLastState, useConnection } from "../src/state/connection";
import { fetchPlayerState } from "../src/state/player";
import { Button } from "../src/ui";
import { fakeApi, samplePlayerState } from "./render";
const networkDown = () => {
throw new TypeError("Failed to fetch");
};
describe("offline cache (Step 11.2)", () => {
it("keeps each fetched state and goes online", async () => {
const state = samplePlayerState();
await expect(fetchPlayerState(fakeApi({ getPlayerState: () => state }))).resolves.toEqual(
state,
);
expect(loadLastState()?.state).toEqual(state);
expect(useConnection.getState()).toMatchObject({ online: true });
});
it("returns the last state when the server can't be reached, and goes offline", async () => {
const state = samplePlayerState({ playerId: "cached" });
saveLastState(state, 1234);
await expect(fetchPlayerState(fakeApi({ getPlayerState: networkDown }))).resolves.toEqual(
state,
);
expect(useConnection.getState()).toEqual({ online: false, lastUpdate: 1234 });
});
it("still fails a first load offline with nothing saved", async () => {
await expect(fetchPlayerState(fakeApi({ getPlayerState: networkDown }))).rejects.toThrow();
});
it("does not fall back on a server error reply, only on no reply", async () => {
saveLastState(samplePlayerState(), 1);
const failing = fakeApi({
getPlayerState: () => {
throw new ApiError(500, "internal-error", "Boom");
},
});
await expect(fetchPlayerState(failing)).rejects.toThrow("Boom");
});
it("forgets the state when signed out, and ignores a saved state of another shape", async () => {
saveLastState(samplePlayerState(), 1);
saveLastState(null, 2);
expect(loadLastState()).toBeNull();
localStorage.setItem("deadhead:last-state", JSON.stringify({ state: { old: 1 }, savedAt: 3 }));
expect(loadLastState()).toBeNull();
});
});
describe("server actions offline", () => {
it("disables a needsServer button only while offline", () => {
render(
<>
<Button needsServer>Buy</Button>
<Button>Close</Button>
</>,
);
expect(screen.getByRole("button", { name: "Buy" })).toBeEnabled();
act(() => useConnection.setState({ online: false }));
expect(screen.getByRole("button", { name: "Buy" })).toBeDisabled();
expect(screen.getByRole("button", { name: "Buy" })).toHaveAttribute(
"title",
"Unavailable offline",
);
expect(screen.getByRole("button", { name: "Close" })).toBeEnabled();
});
});