import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' import { ADMIN_THEME, ADMIN_THEME_KEYS, contrastRatio, isDarkSurface, relativeLuminance, tokenHex, } from '../src/styles/theme.ts' // module 13 task 241:蓝白浅色基线翻转。旧「深色侧栏 + indigo」token 需整体替换为 // admin.html 末层(L4478)生效值:浅色侧栏、蓝灰主色 #4f78a5、淡蓝内容底。 test('test_task_241_baseline_normal_primary_path', () => { // 正常主路径:token 命中 admin.html 蓝白浅色基线的关键 hex。 assert.equal(ADMIN_THEME.sidebar, '#f8fbfe', '侧栏顶色应为浅蓝白') assert.equal(ADMIN_THEME.sidebarDeep, '#edf3f9', '侧栏底/加深色应为浅蓝灰') assert.equal(ADMIN_THEME.primary, '#4f78a5', '主色应为蓝灰(非旧 indigo)') assert.equal(ADMIN_THEME.primarySoft, '#e7f0f8', '主色软底应为浅蓝') assert.equal(ADMIN_THEME.bg, '#f4f7fb', '内容底应为淡蓝') assert.equal(ADMIN_THEME.text, '#24384d', '正文应为深蓝灰') }) test('test_task_241_baseline_normal_variant_input', () => { // 正常变体:侧栏与内容区均为浅色(翻转核心语义),不再要求侧栏为暗。 assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false, '侧栏顶色必须为浅色') assert.equal(isDarkSurface(ADMIN_THEME.sidebarDeep), false, '侧栏底必须为浅色') assert.equal(isDarkSurface(ADMIN_THEME.bg), false, '内容区必须为浅色') assert.equal(isDarkSurface(ADMIN_THEME.border), false, '边框须为亮面中性色') }) test('test_task_241_baseline_normal_repeated_operation_is_idempotent', () => { // 正常重复:浅色判定与对比度幂等。 assert.equal(isDarkSurface(ADMIN_THEME.sidebar), isDarkSurface(ADMIN_THEME.sidebar)) assert.equal(relativeLuminance(ADMIN_THEME.bg), relativeLuminance(ADMIN_THEME.bg)) }) test('test_task_241_baseline_boundary_empty_input', () => { // 边界:token 键集合不变(防丢语义键),全部 #rrggbb。 assert.ok(ADMIN_THEME_KEYS.length >= 7, '必须覆盖侧栏/内容区/正文等核心语义面') for (const key of ADMIN_THEME_KEYS) { assert.match(ADMIN_THEME[key], /^#[0-9a-fA-F]{6}$/, `${key} 须为 #rrggbb`) } }) test('test_task_241_baseline_boundary_single_item', () => { // 边界单元素:侧栏与内容区仍是两个可区分的浅色面(不得同色)。 assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.bg, '侧栏与内容底必须可区分') assert.notEqual(ADMIN_THEME.sidebar, ADMIN_THEME.sidebarDeep) assert.equal(isDarkSurface(ADMIN_THEME.sidebar), false) }) test('test_task_241_baseline_boundary_limit_or_missing_field', () => { // 边界上限:正文/次要文本在浅色底上满足 WCAG AA;CSS 变量名映射稳定。 assert.ok(contrastRatio(ADMIN_THEME.text, ADMIN_THEME.bg) >= 4.5, '正文/内容区需 ≥4.5') assert.ok(contrastRatio(ADMIN_THEME.primary, ADMIN_THEME.bg) >= 3.0, '主色按钮/内容区 ≥3') assert.ok(contrastRatio(ADMIN_THEME.muted, ADMIN_THEME.bg) >= 3.0, '次要文本 ≥3') assert.equal(tokenHex('sidebar'), '#f8fbfe') }) test('test_task_241_baseline_invalid_input_rejected', () => { // 异常:theme.ts 不得残留旧 indigo 主色与深色侧栏值(防只改一半漂移)。 const ts = readSource('src/styles/theme.ts') assert.equal(/#6366f1/.test(ts), false, 'theme.ts 不得残留旧 indigo #6366f1') assert.equal(/#192132|#121827/.test(ts), false, 'theme.ts 不得残留旧深色侧栏') assert.throws(() => tokenHex('unknown' as never), /未知 Admin 主题 token/) }) test('test_task_241_baseline_dependency_failure_returns_actionable_message', () => { // 依赖失败:theme.ts 与 main.css 渲染变量交叉校验一致(防双源漂移)。 const css = readSource('src/styles/main.css') const missing = ADMIN_THEME_KEYS.filter((key) => { const tokenVar = `--admin-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}` return !css.includes(`${tokenVar}: ${ADMIN_THEME[key]}`) }) assert.deepEqual(missing, [], `theme.ts 与 main.css 不一致,缺失变量: ${missing.join(', ')}`) assert.equal(/@import/.test(css), false, 'main.css 不得 @import 旧后台整份样式') })