2d17760a5e
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>
64 lines
2.1 KiB
TypeScript
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);
|
|
});
|
|
});
|