Files
galaxy-game/ui/frontend/tests/history-banner.test.ts
Ilia Denisov 2d17760a5e ui/phase-26: history mode (turn navigator + read-only banner)
Split GameStateStore into currentTurn (server's latest) and viewedTurn
(displayed snapshot) so history excursions don't corrupt the resume
bookmark or the live-turn bound. Add viewTurn / returnToCurrent /
historyMode rune, plus a game-history cache namespace that stores
past-turn reports for fast re-entry. OrderDraftStore.bindClient takes
a getHistoryMode getter and short-circuits add / remove / move while
the user is viewing a past turn; RenderedReportSource skips the order
overlay in the same case. Header replaces the static "turn N" with a
clickable triplet (TurnNavigator), the layout mounts HistoryBanner
under the header, and visibility-refresh is a no-op while history is
active.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 00:13:19 +02:00

64 lines
2.1 KiB
TypeScript

// Phase 26 history-banner component tests. The banner is mounted by
// the in-game shell layout directly under the header; it renders
// only when `gameState.historyMode === true` and carries a return
// action delegating to `gameState.returnToCurrent()`.
import "@testing-library/jest-dom/vitest";
import { fireEvent, render } from "@testing-library/svelte";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { i18n } from "../src/lib/i18n/index.svelte";
import HistoryBanner from "../src/lib/header/history-banner.svelte";
import {
GAME_STATE_CONTEXT_KEY,
GameStateStore,
} from "../src/lib/game-state.svelte";
function buildStore(opts: {
currentTurn: number;
viewedTurn: number;
}): GameStateStore {
const store = new GameStateStore();
store.currentTurn = opts.currentTurn;
store.viewedTurn = opts.viewedTurn;
store.status = "ready";
return store;
}
beforeEach(() => {
i18n.resetForTests("en");
});
describe("HistoryBanner", () => {
test("is hidden in live mode", () => {
const store = buildStore({ currentTurn: 5, viewedTurn: 5 });
const ui = render(HistoryBanner, {
context: new Map([[GAME_STATE_CONTEXT_KEY, store]]),
});
expect(ui.queryByTestId("history-banner")).toBeNull();
});
test("is visible in history mode with the viewed turn interpolated", () => {
const store = buildStore({ currentTurn: 5, viewedTurn: 2 });
const ui = render(HistoryBanner, {
context: new Map([[GAME_STATE_CONTEXT_KEY, store]]),
});
const banner = ui.getByTestId("history-banner");
expect(banner).toBeInTheDocument();
expect(banner).toHaveTextContent("Viewing turn 2");
expect(banner).toHaveTextContent("read-only");
});
test("return action delegates to gameState.returnToCurrent", async () => {
const store = buildStore({ currentTurn: 5, viewedTurn: 2 });
const returnToCurrent = vi
.spyOn(store, "returnToCurrent")
.mockResolvedValue(undefined);
const ui = render(HistoryBanner, {
context: new Map([[GAME_STATE_CONTEXT_KEY, store]]),
});
await fireEvent.click(ui.getByTestId("history-banner-return"));
expect(returnToCurrent).toHaveBeenCalledTimes(1);
});
});