import type { Page } from "@playwright/test"; import type { OperationalTask } from "../../../src/features/workbench/api/operational-task-client"; import { mockAgentApi } from "./mock-agent-api"; import { mockWorkbenchContext } from "./mock-workbench-context"; const projectId = "324ff7ee-c2d4-5184-a84b-48fb14a2933e"; const now = "2026-10-27T08:00:00Z"; export function investigationFixture(index: number, title: string): OperationalTask { const taskId = `92000000-0000-4000-8000-${String(index).padStart(12, "0")}`; const runId = `93000000-0000-4000-8000-${String(index).padStart(12, "0")}`; const requestId = `94000000-0000-4000-8000-${String(index).padStart(12, "0")}`; return { project_id: projectId, task_id: taskId, task_type: "event_investigation", workflow: "burst_report", lifecycle_status: "open", status: "running", priority: "normal", revision: 1, execution_generation: 1, execution_request_id: requestId, title, summary: "仅供浏览器测试的合成调查", source: { kind: "operator", external_id: null, external_revision: null, event_kind: "burst_report" }, trigger: { kind: "manual", occurred_at: now, detail: "synthetic" }, scheduled_for: null, agent_session_id: "investigation-dev-test", agent_lease_expires_at: now, current_plan_revision: null, current_report_revision: null, created_at: now, updated_at: now, active_run: { run_id: runId, task_id: taskId, generation: 1, execution_request_id: requestId, status: "running", revision: 1, agent_session_id: "investigation-dev-test", lease_expires_at: now, heartbeat_at: now, started_at: now, finished_at: null, failure_code: null, failure_detail: null, dispatch_attempts: 1, next_dispatch_at: now, created_at: now, updated_at: now }, latest_plan: null, latest_report: null, latest_progress: { revision: 1, run_id: runId, execution_generation: 1, published_at: now, source_session_id: "investigation-dev-test", progress: { summary: "正在核对候选管段", diagram: { title: "合成调查", mermaid: 'flowchart TD\nA["核查监测"]', node_refs: [{ node_id: "A", label: "核查监测", status: "running", evidence_refs: [], action_refs: [], map_targets: [{ source_id: "scada", feature_id: "P1" }] }] } } }, evidence: [], actions: [], access_requests: [], work_order_snapshot: null, 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, can_decide_access: false } }; } export async function mockInvestigationPresentation(page: Page, tasks: OperationalTask[]) { await mockWorkbenchContext(page); await mockAgentApi(page); const mutations: string[] = []; page.on("request", (request) => { if (request.url().startsWith("https://tjwater-api.invalid/") && ["POST", "PATCH", "DELETE", "PUT"].includes(request.method())) mutations.push(`${request.method()} ${new URL(request.url()).pathname}`); }); await page.route("**/runtime-config.js", (route) => route.fulfill({ contentType: "application/javascript", body: `globalThis.__TJWATER_CONFIG__=${JSON.stringify({ TJWATER_AUTH_MODE: "disabled", TJWATER_SERVER_API_BASE_URL: "https://tjwater-api.invalid", TJWATER_AGENT_URL: "http://127.0.0.1:8787", TJWATER_ENABLE_DEV_PANEL: "true", TJWATER_MAP_URL: "https://workbench-map.invalid/geoserver", TJWATER_GEOSERVER_WORKSPACE: "tjwater_next", TJWATER_MAPBOX_ACCESS_TOKEN: "", TJWATER_TIANDITU_TOKEN: "", TJWATER_ENABLE_MSW: "false" })};` })); await page.route("https://tjwater-api.invalid/api/v1/scada-devices**", (route) => route.fulfill({ json: { items: [{ device_id: "P1", device_type: "pressure", node_id: null, link_id: null, api_query_id: null, measurement_unit: "m", transmission_mode: "test", transmission_frequency: "15min", reliability: 1, x: null, y: null, longitude: 121.48, latitude: 31.10 }], total: 1, limit: 1000, offset: 0 } })); await page.route("https://tjwater-api.invalid/api/v1/workbench/sync-snapshot", (route) => { return route.fulfill({ json: { schema: "workbench-sync-snapshot@1", event_cursor: 10, generated_at: now, 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: now, conditions: [], alerts: [], scenarios: [], latest_analysis: null, work_orders: [] }, tasks: tasks.map((task) => Object.fromEntries(Object.entries(task).filter(([key]) => !["latest_plan", "latest_report", "latest_progress", "evidence", "actions", "access_requests", "work_order_snapshot", "permissions"].includes(key)))), notifications: [] } }); }); await page.route("https://tjwater-api.invalid/api/v1/operational-tasks/**", (route) => { const task = tasks.find((item) => route.request().url().includes(item.task_id)); if (!task) return route.fulfill({ status: 404 }); if (route.request().url().endsWith("/presentation-cursor")) return route.fulfill({ json: { task_id: task.task_id, execution_generation: task.execution_generation, active_run: { run_id: task.active_run!.run_id, generation: task.execution_generation, status: "running" }, event_cursor: 10 } }); if (route.request().url().includes("/reports")) return route.fulfill({ json: { items: [], next_offset: null } }); return route.fulfill({ json: task }); }); await page.addInitScript(() => { const fetch = window.fetch.bind(window); const streams = new Set>(); Object.assign(window, { emitInvestigationTestEvent: (event: unknown) => { const data = new TextEncoder().encode(`event: workbench_event\ndata: ${JSON.stringify(event)}\n\n`); for (const stream of streams) stream.enqueue(data); } }); window.fetch = async (input, init) => { const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; if (!url.includes("/api/v1/workbench/events")) return fetch(input, init); let current: ReadableStreamDefaultController; const body = new ReadableStream({ start(controller) { current = controller; streams.add(controller); }, cancel() { streams.delete(current); } }); init?.signal?.addEventListener("abort", () => { if (streams.delete(current)) current.close(); }, { once: true }); return new Response(body, { headers: { "Content-Type": "text/event-stream" } }); }; }); let sequence = 10; return { mutations, emit: async (task: OperationalTask, command: unknown) => page.evaluate((event) => { (window as unknown as { emitInvestigationTestEvent: (event: unknown) => void }).emitInvestigationTestEvent(event); }, { schema: "workbench-event@1", event_seq: ++sequence, event_id: `95000000-0000-4000-8000-${String(sequence).padStart(12, "0")}`, project_id: projectId, topic: "operational.presentation.requested", aggregate_type: "operational_task", aggregate_id: task.task_id, aggregate_revision: 1, occurred_at: now, metadata: { task_id: task.task_id, run_id: task.active_run!.run_id, execution_generation: task.execution_generation, command } }) }; } export async function selectInvestigation(page: Page, title: string) { await page.goto("/", { waitUntil: "domcontentloaded" }); await page.getByRole("button", { name: "工况任务", exact: true }).click(); const panel = page.getByRole("region", { name: "工况任务", exact: true }); const expand = panel.getByRole("button", { name: "展开工况任务" }); if (await expand.isVisible()) await expand.click(); await panel.getByRole("button", { name: /调查任务/ }).click(); await panel.getByText(title, { exact: true }).first().click(); }