import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { createHash } from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { performance } from 'node:perf_hooks'; import { parse } from '@babel/parser'; import postcss from 'postcss'; import { themeAnalysis } from '../../scripts/guard-theme-analysis.mjs'; const root = fileURLToPath(new URL('../../', import.meta.url)); const script = fileURLToPath(import.meta.url); const rawFile = path.join(root, 'test-results/org-chart-p1/guard-storybook-menu-recheck.json'); const output = path.join(root, 'test-results/org-chart-quality/guard-reproduction.json'); const started = performance.now(); const hash = file => createHash('sha256').update(fs.readFileSync(file)).digest('hex'); const rawHashBefore = hash(rawFile); const raw = JSON.parse(fs.readFileSync(rawFile, 'utf8')); const analyzerHashBefore = hash(path.join(root, 'scripts/guard-theme-analysis.mjs')); 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); } } /** In-memory fixtures use the function's existing injected parser interface. * The script itself is a readable CSS-file sentinel because the real analyzer * reads files before invoking its parser. The parser returns the supplied * fixture CSS AST; no fixture files or production code are changed. */ function analyze(label, jsSource, cssSource) { const jsFile = `memory://${label}.js`; const records = [{ file: jsFile, edges: [], ast: parse(jsSource, { sourceType: 'module' }) }]; if (cssSource !== undefined) records.push({ file: script, edges: [], css: true }); const issues = []; const result = themeAnalysis({ records, workspace: root, postcss: { parse: () => postcss.parse(cssSource ?? '', { from: `memory://${label}.css` }) }, walk, selectedFile: () => true, add: (severity, code, file, node, message) => issues.push({ severity, code, file: file === script ? `memory://${label}.css` : file, line: node?.loc?.start?.line ?? node?.source?.start?.line ?? 1, message, }), }); return { label, fixture: { js: jsSource, css: cssSource ?? null }, analyzerStatus: issues.some(issue => issue.severity === 'FAIL') ? 'FAIL' : issues.length ? 'NOT VERIFIED' : 'PASS', definitions: result.definitions, references: result.references.map(ref => ({ ...ref, file: ref.file === script ? `memory://${label}.css` : ref.file, })), dynamic: result.dynamic, incomplete: result.incomplete, issues, }; } const measurements = []; const assertions = []; function check(name, condition) { assertions.push({ name, status: condition ? 'PASS' : 'FAIL' }); assert.ok(condition, name); } let failure; try { const graphSource = 'const background = "var(--hm-orgchart-resolved-bg-surface)";'; const excluded = analyze('shadow-css-excluded', graphSource); const included = analyze('shadow-css-included', graphSource, '.hm-orgchart-shell { --hm-orgchart-resolved-bg-surface: #ffffff; }'); measurements.push(excluded, included); check('Excluded Shadow CSS reference reproduces TOKEN-001 FAIL', excluded.issues.some(issue => issue.code === 'TOKEN-001' && issue.severity === 'FAIL')); check('Connected CSS declaration resolves the identical reference', included.analyzerStatus === 'PASS' && included.definitions.includes('--hm-orgchart-resolved-bg-surface')); const setterSource = 'element.style.setProperty("--hm-orgchart-current-border", "#194d82");\n' + 'element.style.setProperty("--hm-orgchart-accent", "var(--hm-orgchart-current-border)");'; const setterOnly = analyze('set-property-not-collected', setterSource); const setterWithCss = analyze('set-property-with-css-declaration', setterSource, '.hm-orgchart-group { --hm-orgchart-current-border: #194d82; }'); measurements.push(setterOnly, setterWithCss); check('Actual literal setProperty definition is absent from analyzer definitions', !setterOnly.definitions.includes('--hm-orgchart-current-border') && setterOnly.analyzerStatus === 'FAIL'); check('CSS definition resolves setProperty reference without changing JS fixture', setterWithCss.analyzerStatus === 'PASS'); const optional = analyze('optional-public-input-with-fallback', '', '.hm-orgchart-shell { --hm-orgchart-resolved-fg-primary: var(--hm-orgchart-fg-primary, #10243b); }'); measurements.push(optional); const optionalReference = optional.references.find(ref => ref.name === '--hm-orgchart-fg-primary'); check('Optional standalone input with valid fallback is still classified TOKEN-001 FAIL', optionalReference?.fallback === true && optionalReference.status === 'FAIL' && optional.issues.some(issue => issue.code === 'TOKEN-001' && issue.severity === 'FAIL')); // Diagnostic namespace control only. Renaming OrgChart inputs is NOT a fix. const otherNamespace = analyze('external-input-namespace-control', '', '.content { --resolved-fg-primary: var(--external-fg-primary, #10243b); }'); measurements.push(otherNamespace); check('Same fallback shape outside HM namespace is NOT VERIFIED, not FAIL', otherNamespace.analyzerStatus === 'NOT VERIFIED' && otherNamespace.references[0]?.fallback === true); check('Historical raw report remains FAIL with the original three TOKEN-001 findings', raw.status === 'FAIL' && raw.staticStatus === 'FAIL' && raw.issues.filter(issue => issue.code === 'TOKEN-001').length === 3); check('Original Guard output and analyzer source hashes remain unchanged', hash(rawFile) === rawHashBefore && hash(path.join(root, 'scripts/guard-theme-analysis.mjs')) === analyzerHashBefore); } catch (error) { failure = { name: error.name, message: error.message }; } const report = { schemaVersion: 1, purpose: 'Minimal reproduction of existing analyzer behavior; assertion PASS is NOT OrgChart quality or Guard PASS', reproductionStatus: failure ? 'FAIL' : 'PASS', generatedAt: new Date().toISOString(), durationMs: Math.round((performance.now() - started) * 100) / 100, assertions, measurements, originalGuard: { file: 'test-results/org-chart-p1/guard-storybook-menu-recheck.json', sha256Before: rawHashBefore, sha256After: hash(rawFile), status: raw.status, staticStatus: raw.staticStatus, relevantIssues: raw.issues.filter(issue => ['TOKEN-001', 'TOKEN-002'].includes(issue.code)), }, analyzer: { file: 'scripts/guard-theme-analysis.mjs', sha256Before: analyzerHashBefore, sha256After: hash(path.join(root, 'scripts/guard-theme-analysis.mjs')) }, scope: 'In-memory fixtures passed directly to the actual exported themeAnalysis; no full Guard suite or browser checks', limitations: [ 'This demonstrates analyzer limitations; it does not implement a revised Guard policy', 'Fixture graph inclusion is controlled directly; real Shadow URL dataflow is not implemented or rerun', 'The CSS declaration control is a fixture, not a production dummy declaration proposal', 'The external namespace control is diagnostic; production namespace renaming would be a bypass', 'Original Guard FAIL remains unchanged; runtime evidence cannot overwrite that FAIL', 'Common Guard changes require separate user approval', ], ...(failure ? { failure } : {}), }; fs.mkdirSync(path.dirname(output), { recursive: true }); fs.writeFileSync(output, `${JSON.stringify(report, null, 2)}\n`); console.log(JSON.stringify({ reproductionStatus: report.reproductionStatus, assertions: assertions.length, measurements: measurements.length, originalGuardStatus: raw.status, output: path.relative(root, output) })); if (failure) process.exitCode = 1;