feat(workbench): expand SCADA monitoring and investigation presentation

This commit is contained in:
2026-11-07 19:50:14 +08:00
parent 0801e161ec
commit ba71e2d529
133 changed files with 6754 additions and 1438 deletions
@@ -0,0 +1,98 @@
import { expect, test } from "@playwright/test";
import { mockInvestigationPresentation } from "./support/mock-investigation-presentation";
for (const [width, reduced] of [[1440, false], [375, false], [320, false], [1440, true]] as const) {
test(`SCADA 全屏边缘告警通过按钮预览 ${width} ${reduced ? "静态" : "呼吸"}`, async ({ page }, testInfo) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.emulateMedia({ reducedMotion: reduced ? "reduce" : "no-preference" });
const mock = await mockInvestigationPresentation(page, []);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.clock.install();
const atmosphere = page.getByTestId("scada-alert-atmosphere");
await expect(atmosphere).toHaveCount(0);
await page.getByRole("button", { name: "切换地图 Dev Panel", exact: true }).click();
const panel = page.getByTestId("map-dev-panel");
const preview = panel.getByRole("button", { name: "测试爆管告警效果", exact: true });
await preview.click();
await expect(atmosphere).toHaveAttribute("data-preview", "true");
await expect(atmosphere).toContainText("爆管告警效果预览");
const firstId = await atmosphere.getAttribute("data-alert-id");
await preview.click();
await expect(atmosphere).toHaveCount(1);
await expect(atmosphere).not.toHaveAttribute("data-alert-id", firstId!);
await expect(atmosphere).toHaveAttribute("data-intensity", "standard");
if (width === 1440 && !reduced) {
await page.emulateMedia({ reducedMotion: "reduce" });
for (const [value, label] of [["soft", "柔和"], ["standard", "标准"], ["strong", "醒目"]] as const) {
await panel.getByRole("group", { name: "告警灯效强度" }).getByRole("button", { name: label, exact: true }).click();
await expect(atmosphere).toHaveAttribute("data-intensity", value);
await testInfo.attach(`intensity-${value}`, { body: await page.screenshot({ path: testInfo.outputPath(`intensity-${value}.png`) }), contentType: "image/png" });
}
await panel.getByRole("group", { name: "告警灯效强度" }).getByRole("button", { name: "标准", exact: true }).click();
await page.emulateMedia({ reducedMotion: "no-preference" });
}
// The ambient overlay leaves ordinary workbench buttons and the map hit target usable.
await panel.getByRole("button", { name: "关闭 Dev Panel" }).click();
await expect(panel).toHaveCount(0);
await expect(atmosphere).toBeVisible();
await page.setViewportSize({ width, height: width < 640 ? 640 : 900 });
expect(await atmosphere.boundingBox()).toEqual({ x: 0, y: 0, width, height: width < 640 ? 640 : 900 });
await expect(atmosphere).toHaveCSS("pointer-events", "none");
await expect(atmosphere).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
await expect(atmosphere.locator(".scada-alert-atmosphere-frame")).toHaveCSS("border-top-width", "1px");
const status = atmosphere.getByRole("status");
const material = await status.evaluate((element) => {
const style = getComputedStyle(element);
return { background: style.backgroundColor, backdrop: style.backdropFilter, shadow: style.boxShadow };
});
expect(material.background).toMatch(/(?:,|\/)\s*0\.\d+\)$/);
expect(material.backdrop).toMatch(/blur\((?!0px)[\d.]+px\)/);
expect(material.shadow).not.toBe("none");
const notice = (await status.boundingBox())!;
const closeButton = atmosphere.getByRole("button", { name: "关闭告警灯效" });
await expect(closeButton).toHaveText("关闭灯效");
await expect(closeButton).toHaveCSS("border-top-width", "1px");
const close = (await closeButton.boundingBox())!;
expect(close.height).toBeGreaterThanOrEqual(36);
expect(close.y - notice.y).toBeLessThanOrEqual(14);
expect(notice.x + notice.width - close.x - close.width).toBeLessThanOrEqual(14);
expect(await closeButton.evaluate((element) => {
const box = element.getBoundingClientRect();
return element.contains(document.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2));
})).toBe(true);
const light = atmosphere.locator(".scada-alert-atmosphere-light");
await expect(light).toHaveCSS("animation-name", reduced ? "none" : "scada-alert-breathe");
const samples = await light.evaluate(async (element) => {
const values: number[] = [];
for (let frame = 0; frame < 30; frame++) {
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
values.push(Number(getComputedStyle(element).opacity));
}
return values;
});
if (reduced) expect(new Set(samples).size).toBe(1);
else expect(Math.max(...samples) - Math.min(...samples)).toBeGreaterThan(0.01);
expect(await page.evaluate(() => Boolean(document.elementFromPoint(window.innerWidth / 2, window.innerHeight / 2)?.closest("[data-testid=scada-alert-atmosphere]")))).toBe(false);
expect(mock.mutations).toEqual([]);
await page.evaluate(() => document.fonts.ready);
await testInfo.attach(`scada-atmosphere-${width}-${reduced}`, {
body: await page.screenshot({ path: testInfo.outputPath("scada-atmosphere.png") }), contentType: "image/png"
});
await page.clock.fastForward(60_000);
await expect(atmosphere).toBeVisible();
await page.setViewportSize({ width: 1440, height: 900 });
await page.getByRole("button", { name: "切换地图 Dev Panel", exact: true }).click();
await panel.getByRole("button", { name: "模拟事件处理结束", exact: true }).click();
await expect(atmosphere).toHaveCount(0);
await preview.click();
await atmosphere.getByRole("button", { name: "关闭告警灯效" }).click();
await expect(atmosphere).toHaveCount(0);
await expect(panel.getByRole("button", { name: "模拟事件处理结束", exact: true })).toBeDisabled();
await panel.getByRole("button", { name: "关闭 Dev Panel" }).click();
await page.getByRole("button", { name: "外围系统", exact: true }).click();
const peripheral = page.getByRole("complementary", { name: "外围系统" });
await peripheral.getByRole("tab", { name: "SCADA 系统", exact: true }).click();
await expect(peripheral.getByRole("button", { name: "测试爆管告警效果", exact: true })).toHaveCount(0);
expect(mock.mutations).toEqual([]);
});
}