import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { describe, expect, it } from 'vitest'; import { formatAppVersion, readPackageVersion } from './appVersion'; const srcRoot = join(import.meta.dirname, '..'); function readCss(rel: string): string { return readFileSync(join(srcRoot, rel), 'utf8'); } describe('appVersion', () => { it('prefixes package version with v', () => { expect(formatAppVersion(null)).toMatch(/^v\d+\.\d+\.\d+/); expect(formatAppVersion('2.3.4')).toBe('v2.3.4'); expect(formatAppVersion('v9.0.1')).toBe('v9.0.1'); }); it('reads injected build version', () => { expect(readPackageVersion()).toMatch(/^\d+\.\d+\.\d+/); }); }); describe('design tokens (DV regression)', () => { it('steampunk theme defines unified neon cyan token', () => { const css = readCss('styles/steampunk-theme.css'); expect(css).toMatch(/--neon-cyan:\s*#00e8f5/); expect(css).toMatch(/--neon-cyan-rgb:\s*0,\s*232,\s*245/); expect(css).not.toMatch(/#00f5ff|#00e5ff/); }); it('Pages.css avoids duplicate unscoped page-subtitle blocks', () => { const css = readCss('pages/Pages.css'); expect(css.match(/^\.page-subtitle \{/gm)?.length ?? 0).toBe(1); expect(css.match(/^\.header-count \{/gm)?.length ?? 0).toBe(1); }); it('wealth-deck.css drops dead sample badge selectors', () => { const css = readCss('styles/wealth-deck.css'); expect(css).not.toMatch(/sample-contrib|sample-activity|preview-deck-hint|chart-live\.sample/); }); it('SacredPageHeader CSS uses unified cyan rgba', () => { const css = readCss('components/Visual/sacredGeometry/SacredPageHeader.css'); expect(css).toMatch(/rgba\(0,\s*232,\s*245/); expect(css).not.toMatch(/245,\s*255|#00f5ff/); }); it('Path Tracer deck tokens align with operator-deck cyan accent', () => { const css = readCss('styles/operatorDeck.css'); const block = css.match(/\[data-operator-deck='pathtracer'\][\s\S]*?\}/)?.[0] ?? ''; expect(block).toContain('--deck-accent: var(--neon-cyan'); expect(block).not.toContain('#6b8cff'); }); it('steampunk theme documents optional light shell', () => { const css = readCss('styles/steampunk-theme.css'); expect(css).toMatch(/prefers-color-scheme:\s*light/); }); }); describe('legacy cyan sweep', () => { const cssFiles = [ 'styles/visual-polish.css', 'styles/sacred-geometry.css', 'pages/Pages.css', 'components/Charts/HashrateChart.css', 'components/Layout/Layout.css', ]; it.each(cssFiles)('%s does not use legacy #00f5ff token', (rel) => { const css = readCss(rel); expect(css).not.toMatch(/#00f5ff|#00e5ff/); }); });