64 lines
3.8 KiB
TypeScript
64 lines
3.8 KiB
TypeScript
import { act, cleanup, renderHook } from "@testing-library/react";
|
|
import { afterEach, expect, it } from "vitest";
|
|
import { useScadaEventAtmosphere, type ScadaEventTask } from "./use-scada-event-atmosphere";
|
|
import type { WorkbenchAlert } from "../types";
|
|
|
|
const task: ScadaEventTask = {
|
|
task_id: "task-1", project_id: "project-1", task_type: "event_investigation", workflow: "scada_anomaly",
|
|
lifecycle_status: "open", title: "监测事件", created_at: "2026-07-16T00:00:00Z",
|
|
source: { kind: "external_system", external_id: "scada:1", external_revision: 1, event_kind: "scada_anomaly",
|
|
replay_context: { replay_id: "replay-1", knowledge_cutoff: "2026-07-16T00:00:00Z", batch_sequence: 1 } }
|
|
};
|
|
const alert: WorkbenchAlert = { id: "incident-1", conditionId: "operational-task:task-1", title: "SCADA 指标异常",
|
|
time: "08:00", description: "调查已启动", source: { kind: "scada_replay", replayId: "replay-1", ruleId: "scada_diagnosis", batchSequence: 1 } };
|
|
afterEach(() => { cleanup(); sessionStorage.clear(); });
|
|
|
|
it("retains an event after sensor-alert recovery, human waiting and execution failure, until event completion", () => {
|
|
const { result, rerender } = renderHook(({ alerts, tasks }) => useScadaEventAtmosphere("project-1", alerts, tasks),
|
|
{ initialProps: { alerts: [alert], tasks: [task] } });
|
|
expect(result.current.atmosphere?.count).toBe(1);
|
|
rerender({ alerts: [], tasks: [{ ...task, lifecycle_status: "waiting_human" }] });
|
|
expect(result.current.atmosphere?.alert?.conditionId).toBe(alert.conditionId);
|
|
rerender({ alerts: [], tasks: [{ ...task, lifecycle_status: "failed" }] });
|
|
expect(result.current.atmosphere).not.toBeNull();
|
|
rerender({ alerts: [alert], tasks: [{ ...task, lifecycle_status: "completed" }] });
|
|
expect(result.current.atmosphere).toBeNull();
|
|
});
|
|
|
|
it("restores unfinished events on reload and remembers a manual dismissal for that event", () => {
|
|
const first = renderHook(() => useScadaEventAtmosphere("project-1", null, [task]));
|
|
expect(first.result.current.atmosphere).not.toBeNull();
|
|
act(() => first.result.current.dismissAtmosphere(first.result.current.atmosphere!.id));
|
|
expect(first.result.current.atmosphere).toBeNull();
|
|
first.unmount();
|
|
const next = renderHook(() => useScadaEventAtmosphere("project-1", [alert], [task]));
|
|
expect(next.result.current.atmosphere).toBeNull();
|
|
});
|
|
|
|
it("keeps other events visible when one ends and shows a new event after the earlier group was dismissed", () => {
|
|
const second = { ...task, task_id: "task-2" };
|
|
const { result, rerender } = renderHook(({ tasks }) => useScadaEventAtmosphere("project-1", [], tasks),
|
|
{ initialProps: { tasks: [task, second] } });
|
|
expect(result.current.atmosphere?.count).toBe(2);
|
|
rerender({ tasks: [{ ...task, lifecycle_status: "cancelled" }, second] });
|
|
expect(result.current.atmosphere?.count).toBe(1);
|
|
act(() => result.current.dismissAtmosphere(result.current.atmosphere!.id));
|
|
rerender({ tasks: [second, { ...task, task_id: "task-3" }] });
|
|
expect(result.current.atmosphere?.count).toBe(1);
|
|
expect(result.current.atmosphere?.alert?.conditionId).toBe("operational-task:task-3");
|
|
});
|
|
|
|
it("isolates project state and lets Dev end only the local preview", () => {
|
|
const { result, rerender } = renderHook(({ project, tasks }) => useScadaEventAtmosphere(project, [], tasks),
|
|
{ initialProps: { project: "project-1", tasks: [] as ScadaEventTask[] } });
|
|
act(() => result.current.previewAtmosphere());
|
|
expect(result.current.atmosphere?.preview).toBe(true);
|
|
act(() => result.current.endAtmospherePreview());
|
|
expect(result.current.atmosphere).toBeNull();
|
|
rerender({ project: "project-1", tasks: [task] });
|
|
act(() => result.current.endAtmospherePreview());
|
|
expect(result.current.atmosphere?.preview).toBe(false);
|
|
rerender({ project: "project-2", tasks: [task] });
|
|
expect(result.current.atmosphere).toBeNull();
|
|
});
|