src/vendor 语义是'第三方/参考',却装着线上氢能看板并反向 import
src/modules,依赖方向倒挂。本次把生产代码归位并建立氢能分层:
src/modules/energy/hydrogen/
index.tsx 导航入口(原 HydrogenModule.tsx)
api.ts types.ts HTTP 与 DTO
dev-mock-api.ts 仅 dev 的 vite 插件(仍由 vite.config.ts 引用)
model/ 纯逻辑(format / bearing-labels / daily-detail-format)
board/ 经营看板 UI(原 vendor energy-h2-bi-board)
station-daily/ 单站日报 UI(原 vendor energy-h2-station-daily)
drill/ 下钻弹层 UI(原 hydrogen-bi-v2 的 UI 部分)
common/ 工具与共享组件(原 vendor common + prototype-download)
fonts/ JetBrains Mono(原 vendor resources/design-system)
- UI 与 CSS 逐字节保留,仅移动位置与改写相对 import。
- 删除自挂载原型入口后遗留的 2 个 annotation-source.json。
- vite.config.ts 与 tsconfig 的路径/exclude 同步清理。
- independent-entry.test.ts 不再硬编码路径,改为断言'两个入口解析到同一文件'。
导入改写用一次性 codemod 完成(按旧位置解析、按新位置重写),
lint / test(131) / build 全绿,可达性分析仍为 0 未引用文件。
53 lines
2.3 KiB
TypeScript
53 lines
2.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import { createElement } from "react";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { DailyBranchState, DailyTreeButton } from "./daily-detail-controls";
|
|
import { dailySummaryRows, formatDailyChange } from "../model/daily-detail-format";
|
|
import type { H2BiDailyResponse } from "../types";
|
|
|
|
test("每日环比区分真实零值、缺失值和非数值", () => {
|
|
for (const value of [null, undefined, NaN, Infinity, "0"]) assert.equal(formatDailyChange(value), "环比 —");
|
|
assert.equal(formatDailyChange(0), "环比 0.00%");
|
|
assert.equal(formatDailyChange(-12.5), "环比 -12.50%");
|
|
assert.equal(formatDailyChange(12.5), "环比 +12.50%");
|
|
});
|
|
|
|
test("日期汇总导出包含全部日期,不依赖已展开明细并保留真零", () => {
|
|
const daily = {
|
|
kpis: { stationCount: 2, totalKg: 15.25, totalCost: 450 },
|
|
days: [
|
|
{ date: "2026-09-03", stationCount: 2, kg: 15.25, cost: 450 },
|
|
{ date: "2026-09-02", stationCount: 0, kg: 0, cost: 0 },
|
|
],
|
|
} as H2BiDailyResponse;
|
|
const rows = dailySummaryRows(daily);
|
|
assert.equal(rows.length, 4);
|
|
assert.deepEqual(rows[1], ["区间合计", 2, 15.25, 450]);
|
|
assert.deepEqual(rows[3], ["2026-09-02", 0, 0, 0]);
|
|
});
|
|
|
|
test("层级按钮包含键盘原生语义与明确展开状态", () => {
|
|
for (const open of [false, true]) {
|
|
const html = renderToStaticMarkup(createElement(DailyTreeButton, {
|
|
open, label: "测试站客户明细", children: "测试站", onClick() {},
|
|
}));
|
|
assert.match(html, /<button type="button"/);
|
|
assert.ok(html.includes(`aria-expanded="${open}"`));
|
|
assert.ok(html.includes(`${open ? "收起" : "展开"}测试站客户明细`));
|
|
}
|
|
});
|
|
|
|
test("展开状态区分加载、空数据、失败及重试入口", () => {
|
|
const render = (props: Partial<Parameters<typeof DailyBranchState>[0]>) => renderToStaticMarkup(createElement(DailyBranchState, {
|
|
columns: 3, onRetry() {}, ...props,
|
|
}));
|
|
assert.match(render({}), /正在加载明细/);
|
|
assert.match(render({ empty: true }), /当前范围暂无明细/);
|
|
const error = render({ error: "站点明细加载失败,请重试" });
|
|
assert.match(error, /role="alert"/);
|
|
assert.match(error, /<button type="button"/);
|
|
assert.match(error, /colSpan="3"/i);
|
|
assert.doesNotMatch(error, /正在加载明细/);
|
|
});
|