Files
Jyotisha/frontend/tests/session-list-provider.test.ts
T
jesse-ux fabe0114dd
Independent Staging Quality Gate / validate (push) Successful in 10m5s
Independent Staging Quality Gate / publish (push) Successful in 3m23s
fix(chart): keep subject-scoped failures visible and follow the new routes
A 401 for the current person was stored, then hidden because the subject stamp only happened on success. Session, report, and birth contracts now follow the subject query and the shared birth lookup. Admin reset fixtures can no longer insert a retired self mirror.
2026-09-25 04:51:49 +08:00

55 lines
3.1 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const provider = readFileSync(new URL("../src/lib/session-list-context.tsx", import.meta.url), "utf8");
const layout = readFileSync(new URL("../src/app/(app)/layout.tsx", import.meta.url), "utf8");
const page = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8");
const sessionHook = readFileSync(new URL("../src/hooks/use-session-management.ts", import.meta.url), "utf8");
test("the app layout owns one SessionListProvider and one SidebarProvider", () => {
assert.match(layout, /<SessionListProvider>/);
assert.equal(layout.match(/<SessionListProvider>/g)?.length, 1);
assert.match(layout, /<SidebarProvider escapeBlocked=\{registration\?\.escapeBlocked \?\? false\}>/);
assert.equal(layout.match(/<SidebarProvider/g)?.length, 1);
assert.match(layout, /<AppSidebar/);
assert.doesNotMatch(page, /<SidebarProvider/);
assert.doesNotMatch(page, /<AppSidebar/);
});
test("the provider fetches the session list once and home does not fetch it again", () => {
assert.match(provider, /fetch\(`\/api\/sessions\?limit=\$\{SESSION_PAGE_SIZE\}&subject=\$\{encodeURIComponent\(readCurrentSubjectId\(\)\)\}`/);
assert.match(provider, /fetch\("\/api\/account"/);
// 原值: page.tsx 里 `await sessionListReady;`
// 新值: page.tsx 把 sessionListReady 交给 runHomeBootstrap,函数里 `await deps.sessionListReady`。
// 原因: 启动流程搬出首页后,会话列表仍只由 provider 拉一次。
const bootstrapRun = readFileSync(new URL("../src/lib/home-bootstrap-run.ts", import.meta.url), "utf8");
assert.match(page, /sessionListReady,/);
assert.match(bootstrapRun, /await deps\.sessionListReady;/);
assert.doesNotMatch(page, /fetchSessions\(/);
assert.doesNotMatch(page, /fetchAccount\(/);
assert.match(page, /useHomeShellRegistration\(/);
});
test("a 401 clears the list and marks signedOut", () => {
assert.match(provider, /sessionResponse\.status === 401 \|\| accountResponse\.status === 401/);
assert.match(provider, /signedOut: true/);
assert.doesNotMatch(provider, /localStorage|sessionStorage|document\.cookie|indexedDB/i);
});
test("session writes no longer invalidate a second cache", () => {
assert.doesNotMatch(sessionHook, /invalidateSidebarCache/);
});
test("only the shell consumes registration and it passes Home children through unchanged", () => {
assert.match(layout, /const registration = useShellRegistration\(\)/);
assert.match(layout, /sessionListSidebarModel\(list, registration\)/);
assert.doesNotMatch(layout, /list\.registration|cloneElement/);
assert.match(layout, /<SidebarInset[\s\S]*>\s*\{children\}\s*<\/SidebarInset>/);
assert.doesNotMatch(page, /useShellRegistration/);
const dataValue = provider.slice(provider.indexOf("const value = useMemo"), provider.indexOf("return (", provider.indexOf("const value = useMemo")));
assert.doesNotMatch(dataValue, /\bregistration\b/);
assert.match(dataValue, /registerShellControls/);
assert.match(provider, /<ShellRegistrationContext\.Provider value=\{registration\}>\s*\{children\}/);
});