import { describe, it, expect } from "vitest"; import { Hono } from "hono"; import { BaseLayout } from "../base.js"; import type { WebSession } from "../../lib/session.js"; import { FALLBACK_THEME } from "../../lib/theme-resolution.js"; const app = new Hono().get("/", (c) => c.html( Page content ) ); describe("BaseLayout", () => { it("injects neobrutal tokens as :root CSS custom properties", async () => { const res = await app.request("/"); const html = await res.text(); // css-tree generates compact CSS (no space before brace) expect(html).toContain(":root{"); expect(html).toContain("--color-bg:"); expect(html).toContain("--color-primary:"); }); it("loads reset.css and theme.css stylesheets", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain('href="/static/css/reset.css"'); expect(html).toContain('href="/static/css/theme.css"'); }); it("loads Space Grotesk from Google Fonts", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain("fonts.googleapis.com"); expect(html).toContain("Space+Grotesk"); }); it("renders semantic site-header, content-container, and site-footer", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain('class="site-header"'); expect(html).toContain('class="content-container"'); expect(html).toContain('class="site-footer"'); }); it("renders provided page title", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain("Test Page"); }); it("falls back to default title when none provided", async () => { const defaultApp = new Hono().get("/", (c) => c.html( content ) ); const res = await defaultApp.request("/"); const html = await res.text(); expect(html).toContain("atBB Forum"); }); it("renders children inside content-container", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain("Page content"); }); it("renders header title link pointing to /", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain('href="/"'); expect(html).toContain('class="site-header__title"'); }); it("includes Accept-CH meta tag for color scheme hint", async () => { const res = await app.request("/"); const html = await res.text(); expect(html).toContain('http-equiv="Accept-CH"'); expect(html).toContain('content="Sec-CH-Prefers-Color-Scheme"'); }); it("renders cssOverrides in a style tag when non-null", async () => { const themeWithOverrides = { ...FALLBACK_THEME, cssOverrides: ".card { border: 2px solid black; }", }; const overridesApp = new Hono().get("/", (c) => c.html( content ) ); const res = await overridesApp.request("/"); const html = await res.text(); // css-tree generates compact CSS — check for key selectors and properties expect(html).toContain(".card{"); expect(html).toContain("border:2px solid black"); }); it("does not render Google Fonts preconnect tags when fontUrls is null", async () => { const themeNoFonts = { ...FALLBACK_THEME, fontUrls: null }; const noFontsApp = new Hono().get("/", (c) => c.html( content ) ); const res = await noFontsApp.request("/"); const html = await res.text(); expect(html).not.toContain("fonts.googleapis.com"); }); it("filters out non-https font URLs and does not render them", async () => { const themeWithUnsafeFontUrl = { ...FALLBACK_THEME, fontUrls: ["http://evil.com/style.css", "https://fonts.example.com/safe.css"], }; const unsafeFontApp = new Hono().get("/", (c) => c.html( content ) ); const res = await unsafeFontApp.request("/"); const html = await res.text(); expect(html).not.toContain("http://evil.com/style.css"); expect(html).toContain("https://fonts.example.com/safe.css"); }); it("does not render cssOverrides style tag when cssOverrides is null", async () => { const themeNoOverrides = { ...FALLBACK_THEME, cssOverrides: null }; const noOverridesApp = new Hono().get("/", (c) => c.html( content ) ); const res = await noOverridesApp.request("/"); const html = await res.text(); // The only