import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile, readdir } from 'node:fs/promises'; import { palette, references, themes, themeNames, getTheme } from '../src/tokens.mjs'; const file = name => readFile(new URL(`../${name}`, import.meta.url)); const json = async name => JSON.parse(await file(name)); const luminance = hex => hex.match(/[\da-f]{2}/gi).map(v => parseInt(v, 16) / 255) .map(v => v <= .04045 ? v / 12.92 : ((v + .055) / 1.055) ** 2.4) .reduce((a, v, i) => a + v * [.2126, .7152, .0722][i], 0); const contrast = (a, b) => (Math.max(luminance(a), luminance(b)) + .05) / (Math.min(luminance(a), luminance(b)) + .05); test('four production themes resolve every palette reference and expose the same contract', () => { assert.deepEqual([...themeNames].sort(), ['canvas-dark', 'canvas-light', 'home-dark', 'home-light']); const keys = Object.keys(references['home-light']).sort(); for (const name of themeNames) { assert.deepEqual(Object.keys(references[name]).sort(), keys); for (const ref of Object.values(references[name])) assert.ok(Object.hasOwn(palette, ref), `${name}: ${ref}`); for (const key of ['card', 'popover', 'accent', 'input', 'ring', 'destructive', 'chart-5', 'sidebar', 'sidebar-ring']) assert.ok(keys.includes(key)); assert.equal(themes[name]['accent'], themes[name].selection); assert.notEqual(themes[name].accent, themes[name]['brand-mocha']); } assert.equal(getTheme('canvas-light').background, '#FAFAFA'); assert.equal(getTheme('canvas-dark').background, '#141414'); assert.throws(() => getTheme('canvas-dark-proposed'), RangeError); assert.throws(() => getTheme('toString'), RangeError); }); test('committed CSS and Tailwind bridge match the canonical tokens', async () => { const css = (await file('src/themes.css')).toString(); const bridge = (await file('src/tailwind.css')).toString(); for (const [key, value] of Object.entries(palette)) assert.ok(css.includes(`--el-${key}: ${value};`), key); for (const name of themeNames) { const marker = `[data-theme="${name}"]`; const block = css.slice(css.indexOf(marker)).split('}')[0]; for (const [key, ref] of Object.entries(references[name])) assert.ok(block.includes(`--${key}: var(--el-${ref});`), `${name}/${key}`); } for (const key of Object.keys(references['home-light'])) { if (!['radius', 'shadow', 'scrim'].includes(key)) assert.ok(bridge.includes(`--color-${key}: var(--${key});`), key); } assert.ok(css.includes(':root:not([data-theme]):not(.dark)')); assert.ok(bridge.includes('@theme inline')); }); test('semantic foreground/background pairs and required controls have sufficient contrast', () => { for (const [name, t] of Object.entries(themes)) { for (const base of ['primary', 'secondary', 'muted', 'accent', 'card', 'popover', 'sidebar', 'sidebar-primary', 'sidebar-accent', 'destructive']) { assert.ok(contrast(t[base], t[`${base}-foreground`]) >= 4.5, `${name}/${base}`); } assert.ok(contrast(t.background, t.foreground) >= 4.5, name); for (const state of ['primary-hover', 'primary-active']) assert.ok(contrast(t[state], t['primary-foreground']) >= 4.5, `${name}/${state}`); for (const fg of ['input', 'ring']) for (const bg of ['background', 'card']) assert.ok(contrast(t[fg], t[bg]) >= 3, `${name}/${fg}/${bg}`); } }); test('all eight SVG variants preserve the approved paths and reference colors', async () => { const geometry = await json('tokens/geometry.json'); const logo = await json('tokens/logo.json'); for (const filename of await readdir(new URL('../assets/svg/', import.meta.url))) { const svg = (await file(`assets/svg/${filename}`)).toString(); assert.equal((svg.match(/ { const manifest = await json('assets/manifest.json'); for (const variant of Object.keys(manifest.variants)) for (const size of manifest.png_sizes) { const png = await file(`assets/png/${variant}/endoleap-${size}.png`); assert.equal(png.subarray(1, 4).toString(), 'PNG'); assert.equal(png.readUInt32BE(16), size); assert.equal(png.readUInt32BE(20), size); } for (const [name, variant] of [['favicon.ico', 'icon-dark'], ['favicon-transparent.ico', 'logo-light'], ['favicon-light.ico', 'icon-light']]) { const ico = await file(`assets/icons/${name}`); assert.equal(ico.readUInt16LE(0), 0); assert.equal(ico.readUInt16LE(2), 1); assert.equal(ico.readUInt16LE(4), manifest.ico_sizes.length); for (const [i, size] of manifest.ico_sizes.entries()) { const p = 6 + i * 16; assert.equal(ico[p] || 256, size); assert.equal(ico[p + 1] || 256, size); const length = ico.readUInt32LE(p + 8), offset = ico.readUInt32LE(p + 12); assert.deepEqual(ico.subarray(offset, offset + length), await file(`assets/png/${variant}/endoleap-${size}.png`)); } } });