feat(charts): manage investigation charts in a scrolling workspace
Generic Container CI/CD / test-build-publish (push) Successful in 2m7s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m8s

This commit is contained in:
2026-11-10 17:56:23 +08:00
parent 865491cb23
commit 0ae0a41e61
80 changed files with 4672 additions and 470 deletions
@@ -0,0 +1,115 @@
import { describe, expect, it } from "vitest";
import { groupInvestigationCharts } from "./investigation-chart-groups";
import type { InvestigationChart } from "../hooks/use-investigation-presentation";
const comparison = (id: string, metric = "压力", unit = "m", date = "2026-07-16"): InvestigationChart => ({
action: "open_chart", chart_id: `chart_tc_${id}`, evidence_refs: [`evidence:${id}`],
chart: { title: `${id} ${metric}七日同时刻对照(${unit})`, chart_type: "time_comparison",
x_axis_name: "时间", y_axis_name: `${metric}(${unit})`, x_data: ["07:45", "08:00"],
series: [
{ name: `${id} 当前 ${date}`, type: "line", data: [30, 25], sample_times: [`${date}T07:45:00+08:00`, `${date}T08:00:00+08:00`] },
{ name: `${id} 2026-07-09`, type: "scatter", data: [null, 32], sample_times: [null, "2026-07-09T08:00:00+08:00"] }
] }
});
const legacyComparison = (id: string, metric = "压力", unit = "m", date = "2026-07-16") => {
const command = comparison(id, metric, unit, date);
command.chart.title = `${id} ${metric}三小时趋势与七天同期对照(${unit})`;
command.chart.series[0].name = `${id} ${date.slice(5)}(${unit})`;
command.chart.series[1].name = "07-09";
return command;
};
describe("investigation comparison groups", () => {
it("groups pressure objects and pipe flow objects separately without changing data or evidence", () => {
const charts = [comparison("170486"), comparison("175618"), comparison("542017", "管流", "m³/h"), comparison("486033", "管流", "m³/h")];
const groups = groupInvestigationCharts(charts);
expect(groups).toHaveLength(2);
expect(groups[0].items.map((item) => item.label)).toEqual(["170486", "175618"]);
expect(groups[1].items.map((item) => item.label)).toEqual(["542017", "486033"]);
expect(groups[0].title).toBe("压力七日同时刻对照(m)");
expect(groups[0].items[1].command).toBe(charts[1]);
expect(charts[1].chart.series[1].data).toEqual([null, 32]);
});
it("groups saved pressure and flow charts whose titles put the metric before the object ID", () => {
const charts = [["170486", "压力", "m"], ["175618", "压力", "m"], ["541061", "流量", "m³/h"], ["542017", "流量", "m³/h"]].map(([id, metric, unit]) => {
const command = comparison(id, metric, unit);
command.chart.title = `${metric}测点 ${id} 当前曲线与前七天同期对比(${unit})`;
return command;
});
const groups = groupInvestigationCharts(charts);
expect(groups.map((group) => ({ title: group.title, objects: group.items.map((item) => item.label) }))).toEqual([
{ title: "压力当前曲线与前七天同期对比(m)", objects: ["170486", "175618"] },
{ title: "流量当前曲线与前七天同期对比(m³/h)", objects: ["541061", "542017"] }
]);
});
it("recognizes the same explicit object comparison in all three saved title formats", () => {
const charts = [comparison("170486"), comparison("175618"), comparison("159673")];
charts[0].chart.title = "170486 压力三小时与历史对照(m)";
charts[1].chart.title = "测点 175618 压力三小时与历史对照(m)";
charts[2].chart.title = "压力测点 159673 三小时与历史对照(m)";
const groups = groupInvestigationCharts(charts);
expect(groups).toHaveLength(1);
expect(groups[0].title).toBe("压力三小时与历史对照(m)");
expect(groups[0].items.map((item) => item.label)).toEqual(["170486", "175618", "159673"]);
});
it("groups saved date-only historical labels without rewriting commands, readings or evidence", () => {
const charts = [legacyComparison("170486"), legacyComparison("175618")];
charts[1].chart.series[1].data[1] = 0;
const original = structuredClone(charts);
const groups = groupInvestigationCharts(charts);
expect(groups).toHaveLength(1);
expect(groups[0].items.map((item) => item.label)).toEqual(["170486", "175618"]);
groups[0].items.forEach((item, index) => {
expect(item.command).toBe(charts[index]);
expect(item.command.chart.series).toBe(charts[index].chart.series);
expect(item.command.chart.series[1].sample_times).toBe(charts[index].chart.series[1].sample_times);
expect(item.command.evidence_refs).toBe(charts[index].evidence_refs);
});
expect(charts).toEqual(original);
});
it("keeps legacy comparisons with different units, windows or current dates separate", () => {
const shifted = legacyComparison("175618");
shifted.chart.x_data = ["08:00", "08:15"];
shifted.chart.series[0].sample_times = ["2026-07-16T08:00:00+08:00", "2026-07-16T08:15:00+08:00"];
shifted.chart.series[1].sample_times = [null, "2026-07-09T08:15:00+08:00"];
const charts = [legacyComparison("170486"), legacyComparison("P2", "压力", "MPa"), shifted, legacyComparison("P3", "压力", "m", "2026-07-17")];
expect(groupInvestigationCharts(charts)).toHaveLength(charts.length);
});
it("leaves ambiguous or mixed-object legacy charts and unproven historical dates separate", () => {
const ambiguous = legacyComparison("P2"); ambiguous.chart.title = "压力测点历史对照";
const foreign = legacyComparison("P3"); foreign.chart.series[1].name = "P4 07-09";
const mixed = legacyComparison("P4"); mixed.chart.series.push({ ...mixed.chart.series[0], name: "P5 当前 2026-07-16" });
const missing = legacyComparison("P5"); delete missing.chart.series[1].sample_times;
const mismatched = legacyComparison("P6"); mismatched.chart.series[1].sample_times = [null, "2026-07-10T08:00:00+08:00"];
const empty = legacyComparison("P7"); empty.chart.series[1].data = [null, null]; empty.chart.series[1].sample_times = [null, null];
const sameDateInUtc8 = legacyComparison("P8"); sameDateInUtc8.chart.series[1].sample_times = [null, "2026-07-09T00:00:00Z"];
const charts = [legacyComparison("170486"), ambiguous, foreign, mixed, missing, mismatched, empty, sameDateInUtc8];
const groups = groupInvestigationCharts(charts);
expect(groups).toHaveLength(charts.length - 1);
expect(groups[0].items.map((item) => item.label)).toEqual(["170486", "P8"]);
});
it("keeps incompatible units, sampling windows, comparison dates and ordinary trends separate", () => {
const base = comparison("170486");
const window = comparison("175618"); window.chart.x_data = ["08:00", "08:15"];
const trend = comparison("159673"); trend.chart.series = [trend.chart.series[0]];
expect(groupInvestigationCharts([base, comparison("P2", "压力", "MPa"), comparison("P3", "压力", "m", "2026-07-17"), window, trend])).toHaveLength(5);
const noTimes = [comparison("P4"), comparison("P5", "压力", "m", "2026-07-17")];
for (const command of noTimes) for (const series of command.chart.series) delete series.sample_times;
expect(groupInvestigationCharts(noTimes)).toHaveLength(2);
});
it("keeps group identity stable as objects arrive or are closed and leaves ambiguous titles alone", () => {
const first = comparison("170486"), second = comparison("175618");
expect(groupInvestigationCharts([first])[0].id).toBe(groupInvestigationCharts([second, first])[0].id);
const ambiguous = comparison("159673"); ambiguous.chart.title = "压力同期对照";
expect(groupInvestigationCharts([first, ambiguous])).toHaveLength(2);
expect(groupInvestigationCharts([])).toEqual([]);
});
});