Files
template-app/test/client-modules.test.ts

60 lines
2.6 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The sign-out control ends every enabled module's browser context (joint contract §12, J1). Sign-out
// is a full navigation, so no provider unmounts: without the lifecycle run, the guides progress and
// unsent events of the person who signed out stay in the tab's `sessionStorage`.
import { afterEach, describe, expect, it, vi } from "vitest"
import { composedClientModules, endClientModules } from "../apps/web/src/ui/client-module-list"
/** A `Storage` over a Map, as much of it as the modules' handlers use. */
function memoryStorage(seed: Record<string, string>): Storage {
const values = new Map(Object.entries(seed))
const storage = {
get length() {
return values.size
},
key: (index: number) => [...values.keys()][index] ?? null,
getItem: (key: string) => values.get(key) ?? null,
setItem: (key: string, value: string) => void values.set(key, value),
removeItem: (key: string) => void values.delete(key),
clear: () => values.clear(),
}
// `Object.keys(storage)` lists the stored keys, as it does for the browser's own.
return new Proxy(storage, {
ownKeys: () => [...values.keys()],
getOwnPropertyDescriptor: (_target, key) =>
typeof key === "string" && values.has(key)
? { enumerable: true, configurable: true, value: values.get(key) }
: undefined,
}) as unknown as Storage
}
afterEach(() => {
vi.unstubAllGlobals()
})
describe("signing out ends the client modules' context", () => {
it("clears what each enabled module keeps in the tab and leaves the app's own keys", async () => {
const own = "app.preference"
const keys = [own]
keys.push("conusai.guides.acme.u-7.queue", "conusai.guides.beta.u-7.active") // kit:guides
keys.push("conusai.support.session.v1:spk_example") // kit:support
const storage = memoryStorage(Object.fromEntries(keys.map((key) => [key, "{}"])))
vi.stubGlobal("window", { sessionStorage: storage })
const report = await endClientModules("sign-out")
expect(report.ok).toBe(true)
expect(report.outcomes.map((outcome) => outcome.module).sort()).toEqual([...composedClientModules.modules].sort())
expect(Object.keys(storage)).toEqual([own])
})
it("issues a new context instance, so a late answer of the old one is discarded", async () => {
vi.stubGlobal("window", { sessionStorage: memoryStorage({}) })
const before = composedClientModules.current()
await endClientModules("sign-out")
expect(before.signal.aborted).toBe(true)
expect(composedClientModules.isCurrent(before.instance)).toBe(false)
})
})