import assert from 'node:assert/strict'; import test from 'node:test'; import { DOCTOR_MESSAGES, themeDoctor } from '../src/themeDoctor.ts'; import { sanitizeSpec } from '../src/themeSpec.ts'; const ids = (raw: unknown) => themeDoctor(sanitizeSpec(raw)).map(advice => advice.id); test('a theme made of tokens and component settings gets no advice', () => { assert.deepEqual(ids({ name: 'Clean', colors: { bg: '#000000', text: '#ffffff', accent: '#1d9bf0' }, components: { button: { radius: 4 } } }), []); }); test('free-form CSS is pointed at the lighter mechanism and its costs', () => { const css = [ 'a { border-radius: 0; }', '.x { display: none; }', '.y { color: #ff0000; }', '.z { font-family: Comic Sans MS; }', 'div:has(img) { opacity: .9; }', '* { box-sizing: border-box; }', '.g { backdrop-filter: blur(8px); }', 'button:focus { outline: none; }', '.m { transition: all .2s; }', '.b { box-shadow: inset 1px 1px #fff, inset -1px -1px #000, inset 2px 2px #ddd; }', ].join('\n'); const found = ids({ name: 'Heavy', css }); for (const id of ['css-radius', 'css-hide', 'css-color', 'css-font', 'css-has', 'css-universal', 'css-blur', 'css-outline-none', 'css-motion', 'css-bevel']) { assert.ok(found.includes(id), `${id} is reported`); } }); test('advice is a hint, never a change: the theme is left as it was', () => { const theme = sanitizeSpec({ name: 'Keep', css: '.x { display: none; }' }); const before = JSON.stringify(theme); themeDoctor(theme); assert.equal(JSON.stringify(theme), before); }); test('unreadable colors are named with their contrast', () => { const advice = themeDoctor(sanitizeSpec({ name: 'Low', colors: { bg: '#000000', text: '#111111', accent: '#1d9bf0' } })); const text = advice.find(item => item.id === 'contrast-text'); assert.ok(text, 'text contrast is reported'); assert.equal(text.kind, 'contrast'); assert.match(text.text, /\d\.\d\d:1/); }); test('every finding says where it is (token or selector), what it found and how to fix it', () => { const css = '.card { border-radius: 4px; }\n.row { border-radius: 2px; }\n.row { border-radius: 3px; }\n@media (min-width: 1px) { .nav a { display: none; } }\n* { margin: 0 }'; const theme = sanitizeSpec({ name: 'Where', css, colors: { bg: '#000000', text: '#111111', accent: '#1d9bf0' }, components: { postText: { fontSize: 13 } }, variants: { nav: 'top' } }); const found = themeDoctor(theme); const radius = found.filter(item => item.id === 'css-radius').map(item => item.where); assert.deepEqual(radius, ['.card { border-radius }', '.row { border-radius }'], 'one finding per rule, not per property'); assert.ok(found.some(item => item.id === 'css-hide' && item.where === '.nav a { display }'), 'a rule inside @media names its own selector'); assert.ok(found.some(item => item.id === 'css-universal')); const token = found.find(item => item.id === 'contrast-text')!; assert.equal(token.where, 'colors.text'); assert.equal(token.key, 'contrast'); assert.deepEqual(Object.keys(token.params), ['token', 'ratio']); assert.match(token.text, /^The text color was adjusted/); assert.ok(found.some(item => item.where === 'components.postText.fontSize' && item.params.size === '13')); assert.ok(found.some(item => item.where === 'variants.nav' && item.params.position === 'top')); for (const item of found) { assert.ok(item.fix.length > 10, `${item.id} has a fix hint`); assert.ok(`wb.doctor.${item.key}.found` in DOCTOR_MESSAGES && `wb.doctor.${item.key}.fix` in DOCTOR_MESSAGES, `${item.key} has catalog words`); assert.ok(!/\{\w+\}/.test(item.text + item.fix), `${item.id}: every slot is filled`); } }); test('the doctor\'s words are plain text with only the slots its findings fill', () => { for (const [key, text] of Object.entries(DOCTOR_MESSAGES)) { assert.ok(!/[<>]/.test(text), `${key} has no markup`); for (const slot of text.matchAll(/\{(\w+)\}/g)) assert.ok(['token', 'ratio', 'size', 'position'].includes(slot[1]!), `${key}: {${slot[1]}}`); } const ids = new Set(Object.keys(DOCTOR_MESSAGES).map(key => key.replace(/\.(found|fix)$/, ''))); for (const id of ids) assert.ok(`${id}.found` in DOCTOR_MESSAGES && `${id}.fix` in DOCTOR_MESSAGES, id); });