import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import { fileURLToPath } from 'node:url'; import { parse } from '@babel/parser'; import postcss from 'postcss'; import { themeAnalysis } from './guard-theme-analysis.mjs'; const workspace = fileURLToPath(new URL('../', import.meta.url)); function walk(node, visitor, parent = null, field = null) { if (!node || typeof node !== 'object') return; if (node.type) visitor(node, parent, field); for (const [key, value] of Object.entries(node)) { if (['loc', 'start', 'end', 'comments', 'tokens', 'extra'].includes(key)) continue; if (Array.isArray(value)) value.forEach(item => walk(item, visitor, node, key)); else if (value && typeof value === 'object') walk(value, visitor, node, key); } } function analyze(edges, source) { const issues = []; const file = fileURLToPath(import.meta.url); const result = themeAnalysis({ workspace, postcss, walk, selectedFile: () => true, records: [{ file, edges, ast: parse(source, { sourceType: 'module' }) }], add: (severity, code, file, node, message) => issues.push({ severity, code, message }), }); return { ...result, issues }; } test('OrgChart public CSS and ?url collect the same scoped defaults', () => { const source = 'const values = "var(--hm-orgchart-current-gradient) var(--hm-orgchart-current-card-background) var(--hm-orgchart-current-border) var(--hm-orgchart-resolved-bg-surface)";'; const direct = analyze([{ spec: '@hm-design/org-chart/style.css', runtime: true }], source); const shadow = analyze([{ spec: '@hm-design/org-chart/style.css?url', runtime: false }], source); assert.deepEqual(direct.definitions, shadow.definitions); assert.deepEqual(shadow.issues, []); assert.equal(shadow.incomplete, false); assert.ok(shadow.references.every(ref => ref.status === 'PASS' && ref.origin === 'PUBLIC')); }); test('Disconnected OrgChart CSS cannot lend definitions to UI or another Story', () => { const source = 'const value = "var(--hm-orgchart-current-gradient, red)";'; for (const edges of [[], [{ spec: '@hm-design/ui/theme.css', runtime: true }], [{ spec: '@hm-design/org-chart/style.css?raw', runtime: false }]]) { assert.ok(analyze(edges, source).issues.some(issue => issue.code === 'TOKEN-001' && issue.severity === 'FAIL')); } }); test('Fallbacks and JS setProperty do not waive undeclared HM tokens', () => { const result = analyze([{ spec: '@hm-design/org-chart/style.css?url', runtime: false }], 'el.style.setProperty("--hm-fake", "red"); const value = "var(--hm-fake, red)";'); assert.ok(result.issues.some(issue => issue.code === 'TOKEN-001' && issue.severity === 'FAIL')); }); test('Shared UI declaration collection and unknown token policy remain intact', () => { const result = analyze([{ spec: '@hm-design/ui/theme.css', runtime: true }], 'const value = "var(--hm-sem-fg-primary) var(--hm-missing, blue)";'); assert.equal(result.references.find(ref => ref.name === '--hm-sem-fg-primary').status, 'PASS'); assert.equal(result.references.find(ref => ref.name === '--hm-missing').status, 'FAIL'); }); test('All seven CSS company defaults and six Fixed gradients match approved presets', () => { const css = fs.readFileSync(path.join(workspace, 'packages/org-chart/src/styles/color-presets.css'), 'utf8'); const presets = fs.readFileSync(path.join(workspace, 'packages/org-chart/src/company-color-presets.ts'), 'utf8'); for (const [id, color] of [['hanmac', '#CB2E28'], ['saman', '#F08300'], ['jangheon-industry', '#4580A6'], ['jangheon', '#1D418D'], ['ptc', '#84338E'], ['halla', '#0068B7'], ['baron', '#0C367F']]) { assert.ok(presets.includes(`primary: '${color}'`)); assert.ok(css.includes(`var(--hm-orgchart-company-${id}, ${color})`)); } const fixed = fs.readFileSync(path.join(workspace, 'packages/org-chart/src/core/style-presets.ts'), 'utf8'); for (const match of fixed.matchAll(/backgroundImage: '([^']+)'/g)) assert.ok(css.includes(match[1])); });