Files
Jyotisha/frontend/tests/ui-kit-whitelist.test.ts
T
jesse-uxandGrok (agent loop) 689e01296e
Independent Staging Quality Gate / validate (push) Failing after 9m7s
Independent Staging Quality Gate / publish (push) Skipped
组件库收尾:删掉没人用的旧样式,检查名单只留绘制例外(#111)。
globals.css 从 7249 行减到 7011 行,删了 56 个已经没人用的类,16 个关键帧都还在用。手写控件名单从 9 个文件收到 2 个:天空绘制和校正顶栏探头。星盘和星图的图标豁免仍在规则里。设计说明改成以 shadcn base-nova 和 Motion 预设为准。页面用法没有再改。

自测:tsc --noEmit 退出码 0;frontend 下 npm run lint 0 error、122 warning;白名单、样式隔离、时间线和错误边界测试 18 条通过、失败 0、取消 0;npm run build 后 / 仍是 Static。首屏 JS gzip-9 相对 728ead88 从 644683 到 648993(+0.67%)。没有跑全量 npm test、Python 快速门和 test:db。

Co-Authored-By: Grok (agent loop)
2026-10-25 06:56:39 +08:00

79 lines
3.3 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 assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { fileURLToPath } from "node:url";
import { ESLint } from "eslint";
/**
* K7 ceiling. The list may still shrink. Adding one fails this test.
* 原值:K0 的 62 个文件。
* 新值:天空绘制和校正顶栏探头这两个文件。
* 原因:K7 清空白名单。星盘和星图的图标豁免在规则里,不在这份名单。
* 天空过场必须留着原生 dialog.showModal 和 createPortal,合同测试锁住这段,换组件会改绘制。
* 校正探头必须留着 createPortal 挂到顶栏,合同测试锁住这句;改成弹窗会改 K6 的交互。
*/
const BASELINE = [
"src/components/birth-sky/birth-sky-converge.tsx",
"src/components/rectification-agentic-chat.tsx",
] as const;
const RULE = "jyotisha-ui/no-handwritten-control";
const frontendRoot = fileURLToPath(new URL("..", import.meta.url));
const eslint = new ESLint({ cwd: frontendRoot });
const drawingSnippet = `export function Chart() {
return (
<div>
<button type="button">保存</button>
<span>☰</span>
<svg viewBox="0 0 10 10"><text>×</text></svg>
</div>
);
}
`;
async function ruleMessages(filename: string, code: string) {
const [result] = await eslint.lintText(code, { filePath: filename });
return (result?.messages ?? []).filter((message) => message.ruleId === RULE);
}
test("the handwritten-control whitelist can only shrink", () => {
const live = JSON.parse(readFileSync(new URL("../eslint/ui-primitive-whitelist.json", import.meta.url), "utf8")) as string[];
assert.deepEqual(live, [...live].sort());
assert.equal(new Set(live).size, live.length);
assert.ok(live.length <= BASELINE.length);
for (const file of live) assert.ok(BASELINE.includes(file as (typeof BASELINE)[number]), file);
});
test("a new file under src/components with a native button fails lint", async () => {
const messages = await ruleMessages(
"src/components/ui-kit-k0-probe.tsx",
`export function Probe() { return <button type="button">保存</button>; }\n`,
);
assert.ok(messages.some((message) => message.message.includes("<button>")));
});
test("src/components/ui is outside the handwritten-control rule", async () => {
const messages = await ruleMessages(
"src/components/ui/ui-kit-k0-probe.tsx",
`export function Probe() { return <button type="button">保存</button>; }\n`,
);
assert.equal(messages.length, 0);
});
test("chart and star-map SVG drawings are exempt from the icon rule only", async () => {
const ruleSource = readFileSync(new URL("../eslint/no-handwritten-control.mjs", import.meta.url), "utf8");
assert.match(ruleSource, /src\/components\/personal-report\/vedic-chart-svg\.tsx/);
assert.match(ruleSource, /src\/components\/chart-page\/western-wheel-svg\.tsx/);
for (const filename of [
"src/components/personal-report/vedic-chart-svg.tsx",
"src/components/chart-page/western-wheel-svg.tsx",
]) {
const messages = await ruleMessages(filename, drawingSnippet);
assert.ok(messages.some((message) => message.message.includes("<button>")), filename);
assert.ok(messages.some((message) => message.message.includes("☰")), filename);
assert.equal(messages.some((message) => message.message.includes("×")), false, filename);
}
});