feat(workbench): refine alert actions and removal transitions

This commit is contained in:
2026-11-07 19:56:14 +08:00
parent eee65ac8fd
commit ea3b62573c
2 changed files with 91 additions and 42 deletions
+35 -7
View File
@@ -2,9 +2,10 @@ import { expect, test } from "@playwright/test";
import { investigationFixture, mockInvestigationPresentation } from "./support/mock-investigation-presentation";
import type { OperationalTaskNotification } from "../../src/features/workbench/api/operational-task-client";
for (const width of [1280, 375, 320]) {
test(`待办和告警可独立移除且刷新后保持 ${width}`, async ({ page }, testInfo) => {
for (const { width, reducedMotion } of [{ width: 1280, reducedMotion: false }, { width: 375, reducedMotion: false }, { width: 320, reducedMotion: false }, { width: 320, reducedMotion: true }]) {
test(`待办和告警可独立移除且刷新后保持 ${width}${reducedMotion ? " 减少动态效果" : ""}`, async ({ page }, testInfo) => {
await page.setViewportSize({ width, height: 900 });
await page.emulateMedia({ reducedMotion: reducedMotion ? "reduce" : "no-preference" });
const task = investigationFixture(1, "通知移除测试调查");
const notification: OperationalTaskNotification = {
notification_id: "96000000-0000-4000-8000-000000000011", task_id: task.task_id,
@@ -32,25 +33,52 @@ for (const width of [1280, 375, 320]) {
});
await page.goto("/", { waitUntil: "domcontentloaded" });
const trigger = page.getByRole("button", { name: /查看待办与告警/ });
await expect(trigger).toHaveAccessibleName("查看待办与告警,当前 2 条待处理事项");
// Runtime alerts and task notifications finish loading independently on a cold page.
await expect(trigger).toHaveAccessibleName("查看待办与告警,当前 2 条待处理事项", { timeout: 15_000 });
await trigger.click();
const menu = page.getByRole("menu");
await expect(menu).toBeVisible();
const removeTask = page.getByRole("menuitem", { name: `移除通知:${notification.title}` });
await expect(removeTask).toBeVisible();
const openTask = page.getByRole("menuitem", { name: `打开通知:${notification.title}` });
await expect(openTask).toHaveText("");
const actions = await openTask.evaluate((element) => {
const open = element.getBoundingClientRect();
const remove = element.nextElementSibling!.getBoundingClientRect();
return { topDifference: Math.abs(open.top - remove.top), gap: remove.left - open.right };
const remove = element.previousElementSibling!.getBoundingClientRect();
return { centerDifference: Math.abs((open.left + open.width / 2) - (remove.left + remove.width / 2)), gap: open.top - remove.bottom };
});
expect(actions.topDifference).toBeLessThan(1);
expect(actions.centerDifference).toBeLessThan(1);
expect(actions.gap).toBeGreaterThanOrEqual(0);
const box = await menu.boundingBox();
expect(box!.x).toBeGreaterThanOrEqual(0);
expect(box!.x + box!.width).toBeLessThanOrEqual(width);
await testInfo.attach(`alert-menu-${width}`, { body: await page.screenshot({ path: testInfo.outputPath("alert-menu.png"), style: "[data-sonner-toaster] { visibility: hidden !important; }" }), contentType: "image/png" });
await removeTask.click();
const row = page.locator(`[data-alert-id="operational-task-notification:${notification.notification_id}"]`);
if (!reducedMotion) {
const exitState = await removeTask.evaluate(async (element) => {
const row = element.closest("[data-alert-id]")!;
(element as HTMLElement).click();
const list = row.parentElement!;
let opacity = 1;
for (let frame = 0; frame < 30 && opacity === 1; frame++) {
await new Promise(requestAnimationFrame);
opacity = Number(getComputedStyle(row).opacity);
}
const style = getComputedStyle(row);
return { retained: row.isConnected, opacity, pointerEvents: style.pointerEvents,
overflowX: getComputedStyle(list).overflowX,
pageOverflows: document.documentElement.scrollWidth > window.innerWidth };
});
expect(exitState.retained).toBe(true);
expect(exitState.opacity).toBeGreaterThan(0);
expect(exitState.opacity).toBeLessThan(1);
expect(exitState.pointerEvents).toBe("none");
expect(exitState.overflowX).toBe("hidden");
expect(exitState.pageOverflows).toBe(false);
} else {
await removeTask.click();
}
await expect(row).toHaveCount(0);
await expect(menu).toBeVisible();
await expect(trigger).toHaveAccessibleName("查看待办与告警,当前 1 条待处理事项");
await expect(page.getByRole("region", { name: "工况任务", exact: true })).toHaveCount(0);