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

72 lines
3.3 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// A screen is a declaration: data from `load`, columns, actions and labels, handed to the kit's patterns
// (docs/how-to/compose-a-page.md in the platform's documentation). So a screen has no styling of its own: no `className`, no
// colour value. React Aria is out of a screen's reach too, by the `ui-comes-from-the-kit` rule of
// scripts/dep-rules.cjs. A screen that is truly domain-specific says so, and why, in its first lines:
// `// kit:custom-ui <why>`. `pnpm boundaries` and this suite read the same marker.
import { readFileSync } from "node:fs"
import { createRequire } from "node:module"
import { join } from "node:path"
import { describe, expect, it } from "vitest"
const require = createRequire(import.meta.url)
const { buildForbidden, listCustomScreens, listScreens } = require("../scripts/dep-rules.cjs") as {
buildForbidden(
moduleIds: string[],
custom?: string[],
): { name: string; from: { path?: string; pathNot?: string | string[] }; to: { path?: string | string[] } }[]
listCustomScreens(root: string): string[]
listScreens(root: string): string[]
}
const root = new URL("..", import.meta.url).pathname
const COLOUR = /["'`](?:#[0-9a-fA-F]{3,8}|rgba?\(|hsla?\(|oklch\()/
/** What a declared screen may not hold, by file, with the way out in the message. */
function styling(source: string): string[] {
const found: string[] = []
if (/<(?:button|input|select|textarea|table)(?:\s|>)/.test(source))
found.push("raw control: compose kit primitives or patterns")
if (/\bclassName=/.test(source)) found.push("className: use a pattern's slot, or a variant")
if (COLOUR.test(source)) found.push("a colour value: the kit's roles carry every colour")
return found
}
describe("a screen is a declaration", () => {
const custom = new Set(listCustomScreens(root))
it("holds no styling of its own unless it declares itself custom", () => {
const problems = listScreens(root)
.filter((file) => !custom.has(file))
.flatMap((file) =>
styling(readFileSync(join(root, file), "utf8")).map(
(problem) => `${file}: ${problem}, or \`// kit:custom-ui <why>\``,
),
)
expect(problems).toEqual([])
})
it("catches what it means to", () => {
expect(styling(`<p className="x" />`)).toHaveLength(1)
expect(styling(`const c = "#123abc"`)).toHaveLength(1)
expect(styling(`const c = "oklch(0.5 0 0)"`)).toHaveLength(1)
expect(styling(`<a href="#section" />`)).toEqual([])
expect(styling(`<button type="button" />`)).toHaveLength(1)
expect(styling(`<PageHeader title={title} />`)).toEqual([])
})
it("keeps React Aria behind the kit, for every screen that has not declared itself", () => {
const rule = buildForbidden([], ["modules/x/ui/own.tsx"]).find(({ name }) => name === "ui-comes-from-the-kit")
const target = new RegExp(rule?.to.path as string)
expect(target.test("react-aria-components")).toBe(true)
expect(target.test("node_modules/.pnpm/x/node_modules/@react-aria/button/dist/index.js")).toBe(true)
expect(target.test("@conusai-kit/ui/patterns")).toBe(false)
expect(rule?.from.pathNot).toEqual(["^modules/x/ui/own\\.tsx$"])
})
it("leaves custom screens the exception", () => {
expect(custom.size).toBeLessThan(listScreens(root).length / 2)
})
})