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:
Ilia Denisov
2026-05-23 20:49:35 +02:00
parent 80545e9f9d
commit 4e0058d46c
36 changed files with 707 additions and 343 deletions
+23 -11
View File
@@ -1,9 +1,11 @@
// Login page component tests. The `auth` API and the navigation
// helper are mocked at module level; the session singleton is wired
// to a per-test `SessionStore`-backing IndexedDB so the keypair the
// form passes to `confirmEmailCode` is a genuine 32-byte Ed25519
// public key without polluting the production `dbConnection()`
// cache.
// Login screen component tests. The `auth` API and the app-shell
// navigation store are mocked at module level; the session singleton
// is wired to a per-test `SessionStore`-backing IndexedDB so the
// keypair the form passes to `confirmEmailCode` is a genuine 32-byte
// Ed25519 public key without polluting the production `dbConnection()`
// cache. The single-URL app-shell has no `/lobby` route: a successful
// sign-in advances the in-memory screen via `appScreen.go("lobby")`,
// so the test asserts against the mocked store instead of `goto`.
import "fake-indexeddb/auto";
import { fireEvent, render, waitFor } from "@testing-library/svelte";
@@ -24,8 +26,13 @@ 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 screen drives navigation through `appScreen.go(...)`, which
// internally calls SvelteKit `pushState`. Mock the whole nav module
// so the spy captures the screen transition and no real history
// mutation runs in JSDOM.
const appScreenGoSpy = vi.fn();
vi.mock("$lib/app-nav.svelte", () => ({
appScreen: { go: (...args: unknown[]) => appScreenGoSpy(...args) },
}));
const sendEmailCodeSpy = vi.fn();
@@ -58,11 +65,13 @@ beforeEach(async () => {
i18n.resetForTests("en");
sendEmailCodeSpy.mockReset();
confirmEmailCodeSpy.mockReset();
appScreenGoSpy.mockReset();
});
afterEach(async () => {
sendEmailCodeSpy.mockReset();
confirmEmailCodeSpy.mockReset();
appScreenGoSpy.mockReset();
session.resetForTests();
i18n.resetForTests("en");
db.close();
@@ -74,11 +83,13 @@ afterEach(async () => {
});
});
async function importLoginPage(): Promise<typeof import("../src/routes/login/+page.svelte")> {
return import("../src/routes/login/+page.svelte");
async function importLoginPage(): Promise<
typeof import("../src/lib/screens/login-screen.svelte")
> {
return import("../src/lib/screens/login-screen.svelte");
}
describe("login page", () => {
describe("login screen", () => {
test("submitting the email step calls sendEmailCode and advances to step=code", async () => {
sendEmailCodeSpy.mockResolvedValueOnce({ challengeId: "ch-1" });
const Page = (await importLoginPage()).default;
@@ -145,6 +156,7 @@ describe("login page", () => {
expect(confirmEmailCodeSpy).toHaveBeenCalledTimes(1);
expect(session.deviceSessionId).toBe("dev-1");
expect(session.status).toBe("authenticated");
expect(appScreenGoSpy).toHaveBeenCalledWith("lobby");
});
const args = confirmEmailCodeSpy.mock.calls[0]![1]!;
expect(args.challengeId).toBe("ch-1");