Files
next-tjwater-frontend/src/features/workbench/operational-timeline/operational-timeline-model.test.ts
T

108 lines
3.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { ScheduledConditionItem } from "../types";
import {
canCenterTimelineBesideMapScale,
clusterOperationalEvents,
createOperationalEvents,
DEFAULT_OPERATIONAL_EVENT_VISIBILITY,
filterOperationalEvents,
OPERATIONAL_TIMELINE_LAYOUT,
resolveTimelineScaleLayout
} from "./operational-timeline-model";
const workOrder: ScheduledConditionItem = {
id: "work-1",
kind: "work_order",
code: "WO-1",
scheduledAt: "2026-08-19T10:00:00.000Z",
title: "阀门调整",
summary: "调整阀门",
status: "running",
riskLevel: "attention",
updatedAt: 1,
durationMinutes: 40,
source: "调度方案",
location: "边界阀门 BV-07",
dispatcher: "调度",
assignee: "班组",
priority: "urgent",
replyWindowMinutes: 20,
stages: [],
replyRequirements: []
};
describe("operational timeline model", () => {
it("does not reserve workspace layout space for the floating timeline", () => {
expect(OPERATIONAL_TIMELINE_LAYOUT.overlayReservedHeight).toBe(0);
});
it("creates linked plan and actual events for active work orders", () => {
const events = createOperationalEvents([workOrder]);
expect(events.map((event) => event.lane)).toEqual(["plan", "actual"]);
expect(events[0].correlationId).toBe(events[1].correlationId);
});
it("filters event lanes from the interactive legend", () => {
const events = createOperationalEvents([workOrder]);
const filtered = filterOperationalEvents(events, {
...DEFAULT_OPERATIONAL_EVENT_VISIBILITY,
actual: false
});
expect(filtered.map((event) => event.lane)).toEqual(["plan"]);
});
it("only promotes abnormal condition runs into exception events", () => {
const normal = {
id: "condition-normal",
kind: "condition",
taskId: "scada-diagnosis",
sessionId: "s-1",
scheduledAt: "2026-08-19T10:00:00.000Z",
title: "诊断",
summary: "正常",
status: "completed",
riskLevel: "normal",
updatedAt: 1
} satisfies ScheduledConditionItem;
const abnormal = {
...normal,
id: "condition-error",
status: "error",
riskLevel: "critical"
} satisfies ScheduledConditionItem;
expect(createOperationalEvents([normal])).toHaveLength(0);
expect(createOperationalEvents([abnormal])[0].lane).toBe("exception");
});
it("aggregates nearby events into ten minute lane buckets", () => {
const events = createOperationalEvents([workOrder]);
const nearbyPlan = {
...events[0],
id: "plan-nearby",
cursorTime: "2026-08-19T10:05:00.000Z"
};
const clusters = clusterOperationalEvents([...events, nearbyPlan], 10);
expect(clusters).toHaveLength(2);
expect(clusters.find((cluster) => cluster.lane === "plan")?.events).toHaveLength(2);
});
it("keeps a centered timeline beside scale info only when both side margins are wide enough", () => {
expect(canCenterTimelineBesideMapScale(2560, "expanded", true)).toBe(true);
expect(canCenterTimelineBesideMapScale(2048, "expanded", true)).toBe(false);
expect(canCenterTimelineBesideMapScale(1380, "expanded", false)).toBe(true);
});
it("keeps scale info in the map corner and moves the timeline when horizontal space is tight", () => {
expect(resolveTimelineScaleLayout(2560, "expanded", true)).toEqual({
scaleBottom: 0,
timelineBottom: 24
});
expect(resolveTimelineScaleLayout(2048, "expanded", true)).toEqual({
scaleBottom: 0,
timelineBottom: 56
});
});
});