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