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>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
// Component tests for the in-game shell header. The header composes
|
||||
// the headline strip (`<race> @ <game>, turn N`, falling back to `?`
|
||||
// while the lobby / report calls are in flight), the view-menu, and
|
||||
// the account-menu. The tests assert the headline copy, that every
|
||||
// view-menu entry dispatches `goto` with the right URL, and that the
|
||||
// Logout entry of the account-menu calls `session.signOut("user")`.
|
||||
// the identity strip (`<race> @ <game>`, falling back to `?` while
|
||||
// the lobby / report calls are in flight), the Phase 26 turn
|
||||
// navigator (`← turn N →` with a popover of every turn), the
|
||||
// view-menu, and the account-menu. The tests assert the visible
|
||||
// copy, that every view-menu entry dispatches `goto` with the right
|
||||
// URL, and that the Logout entry of the account-menu calls
|
||||
// `session.signOut("user")`.
|
||||
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { fireEvent, render } from "@testing-library/svelte";
|
||||
@@ -48,6 +50,8 @@ function withGameState(opts: {
|
||||
localPlayerCargo: 0,
|
||||
...EMPTY_SHIP_GROUPS,
|
||||
};
|
||||
store.currentTurn = opts.turn ?? 0;
|
||||
store.viewedTurn = opts.turn ?? 0;
|
||||
store.status = "ready";
|
||||
}
|
||||
return new Map<unknown, unknown>([[GAME_STATE_CONTEXT_KEY, store]]);
|
||||
@@ -75,8 +79,11 @@ describe("game-shell header", () => {
|
||||
props: { gameId: "g1", sidebarOpen: false, onToggleSidebar },
|
||||
context: withGameState(),
|
||||
});
|
||||
expect(ui.getByTestId("game-shell-headline")).toHaveTextContent(
|
||||
"? @ ?, turn ?",
|
||||
expect(ui.getByTestId("game-shell-identity")).toHaveTextContent(
|
||||
"? @ ?",
|
||||
);
|
||||
expect(ui.getByTestId("turn-navigator-trigger")).toHaveTextContent(
|
||||
"turn ?",
|
||||
);
|
||||
expect(ui.getByTestId("view-menu-trigger")).toBeInTheDocument();
|
||||
expect(ui.getByTestId("account-menu-trigger")).toBeInTheDocument();
|
||||
@@ -91,8 +98,11 @@ describe("game-shell header", () => {
|
||||
turn: 7,
|
||||
}),
|
||||
});
|
||||
expect(ui.getByTestId("game-shell-headline")).toHaveTextContent(
|
||||
"Federation @ Phase 14, turn 7",
|
||||
expect(ui.getByTestId("game-shell-identity")).toHaveTextContent(
|
||||
"Federation @ Phase 14",
|
||||
);
|
||||
expect(ui.getByTestId("turn-navigator-trigger")).toHaveTextContent(
|
||||
"turn 7",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -101,8 +111,11 @@ describe("game-shell header", () => {
|
||||
props: { gameId: "g1", sidebarOpen: false, onToggleSidebar: () => {} },
|
||||
context: withGameState({ race: "Federation", turn: 3 }),
|
||||
});
|
||||
expect(ui.getByTestId("game-shell-headline")).toHaveTextContent(
|
||||
"Federation @ ?, turn 3",
|
||||
expect(ui.getByTestId("game-shell-identity")).toHaveTextContent(
|
||||
"Federation @ ?",
|
||||
);
|
||||
expect(ui.getByTestId("turn-navigator-trigger")).toHaveTextContent(
|
||||
"turn 3",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user