import { expect, test } from "@playwright/test"; import { mockAgentApi } from "./support/mock-agent-api"; import { mockWorkbenchContext } from "./support/mock-workbench-context"; import { investigationFixture, mockInvestigationPresentation } from "./support/mock-investigation-presentation"; import type { OperationalTaskNotification } from "../../src/features/workbench/api/operational-task-client"; for (const width of [1440, 375]) test(`task question keyboard submission and retry at ${width}px`, async ({ page }) => { await page.setViewportSize({ width, height: 900 }); await mockWorkbenchContext(page); await mockAgentApi(page); await page.goto("/", { waitUntil: "domcontentloaded" }); await page.setContent('
'); await page.evaluate(async () => { const entry = await (await fetch("/src/main.tsx")).text(); const reactUrl = entry.match(/from "([^"\n]*\/react\.js[^"\n]*)"/)?.[1]; const clientUrl = entry.match(/from "([^"\n]*\/react-dom_client\.js[^"\n]*)"/)?.[1]; if (!reactUrl || !clientUrl) throw new Error("Vite React paths unavailable"); const reactModule = await import(reactUrl); const React = reactModule.default ?? reactModule; const clientModule = await import(clientUrl); const { createRoot } = clientModule.default ?? clientModule; const { InvestigationQuestions } = await import("/src/features/workbench/components/investigation-questions.tsx"); const calls: unknown[][] = []; Object.assign(window, { questionCalls: calls }); const question = { question_request_id: "q1", version: 1, execution_generation: 1, status: "pending", answers: [], questions: [{ question_id: "flow", title: "是否有流量计记录", body: "已查询接入测点,需要补充事件时段的计量依据。", options: [{ id: "unknown", label: "目前未知" }, { id: "yes", label: "有计量记录" }], mode: "single", allow_custom: true, required: true }] }; createRoot(document.getElementById("questions")).render(React.createElement(InvestigationQuestions, { task: { questions: [question], execution_generation: 1, lifecycle_status: "waiting_human", active_run: null, permissions: { can_answer_questions: true } }, onAnswer: async (...args: unknown[]) => { calls.push(args); if (calls.length === 1) throw new Error("网络暂时不可用,请重试"); } })); }); const submit = page.getByRole("button", { name: "提交回答并继续调查" }); await expect(submit).toBeDisabled(); await page.getByRole("radio", { name: "目前未知" }).focus(); await page.keyboard.press("Space"); await page.getByRole("textbox").fill("目前没有记录,请继续已有候选分析。"); await submit.focus(); await page.keyboard.press("Enter"); await expect(page.getByRole("alert")).toContainText("网络暂时不可用"); await expect(page.getByRole("textbox")).toHaveValue("目前没有记录,请继续已有候选分析。"); await submit.focus(); await page.keyboard.press("Enter"); await expect(page.getByText("回答已保存,调查已排队继续。")).toBeVisible(); const calls = await page.evaluate(() => (window as unknown as { questionCalls: unknown[][] }).questionCalls); expect(calls).toHaveLength(2); expect(calls[0]).toEqual(calls[1]); expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); }); test("task notification opens the question and submits through the scoped task API", async ({ page }) => { const task = investigationFixture(11, "等待补充流量依据"); const run = task.active_run!; task.active_run = null; task.latest_progress = null; task.lifecycle_status = "waiting_human"; task.status = "waiting_human"; task.permissions.can_answer_questions = true; task.questions = [{ question_request_id: "95000000-0000-4000-8000-000000000011", task_id: task.task_id, run_id: run.run_id, source_session_id: "investigation-dev-test", execution_generation: 1, version: 1, status: "pending", created_at: task.created_at, answered_at: null, answered_by: null, answers: [], questions: [{ question_id: "flow", title: "是否有流量计记录", body: "需要补充事件时段的计量依据。", options: [{ id: "unknown", label: "目前未知" }], mode: "single", allow_custom: true, required: true }] }]; const notification: OperationalTaskNotification = { notification_id: "96000000-0000-4000-8000-000000000011", task_id: task.task_id, kind: "human_input_required", title: task.title, description: "是否有流量计记录", lifecycle_status: "active", subject_type: "question", subject_id: task.questions[0].question_request_id, resolved_at: null, resolution_reason: null, status: "unread", version: 1, required_permission: "operational_task.write", created_at: task.created_at, read_at: null, read_by: null }; await mockInvestigationPresentation(page, [task], [], [notification]); await page.route("**/operational-task-notifications/**/acknowledge", (route) => route.fulfill({ json: { ...notification, status: "read", version: 2, read_at: task.created_at, read_by: "user" } })); let submitted: unknown; await page.route(`**/operational-tasks/${task.task_id}/questions/*/answer`, async (route) => { submitted = route.request().postDataJSON(); task.questions![0] = { ...task.questions![0], status: "answered", version: 2, answered_by: "user", answered_at: task.updated_at, answers: route.request().postDataJSON().answers }; task.execution_generation = 2; task.lifecycle_status = "open"; task.status = "queued"; task.revision += 1; task.active_run = { ...run, generation: 2, status: "queued" }; await route.fulfill({ json: task }); }); await page.goto("/", { waitUntil: "domcontentloaded" }); await page.getByRole("button", { name: /查看待办与告警,当前 1 条/ }).first().click(); await page.getByText(task.title, { exact: true }).first().click(); const question = page.getByRole("region", { name: "调查提问" }); await expect(question).toBeVisible(); await expect(page.getByRole("region", { name: "工况任务", exact: true }).getByRole("region", { name: "调查提问" })).toBeVisible(); const chatWrites: string[] = []; page.on("request", (request) => { if (request.method() === "POST" && /127\.0\.0\.1:8787\/api\/v1\/(sessions|responses)/.test(request.url())) chatWrites.push(request.url()); }); await question.getByRole("radio", { name: "目前未知" }).check(); await question.getByRole("textbox").fill("尚无计量记录"); await question.getByRole("button", { name: "提交回答并继续调查" }).click(); await expect(question.getByText("目前未知;尚无计量记录")).toBeVisible(); expect(submitted).toMatchObject({ expected_version: 1, request_id: expect.any(String), answers: [{ question_id: "flow", selected_option_ids: ["unknown"], custom_text: "尚无计量记录" }] }); await expect(question.getByRole("button")).toHaveCount(0); expect(chatWrites).toEqual([]); });