import { readdirSync, readFileSync } from "node:fs"; import { join, relative } from "node:path"; import { describe, expect, it } from "vitest"; const ROOTS = ["src/app/admin", "src/components/admin"]; const GRAPHICAL_ALLOWLIST = [ "src/app/admin/favicon/favicon-generator.tsx", "src/app/admin/import/clone/import-clone-client.tsx", "src/app/admin/import/furni/import-furni-client.tsx", "src/components/admin/catalog/items-shop-preview.tsx", ]; const DATA_COLOR_ALLOWLIST = [ "src/app/admin/alerts/page.tsx", "src/app/admin/banners/banners-manager.tsx", "src/app/admin/events/events-table.tsx", "src/app/admin/events/types/event-types-manager.tsx", "src/app/admin/favicon/favicon-generator.tsx", "src/app/admin/help-questions/new/page.tsx", "src/app/admin/help-questions/[id]/page.tsx", "src/app/admin/prefixes/prefixes-client.tsx", "src/app/admin/tags/page.tsx", "src/app/admin/teams/page.tsx", "src/app/admin/theme/page.tsx", ]; const PUBLIC_STRUCTURAL_TOKEN = /var\(--(?:color-(?:background|surface|dropdown|navbar|navbar-text|text|text-muted|primary|primary-hover)|border-subtle|border-color)\)/g; const HARDCODED_UI_PALETTE = /(?:text|bg|border|ring|from|to|via)-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:[1-9]00|50)(?:\/\d+)?/g; function sourceFiles(directory: string): string[] { return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { const path = join(directory, entry.name); return entry.isDirectory() ? sourceFiles(path) : /\.(?:ts|tsx)$/.test(entry.name) ? [path] : []; }); } describe("admin theme source audit", () => { it("keeps ordinary admin text theme-aware", () => { const violations: string[] = []; for (const root of ROOTS) { for (const file of sourceFiles(root)) { const normalized = relative(process.cwd(), file).replaceAll("\\", "/"); if (GRAPHICAL_ALLOWLIST.includes(normalized)) continue; const source = readFileSync(file, "utf8"); const risky = source.match( /(?:text-(?:gray|slate)-(?:400|500|600|700|800|900)|text-white(?:\/\d+)?|\)\]0)/g, ); if (risky) violations.push(`${normalized}: ${[...new Set(risky)].join(", ")}`); } } expect(violations).toEqual([]); }); it("keeps admin chrome independent from public structural colors", () => { const violations: string[] = []; for (const root of ROOTS) { for (const file of sourceFiles(root)) { const normalized = relative(process.cwd(), file).replaceAll("\\", "/"); if (normalized.endsWith("admin-theme-source-audit.test.ts")) continue; const source = readFileSync(file, "utf8"); const risky = [ ...(source.match(PUBLIC_STRUCTURAL_TOKEN) ?? []), ...(source.match(HARDCODED_UI_PALETTE) ?? []), ]; const filtered = [ ...DATA_COLOR_ALLOWLIST, ...GRAPHICAL_ALLOWLIST, ].includes(normalized) ? risky.filter((match) => match.startsWith("var(")) : risky; if (filtered.length) violations.push( `${normalized}: ${[...new Set(filtered)].join(", ")}`, ); } } expect(violations).toEqual([]); }); it("keeps every import workflow on semantic admin status and overlay colors", () => { const violations = sourceFiles("src/app/admin/import").flatMap((file) => { const source = readFileSync(file, "utf8"); const risky = source.match(/rgba?\([^)]*\)|(?:bg-black|text-white)(?:\/\d+)?/g) ?? []; return risky.length ? [ `${relative(process.cwd(), file).replaceAll("\\", "/")}: ${[...new Set(risky)].join(", ")}`, ] : []; }); expect(violations).toEqual([]); }); });