From b1b68d2754acc8b376778affe5e17cae3b6febc9 Mon Sep 17 00:00:00 2001 From: Huarch Date: Tue, 28 Jul 2026 18:08:02 +0800 Subject: [PATCH] fix: complete agent question interactions --- .../components/agent-message-details.test.tsx | 90 ++++++++++ .../components/agent-message-details.tsx | 74 ++++++-- src/features/agent/session-state.test.ts | 27 +++ src/features/agent/session-state.ts | 23 ++- tests/browser/agent-question.e2e.ts | 160 ++++++++++++++++++ 5 files changed, 357 insertions(+), 17 deletions(-) create mode 100644 src/features/agent/components/agent-message-details.test.tsx create mode 100644 tests/browser/agent-question.e2e.ts diff --git a/src/features/agent/components/agent-message-details.test.tsx b/src/features/agent/components/agent-message-details.test.tsx new file mode 100644 index 0000000..f2bc83e --- /dev/null +++ b/src/features/agent/components/agent-message-details.test.tsx @@ -0,0 +1,90 @@ +import { cleanup, render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { AgentMessageDetails } from "./agent-message-details"; +import type { AgentQuestionInfo, AgentQuestionRequest } from "../types"; + +afterEach(cleanup); + +describe("AgentMessageDetails question answers", () => { + it("submits a custom answer instead of the selected option for a single-choice question", async () => { + const user = userEvent.setup(); + const onReplyQuestion = vi.fn(); + + renderQuestion( + { + header: "分析范围", + question: "你想分析哪一部分?", + options: [ + { label: "雨污混接分析", description: "检查混接问题" }, + { label: "水质异常分析", description: "检查水质指标" } + ] + }, + onReplyQuestion + ); + + await user.click(screen.getByRole("radio", { name: /雨污混接分析/ })); + await user.click(screen.getByRole("radio", { name: "自定义回答" })); + await user.type(screen.getByPlaceholderText("输入自定义回答"), "检查高峰时段压力"); + await user.click(screen.getByRole("button", { name: "提交回答" })); + + expect(onReplyQuestion).toHaveBeenCalledWith( + expect.objectContaining({ requestId: "question-1" }), + [["检查高峰时段压力"]] + ); + }); + + it("combines selected options with a custom answer for a multiple-choice question", async () => { + const user = userEvent.setup(); + const onReplyQuestion = vi.fn(); + + renderQuestion( + { + header: "分析内容", + question: "需要包含哪些内容?", + options: [ + { label: "压力", description: "分析节点压力" }, + { label: "流量", description: "分析管段流量" } + ], + multiple: true, + custom: true + }, + onReplyQuestion + ); + + await user.click(screen.getByRole("checkbox", { name: /压力/ })); + await user.click(screen.getByRole("checkbox", { name: "自定义回答" })); + await user.type(screen.getByPlaceholderText("输入自定义回答"), "补充水龄"); + await user.click(screen.getByRole("button", { name: "提交回答" })); + + expect(onReplyQuestion).toHaveBeenCalledWith( + expect.objectContaining({ requestId: "question-1" }), + [["压力", "补充水龄"]] + ); + }); +}); + +function renderQuestion( + question: AgentQuestionInfo, + onReplyQuestion: (request: AgentQuestionRequest, answers: string[][]) => void +) { + render( + + ); +} diff --git a/src/features/agent/components/agent-message-details.tsx b/src/features/agent/components/agent-message-details.tsx index 8a9c152..d33fa3f 100644 --- a/src/features/agent/components/agent-message-details.tsx +++ b/src/features/agent/components/agent-message-details.tsx @@ -574,7 +574,7 @@ function QuestionRequestCard({ key={`${request.requestId}-${questionIndex}`} question={question} questionIndex={questionIndex} - value={draftAnswers[questionIndex] ?? { selected: [], custom: "" }} + value={draftAnswers[questionIndex] ?? { selected: [], customSelected: false, custom: "" }} disabled={!actionable || submitting} onChange={(nextValue) => setDraftAnswers((current) => current.map((item, index) => (index === questionIndex ? nextValue : item))) @@ -633,6 +633,7 @@ function QuestionRequestCard({ type QuestionDraftAnswer = { selected: string[]; + customSelected: boolean; custom: string; }; @@ -649,8 +650,11 @@ function QuestionInput({ disabled: boolean; onChange: (value: QuestionDraftAnswer) => void; }) { - const name = `agent-question-${questionIndex}-${question.question}`; - const showCustomInput = question.custom || question.options.length === 0; + const inputId = useId(); + const name = `agent-question-${questionIndex}-${inputId}`; + const customEnabled = question.custom !== false; + const showCustomChoice = customEnabled && question.options.length > 0; + const showCustomInput = customEnabled && value.customSelected; const toggleOption = (label: string, checked: boolean) => { if (question.multiple) { @@ -662,21 +666,36 @@ function QuestionInput({ } onChange({ ...value, - selected: checked ? [label] : [] + selected: checked ? [label] : [], + customSelected: false + }); + }; + + const toggleCustom = (checked: boolean) => { + onChange({ + ...value, + selected: question.multiple ? value.selected : [], + customSelected: checked }); }; return (

{question.question}

- {question.options.length ? ( + {question.options.length || showCustomChoice ? (
{question.options.map((option) => { const checked = value.selected.includes(option.label); return (
) : null} {showCustomInput ? (