Files
writer-work-flow/apps/web/lib/ui/themeParity.test.ts
Yaojia Wang fd6551b0ba feat(ui): 稳健跨 OS 衬线兜底栈 + 双主题 token 奇偶校验
- 决策:不自托管 CJK webfont(数 MB + 构建期外拉,违反 CSP 友好/首屏)
  改用跨 OS 系统衬线栈,保证中文标题可靠命中衬线,零 webfont 字节
- 新增 themeParity 测试:paper/night 两块 --color-* 必须成对,防漏配破面
2026-07-11 07:18:23 +02:00

43 lines
1.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
// 双主题 token 奇偶校验P1-8paper 与 night 两块的 --color-* 变量必须成对,
// 任一模式漏配某个颜色 token 都会让该主题回退为无效变量而破面——此测试提前拦截。
const cssPath = fileURLToPath(
new URL("../../app/globals.css", import.meta.url),
);
const css = readFileSync(cssPath, "utf8");
function colorVarsInBlock(selector: string): Set<string> {
const start = css.indexOf(selector);
if (start === -1) {
throw new Error(`未找到主题块选择器:${selector}`);
}
const open = css.indexOf("{", start);
const close = css.indexOf("}", open);
const body = css.slice(open + 1, close);
const names = body.match(/--color-[\w-]+/g) ?? [];
return new Set(names);
}
describe("theme token parity", () => {
const paper = colorVarsInBlock('[data-theme="paper"]');
const night = colorVarsInBlock('[data-theme="night"]');
it("defines at least the core palette in each theme", () => {
expect(paper.size).toBeGreaterThan(10);
expect(night.size).toBeGreaterThan(10);
});
it("keeps paper and night color tokens paired (no theme drops a --color-* var)", () => {
const missingInNight = [...paper].filter((name) => !night.has(name));
const missingInPaper = [...night].filter((name) => !paper.has(name));
expect(missingInNight, "night 缺失paper 有)").toEqual([]);
expect(missingInPaper, "paper 缺失night 有)").toEqual([]);
});
});