Files
template-app/test/platform-theme.test.ts
T
2026-11-14 09:56:45 +00:00

211 lines
10 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The organization's theme from the platform, read on the server and written as the page's stylesheet
// (brand-kit BK12). The platform's answers are fakes here: the derivation is the kit's own, and every
// test looks at what the app would write, not at how the kit fetches it.
import { readdirSync, readFileSync, statSync } from "node:fs"
import { createRequire } from "node:module"
import path from "node:path"
import { deriveTheme, resolvedThemeStyles } from "@conusai-kit/brand"
import { createPlatformThemeCache } from "@conusai-kit/brand/platform-theme"
import { afterEach, describe, expect, it, vi } from "vitest"
import {
cloudOrganizationLogo,
createPlatformThemeReader,
ORGANIZATION_LOGO_PATH,
organizationIdentity,
type PlatformThemeDeployment,
} from "../apps/web/src/server/platform-theme.js"
const root = path.resolve(import.meta.dirname, "..")
/** The platform's answer for the navy fixture: the kit's derivation as OKLCH role maps (BrandingResponse). */
function branding(etag: string) {
const styles = resolvedThemeStyles(deriveTheme({ primary: "#001f4d", accent: "#1f6feb" }))
const roles = (mode: "light" | "dark") =>
Object.fromEntries(Object.entries(styles[mode]).map(([name, value]) => [name.slice(2), value]))
return {
version: 1,
seed: { primary: "#001f4d", accent: "#1f6feb" },
light: roles("light"),
dark: roles("dark"),
logo_url: null,
etag,
}
}
type Reply = { status: number; body?: unknown; etag?: string }
/** A fetch that answers from a script and records every request. */
function platform(...replies: Reply[]) {
const calls: Array<{ url: string; headers: Record<string, string> }> = []
const fetch = (async (url: string | URL | Request, init?: RequestInit) => {
calls.push({ url: String(url), headers: { ...((init?.headers ?? {}) as Record<string, string>) } })
const reply = replies.shift()
if (!reply) throw new Error("no scripted reply left")
if (reply.status === 304) return new Response(null, { status: 304, headers: { etag: reply.etag ?? "" } })
return new Response(reply.body === undefined ? null : JSON.stringify(reply.body), {
status: reply.status,
headers: { "content-type": "application/json", ...(reply.etag ? { etag: reply.etag } : {}) },
})
}) as typeof globalThis.fetch
return { fetch, calls }
}
const deployment = (org: string | undefined): PlatformThemeDeployment => ({
CONUSAI_API_URL: "https://platform.example.test/api",
CONUSAI_API_TOKEN: "dt_test_fixture",
...(org === undefined ? {} : { CONUSAI_ORG_SLUG: org }),
})
describe("the organization's theme on the server", () => {
it("writes the organization's roles as the page's stylesheet, dark on the root and light under .light", async () => {
const { fetch, calls } = platform({ status: 200, body: branding('"etag-1"'), etag: '"etag-1"' })
const css = await createPlatformThemeReader({ fetch })(deployment("acme"))
expect(calls[0]?.url).toBe("https://platform.example.test/api/v1/orgs/acme/branding")
expect(calls[0]?.headers.authorization).toBe("Bearer dt_test_fixture")
expect(css.startsWith(":root{--background:oklch(")).toBe(true)
expect(css).toContain(".light{--background:oklch(")
expect(css).toContain("--primary:oklch(")
})
it("with no organization slug the app asks nothing and writes nothing: the kit defaults stand", async () => {
const { fetch, calls } = platform()
expect(await createPlatformThemeReader({ fetch })(deployment(undefined))).toBe("")
expect(await createPlatformThemeReader({ fetch })(deployment(" "))).toBe("")
expect(calls).toHaveLength(0)
})
it("a cleared cache reads the organization again, with no validator", async () => {
const first = platform({ status: 200, body: branding('"etag-1"'), etag: '"etag-1"' })
await createPlatformThemeReader({ fetch: first.fetch })(deployment("acme"))
const second = platform({ status: 200, body: branding('"etag-2"'), etag: '"etag-2"' })
await createPlatformThemeReader({ fetch: second.fetch })(deployment("acme"))
expect(second.calls[0]?.headers["if-none-match"]).toBeUndefined()
})
it("an unavailable platform (503) gives the kit defaults, an empty stylesheet", async () => {
const { fetch } = platform({ status: 503, body: { title: "Unavailable" } })
expect(await createPlatformThemeReader({ fetch })(deployment("acme"))).toBe("")
})
it("a 304 writes the cached theme again with the cached ETag as the validator", async () => {
const cache = createPlatformThemeCache()
const first = platform({ status: 200, body: branding('"etag-1"'), etag: '"etag-1"' })
const fresh = await createPlatformThemeReader({ fetch: first.fetch, cache })(deployment("acme"))
const again = platform({ status: 304, etag: '"etag-1"' })
const cached = await createPlatformThemeReader({ fetch: again.fetch, cache })(deployment("acme"))
expect(again.calls[0]?.headers["if-none-match"]).toBe('"etag-1"')
expect(cached).toBe(fresh)
})
it("a changed organization is never written with another organization's theme", async () => {
const cache = createPlatformThemeCache()
const acme = platform({ status: 200, body: branding('"etag-acme"'), etag: '"etag-acme"' })
const acmeCss = await createPlatformThemeReader({ fetch: acme.fetch, cache })(deployment("acme"))
const other = platform({ status: 404, body: { title: "Organization Not Found" } })
const otherCss = await createPlatformThemeReader({ fetch: other.fetch, cache })(deployment("other"))
expect(other.calls[0]?.url).toContain("/v1/orgs/other/branding")
expect(other.calls[0]?.headers["if-none-match"]).toBeUndefined()
expect(otherCss).toBe("")
expect(acmeCss).not.toBe("")
})
})
describe("the organization's name and logo on Cloud", () => {
const PNG = new Uint8Array(
readFileSync(createRequire(import.meta.url).resolve("@conusai-kit/brand/logo/favicon.png")),
)
const on = { CONUSAI_API_URL: "https://platform.example.test/api", CONUSAI_API_TOKEN: "dt", CONUSAI_ORG_SLUG: "logo" }
afterEach(() => vi.unstubAllGlobals())
it("names the app after the organization and serves its logo from the app, with the token only to the platform", async () => {
const seen: Array<{ url: string; authorization?: string }> = []
vi.stubGlobal("fetch", async (url: string | URL, init?: RequestInit) => {
const headers = (init?.headers ?? {}) as Record<string, string>
seen.push({ url: String(url), ...(headers.authorization ? { authorization: headers.authorization } : {}) })
if (String(url).endsWith("/branding")) {
return Response.json(
{ ...branding('"n"'), name: "Navy UAB", logo_url: "/blob/org/7/logo.png" },
{ headers: { etag: '"n"' } },
)
}
return new Response(PNG, { headers: { "content-type": "image/png" } })
})
expect(await organizationIdentity(on)).toEqual({ name: "Navy UAB", logo: ORGANIZATION_LOGO_PATH })
const logo = await cloudOrganizationLogo(on)
expect(logo?.type).toBe("image/png")
expect(seen.at(-1)).toEqual({
url: "https://platform.example.test/blob/org/7/logo.png",
authorization: "Bearer dt",
})
})
it("keeps the app's own when Cloud has none, and refuses what is not a logo", async () => {
vi.stubGlobal("fetch", async (url: string | URL) =>
String(url).endsWith("/branding")
? Response.json(
{ ...branding('"x"'), logo_url: "https://cdn.example.test/x.png" },
{ headers: { etag: '"x"' } },
)
: new Response("<html>", { headers: { "content-type": "image/png" } }),
)
const deployment = { ...on, CONUSAI_ORG_SLUG: "nologo" }
expect(await organizationIdentity(deployment)).toEqual({ logo: ORGANIZATION_LOGO_PATH })
expect(await cloudOrganizationLogo(deployment)).toBeNull()
expect(await organizationIdentity({ ...on, CONUSAI_ORG_SLUG: "" })).toEqual({})
})
})
describe("the app's code and its deployment contract", () => {
it("names no hex colour and no colour function in the app's pages, layout, stylesheet or server code", () => {
const files = listFiles(path.join(root, "apps/web/src"), /\.(?:tsx?|css)$/)
expect(files.some((file) => file.endsWith("src/routes/__root.tsx"))).toBe(true)
expect(files.some((file) => file.endsWith("src/ui/globals.css"))).toBe(true)
for (const file of files) {
const code = readFileSync(file, "utf8")
.replace(/\/\*[\s\S]*?\*\//g, "")
.replace(/^\s*\/\/.*$/gm, "")
.replace(/(?<=\s)\/\/ .*$/gm, "")
const relative = path.relative(root, file)
expect(code, relative).not.toMatch(/#[0-9a-fA-F]{3,8}\b/)
expect(code, relative).not.toMatch(/\b(?:rgba?|hsla?|hwb|lab|lch|oklch)\(/)
}
})
it("reads only the font, type scale and radius brand tokens in its stylesheet, never a colour token (BK3)", () => {
const css = readFileSync(path.join(root, "apps/web/src/ui/globals.css"), "utf8")
const reads = css.match(/var\(--brand-[a-z0-9-]+/g) ?? []
expect(reads.length).toBeGreaterThan(0)
for (const read of reads) expect(read, read).toMatch(/--brand-(?:font|radius|text|leading|app-font-size)-/)
})
it("draws the product UI at the app's 16px root and its one type scale, not the site's 18px root", () => {
const css = readFileSync(path.join(root, "apps/web/src/ui/globals.css"), "utf8")
expect(css).toMatch(/html:root \{\s*font-size: var\(--brand-app-font-size-root\);/)
expect(css).not.toContain("--brand-font-size-root")
expect(css).not.toContain("--brand-font-display")
})
it("asks the platform for branding:read, and only that new scope", () => {
const contract = JSON.parse(readFileSync(path.join(root, "conusai.json"), "utf8")) as {
token: { permissions: string[] }
}
expect(contract.token.permissions).toContain("branding:read")
expect(contract.token.permissions).toEqual(expect.arrayContaining(["blob:read", "blob:write"]))
})
it("lists the files it adds as the template's own", () => {
const list = readFileSync(path.join(root, "TEMPLATE-FILES"), "utf8").split("\n")
expect(list).toContain("apps/web/src/server/platform-theme.ts")
expect(list).toContain("test/platform-theme.test.ts")
})
})
function listFiles(directory: string, pattern: RegExp): string[] {
return readdirSync(directory).flatMap((name) => {
const full = path.join(directory, name)
return statSync(full).isDirectory() ? listFiles(full, pattern) : pattern.test(name) ? [full] : []
})
}