// Covers the signed-in navbar (nav.js): "admin" and "log out" in place of "log in", built from // the readable mark signing in sets beside the session. Notifications load separately. import { describe, expect, test } from "bun:test"; import { runInNewContext } from "node:vm"; import { Browser, instance } from "./browser.ts"; import { makeRepos } from "./fixture.ts"; const script = await Bun.file(new URL("../public/js/nav.js", import.meta.url)).text(); const TOKEN = "ab".repeat(32); function run(cookie: string) { const nodes: Record[] = []; let replacement: unknown[] = []; const document = { cookie, addEventListener: () => {}, querySelector: () => ({ parentElement: { after: () => {} }, replaceWith: (...replaced: unknown[]) => { replacement = replaced; }, }), createElement: (tag: string) => { const node: Record = { tag, setAttribute: (name: string, value: string) => { node[name] = value; }, addEventListener: () => {}, replaceChildren: () => {}, append: (...children: unknown[]) => { node.children = children; }, }; nodes.push(node); return node; }, }; runInNewContext(script, { document, fetch: async () => ({ ok: true, json: async () => ({ unread: 0, notifications: [] }) }), }); return { nodes, replacement }; } describe("the signed-in navbar", () => { test("stays as it is without a mark carrying a form token", () => { for (const cookie of ["", "kepr_editor=1", `kepr_editor=${TOKEN.slice(1)}`]) { expect(run(cookie).replacement).toEqual([]); } }); test("offers admin and a log out form posting the mark's token", () => { const { nodes, replacement } = run(`theme=dark; kepr_editor=${TOKEN}`); const [admin, form] = replacement as Record[]; expect(admin).toMatchObject({ tag: "a", href: "/admin", textContent: "admin" }); expect(form).toMatchObject({ tag: "form", method: "post", action: "/admin/sign-out" }); const [field, button] = (form?.children ?? []) as Record[]; expect(field).toMatchObject({ tag: "input", type: "hidden", name: "form", value: TOKEN }); expect(button).toMatchObject({ tag: "button", type: "submit", textContent: "log out" }); expect(nodes.find((node) => node.tag === "summary")).toMatchObject({ "aria-label": "notifications", }); }); test("the mark's token is the one log out needs", async () => { const repos = await makeRepos(["app"]); const browser = new Browser((await instance(repos.bucketDir)).app); await browser.signIn(); const mark = browser.cookies.get("kepr_editor") ?? ""; expect(mark).toMatch(/^[0-9a-f]{64}$/); // A mark from before it carried the token is brought up to date by any admin page. browser.cookies.set("kepr_editor", "1"); await browser.get("/admin"); expect(browser.cookies.get("kepr_editor")).toBe(mark); expect((await browser.post("/admin/sign-out", { form: mark })).status).toBe(303); expect((await browser.get("/admin")).status).toBe(401); }); });