import { describe, expect, it } from "vitest"; import { compileGlobalGovernanceFacts, FactIndex, g, makeTailwindClassFact, makeTailwindTokenResolvedFact, makeUsageNodeFact, ruleTailwindOffScaleSpacingToken, runRules, } from "../index.js"; const FILE = "src/app.tsx"; const LOC = { file: FILE, line: 1, column: 7 }; function indexWithScale(): FactIndex { const ix = new FactIndex(); ix.addMany( compileGlobalGovernanceFacts({ scales: { space: g.scale.px([0, 4, 8, 16, 32]), }, styles: [ g.styles.rawSpacing().mustMatchScale("space", { appliesTo: ["padding"], severity: "warn", }), ], }) ); return ix; } function addResolvedSpacing(ix: FactIndex, raw = "p-3", token = "3", value = "0.75rem"): void { const node = makeUsageNodeFact({ file: FILE, nodePath: "0", element: "div", location: LOC, }); ix.add(node); ix.add( makeTailwindClassFact({ file: FILE, nodeId: node.id, raw, originPath: `attribute#0/class#${raw}`, prefix: null, modifiers: [], important: false, negative: false, utility: "p", value: { kind: "token", token }, location: LOC, }) ); ix.add( makeTailwindTokenResolvedFact({ utility: "p", token, resolved: { kind: "length", value, source: "theme-css" }, }) ); } describe("tailwind/off-scale-spacing-token", () => { it("flags spacing tokens whose resolved value misses the scale — ACCEPTANCE §5.29", () => { const ix = indexWithScale(); addResolvedSpacing(ix); const findings = ruleTailwindOffScaleSpacingToken(ix); expect(findings).toHaveLength(1); expect(findings[0]).toMatchObject({ ruleId: "tailwind/off-scale-spacing-token", severity: "moderate", message: "`p-3` resolves to 12px, which is not on the project's spacing scale [0, 4, 8, 16, 32].", attributes: { utility: "p", token: "3", resolvedPx: 12, }, }); }); it("allows resolved spacing tokens on the scale", () => { const ix = indexWithScale(); addResolvedSpacing(ix, "p-4", "4", "1rem"); expect(ruleTailwindOffScaleSpacingToken(ix)).toHaveLength(0); }); it("is registered in runRules", () => { const ix = indexWithScale(); addResolvedSpacing(ix); expect(runRules(ix).map((f) => f.ruleId)).toContain("tailwind/off-scale-spacing-token"); }); });