import type { Counter, Meter, MeterProvider } from "@opentelemetry/api"; import { describe, expect, it } from "@suaveplan/testing/runner"; import type { BundledAssetProbe } from "../assets/assets.js"; import { createScalarUiHandler } from "./handler.js"; describe("createScalarUiHandler", () => { it("handler returns HTML referencing openapiUrl", { timeout: 5000 }, () => { const bundle = createScalarUiHandler({ openapiUrl: "/openapi.json" }); const response = bundle.handler({ method: "GET", path: "/docs/ui/scalar", }); expect(response.status).toBe(200); expect(response.headers["Content-Type"]).toBe( "text/html; charset=utf-8" ); // The OpenAPI URL appears in the Scalar configuration block. const occurrences = response.body.split("/openapi.json").length - 1; expect(occurrences).toBe(1); }); it("emits strict CSP — never permits inline scripts", { timeout: 5000, }, () => { const { handler } = createScalarUiHandler({ openapiUrl: "/openapi.json", assetMode: "cdn", }); const response = handler({ method: "GET", path: "/docs/ui/scalar" }); const csp = response.headers["Content-Security-Policy"]; expect(csp).toBeDefined(); expect(csp).not.toContain("script-src 'self' 'unsafe-inline'"); expect(response.headers["X-Content-Type-Options"]).toBe("nosniff"); expect(response.headers["Referrer-Policy"]).toBe("no-referrer"); }); it("cdn mode whitelists the CDN origin in CSP", { timeout: 5000 }, () => { const { handler } = createScalarUiHandler({ openapiUrl: "/openapi.json", assetMode: "cdn", cdnUrl: "https://cdn.example.com/scalar/api-reference", }); const response = handler({ method: "GET", path: "/docs/ui/scalar" }); const csp = response.headers["Content-Security-Policy"] ?? ""; expect(csp).toContain("https://cdn.example.com"); }); it("bundled mode keeps CSP origin-restricted (no cdn host)", { timeout: 5000, }, () => { const { handler } = createScalarUiHandler({ openapiUrl: "/openapi.json", assetMode: "bundled", }); const response = handler({ method: "GET", path: "/docs/ui/scalar" }); const csp = response.headers["Content-Security-Policy"] ?? ""; expect(csp).not.toContain("cdn.jsdelivr.net"); }); it("embeds caller-supplied nonce on inline script tag", { timeout: 5000, }, () => { const { handler } = createScalarUiHandler({ openapiUrl: "/openapi.json", }); const response = handler({ method: "GET", path: "/docs/ui/scalar", nonce: "n-1234567", }); expect(response.body).toContain('nonce="n-1234567"'); }); it("is idempotent across requests", { timeout: 5000 }, () => { const { handler } = createScalarUiHandler({ openapiUrl: "/openapi.json", }); const a = handler({ method: "GET", path: "/docs/ui/scalar" }); const b = handler({ method: "GET", path: "/docs/ui/scalar" }); expect(a.body).toBe(b.body); expect(a.headers).toEqual(b.headers); }); it("openapiUrl carrying < is JSON-escaped inside the config block", { timeout: 5000, }, () => { const { handler } = createScalarUiHandler({ openapiUrl: "/openapi.json?q=