import { describe, expect, it } from "vitest"; import { type ModuleUiHostContext, type ModuleUiNavigation, ModuleUiNavigationError, NO_MODULE_UI_CONTRIBUTIONS, } from "../src/index.js"; /** The navigation capability's public surface (ADR 0063). Its behaviour is the host's: the * Dashboard's implementation is proven in `surfaces/shell/tests/module-navigation.test.tsx`. */ describe("module UI navigation contract", () => { it("reports a refused path with a stable code and a fixed message that never echoes the path", () => { for (const code of [ "MODULE_NAVIGATION_PATH_INVALID", "MODULE_NAVIGATION_PATH_UNKNOWN", ] as const) { const error = new ModuleUiNavigationError(code); expect(error).toBeInstanceOf(Error); expect(error.name).toBe("ModuleUiNavigationError"); expect(error.code).toBe(code); expect(error.stack).toBe(`ModuleUiNavigationError: ${error.message}`); expect(error.message).not.toMatch(/[/:?#]/u); } }); it("keeps a context built before navigation existed a complete, valid context", () => { // Exactly the seven members every host supplied before ADR 0063, with no `navigation` member at // all. This must keep compiling unchanged: `tsc` over this file is the compatibility proof, and // `satisfies` makes the check exact rather than structural-by-accident. const preNavigationContext = { api: { request: async () => new Response(null, { status: 204 }) }, contributions: NO_MODULE_UI_CONTRIBUTIONS, display: undefined, moduleId: "announcements", permissions: { has: () => false }, principal: { kind: "anonymous" }, realtime: { subscribe: () => ({ close: () => undefined }) }, } satisfies ModuleUiHostContext; const asContext: ModuleUiHostContext = preNavigationContext; expect("navigation" in asContext).toBe(false); expect(asContext.navigation?.location().path).toBeUndefined(); }); it("lets a host supply navigation, omit it, or set it to undefined, and a module feature-detect it", () => { const navigation: ModuleUiNavigation = { link: () => ({ href: "/m/x", onClick: () => undefined }), location: () => ({ page: "x", path: "x" }), navigate: () => undefined, settings: undefined, subscribe: () => () => undefined, }; const where = (context: Pick): string => context.navigation?.location().path ?? "no navigation"; expect(where({ navigation })).toBe("x"); expect(where({})).toBe("no navigation"); expect(where({ navigation: undefined })).toBe("no navigation"); // Present, it must be the real capability — not any object. // @ts-expect-error a navigation member must satisfy ModuleUiNavigation expect(where({ navigation: { location: () => "x" } })).toBeDefined(); }); });