diff --git a/web/scripts/tokens-to-css.test.js b/web/scripts/tokens-to-css.test.js new file mode 100644 index 00000000..0190d81c --- /dev/null +++ b/web/scripts/tokens-to-css.test.js @@ -0,0 +1,43 @@ +import { describe, expect, test } from 'vitest'; +import { emit } from './tokens-to-css.js'; + +const sample = { + colors: { + dark: { obsidian: '#000', parchment: '#FFF' }, + light: { obsidian: '#FFF', parchment: '#000' }, + flat: { accent: '#4A6B5C', 'on-action': '#E8E4D8' } + }, + radii: { sm: '4px' }, + fontStacks: { + display: "'Fraunces', serif", + body: "'Inter', sans-serif", + mono: "'JetBrains Mono', monospace" + } +}; + +describe('tokens-to-css emit()', () => { + test('emits :root block with dark and flat tokens', () => { + const css = emit(sample); + expect(css).toMatch(/:root \{[\s\S]*--fs-obsidian: #000;[\s\S]*--fs-parchment: #FFF;[\s\S]*--fs-accent: #4A6B5C;[\s\S]*--fs-on-action: #E8E4D8;[\s\S]*\}/); + }); + + test('emits [data-theme="light"] block with light tokens only', () => { + const css = emit(sample); + expect(css).toMatch(/\[data-theme="light"\] \{[\s\S]*--fs-obsidian: #FFF;[\s\S]*--fs-parchment: #000;[\s\S]*\}/); + }); + + test('flat tokens are not duplicated into the light block', () => { + const css = emit(sample); + const match = css.match(/\[data-theme="light"\] \{([\s\S]*?)\}/); + expect(match).not.toBeNull(); + const lightBlock = match[1]; + expect(lightBlock).not.toContain('--fs-accent'); + expect(lightBlock).not.toContain('--fs-on-action'); + }); + + test('preserves radii and font stacks in :root', () => { + const css = emit(sample); + expect(css).toContain('--fs-radius-sm: 4px;'); + expect(css).toContain("--fs-font-display: 'Fraunces', serif;"); + }); +}); diff --git a/web/vitest.config.ts b/web/vitest.config.ts index b763a602..9a071c45 100644 --- a/web/vitest.config.ts +++ b/web/vitest.config.ts @@ -6,7 +6,7 @@ export default defineConfig({ plugins: [sveltekit(), svelteTesting()], test: { environment: 'jsdom', - include: ['src/**/*.test.ts', 'src/**/*.svelte.test.ts'], + include: ['src/**/*.test.ts', 'src/**/*.svelte.test.ts', 'scripts/**/*.test.js'], // M7 #374: these two test files explode at module-load with // `TypeError: notifiable_store is not a function` from SvelteKit's // internal client.js — the import chain triggers SvelteKit's client