test(ui): migrate suite to the app-shell (state-driven navigation)
- Unit: repoint moved screen imports (lib/screens, lib/game), mock $lib/app-nav (appScreen/activeView) instead of $app/navigation, drop the removed gameId props, assert screen/view selection. - e2e: add a dev-only window.__galaxyNav affordance; specs enter a game via enterGame(...) instead of a /games/:id URL; URL assertions become content assertions (the URL stays /game/); reload uses waitUntil:"commit" (shallow routing) and mocks /rpc on game entry. - Remove the obsolete report scroll-restore test (it relied on a SvelteKit route Snapshot that no longer exists); update the missing-membership test to the new lobby-redirect+toast behaviour. Fix a stale report.svelte docstring. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
// Component tests for the Phase 8 lobby page. The lobby API and the
|
||||
// Component tests for the Phase 8 lobby screen. The lobby API and the
|
||||
// gateway client are mocked at module level; the session singleton is
|
||||
// wired to a per-test `SessionStore`-backing IndexedDB so the page's
|
||||
// boot path settles on `authenticated` and constructs a real
|
||||
// GalaxyClient (which is then never called because the lobby API
|
||||
// wrappers are stubs). The tests assert the section rendering, the
|
||||
// inline race-name form for public games, and the invitation Accept
|
||||
// flow.
|
||||
// flow. The app-shell navigation store is mocked so opening a game
|
||||
// (`activeView.reset()` + `appScreen.go("game", …)`) or the create
|
||||
// form (`appScreen.go("lobby-create")`) never runs real `pushState`
|
||||
// in JSDOM; the single-URL shell has no `/lobby`/`/games` routes.
|
||||
|
||||
import "fake-indexeddb/auto";
|
||||
import { fireEvent, render, waitFor } from "@testing-library/svelte";
|
||||
@@ -25,8 +28,19 @@ import { type GalaxyDB, openGalaxyDB } from "../src/platform/store/idb";
|
||||
import { IDBCache } from "../src/platform/store/idb-cache";
|
||||
import { WebCryptoKeyStore } from "../src/platform/store/webcrypto-keystore";
|
||||
|
||||
vi.mock("$app/navigation", () => ({
|
||||
goto: vi.fn(async () => {}),
|
||||
// The lobby screen navigates through the app-shell stores
|
||||
// (`appScreen.go`, `activeView.reset`/`select`), which internally call
|
||||
// SvelteKit `pushState`. Mock the whole nav module so the spies
|
||||
// capture the transitions and no real history mutation runs in JSDOM.
|
||||
const appScreenGoSpy = vi.fn();
|
||||
const activeViewResetSpy = vi.fn();
|
||||
const activeViewSelectSpy = vi.fn();
|
||||
vi.mock("$lib/app-nav.svelte", () => ({
|
||||
appScreen: { go: (...args: unknown[]) => appScreenGoSpy(...args) },
|
||||
activeView: {
|
||||
reset: (...args: unknown[]) => activeViewResetSpy(...args),
|
||||
select: (...args: unknown[]) => activeViewSelectSpy(...args),
|
||||
},
|
||||
}));
|
||||
|
||||
const listMyGamesSpy = vi.fn();
|
||||
@@ -105,6 +119,9 @@ beforeEach(async () => {
|
||||
submitApplicationSpy.mockReset();
|
||||
redeemInviteSpy.mockReset();
|
||||
declineInviteSpy.mockReset();
|
||||
appScreenGoSpy.mockReset();
|
||||
activeViewResetSpy.mockReset();
|
||||
activeViewSelectSpy.mockReset();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -119,8 +136,10 @@ afterEach(async () => {
|
||||
});
|
||||
});
|
||||
|
||||
async function importLobbyPage(): Promise<typeof import("../src/routes/lobby/+page.svelte")> {
|
||||
return import("../src/routes/lobby/+page.svelte");
|
||||
async function importLobbyPage(): Promise<
|
||||
typeof import("../src/lib/screens/lobby-screen.svelte")
|
||||
> {
|
||||
return import("../src/lib/screens/lobby-screen.svelte");
|
||||
}
|
||||
|
||||
const baseDate = new Date("2026-05-07T10:00:00Z");
|
||||
@@ -184,7 +203,7 @@ function makeApplication(id: string, status: string) {
|
||||
};
|
||||
}
|
||||
|
||||
describe("lobby page", () => {
|
||||
describe("lobby screen", () => {
|
||||
test("renders empty states for every section when API returns no items", async () => {
|
||||
listMyGamesSpy.mockResolvedValue([]);
|
||||
listPublicGamesSpy.mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 });
|
||||
@@ -375,6 +394,18 @@ describe("lobby page", () => {
|
||||
expect(disabledByLabel["Closed Run"]).toBe(false);
|
||||
expect(disabledByLabel["Cancelled Run"]).toBe(true);
|
||||
expect(disabledByLabel["Draft Run"]).toBe(true);
|
||||
|
||||
// Clicking a playable card resets the in-game view and enters the
|
||||
// game screen with its id (the single-URL app-shell switches
|
||||
// in-memory state instead of navigating to `/games/:id`).
|
||||
const liveCard = cards.find(
|
||||
(card) => card.querySelector("strong")?.textContent === "Live",
|
||||
);
|
||||
await fireEvent.click(liveCard!);
|
||||
expect(activeViewResetSpy).toHaveBeenCalledTimes(1);
|
||||
expect(appScreenGoSpy).toHaveBeenCalledWith("game", {
|
||||
gameId: "g-running",
|
||||
});
|
||||
});
|
||||
|
||||
test("application status badges localise pending and approved states", async () => {
|
||||
|
||||
Reference in New Issue
Block a user