1234import { describe, expect, test } from "bun:test";5import { runInNewContext } from "node:vm";6import { Browser, instance } from "./browser.ts";7import { makeRepos } from "./fixture.ts";89const script = await Bun.file(new URL("../public/js/nav.js", import.meta.url)).text();10const TOKEN = "ab".repeat(32);1112function run(cookie: string) {13 const nodes: Record<string, unknown>[] = [];14 let replacement: unknown[] = [];15 const document = {16 cookie,17 addEventListener: () => {},18 querySelector: () => ({19 parentElement: { after: () => {} },20 replaceWith: (...replaced: unknown[]) => {21 replacement = replaced;22 },23 }),24 createElement: (tag: string) => {25 const node: Record<string, unknown> = {26 tag,27 setAttribute: (name: string, value: string) => {28 node[name] = value;29 },30 addEventListener: () => {},31 replaceChildren: () => {},32 append: (...children: unknown[]) => {33 node.children = children;34 },35 };36 nodes.push(node);37 return node;38 },39 };40 runInNewContext(script, {41 document,42 fetch: async () => ({ ok: true, json: async () => ({ unread: 0, notifications: [] }) }),43 });44 return { nodes, replacement };45}4647describe("the signed-in navbar", () => {48 test("stays as it is without a mark carrying a form token", () => {49 for (const cookie of ["", "kepr_editor=1", `kepr_editor=${TOKEN.slice(1)}`]) {50 expect(run(cookie).replacement).toEqual([]);51 }52 });5354 test("offers admin and a log out form posting the mark's token", () => {55 const { nodes, replacement } = run(`theme=dark; kepr_editor=${TOKEN}`);56 const [admin, form] = replacement as Record<string, unknown>[];57 expect(admin).toMatchObject({ tag: "a", href: "/admin", textContent: "admin" });58 expect(form).toMatchObject({ tag: "form", method: "post", action: "/admin/sign-out" });59 const [field, button] = (form?.children ?? []) as Record<string, unknown>[];60 expect(field).toMatchObject({ tag: "input", type: "hidden", name: "form", value: TOKEN });61 expect(button).toMatchObject({ tag: "button", type: "submit", textContent: "log out" });62 expect(nodes.find((node) => node.tag === "summary")).toMatchObject({63 "aria-label": "notifications",64 });65 });6667 test("the mark's token is the one log out needs", async () => {68 const repos = await makeRepos(["app"]);69 const browser = new Browser((await instance(repos.bucketDir)).app);70 await browser.signIn();71 const mark = browser.cookies.get("kepr_editor") ?? "";72 expect(mark).toMatch(/^[0-9a-f]{64}$/);73 74 browser.cookies.set("kepr_editor", "1");75 await browser.get("/admin");76 expect(browser.cookies.get("kepr_editor")).toBe(mark);77 expect((await browser.post("/admin/sign-out", { form: mark })).status).toBe(303);78 expect((await browser.get("/admin")).status).toBe(401);79 });80});