import { readFileSync } from 'fs' import { describe, expect, it } from 'vitest' import { CHART_CATEGORICAL, CHART_MEMORY, CHART_OTHER, CHART_SERIES, readChartColor, } from '../../packages/client/src/styles/chart-palette' const readClientFile = (path: string) => readFileSync(`packages/client/src/${path}`, 'utf8') function scssVar(source: string, name: string): string { const match = source.match(new RegExp(`\\$${name}:\\s*(#[0-9a-fA-F]{6});`)) if (!match) throw new Error(`missing SCSS variable $${name}`) return match[1].toLowerCase() } describe('chart palette single source', () => { const scss = readClientFile('styles/chart-colors.scss') it('keeps the TS series colors in sync with SCSS', () => { expect(CHART_SERIES.input).toBe(scssVar(scss, 'chart-input')) expect(CHART_SERIES.output).toBe(scssVar(scss, 'chart-output')) expect(CHART_SERIES.cache).toBe(scssVar(scss, 'chart-cache')) }) it('keeps all 12 categorical colors in sync with SCSS', () => { CHART_CATEGORICAL.forEach((color, index) => { expect(color.toLowerCase()).toBe(scssVar(scss, `chart-cat-${index + 1}`)) }) }) it('keeps the neutral colors in sync with SCSS', () => { expect(CHART_OTHER.toLowerCase()).toBe(scssVar(scss, 'chart-other')) expect(CHART_MEMORY.toLowerCase()).toBe(scssVar(scss, 'chart-memory')) }) it('resolves categorical colors by index with wraparound', () => { expect(readChartColor(0)).toBe(CHART_CATEGORICAL[0]) expect(readChartColor(CHART_CATEGORICAL.length)).toBe(CHART_CATEGORICAL[0]) expect(readChartColor(Number.NaN)).toBe(CHART_OTHER) }) it('keeps palette definitions out of consumer files', () => { // 色板只允许出现在 chart-colors.scss / chart-palette.ts,消费方一律引用。 for (const path of [ 'stores/hermes/usage.ts', 'views/hermes/SkillsUsageView.vue', 'views/hermes/JourneyView.vue', ]) { const hexes = readClientFile(path).match(/#[0-9a-fA-F]{6}\b/g) ?? [] expect(hexes, `${path} 不应再定义图表色`).toHaveLength(0) } }) })