fix: v2.7.5 — the chat shows each AskUserQuestion with the user's answer
- Chat: an AskUserQuestion is its own card (AskCard in MessageList.tsx), not part of the collapsed work line — each question with the option picked or the text typed, "declined" when refused. The transcript route passes the answers from Claude Code's toolUseResult (askAnswers, beside diff). - Chat: the activity panel keeps the user's open/close per session for the tab, and opens by default only once the session has an output. - Workspace home: the sent prompt's draft is dropped even though opening the session unmounts the home. - Terminal: Ctrl+V with text on the clipboard pastes the text; only an empty clipboard still sends ^V. - AGENTS.md / CLAUDE.md: run only the tests a change touches while iterating, the full suite once before committing.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @file MessageList.ask.test.tsx
|
||||
* @description An AskUserQuestion call is not folded into the collapsed work
|
||||
* line: the chat shows it as its own card — each question with the answer the
|
||||
* user picked or typed, "declined" when they refused it, nothing yet while the
|
||||
* CLI still waits.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import { MessageList } from "../MessageList";
|
||||
import type { TranscriptMessage } from "../../../lib/types";
|
||||
|
||||
const at = "2026-10-13T08:00:00.000Z";
|
||||
const user = (text: string): TranscriptMessage =>
|
||||
({
|
||||
type: "user",
|
||||
sender: "user",
|
||||
timestamp: at,
|
||||
content: [{ type: "text", text }],
|
||||
}) as TranscriptMessage;
|
||||
const ask = (id: string, questions: string[]): TranscriptMessage =>
|
||||
({
|
||||
type: "assistant",
|
||||
sender: "assistant",
|
||||
timestamp: at,
|
||||
content: [
|
||||
{ type: "tool_use", id: `${id}r`, name: "Read", input: { file_path: "/w/a.ts" } },
|
||||
{
|
||||
type: "tool_use",
|
||||
id,
|
||||
name: "AskUserQuestion",
|
||||
input: {
|
||||
questions: questions.map((question) => ({
|
||||
question,
|
||||
header: "Pick",
|
||||
options: [{ label: "Yes" }, { label: "No" }],
|
||||
})),
|
||||
},
|
||||
},
|
||||
],
|
||||
}) as TranscriptMessage;
|
||||
const result = (id: string, extra: Record<string, unknown>) =>
|
||||
({
|
||||
type: "user",
|
||||
sender: "tool",
|
||||
timestamp: at,
|
||||
content: [{ type: "tool_result", id, output: "x", ...extra }],
|
||||
}) as TranscriptMessage;
|
||||
|
||||
describe("an AskUserQuestion card", () => {
|
||||
it("shows each question with the user's answer, outside the work line", () => {
|
||||
render(
|
||||
<MessageList
|
||||
loading={false}
|
||||
messages={[
|
||||
user("go"),
|
||||
ask("q1", ["Commit?", "Push?"]),
|
||||
result("q1", { answers: { "Commit?": "Yes", "Push?": "only after review" } }),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
const card = screen.getByTestId("chat-ask");
|
||||
expect(within(card).getByText("Commit?")).toBeTruthy();
|
||||
expect(within(card).getByText("Yes")).toBeTruthy();
|
||||
expect(within(card).getByText("only after review")).toBeTruthy();
|
||||
expect(within(screen.getByTestId("chat-tools")).queryByText(/Commit\?/)).toBeNull();
|
||||
});
|
||||
|
||||
it("says declined when refused, and shows no answer while still asked", () => {
|
||||
const { rerender } = render(
|
||||
<MessageList loading={false} messages={[user("go"), ask("q1", ["Commit?"])]} />
|
||||
);
|
||||
const card = screen.getByTestId("chat-ask");
|
||||
expect(within(card).getByText("Commit?")).toBeTruthy();
|
||||
expect(within(card).queryByText(/declined/i)).toBeNull();
|
||||
rerender(
|
||||
<MessageList
|
||||
loading={false}
|
||||
messages={[user("go"), ask("q1", ["Commit?"]), result("q1", { is_error: true })]}
|
||||
/>
|
||||
);
|
||||
expect(within(screen.getByTestId("chat-ask")).getByText(/declined/i)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user