Files
next-tjwater-frontend/tests/browser/investigation-questions.e2e.ts
T
jiang c0e4231b83
Generic Container CI/CD / test-build-publish (push) Successful in 1m52s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m52s
feat: expand intelligent investigation workspace
2026-10-30 17:30:19 +08:00

100 lines
7.1 KiB
TypeScript

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('<!doctype html><html lang="zh-CN"><head><link rel="stylesheet" href="/src/styles.css"></head><body><main id="questions" style="max-width:680px;margin:auto;padding:16px"></main></body></html>');
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([]);
});