/** * T09 预审工作台与沙盘界面源码守卫。 * * 这是**源码断言**,不是浏览器端到端测试(docs/v2/06-validation.md §2 已明确 * 现有 UI 脚本的性质)。它锁住的是可机械核对的约束:数值只能来自服务端、 * 演示值只在演示域、雷达只画 3–8 维、乱序响应丢弃、失败保留基线、 * 图的等价文字与键盘可达。 */ const assert = require("node:assert/strict"); const fs = require("node:fs"); const path = require("node:path"); const root = path.resolve(__dirname, ".."); const read = (file) => fs.readFileSync(path.join(root, file), "utf8"); const panel = read("src/components/xingbang/PrescreenPanel.tsx"); const lib = read("src/lib/prescreen-run.ts"); const workbench = read("src/app/xingbang-workbench/page.tsx"); // 1. 挂载在既有工作台内,不新建路由(04-ui-spec §1:不恢复已合并的旧页面) assert.match(workbench, /import PrescreenPanel from "@\/components\/xingbang\/PrescreenPanel"/, "工作台应导入预审面板"); assert.match(workbench, //, "空维度用空状态,不补六轴"); assert.match(panel, /文字列表与图等价/, "图必须有等价文字列表"); // 6. 乱序与过期响应必须丢弃(AC-31) assert.match(panel, /ticket !== seq\.current/, "旧请求晚到必须丢弃"); assert.match(panel, /returned !== \[\.\.\.keys\]\.sort\(\)\.join\(","\)/, "结果与当前选择不一致时必须丢弃"); // 7. 失败保留基线并标未完成;计算中不得先给乐观增幅 assert.match(panel, /该情景未完成。基线数值与原有意见保持不变/, "失败必须保留基线"); assert.match(panel, /不会先给一个乐观增幅/, "计算中不得预告结果"); assert.match(panel, /不是各项增量的简单相加/, "沙盘必须说明组合非加和"); // 8. 键盘可达与无障碍 assert.match(panel, /]*aria-label=/.test(panel.replace(/\n/g, " ")), "雷达必须有文字等价说明"); // 9. 来源与数据域用文字+图标共同区分,不靠颜色单独传意(AC-42) assert.match(lib, /text: "历史提炼"/); assert.match(lib, /text: "人工创建"/); assert.match(lib, /text: "制度导入"/); assert.match(lib, /icon: "history"/); assert.match(panel, /domain\.text/, "数据域必须以文字呈现"); // 10. 阶段是序数,不得换算成概率 assert.match(lib, /雷达面积不换算成百分比|STAGE_LEGEND/, "必须说明阶段与概率无关"); assert.ok(!/probability\s*[:=]\s*[0-9]/.test(panel), "面板不得写入任何概率数值"); console.log("prescreen-ui: PASS");