/** * Browser JSON code-block — Accept negotiation, highlight, HTML envelope. */ import { beforeEach, describe, expect, test } from "bun:test"; import { oke } from "../kernel/app.ts"; import { flow, resetFlowSeq } from "../kernel/flow.ts"; import { on, resetBindings } from "../kernel/on.ts"; import { http } from "../kernel/triggers.ts"; import { acceptQuality, asBrowserJsonCodeBlock, consoleUrlFromRequest, escapeHtml, fillPathTemplate, formatJsonCodeLatency, jsonCodeAuthFrom, jsonCodeLatencyTone, httpGetNavPaths, httpNavGroups, matchPathTemplate, pathParamNames, prefersHtml, prettyHrefFromRequest, prettyJson, rawHrefFromRequest, renderJsonCodeBlockHtml, shouldRenderJsonCodeBlock, tokenizeJson, } from "./json-code-block.ts"; const CHROME_ACCEPT = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"; beforeEach(() => { resetBindings(); resetFlowSeq(); }); describe("prefersHtml", () => { test("Chrome navigation prefers HTML over */* JSON", () => { expect(prefersHtml(CHROME_ACCEPT)).toBe(true); expect(acceptQuality(CHROME_ACCEPT, "text", "html")).toBe(1); expect(acceptQuality(CHROME_ACCEPT, "application", "json")).toBe(0.8); }); test("missing, */*, and explicit JSON stay on the envelope", () => { expect(prefersHtml(null)).toBe(false); expect(prefersHtml("*/*")).toBe(false); expect(prefersHtml("application/json")).toBe(false); expect(prefersHtml("text/html, application/json")).toBe(false); }); }); describe("tokenizeJson", () => { test("marks keys, strings, numbers, and literals", () => { const kinds = tokenizeJson('{"ok":true,"n":1,"s":"x"}').map((t) => t.kind); expect(kinds).toContain("key"); expect(kinds).toContain("literal"); expect(kinds).toContain("number"); expect(kinds).toContain("string"); }); test("prettyJson leaves invalid text alone", () => { expect(prettyJson("not-json")).toBe("not-json"); expect(prettyJson('{"a":1}')).toBe('{\n "a": 1\n}'); }); }); describe("shouldRenderJsonCodeBlock", () => { test("only GET JSON that prefers HTML", () => { const json = new Response(JSON.stringify({ data: { ok: true }, error: null }), { headers: { "content-type": "application/json" }, }); expect( shouldRenderJsonCodeBlock( new Request("http://127.0.0.1:6530/", { headers: { accept: CHROME_ACCEPT } }), json, ), ).toBe(true); expect(shouldRenderJsonCodeBlock(new Request("http://127.0.0.1:6530/"), json)).toBe(false); expect( shouldRenderJsonCodeBlock( new Request("http://127.0.0.1:6530/?raw=1", { headers: { accept: CHROME_ACCEPT } }), json, ), ).toBe(true); expect( shouldRenderJsonCodeBlock( new Request("http://127.0.0.1:6530/?format=json", { headers: { accept: CHROME_ACCEPT } }), json, ), ).toBe(false); expect( shouldRenderJsonCodeBlock( new Request("http://127.0.0.1:6530/_/ready", { headers: { accept: CHROME_ACCEPT } }), json, ), ).toBe(false); }); }); describe("renderJsonCodeBlockHtml", () => { test("escapes a script breakout in JSON", () => { const html = renderJsonCodeBlockHtml({ json: JSON.stringify({ data: { x: "" }, error: null }), status: 200, method: "GET", path: "/", app: "keel", rawHref: "/?raw=1", prettyHref: "/", }); expect(html).toContain('data-slot="json-code-block"'); expect(html).toContain('class="strip"'); expect(html).not.toContain("border-radius: 1rem"); expect(html).toContain("GET /"); expect(html).toContain("Ready"); expect(html).not.toContain('data-slot="json-code-auth-switch"'); expect(html).toContain('data-slot="json-code-view-toggle"'); expect(html).toContain('data-view="fields"'); expect(html).toContain('data-slot="json-code-fields"'); expect(html).toContain(">Fields"); expect(html).toContain(">JSON"); expect(html).not.toContain(">Pretty"); expect(html).not.toContain(">Raw"); expect(html).not.toContain(">Console"); expect(html).not.toContain(""); expect(html).toContain("</script>"); }); test("escapeHtml covers markup", () => { expect(escapeHtml(`<&"`)).toBe("<&""); }); test("response fields preview nested values and redact credential headers", () => { const html = renderJsonCodeBlockHtml({ json: JSON.stringify({ data: [{ id: "ENG-12", n: 2 }], error: null }), status: 200, method: "GET", path: "/views", app: "keel", rawHref: "/views?raw=1", prettyHref: "/views", headers: { "content-type": "application/json", authorization: "Bearer secret" }, }); expect(html).toContain('data-slot="json-code-response"'); expect(html).toContain('data-slot="json-code-response-status"'); expect(html).toContain('data-slot="json-code-response-headers"'); expect(html).toContain("id: ENG-12 · n: 2"); expect(html).toContain(">1 item<"); expect(html).toContain("application/json"); expect(html).toContain("[redacted]"); expect(html).not.toContain("Bearer secret"); expect(html).toContain(">OK<"); }); test("renders latency next to the status", () => { const html = renderJsonCodeBlockHtml({ json: '{"ok":true}', status: 200, method: "GET", path: "/", app: "keel", rawHref: "/?raw=1", prettyHref: "/", latencyMs: 12, }); expect(html).toContain('data-slot="json-code-latency"'); expect(html).toContain('data-tone="good"'); expect(html).toContain("lat-good"); expect(html).toContain("12ms"); expect(formatJsonCodeLatency(0.4)).toBe("400μs"); expect(formatJsonCodeLatency(1500)).toBe("1.5s"); expect(jsonCodeLatencyTone(0.4)).toBe("fast"); expect(jsonCodeLatencyTone(23.4)).toBe("good"); expect(jsonCodeLatencyTone(6_000)).toBe("critical"); expect(html).toContain('data-slot="json-code-cache"'); expect(html).toContain('data-cache="none"'); }); test("renders a cache hit mark", () => { const html = renderJsonCodeBlockHtml({ json: '{"ok":true}', status: 200, method: "GET", path: "/", app: "keel", rawHref: "/?raw=1", prettyHref: "/", cache: "hit", }); expect(html).toContain('data-cache="hit"'); expect(html).toContain("cache-hit"); expect(html).toContain("Cache hit"); expect(html).toContain("Hit"); }); test("renders auth for the handled request", () => { expect(jsonCodeAuthFrom({ publicGate: true })).toEqual({ kind: "public" }); expect(jsonCodeAuthFrom({ userId: "usr_1" })).toEqual({ kind: "user", id: "usr_1" }); expect(jsonCodeAuthFrom({ apiKeyId: "key_1" })).toEqual({ kind: "key", id: "key_1" }); expect(jsonCodeAuthFrom({})).toEqual({ kind: "none" }); const publicHtml = renderJsonCodeBlockHtml({ json: '{"ok":true}', status: 200, method: "GET", path: "/notes", app: "notes", rawHref: "/notes?raw=1", prettyHref: "/notes", auth: { kind: "public" }, }); expect(publicHtml).toContain('data-slot="json-code-auth"'); expect(publicHtml).toContain('data-auth="public"'); expect(publicHtml).toContain("intentionally unauthenticated"); expect(publicHtml).not.toContain('data-slot="json-code-auth-switch"'); expect(publicHtml).not.toContain("data-auth-default"); expect(publicHtml).not.toContain("data-route-public"); expect(publicHtml).toContain('data-rail-section="auth"'); expect(publicHtml).toContain(">Auth"); expect(publicHtml).toContain(">Inherit"); expect(publicHtml).toContain(">Custom"); const userHtml = renderJsonCodeBlockHtml({ json: '{"ok":true}', status: 200, method: "GET", path: "/me", app: "notes", rawHref: "/me?raw=1", prettyHref: "/me", auth: { kind: "user", id: "usr_alice" }, }); expect(userHtml).toContain('data-auth="user"'); expect(userHtml).toContain("usr_alice"); expect(userHtml).toContain("Authenticated user · usr_alice"); expect(userHtml).not.toContain('data-slot="json-code-auth-switch"'); expect(userHtml).not.toContain('data-slot="json-code-api-key-global"'); expect(userHtml).not.toContain('data-slot="json-code-api-key-route"'); expect(userHtml).toContain('data-rail-section="auth"'); expect(userHtml).toContain('data-slot="json-code-global-auth"'); expect(userHtml).not.toContain(">Console"); }); test("Authorization follows headers and global auth lives on the strip", () => { const html = renderJsonCodeBlockHtml({ json: '{"ok":true}', status: 200, method: "GET", path: "/", app: "keel", rawHref: "/?raw=1", prettyHref: "/", auth: { kind: "public" }, }); expect(html).not.toContain('data-slot="json-code-auth-switch"'); expect(html).toContain(">Inherit"); expect(html).toContain(">Custom"); expect(html).toContain('aria-label="Request headers"'); expect(html).toContain('data-headers-mode="inherit"'); expect(html).toContain('data-headers-mode="custom"'); expect(html).toContain("data-headers-custom"); expect(html).toContain("oke:json-code:headers-mode"); expect(html).toContain("function headersMode"); const headersAt = html.indexOf('
Auth"); expect(html).toContain('data-auth-type="none"'); expect(html).toContain(">No"); expect(html).toContain(">Bearer"); expect(html).toContain(">Basic"); expect(html).toContain(">API"); expect(html).not.toContain("auth-opt"); expect(html).toContain('data-auth-scope="request"'); expect(html).toContain('data-auth-scope="global"'); expect(html).toContain('data-slot="json-code-global-auth"'); expect(html).toContain('data-slot="json-code-global-headers"'); expect(html).toContain('data-slot="json-code-global-panel"'); expect(html).toContain('data-kv="headers-global"'); expect(html).toContain("oke:json-code:auth"); expect(html).toContain("oke:json-code:auth-global"); expect(html).toContain("oke:json-code:headers-global"); expect(html).toContain("function effectiveAuth"); expect(html).toContain("function storedAuth"); expect(html).toContain("function forgetSecretsOnReload"); expect(html).toContain("function replayStoredAuth"); expect(html).toContain("oke:json-code:auth-replay"); expect(html).toContain("parsed.token"); expect(html).toContain('type="password"'); expect(html).toContain("data-auth-reveal"); expect(html).toContain('data-slot="json-code-send"'); }); test("renders the Request rail with tabs and current GET on", () => { const html = renderJsonCodeBlockHtml({ json: '{"ok":true}', status: 200, method: "GET", path: "/tasks", app: "keel", rawHref: "/tasks?raw=1", prettyHref: "/tasks", nav: [ { name: "/", routes: [ { method: "GET", path: "/", href: "/", current: false, paramNames: [], }, ], }, { name: "tasks", routes: [ { method: "GET", path: "/tasks", href: "/tasks", current: true, paramNames: [], }, { method: "POST", path: "/tasks", href: null, current: false, paramNames: [], }, { method: "GET", path: "/tasks/:id", href: null, current: false, paramNames: ["id"], }, ], }, ], }); expect(html).toContain('data-slot="json-code-nav-panel"'); expect(html).not.toContain("data-route-public"); expect(html).toContain('aria-label="Expand request"'); expect(html).not.toContain('id="json-code-rail" checked'); expect(html).toContain('aria-label="Request"'); expect(html).toContain('data-rail-section="routes"'); expect(html).toContain('class="rail-nav"'); expect(html).toContain('data-slot="json-code-request-dock"'); expect(html).toContain('data-slot="json-code-send"'); expect(html).toContain('data-slot="json-code-reset"'); expect(html).toContain('data-slot="json-code-global-auth"'); expect(html).toContain('data-rail-section="query"'); expect(html).toContain(">Params"); expect(html).toContain('data-rail-section="body"'); expect(html).toContain(">Body"); expect(html).toContain('data-slot="json-code-body-mode"'); expect(html).toContain('data-mode="form"'); expect(html).toContain('data-mode="json"'); expect(html).toContain('data-slot="json-code-body-raw"'); expect(html).toContain('data-slot="json-code-body-hi"'); expect(html).toContain('data-kv="body"'); expect(html).toContain('data-rail-section="auth"'); expect(html).toContain(">Inherit"); expect(html).toContain(">Custom"); expect(html).toContain('data-slot="json-code-global-headers"'); expect(html).toContain('data-rail-section="cookies"'); expect(html).toContain('data-rail-section="headers"'); expect(html).toContain('data-rail-section="path"'); expect(html.indexOf('
Routes"); expect(html).toContain('aria-label="Collapse routes"'); expect(html).toContain('aria-label="Expand routes"'); expect(html).toContain('data-kv="cookies"'); expect(html).toContain('data-kv="headers"'); expect(html).toContain('data-kv="query"'); expect(html).toContain('data-kv="path"'); expect(html).not.toContain("data-kv-apply="); expect(html).toContain('data-path="/tasks"'); expect(html).toContain('href="/tasks"'); expect(html).toContain('class="leaf is-on"'); expect(html).toContain("POST"); expect(html).toContain("/tasks/:id"); expect(html).toContain('data-param-template="/tasks/:id"'); expect(html).not.toContain('href="/tasks/:id"'); expect(html).toContain("data-route-leaf"); expect(html).toContain('data-route-options="1"'); expect(html).toContain("function selectRouteLeaf"); expect(html).toContain("a.leaf[data-route-leaf][href]"); expect(html).toContain("oke:json-code:rail-section"); expect(html).not.toContain("data-rail-tab="); expect(html).not.toContain('data-slot="json-code-auth-panel"'); expect(html).not.toContain('data-rail-section="routes" open'); expect(html).toContain("
"); }); }); describe("path templates", () => { test("pathParamNames extracts :segments", () => { expect(pathParamNames("/notes/:id/archive")).toEqual(["id"]); expect(pathParamNames("/a/:x/b/:y")).toEqual(["x", "y"]); expect(pathParamNames("/tasks")).toEqual([]); }); test("matchPathTemplate and fillPathTemplate round-trip", () => { expect(matchPathTemplate("/notes/:id", "/notes/abc")).toEqual({ id: "abc" }); expect(matchPathTemplate("/notes/:id", "/notes/abc/extra")).toBeNull(); expect(fillPathTemplate("/notes/:id", { id: "abc" })).toBe("/notes/abc"); expect(fillPathTemplate("/notes/:id", { id: "" })).toBeNull(); expect(fillPathTemplate("/notes/:id/x/:slug", { id: "1", slug: "hi there" })).toBe( "/notes/1/x/hi%20there", ); }); }); describe("httpGetNavPaths", () => { test("keeps static GET paths and drops params and internals", () => { expect( httpGetNavPaths([ { trigger: { kind: "http", method: "GET", path: "/" } }, { trigger: { kind: "http", method: "GET", path: "/tasks" } }, { trigger: { kind: "http", method: "GET", path: "/tasks/:id" } }, { trigger: { kind: "http", method: "POST", path: "/tasks" } }, { trigger: { kind: "http", method: "GET", path: "/_/ready" } }, { trigger: { kind: "signal" } }, ]), ).toEqual(["/", "/tasks"]); }); test("httpNavGroups groups by first segment and keeps raw on static GET", () => { const groups = httpNavGroups( [ { trigger: { kind: "http", method: "GET", path: "/" } }, { trigger: { kind: "http", method: "GET", path: "/tasks" } }, { trigger: { kind: "http", method: "GET", path: "/tasks/:id" } }, { trigger: { kind: "http", method: "POST", path: "/tasks" } }, { trigger: { kind: "http", method: "GET", path: "/me/tasks" } }, { trigger: { kind: "http", method: "GET", path: "/_/ready" } }, { trigger: { kind: "signal" } }, ], new Request("http://127.0.0.1:6530/tasks?raw=1"), ); expect(groups).toEqual([ { name: "/", routes: [ { method: "GET", path: "/", href: "/?raw=1", current: false, paramNames: [], }, ], }, { name: "me", routes: [ { method: "GET", path: "/me/tasks", href: "/me/tasks?raw=1", current: false, paramNames: [], }, ], }, { name: "tasks", routes: [ { method: "GET", path: "/tasks", href: "/tasks?raw=1", current: true, paramNames: [], }, { method: "POST", path: "/tasks", href: null, current: false, paramNames: [], }, { method: "GET", path: "/tasks/:id", href: null, current: false, paramNames: ["id"], }, ], }, ]); }); }); describe("asBrowserJsonCodeBlock — HTTP", () => { test("Chrome GET / becomes the code block; curl stays JSON", async () => { on( http.get("/").public(), flow("main.root", { do: () => ({ ok: true as const, app: "keel" }), }), ); const app = oke({ autoBoot: false, name: "keel" }); const browser = await app.fetch( new Request("http://127.0.0.1:6530/", { headers: { accept: CHROME_ACCEPT } }), ); expect(browser.status).toBe(200); expect(browser.headers.get("content-type")).toContain("text/html"); expect(browser.headers.get("vary")).toMatch(/Accept/i); const html = await browser.text(); expect(html).toContain('data-slot="json-code-block"'); expect(html).toContain("keel"); expect(html).toContain(""ok""); expect(html).toContain('data-slot="json-code-nav-panel"'); expect(html).toContain('aria-label="Request"'); expect(html).toContain('href="/"'); expect(html).toContain('data-slot="json-code-latency"'); expect(html).toContain('data-slot="json-code-cache"'); expect(html).toContain('data-cache="none"'); expect(html).toContain('data-slot="json-code-auth"'); expect(html).toContain('data-auth="public"'); expect(html).not.toContain('data-slot="json-code-auth-switch"'); expect(html).not.toContain("data-auth-default"); expect(html).not.toContain(">Console"); expect(html).toMatch(/\d+(?:\.\d+)?(?:μs|ms|s)/); const curl = await app.fetch(new Request("http://127.0.0.1:6530/")); expect(curl.headers.get("content-type")).toContain("application/json"); expect(await curl.json()).toEqual({ data: { ok: true, app: "keel" }, error: null }); }); test("?raw=1 stays on the traces page; format=json and Accept stay the envelope", async () => { on(http.get("/health").public(), flow("main.health", { do: () => ({ ok: true as const }) })); const app = oke({ autoBoot: false, name: "keel" }); const raw = await app.fetch( new Request("http://127.0.0.1:6530/health?raw=1", { headers: { accept: CHROME_ACCEPT } }), ); expect(raw.headers.get("content-type")).toContain("text/html"); const html = await raw.text(); expect(html).toContain('data-view="raw"'); expect(html).toContain('data-code-view="raw"'); expect(html).toContain('data-slot="json-code-view-toggle"'); expect(html).toContain(">JSON"); expect(html).toContain('data-response-view="json" aria-pressed="true"'); expect(html).toContain(""ok":true"); const forced = await app.fetch( new Request("http://127.0.0.1:6530/health?format=json", { headers: { accept: CHROME_ACCEPT }, }), ); expect(await forced.json()).toEqual({ data: { ok: true }, error: null }); const json = await app.fetch( new Request("http://127.0.0.1:6530/health", { headers: { accept: "application/json" } }), ); expect(await json.json()).toEqual({ data: { ok: true }, error: null }); }); test("GET /_/ready stays JSON even when Accept prefers HTML", async () => { const app = oke({ autoBoot: false, name: "keel" }); const res = await app.fetch( new Request("http://127.0.0.1:6530/_/ready", { headers: { accept: CHROME_ACCEPT } }), ); expect(res.headers.get("content-type")).toContain("application/json"); expect(await res.json()).toEqual({ ready: false, reason: "booting" }); }); test("console helper and raw href follow the request host", () => { const request = new Request("http://127.0.0.1:6530/tasks?x=1"); expect(consoleUrlFromRequest(request)).toBe("http://127.0.0.1:6533"); expect(rawHrefFromRequest(request)).toBe("/tasks?x=1&raw=1"); expect(prettyHrefFromRequest(new Request("http://127.0.0.1:6530/tasks?x=1&raw=1"))).toBe( "/tasks?x=1", ); }); test("wrapper copies status and skips non-JSON", async () => { const htmlIn = new Response("

hi

", { status: 200, headers: { "content-type": "text/html" }, }); const skipped = await asBrowserJsonCodeBlock( new Request("http://127.0.0.1:6530/", { headers: { accept: CHROME_ACCEPT } }), htmlIn, "keel", ); expect(await skipped.text()).toBe("

hi

"); const fail = new Response(JSON.stringify({ data: null, error: { code: "NotFound" } }), { status: 400, headers: { "content-type": "application/json" }, }); const page = await asBrowserJsonCodeBlock( new Request("http://127.0.0.1:6530/missing", { headers: { accept: CHROME_ACCEPT } }), fail, "keel", ); expect(page.status).toBe(400); expect(await page.text()).toContain("400"); }); });