feat(workbench): expand SCADA monitoring and investigation presentation
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { act, cleanup, renderHook } from "@testing-library/react";
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
import { dismissMapNotice, showMapNotice } from "@/features/map/core";
|
||||
import type { WorkbenchAlert } from "../types";
|
||||
import { useScadaAlertNotices } from "./use-scada-alert-notices";
|
||||
|
||||
vi.mock("@/features/map/core", () => ({ showMapNotice: vi.fn(), dismissMapNotice: vi.fn() }));
|
||||
const alert: WorkbenchAlert = {
|
||||
id: "incident-1", title: "SCADA 指标异常", description: "已启动调查,详细诊断见调查报告。",
|
||||
time: "08:00", severity: "warning", conditionId: "operational-task:task-1",
|
||||
source: { kind: "scada_replay", replayId: "replay-1", ruleId: "scada_diagnosis", batchSequence: 2 }
|
||||
};
|
||||
afterEach(() => { cleanup(); sessionStorage.clear(); vi.clearAllMocks(); });
|
||||
|
||||
it("restores old notifications quietly and announces each newly persisted SCADA incident once", () => {
|
||||
const onOpen = vi.fn();
|
||||
const { result, rerender } = renderHook(({ alerts }: { alerts: WorkbenchAlert[] | null }) =>
|
||||
useScadaAlertNotices("project-1", alerts, onOpen), { initialProps: { alerts: null as WorkbenchAlert[] | null } });
|
||||
rerender({ alerts: [alert] });
|
||||
expect(showMapNotice).not.toHaveBeenCalled();
|
||||
expect(result.current.atmosphere?.alert).toEqual(alert);
|
||||
const next = { ...alert, id: "incident-2", conditionId: "operational-task:task-2" };
|
||||
rerender({ alerts: [alert, next] });
|
||||
rerender({ alerts: [alert, { ...next }] });
|
||||
expect(showMapNotice).toHaveBeenCalledTimes(1);
|
||||
expect(result.current.atmosphere).toMatchObject({ preview: false, count: 2 });
|
||||
act(() => result.current.dismissAtmosphere(result.current.atmosphere!.id));
|
||||
rerender({ alerts: [alert, { ...next }] });
|
||||
expect(result.current.atmosphere).toBeNull();
|
||||
expect(showMapNotice).toHaveBeenCalledWith(expect.objectContaining({
|
||||
title: alert.title, tone: "warning", position: "top-center", actionLabel: "查看调查", duration: Infinity
|
||||
}));
|
||||
vi.mocked(showMapNotice).mock.calls[0][0].onAction?.();
|
||||
expect(onOpen).toHaveBeenCalledWith(next);
|
||||
expect(dismissMapNotice).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("retains linked notices after recovery and never reannounces the same incident", () => {
|
||||
const { result, rerender } = renderHook(({ alerts }) => useScadaAlertNotices("project-1", alerts, vi.fn()),
|
||||
{ initialProps: { alerts: [] as WorkbenchAlert[] } });
|
||||
rerender({ alerts: [alert] });
|
||||
expect(result.current.atmosphere?.alert).toEqual(alert);
|
||||
rerender({ alerts: [] });
|
||||
expect(result.current.atmosphere).toBeNull();
|
||||
expect(dismissMapNotice).not.toHaveBeenCalled();
|
||||
rerender({ alerts: [alert] });
|
||||
expect(showMapNotice).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("clears recovered data-quality notices without task links", () => {
|
||||
const { rerender } = renderHook(({ alerts }) => useScadaAlertNotices("project-1", alerts, vi.fn()),
|
||||
{ initialProps: { alerts: [] as WorkbenchAlert[] } });
|
||||
rerender({ alerts: [{ ...alert, conditionId: undefined }] });
|
||||
rerender({ alerts: [] });
|
||||
expect(dismissMapNotice).toHaveBeenCalledWith("scada-alert:project-1:incident-1");
|
||||
});
|
||||
|
||||
it("clears popups when leaving a project and keeps a different project's initial alerts quiet", () => {
|
||||
const { result, rerender, unmount } = renderHook(({ project, alerts }) =>
|
||||
useScadaAlertNotices(project, alerts, vi.fn()),
|
||||
{ initialProps: { project: "project-1", alerts: [] as WorkbenchAlert[] } });
|
||||
rerender({ project: "project-1", alerts: [alert] });
|
||||
rerender({ project: "project-2", alerts: [alert] });
|
||||
expect(result.current.atmosphere?.projectId).toBe("project-2");
|
||||
expect(dismissMapNotice).toHaveBeenCalledWith("scada-alert:project-1:incident-1");
|
||||
expect(showMapNotice).toHaveBeenCalledTimes(1);
|
||||
rerender({ project: "project-2", alerts: [alert, { ...alert, id: "incident-2", severity: "critical" }] });
|
||||
expect(showMapNotice).toHaveBeenLastCalledWith(expect.objectContaining({ tone: "error" }));
|
||||
unmount();
|
||||
expect(dismissMapNotice).toHaveBeenCalledWith("scada-alert:project-2:incident-2");
|
||||
});
|
||||
|
||||
it("ignores other sources and leaves data quality notices without a fabricated investigation link", () => {
|
||||
const { result, rerender } = renderHook(({ alerts }) => useScadaAlertNotices("project-1", alerts, vi.fn()),
|
||||
{ initialProps: { alerts: [] as WorkbenchAlert[] } });
|
||||
rerender({ alerts: [{ ...alert, source: undefined }] });
|
||||
expect(showMapNotice).not.toHaveBeenCalled();
|
||||
rerender({ alerts: [{ ...alert, conditionId: undefined }] });
|
||||
expect(showMapNotice).toHaveBeenLastCalledWith(expect.objectContaining({ actionLabel: undefined, onAction: undefined }));
|
||||
expect(result.current.atmosphere).toBeNull();
|
||||
rerender({ alerts: [{ ...alert, id: "quality-only", source: { ...alert.source!, kind: "scada_replay", replayId: "replay-1", ruleId: "quality-P1", batchSequence: 2 } }] });
|
||||
expect(result.current.atmosphere).toBeNull();
|
||||
});
|
||||
|
||||
it("previews locally and an older dismissal cannot clear a newer real incident", () => {
|
||||
const { result, rerender } = renderHook(({ project, alerts }) => useScadaAlertNotices(project, alerts, vi.fn()),
|
||||
{ initialProps: { project: "project-1", alerts: [] as WorkbenchAlert[] } });
|
||||
act(() => result.current.previewAtmosphere());
|
||||
const preview = result.current.atmosphere!;
|
||||
expect(preview).toMatchObject({ preview: true, alert: null, projectId: "project-1" });
|
||||
expect(showMapNotice).not.toHaveBeenCalled();
|
||||
rerender({ project: "project-1", alerts: [alert] });
|
||||
act(() => result.current.dismissAtmosphere(preview.id));
|
||||
expect(result.current.atmosphere?.alert).toEqual(alert);
|
||||
rerender({ project: "project-2", alerts: [] });
|
||||
expect(result.current.atmosphere).toBeNull();
|
||||
});
|
||||
Reference in New Issue
Block a user