pcbjam/web/standalone/src/lib/session-identity.test.ts
Gergő Törcsvári 8ee8db69e6
feat: standalone download-consent gate + truthful loading states (standalone-load-ux 0001/0002)
Cold loads on versioned CDN deploys now show a consent card (editor MB +
symbol/footprint lib figures, downloaded-now vs on-demand) and wait for OK
before any big fetch; warm loads skip it and show truthful stages (loading
from cache / Compiling / Starting KiCad) instead of the first-download line.

- wasm-assets: resolveWasmMeta (bundle/ver/sizes), download-completion marker
  keyed by content-addressed bundle/ver, update wording, auto-download opt-out,
  HEAD size fallback
- boot: manifest raw size as the progress total (fixes the br/gzip
  Content-Length mismatch), marker written after download+instantiate succeed
- cdn-source: syncState() — IDB warmth peek + sizes.json cold sums
- synced-source: syncState() from the backend envelope's sync refs (private
  platform); remote-source passes libSchema.sync through
- publish-wasm: manifest schema 2 with per-bundle sizes (registry-persisted,
  reuse + snapshot modes); publish-libs: sizes.json sibling key + top-up mode
- fixed 4 stale unit tests (bundle mapping, session-identity email)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011CC8aAnUUHcnHy3QCJtUwb
2026-07-29 07:48:25 +02:00

72 lines
2.2 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import {
loadSessionIdentity,
resetSessionIdentityForTest,
sessionIdentity,
} from "./session-identity";
function mockMe(body: unknown, ok = true) {
return vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok,
json: () => Promise.resolve(body),
} as Response);
}
afterEach(() => {
vi.restoreAllMocks();
resetSessionIdentityForTest();
});
describe("loadSessionIdentity", () => {
it("resolves the session user (name preferred, slug as id)", async () => {
const f = mockMe({ user: { slug: "alice", name: "Alice A", email: "a@x.y" } });
expect(await loadSessionIdentity("http://api")).toEqual({
slug: "alice",
name: "Alice A",
email: "a@x.y",
});
expect(sessionIdentity()).toEqual({
slug: "alice",
name: "Alice A",
email: "a@x.y",
});
expect(f).toHaveBeenCalledWith("http://api/api/me", {
credentials: "include",
});
});
it("falls back to email, then slug, for the display name", async () => {
mockMe({ user: { slug: "bob", name: "", email: "bob@x.y" } });
expect(await loadSessionIdentity("http://api")).toEqual({
slug: "bob",
name: "bob@x.y",
email: "bob@x.y",
});
});
it("is null for anonymous sessions ({user: null})", async () => {
mockMe({ user: null, authMode: "open" });
expect(await loadSessionIdentity("http://api")).toBeNull();
expect(sessionIdentity()).toBeNull();
});
it("is null when the endpoint is missing (example backend / demo)", async () => {
mockMe({ error: "not found" }, false);
expect(await loadSessionIdentity("http://api")).toBeNull();
});
it("is null on network failure and never throws", async () => {
vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("offline"));
expect(await loadSessionIdentity("http://api")).toBeNull();
});
it("fetches once — concurrent and later callers share the flight", async () => {
const f = mockMe({ user: { slug: "alice", name: "A", email: "" } });
await Promise.all([
loadSessionIdentity("http://api"),
loadSessionIdentity("http://api"),
]);
await loadSessionIdentity("http://api");
expect(f).toHaveBeenCalledTimes(1);
});
});