Files
work-hours-tracker/test/app-parity.test.js
T
OpenClaw Agent 2329955e32 baseline: 薪酬逻辑收口到 shared/payroll.js 后的当前全量状态
这是第一个包含完整工作区的提交,也是后续「拆 SFC / 上 Vite」的回滚基线。

此前的提交均为 index.html 单文件历史快照,由 .bak 文件重建。

当前状态(提交前已实测):
- index.html 内联薪酬算法已删除,改调 WHTPayroll
- shared/payroll.js 为唯一薪酬实现,浏览器与 Node 共用
- 对外接口未变:aggregateMonth / isCutEdge / payDayKind 等保留同名薄包装

未入库内容见 .gitignore:依赖、Chrome 验证 profile、验证截图、
运行时 sqlite 数据与日志、时间戳备份、.trash。
2026-09-05 14:35:00 +08:00

51 lines
2.4 KiB
JavaScript
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.
// 重构护栏:index.html 里跑出来的数 == shared/payroll.js 算出来的数。
//
// 这层网的用途只有一个:**证明前端可以安全地改用纯模块**。
// 现在 index.html 还带着自己那份内联算法(grep WHTPayroll 为 0 处),
// 两份实现并存期间,本文件保证它们逐字段等价;等 index.html 真的换成调 WHTPayroll,
// 这些断言会自动变成「同一份代码比自己」,届时连同 app-harness.js 一起删掉。
//
// 失败怎么读:
// 报「app 与 pure 不一致」→ 两份实现漂了。先确认哪份是对的,别急着改基准。
// 报「index.html 里没找到内联 <script>」→ 前端结构已变,看 app-harness.js 顶部说明。
'use strict';
const test = require('node:test');
const assert = require('node:assert/strict');
const { loadApp } = require('./lib/app-harness.js');
const { probeApp, probePure } = require('./lib/probe.js');
const { SCENARIOS, NOW } = require('./lib/scenarios.js');
/** 差异摘要:deepStrictEqual 的报文很长,先把「哪几个字段不一样」摘出来放最前面。 */
function diffKeys(a, b, path = '') {
const out = [];
const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]);
for (const k of keys) {
const p = path ? `${path}.${k}` : k;
const va = a ? a[k] : undefined;
const vb = b ? b[k] : undefined;
if (va && vb && typeof va === 'object' && typeof vb === 'object') out.push(...diffKeys(va, vb, p));
else if (JSON.stringify(va) !== JSON.stringify(vb)) out.push(`${p}: app=${JSON.stringify(va)} pure=${JSON.stringify(vb)}`);
}
return out;
}
for (const sc of SCENARIOS) {
test(`app==pure · ${sc.name}`, () => {
const { app } = loadApp({
records: sc.records, year: sc.year, month: sc.month,
settings: sc.settings, adjust: sc.adjust, now: NOW
});
const fromApp = probeApp(app);
const fromPure = probePure(sc, NOW);
const diff = diffKeys(fromApp, fromPure);
assert.deepStrictEqual(fromApp, fromPure, diff.length ? '字段差异:\n ' + diff.join('\n ') : undefined);
});
}
test('场景库不是空的(防止 SCENARIOS 被误清后整层网静默失效)', () => {
assert.ok(SCENARIOS.length >= 30, `场景数 ${SCENARIOS.length},少于 30 说明用例被删了`);
const names = SCENARIOS.map((s) => s.name);
assert.equal(new Set(names).size, names.length, '场景名重复,基准里会互相盖掉');
});