The not_configured branch previously promoted mock task failures into critical alerts. Return an explicit empty alert source so the exception badge reflects only actionable backend state.
474 lines
22 KiB
TypeScript
474 lines
22 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import type {
|
|
OperationalTask,
|
|
OperationalTaskAction,
|
|
OperationalTaskSummary
|
|
} from "../../src/features/workbench/api/operational-task-client";
|
|
import { mockAgentApi } from "./support/mock-agent-api";
|
|
|
|
const projectId = "324ff7ee-c2d4-5184-a84b-48fb14a2933e";
|
|
const taskId = "72000000-0000-4000-8000-000000000001";
|
|
const appointmentTaskId = "72000000-0000-4000-8000-000000000002";
|
|
const actionId = "73000000-0000-4000-8000-000000000001";
|
|
|
|
test("审批动作展示中文参数和证据,viewer 不可审批,冲突后可核对未知回执", async ({ page }, testInfo) => {
|
|
test.setTimeout(60_000);
|
|
await mockAgentApi(page);
|
|
await page.route("**/runtime-config.js", (route) => route.fulfill({
|
|
contentType: "text/javascript",
|
|
body: `globalThis.__TJWATER_CONFIG__=${JSON.stringify({
|
|
TJWATER_AUTH_MODE: "disabled",
|
|
TJWATER_SERVER_API_BASE_URL: "http://server.test",
|
|
TJWATER_MAP_URL: "http://map.test/geoserver",
|
|
TJWATER_GEOSERVER_WORKSPACE: "tjwater_next"
|
|
})}`
|
|
}));
|
|
await page.route("http://server.test/api/v1/projects**", (route) => route.fulfill({ json: {
|
|
items: [{ project_id: projectId, gs_workspace: "tjwater_next", status: "active" }],
|
|
total: 1, limit: 1000, offset: 0
|
|
} }));
|
|
await page.route("http://server.test/api/v1/scada-devices**", (route) =>
|
|
route.fulfill({ json: { items: [], total: 0, limit: 1000, offset: 0 } })
|
|
);
|
|
await page.route("http://map.test/**", (route) => route.fulfill({ status: 204 }));
|
|
|
|
const baseAction: OperationalTaskAction = {
|
|
action_id: actionId,
|
|
task_id: taskId,
|
|
version: 3,
|
|
kind: "issue_command",
|
|
node_id: null,
|
|
reason: "隔离疑似漏损支路前需要调度员确认阀门目标与开度",
|
|
payload: {
|
|
command_type: "set_valve",
|
|
target: { kind: "valve", id: "V-102" },
|
|
parameters: { opening: 35 },
|
|
impact_summary: "隔离疑似漏损支路,可能短时影响演示小区供水压力",
|
|
preconditions: ["上游压力不低于 28m", "现场人员已到位"],
|
|
expires_at: "2026-09-23T09:00:00Z"
|
|
},
|
|
evidence_refs: ["e-pressure"],
|
|
required_approval_permission: "operational_action.command.approve",
|
|
required_execution_permission: "operational_action.command.execute",
|
|
approval_status: "pending",
|
|
execution_status: "not_submitted",
|
|
execution_request_id: null,
|
|
proposed_by: "agent",
|
|
proposed_at: "2026-09-23T08:25:00Z",
|
|
decided_by: null,
|
|
decided_at: null,
|
|
decision_comment: null,
|
|
executed_by: null,
|
|
executed_at: null,
|
|
receipt: null,
|
|
execution_detail: null
|
|
};
|
|
let task: OperationalTask = {
|
|
project_id: projectId,
|
|
task_id: taskId,
|
|
task_type: "event_investigation",
|
|
workflow: "burst_report",
|
|
lifecycle_status: "waiting_human",
|
|
active_run: null,
|
|
status: "waiting_human",
|
|
priority: "urgent",
|
|
revision: 2,
|
|
execution_generation: 2,
|
|
execution_request_id: "74000000-0000-4000-8000-000000000001",
|
|
title: "阀门调节审批",
|
|
summary: "爆管事件调查需要确认隔离阀门动作",
|
|
source: { kind: "operator", external_id: null, external_revision: null, event_kind: "burst_report" },
|
|
trigger: { kind: "manual", occurred_at: "2026-09-23T08:20:00Z", detail: "演示路与样板路交叉口" },
|
|
scheduled_for: null,
|
|
agent_session_id: "approval-session",
|
|
agent_lease_expires_at: null,
|
|
current_plan_revision: null,
|
|
current_report_revision: 1,
|
|
created_at: "2026-09-23T08:20:00Z",
|
|
updated_at: "2026-09-23T08:25:00Z",
|
|
latest_plan: null,
|
|
latest_report: {
|
|
revision: 1,
|
|
execution_generation: 1,
|
|
source_session_id: "approval-session",
|
|
published_at: "2026-09-23T08:24:00Z",
|
|
published_by: "agent",
|
|
report: {
|
|
title: "爆管事件调查报告",
|
|
current_conclusion: "疑似支路漏损,阀门动作尚未执行。",
|
|
conclusion_boundary: "等待人工确认和设备回执。",
|
|
evidence_refs: ["e-pressure"],
|
|
recommendations: ["确认 V-102 目标与开度后再执行"],
|
|
pending_items: ["调度员确认阀门动作"]
|
|
}
|
|
},
|
|
evidence: [{
|
|
evidence_id: "e-pressure",
|
|
kind: "system_data",
|
|
summary: "V-102 下游压力在五分钟内下降 8.2m",
|
|
source_label: "SCADA 压力监测",
|
|
source_ref: "PRESSURE-V102",
|
|
observed_at: "2026-09-23T08:18:00Z",
|
|
recorded_at: "2026-09-23T08:23:00Z",
|
|
recorded_by: "agent",
|
|
has_snapshot: false
|
|
}],
|
|
actions: [baseAction],
|
|
permissions: {
|
|
can_publish_artifacts: false,
|
|
can_propose_actions: false,
|
|
can_sync_external_feedback: false,
|
|
can_decide_work_orders: false,
|
|
can_execute_work_orders: false,
|
|
can_decide_commands: false,
|
|
can_execute_commands: false
|
|
},
|
|
work_order_snapshot: null
|
|
};
|
|
|
|
const appointmentTask: OperationalTask = {
|
|
...task,
|
|
task_id: appointmentTaskId,
|
|
task_type: "external_work_order",
|
|
workflow: "general",
|
|
lifecycle_status: "open",
|
|
active_run: null,
|
|
status: "queued",
|
|
priority: "normal",
|
|
execution_request_id: "74000000-0000-4000-8000-000000000002",
|
|
title: "城北阀井预约维修",
|
|
summary: "外围工单同步的预约维修任务",
|
|
source: {
|
|
kind: "external_system",
|
|
external_id: "sample-work-order-1",
|
|
external_revision: 2,
|
|
event_kind: "general"
|
|
},
|
|
trigger: {
|
|
kind: "external_sync",
|
|
occurred_at: "2026-09-23T08:30:00Z",
|
|
detail: "预约 09/23 18:00 由北片区维修组处理"
|
|
},
|
|
scheduled_for: "2026-09-23T10:00:00Z",
|
|
agent_session_id: null,
|
|
current_report_revision: null,
|
|
latest_report: null,
|
|
evidence: [],
|
|
actions: [],
|
|
work_order_snapshot: {
|
|
work_order_id: "50000000-0000-4000-8000-000000000001",
|
|
revision: 2,
|
|
title: "城北阀井预约维修",
|
|
summary: "编辑后的预约维修说明",
|
|
status: "open",
|
|
priority: "normal",
|
|
assignee: "测试班组乙",
|
|
scheduled_at: "2026-09-23T10:00:00Z",
|
|
work_order_type: "general",
|
|
location: "联调测试点东侧",
|
|
scenario_code: "APPOINTMENT-TEST-001",
|
|
feedback_requirements: ["回填现场处理结果"],
|
|
activity: [],
|
|
origin: "synthetic",
|
|
updated_at: "2026-09-23T08:30:00Z"
|
|
}
|
|
};
|
|
|
|
const summary = (item: OperationalTask): OperationalTaskSummary => {
|
|
const {
|
|
latest_plan,
|
|
latest_report,
|
|
evidence,
|
|
actions,
|
|
permissions,
|
|
work_order_snapshot,
|
|
...taskSummary
|
|
} = item;
|
|
void latest_plan; void latest_report; void evidence; void actions; void permissions; void work_order_snapshot;
|
|
return taskSummary;
|
|
};
|
|
|
|
let scheduleRequests = 0;
|
|
|
|
const runtimeSnapshot = {
|
|
availability: "not_configured", source: "none",
|
|
scenario_availability: "not_configured", artifact_availability: "not_configured",
|
|
schedule_availability: "not_configured", work_order_availability: "not_configured",
|
|
alert_availability: "not_configured", generated_at: "2026-09-23T08:30:00Z",
|
|
conditions: [], alerts: [], scenarios: [], latest_analysis: null, work_orders: []
|
|
};
|
|
let snapshotRequests = 0;
|
|
await page.route("http://server.test/api/v1/workbench/sync-snapshot", (route) => {
|
|
snapshotRequests += 1;
|
|
return route.fulfill({ json: {
|
|
schema: "workbench-sync-snapshot@1", event_cursor: 0, runtime: runtimeSnapshot,
|
|
tasks: [summary(task), summary(appointmentTask)], notifications: [],
|
|
generated_at: "2026-09-23T08:30:00Z"
|
|
} });
|
|
});
|
|
await page.route("http://server.test/api/v1/workbench/events**", (route) =>
|
|
route.fulfill({ contentType: "text/event-stream", body: ": test keepalive\n\n" })
|
|
);
|
|
|
|
await page.route("http://server.test/api/v1/operational-task-notifications**", (route) =>
|
|
route.fulfill({ json: { items: [], limit: 100, offset: 0, next_offset: null } })
|
|
);
|
|
await page.route("http://server.test/api/v1/operational-tasks**", async (route) => {
|
|
const request = route.request();
|
|
const pathname = new URL(request.url()).pathname;
|
|
if (pathname.endsWith("/operational-tasks/schedules")) {
|
|
scheduleRequests += 1;
|
|
return route.fulfill({ json: { items: [] } });
|
|
}
|
|
if (request.method() === "GET" && pathname.endsWith("/operational-tasks")) {
|
|
return route.fulfill({ json: { items: [summary(task), summary(appointmentTask)], limit: 50, offset: 0, next_offset: null } });
|
|
}
|
|
if (request.method() === "GET" && pathname.endsWith(`/operational-tasks/${taskId}`)) {
|
|
return route.fulfill({ json: task });
|
|
}
|
|
if (request.method() === "GET" && pathname.endsWith(`/operational-tasks/${appointmentTaskId}`)) {
|
|
return route.fulfill({ json: appointmentTask });
|
|
}
|
|
if (request.method() === "POST" && pathname.endsWith(`/actions/${actionId}/decision`)) {
|
|
return route.fulfill({ status: 409, json: { detail: "动作版本冲突:expected 3, actual 4" } });
|
|
}
|
|
if (request.method() === "POST" && pathname.endsWith(`/actions/${actionId}/reconcile`)) {
|
|
const receiptAction = {
|
|
...task.actions[0],
|
|
version: 5,
|
|
execution_status: "succeeded",
|
|
receipt: {
|
|
adapter: "scada-command-adapter",
|
|
receipt_id: "SCADA-RECEIPT-20260923-01",
|
|
submitted_at: "2026-09-23T08:28:00Z",
|
|
completed_at: "2026-09-23T08:29:00Z",
|
|
external_status: "completed",
|
|
detail: "V-102 开度已调整至 35%"
|
|
},
|
|
execution_detail: "设备回执确认完成"
|
|
};
|
|
task = { ...task, actions: [receiptAction] };
|
|
return route.fulfill({ json: receiptAction });
|
|
}
|
|
return route.fulfill({ status: 404, json: {} });
|
|
});
|
|
|
|
const openTask = async () => {
|
|
await page.getByRole("button", { name: "工况任务", exact: true }).click();
|
|
const conditionPanel = page.getByRole("region", { name: "工况任务", exact: true });
|
|
const expandButton = conditionPanel.getByRole("button", { name: "展开工况任务" });
|
|
if (await expandButton.isVisible()) await expandButton.click();
|
|
await conditionPanel.getByRole("button", { name: /调查任务/ }).click();
|
|
await conditionPanel.getByText("阀门调节审批", { exact: true }).first().click();
|
|
return conditionPanel;
|
|
};
|
|
|
|
await page.goto("/");
|
|
const alertTrigger = page.getByRole("button", {
|
|
name: "查看异常处置面板,当前 0 条待复核工况"
|
|
}).first();
|
|
await expect(alertTrigger).toBeVisible();
|
|
await alertTrigger.click();
|
|
await expect(page.getByText("当前无待复核工况", { exact: true }).first()).toBeVisible();
|
|
await expect(page.getByText("来源:未配置告警源", { exact: true })).toBeVisible();
|
|
await page.keyboard.press("Escape");
|
|
const reconnectNotice = page.locator("[data-sonner-toast]").filter({
|
|
hasText: "业务同步正在重连"
|
|
});
|
|
await expect(reconnectNotice).toBeVisible();
|
|
await expect(reconnectNotice).toContainText("当前数据可能陈旧,连接恢复后将自动更新。");
|
|
await expect(page.getByText("正在重连,业务数据可能陈旧", { exact: true })).toHaveCount(0);
|
|
await page.evaluate(() => document.fonts.ready);
|
|
await testInfo.attach("workbench-reconnect-notice", {
|
|
body: await reconnectNotice.screenshot({ path: testInfo.outputPath("workbench-reconnect-notice.png") }),
|
|
contentType: "image/png"
|
|
});
|
|
let conditionPanel = await openTask();
|
|
await expect(conditionPanel.getByRole("navigation", { name: "工况任务分类" })).toBeVisible();
|
|
await expect(conditionPanel.getByRole("button", { name: /定时任务/ })).toContainText("模拟数据");
|
|
await expect(conditionPanel.getByRole("button", { name: /调查任务/ })).toContainText("后端");
|
|
await expect(conditionPanel.getByRole("button", { name: /预约任务/ })).toContainText("后端");
|
|
const conditionRefreshButton = conditionPanel.getByRole("button", { name: "刷新工况任务" });
|
|
await expect(conditionRefreshButton).toBeVisible();
|
|
const refreshStartedAt = Date.now();
|
|
await conditionRefreshButton.click();
|
|
await expect.poll(() => snapshotRequests).toBe(2);
|
|
const refreshingButton = conditionPanel.getByRole("button", { name: "正在刷新工况任务" });
|
|
await expect(refreshingButton).toBeDisabled();
|
|
await refreshingButton.evaluate((button: HTMLButtonElement) => button.click());
|
|
await page.waitForTimeout(500);
|
|
expect(snapshotRequests).toBe(2);
|
|
await expect(conditionRefreshButton).toBeEnabled({ timeout: 4_000 });
|
|
expect(Date.now() - refreshStartedAt).toBeGreaterThanOrEqual(2_900);
|
|
await conditionPanel.getByRole("button", { name: "配置定时流程" }).click();
|
|
await conditionPanel.getByLabel("定时流程名称").fill("夜间阀门巡检");
|
|
await conditionPanel.getByLabel("任务说明").fill("每十五分钟检查预约维修涉及的边界阀门。");
|
|
await conditionPanel.getByRole("button", { name: "创建定时流程" }).click();
|
|
await expect(conditionPanel.getByText("夜间阀门巡检", { exact: true }).first()).toBeVisible();
|
|
await conditionPanel.getByRole("button", { name: /预约任务/ }).click();
|
|
await expect(conditionPanel.getByText("最近工况", { exact: true })).toHaveCount(1);
|
|
await expect(conditionPanel.getByText("城北阀井预约维修", { exact: true }).first()).toBeVisible();
|
|
await conditionPanel.getByText("城北阀井预约维修", { exact: true }).first().click();
|
|
await expect(conditionPanel.getByText("回执要求", { exact: true })).toBeVisible();
|
|
await expect(conditionPanel.getByText("回填现场处理结果", { exact: true })).toBeVisible();
|
|
await expect(conditionPanel.getByText("测试班组乙", { exact: true }).first()).toBeVisible();
|
|
await expect(conditionPanel.getByText(/位置:联调测试点东侧/)).toBeVisible();
|
|
await expect(conditionPanel.getByText("编辑后的预约维修说明", { exact: true })).toBeVisible();
|
|
await expect(conditionPanel.getByText("预约 09/23 18:00 由北片区维修组处理", { exact: true })).toHaveCount(0);
|
|
await page.waitForTimeout(3_500);
|
|
await page.addStyleTag({ content: "[data-sonner-toaster] { display: none !important; }" });
|
|
await page.evaluate(() => document.fonts.ready);
|
|
await page.screenshot({ path: "/tmp/tjwater-operational-ui-review/task-categories-desktop.png" });
|
|
await conditionPanel.getByRole("button", { name: /调查任务/ }).click();
|
|
expect(scheduleRequests).toBe(0);
|
|
await expect(conditionPanel.getByText("上一轮报告,当前任务待人工", { exact: true })).toBeVisible();
|
|
await expect(conditionPanel.getByText(/第\s*[12]\s*代|generation/i)).toHaveCount(0);
|
|
const actionCard = conditionPanel.locator(`[data-action-id="${actionId}"]`);
|
|
await expect(actionCard.getByText("命令类型", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("对象类型", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("目标对象", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("执行参数", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("影响说明", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("调节阀门", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("阀门", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("开度 35%", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText("确认依据", { exact: true })).toBeVisible();
|
|
await expect(actionCard.getByText(/V-102 下游压力.*SCADA 压力监测/)).toBeVisible();
|
|
await expect(actionCard.getByText(/当前账号仅可查看/)).toBeVisible();
|
|
await expect(actionCard.getByText(/operational_action\.command\.approve/)).toHaveCount(0);
|
|
await expect(actionCard.getByRole("button", { name: "批准下发命令" })).toHaveCount(0);
|
|
await page.evaluate(() => document.fonts.ready);
|
|
await testInfo.attach("approval-viewer-desktop", {
|
|
body: await actionCard.screenshot({ path: testInfo.outputPath("approval-viewer-desktop.png") }),
|
|
contentType: "image/png"
|
|
});
|
|
|
|
task = {
|
|
...task,
|
|
permissions: { ...task.permissions, can_decide_commands: true }
|
|
};
|
|
await page.reload();
|
|
conditionPanel = await openTask();
|
|
await conditionPanel.getByRole("button", { name: "批准下发命令" }).click();
|
|
await expect(conditionPanel.getByRole("alert")).toContainText("动作版本冲突:expected 3, actual 4");
|
|
const refreshButton = conditionPanel.getByRole("button", { name: "刷新后重新确认" });
|
|
|
|
task = {
|
|
...task,
|
|
actions: [{
|
|
...task.actions[0],
|
|
version: 4,
|
|
approval_status: "approved",
|
|
execution_status: "unknown",
|
|
execution_request_id: "75000000-0000-4000-8000-000000000001",
|
|
decided_by: "dispatcher",
|
|
decided_at: "2026-09-23T08:27:00Z"
|
|
}],
|
|
permissions: { ...task.permissions, can_execute_commands: true }
|
|
};
|
|
await refreshButton.click();
|
|
const reconcileButton = conditionPanel.getByRole("button", { name: "核对执行回执" });
|
|
await expect(reconcileButton).toBeVisible();
|
|
await reconcileButton.click();
|
|
const receipt = conditionPanel.getByRole("region", { name: "执行回执" });
|
|
await expect(receipt).toContainText("SCADA-RECEIPT-20260923-01");
|
|
await expect(receipt).toContainText("已完成");
|
|
await expect(receipt).toContainText("V-102 开度已调整至 35%");
|
|
await testInfo.attach("approval-receipt-desktop", {
|
|
body: await actionCard.screenshot({ path: testInfo.outputPath("approval-receipt-desktop.png") }),
|
|
contentType: "image/png"
|
|
});
|
|
});
|
|
|
|
test("后端没有调查任务时显示空态,分类标签按压不缩放", async ({ page }, testInfo) => {
|
|
test.setTimeout(60_000);
|
|
await mockAgentApi(page);
|
|
await page.route("**/runtime-config.js", (route) => route.fulfill({
|
|
contentType: "text/javascript",
|
|
body: `globalThis.__TJWATER_CONFIG__=${JSON.stringify({
|
|
TJWATER_AUTH_MODE: "disabled",
|
|
TJWATER_SERVER_API_BASE_URL: "http://server.test",
|
|
TJWATER_MAP_URL: "http://map.test/geoserver",
|
|
TJWATER_GEOSERVER_WORKSPACE: "tjwater_next"
|
|
})}`
|
|
}));
|
|
await page.route("http://server.test/api/v1/projects**", (route) => route.fulfill({ json: {
|
|
items: [{ project_id: projectId, gs_workspace: "tjwater_next", status: "active" }],
|
|
total: 1, limit: 1000, offset: 0
|
|
} }));
|
|
await page.route("http://server.test/api/v1/scada-devices**", (route) =>
|
|
route.fulfill({ json: { items: [], total: 0, limit: 1000, offset: 0 } })
|
|
);
|
|
await page.route("http://server.test/api/v1/operational-task-notifications**", (route) =>
|
|
route.fulfill({ json: { items: [], limit: 100, offset: 0, next_offset: null } })
|
|
);
|
|
await page.route("http://server.test/api/v1/operational-tasks**", (route) =>
|
|
route.fulfill({ json: { items: [], limit: 50, offset: 0, next_offset: null } })
|
|
);
|
|
await page.route("http://server.test/api/v1/workbench/sync-snapshot", (route) =>
|
|
route.fulfill({ json: {
|
|
schema: "workbench-sync-snapshot@1", event_cursor: 0,
|
|
runtime: {
|
|
availability: "not_configured", source: "none",
|
|
scenario_availability: "not_configured", artifact_availability: "not_configured",
|
|
schedule_availability: "not_configured", work_order_availability: "not_configured",
|
|
alert_availability: "not_configured", generated_at: "2026-09-23T08:30:00Z",
|
|
conditions: [], alerts: [], scenarios: [], latest_analysis: null, work_orders: []
|
|
},
|
|
tasks: [], notifications: [], generated_at: "2026-09-23T08:30:00Z"
|
|
} })
|
|
);
|
|
await page.route("http://server.test/api/v1/workbench/events**", (route) =>
|
|
route.fulfill({ contentType: "text/event-stream", body: ": test keepalive\n\n" })
|
|
);
|
|
await page.route("http://map.test/**", (route) => route.fulfill({ status: 204 }));
|
|
|
|
await page.goto("/");
|
|
await page.getByRole("button", { name: "工况任务", exact: true }).press("Enter");
|
|
const conditionPanel = page.getByRole("region", { name: "工况任务", exact: true });
|
|
const expandButton = conditionPanel.getByRole("button", { name: "展开工况任务" });
|
|
if (await expandButton.isVisible()) await expandButton.click();
|
|
await conditionPanel.getByRole("button", { name: /调查任务/ }).click();
|
|
await expect(conditionPanel.getByText("暂无任务", { exact: true })).toBeVisible();
|
|
await expect(conditionPanel.getByText("新任务进入当前分类后会出现在这里。", { exact: true })).toBeVisible();
|
|
for (const width of [1280, 375, 320]) {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
if (width <= 375) {
|
|
const openMobileTasks = page.getByRole("button", { name: "打开工况任务", exact: true });
|
|
if (await openMobileTasks.isVisible()) await openMobileTasks.click();
|
|
const expandMobile = conditionPanel.getByRole("button", { name: "展开工况任务" });
|
|
if (await expandMobile.isVisible()) await expandMobile.click();
|
|
}
|
|
const categories = conditionPanel.getByRole("navigation", { name: "工况任务分类" });
|
|
for (const label of ["定时任务", "调查任务", "预约任务"]) {
|
|
const tab = categories.getByRole("button", { name: new RegExp(label) });
|
|
await tab.hover();
|
|
const before = await tab.boundingBox();
|
|
expect(before).not.toBeNull();
|
|
await page.mouse.down();
|
|
try {
|
|
await tab.evaluate(async (element) => {
|
|
await Promise.all(element.getAnimations().map((animation) => animation.finished));
|
|
});
|
|
await expect.poll(() => tab.evaluate((element) => getComputedStyle(element).scale)).toBe("none");
|
|
const pressed = await tab.boundingBox();
|
|
expect(pressed).not.toBeNull();
|
|
expect(Math.abs(pressed!.width - before!.width)).toBeLessThan(0.5);
|
|
expect(Math.abs(pressed!.height - before!.height)).toBeLessThan(0.5);
|
|
} finally {
|
|
await page.mouse.up();
|
|
}
|
|
await expect(tab).toHaveAttribute("aria-current", "page");
|
|
}
|
|
await categories.screenshot({ path: testInfo.outputPath(`task-tabs-${width}.png`) });
|
|
}
|
|
await page.emulateMedia({ reducedMotion: "reduce" });
|
|
const investigationTab = conditionPanel.getByRole("navigation", { name: "工况任务分类" })
|
|
.getByRole("button", { name: /调查任务/ });
|
|
await investigationTab.focus();
|
|
await page.keyboard.press("Enter");
|
|
await expect(investigationTab).toHaveAttribute("aria-current", "page");
|
|
await expect(investigationTab).toBeFocused();
|
|
await expect.poll(() => investigationTab.evaluate((element) => getComputedStyle(element).transitionProperty)).toBe("none");
|
|
});
|