210 lines
6.2 KiB
TypeScript
210 lines
6.2 KiB
TypeScript
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { describe, expect, test } from "vitest";
|
|
import {
|
|
AgentConversation,
|
|
AgentMessage,
|
|
AgentPromptInput,
|
|
type AgentChatMessage,
|
|
} from "./AgentChat";
|
|
import {
|
|
buildSourcesFromToolTrace,
|
|
buildReasoningSteps,
|
|
getToolTraceSummary,
|
|
scrollConversationToBottom,
|
|
} from "./agentChatModel";
|
|
|
|
const assistantMessage: AgentChatMessage = {
|
|
id: "assistant-1",
|
|
role: "assistant",
|
|
content: "Deine groesste Ausgabe war Miete.",
|
|
toolTrace: [
|
|
{
|
|
name: "summarize_transactions",
|
|
inputSummary: "Mai 2026",
|
|
resultSummary: "12 Umsaetze zusammengefasst",
|
|
},
|
|
{
|
|
name: "list_transactions",
|
|
inputSummary: "Miete",
|
|
resultSummary: "1 Treffer",
|
|
},
|
|
],
|
|
};
|
|
|
|
describe("AgentChat phase 1 components", () => {
|
|
test("scrolls the conversation container directly to the bottom", () => {
|
|
const container = {
|
|
scrollTop: 0,
|
|
scrollHeight: 1200,
|
|
};
|
|
|
|
scrollConversationToBottom(container);
|
|
|
|
expect(container.scrollTop).toBe(1200);
|
|
});
|
|
|
|
test("summarizes tool traces for compact agent transparency", () => {
|
|
expect(getToolTraceSummary(undefined)).toBe("Keine Werkzeuge");
|
|
expect(getToolTraceSummary([])).toBe("Keine Werkzeuge");
|
|
expect(getToolTraceSummary(assistantMessage.toolTrace)).toBe("2 Werkzeuge verwendet");
|
|
});
|
|
|
|
test("renders conversation messages with assistant tool trace details", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<AgentConversation
|
|
messages={[
|
|
{ id: "user-1", role: "user", content: "Was war teuer?" },
|
|
assistantMessage,
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain("Was war teuer?");
|
|
expect(markup).toContain("Deine groesste Ausgabe war Miete.");
|
|
expect(markup).toContain("2 Werkzeuge verwendet");
|
|
expect(markup).toContain("summarize_transactions");
|
|
expect(markup).toContain("12 Umsaetze zusammengefasst");
|
|
});
|
|
|
|
test("renders the prompt input with ready and busy states", () => {
|
|
const readyMarkup = renderToStaticMarkup(
|
|
<AgentPromptInput
|
|
value="Bitte analysieren"
|
|
disabled={false}
|
|
isSubmitting={false}
|
|
placeholder="Welche Auswertung soll ich machen?"
|
|
onChange={() => undefined}
|
|
onSubmit={() => undefined}
|
|
/>,
|
|
);
|
|
const busyMarkup = renderToStaticMarkup(
|
|
<AgentPromptInput
|
|
value="Bitte analysieren"
|
|
disabled
|
|
isSubmitting
|
|
placeholder="Chat wird vorbereitet..."
|
|
onChange={() => undefined}
|
|
onSubmit={() => undefined}
|
|
/>,
|
|
);
|
|
|
|
expect(readyMarkup).toContain("Welche Auswertung soll ich machen?");
|
|
expect(readyMarkup).toContain("Senden");
|
|
expect(busyMarkup).toContain("Antwort laeuft");
|
|
expect(busyMarkup).toContain("disabled");
|
|
});
|
|
|
|
test("disables prompt submit for whitespace-only input", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<AgentPromptInput
|
|
value=" "
|
|
disabled={false}
|
|
isSubmitting={false}
|
|
placeholder="Welche Auswertung soll ich machen?"
|
|
onChange={() => undefined}
|
|
onSubmit={() => undefined}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain("disabled");
|
|
});
|
|
|
|
test("renders a thinking indicator while the agent is submitting", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<AgentConversation messages={[assistantMessage]} isSubmitting />,
|
|
);
|
|
|
|
expect(markup).toContain("Denk mit der KI nach...");
|
|
});
|
|
|
|
test("renders a single message without a tool section when no trace exists", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<AgentMessage message={{ id: "assistant-empty", role: "assistant", content: "Hallo" }} />,
|
|
);
|
|
|
|
expect(markup).toContain("Assistant");
|
|
expect(markup).toContain("Hallo");
|
|
expect(markup).not.toContain("Werkzeuge verwendet");
|
|
});
|
|
});
|
|
|
|
describe("AgentChat phase 2 reasoning disclosure", () => {
|
|
test("builds safe reasoning steps from tool traces without exposing raw inputs", () => {
|
|
const steps = buildReasoningSteps(assistantMessage.toolTrace);
|
|
|
|
expect(steps).toEqual([
|
|
{
|
|
label: "summarize_transactions",
|
|
description: "12 Umsaetze zusammengefasst",
|
|
status: "complete",
|
|
},
|
|
{
|
|
label: "list_transactions",
|
|
description: "1 Treffer",
|
|
status: "complete",
|
|
},
|
|
]);
|
|
expect(JSON.stringify(steps)).not.toContain("Mai 2026");
|
|
});
|
|
|
|
test("renders an assistant work-progress disclosure from tool traces", () => {
|
|
const markup = renderToStaticMarkup(<AgentMessage message={assistantMessage} />);
|
|
|
|
expect(markup).toContain("So wurde gearbeitet");
|
|
expect(markup).toContain("summarize_transactions");
|
|
expect(markup).toContain("12 Umsaetze zusammengefasst");
|
|
});
|
|
|
|
test("marks the current agent step as active while submitting", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<AgentConversation messages={[assistantMessage]} isSubmitting />,
|
|
);
|
|
|
|
expect(markup).toContain("Antwort wird vorbereitet");
|
|
expect(markup).toContain("data-status=\"active\"");
|
|
});
|
|
});
|
|
|
|
describe("AgentChat phase 3 sources and inline citations", () => {
|
|
test("builds private finance sources from tool traces", () => {
|
|
expect(buildSourcesFromToolTrace(assistantMessage.toolTrace)).toEqual([
|
|
{
|
|
id: "tool-1",
|
|
title: "summarize_transactions",
|
|
description: "12 Umsaetze zusammengefasst",
|
|
},
|
|
{
|
|
id: "tool-2",
|
|
title: "list_transactions",
|
|
description: "1 Treffer",
|
|
},
|
|
]);
|
|
});
|
|
|
|
test("renders source list and inline citation markers for assistant messages", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<AgentMessage
|
|
message={{
|
|
id: "assistant-cited",
|
|
role: "assistant",
|
|
content: "Die Miete war der groesste Posten [1].",
|
|
sources: [
|
|
{
|
|
id: "tool-1",
|
|
title: "list_transactions",
|
|
description: "1 Mietumsatz gefunden",
|
|
},
|
|
],
|
|
citations: [{ marker: "1", sourceId: "tool-1" }],
|
|
}}
|
|
/>,
|
|
);
|
|
|
|
expect(markup).toContain("Quellen");
|
|
expect(markup).toContain("list_transactions");
|
|
expect(markup).toContain("1 Mietumsatz gefunden");
|
|
expect(markup).toContain("[1]");
|
|
expect(markup).toContain("data-source-id=\"tool-1\"");
|
|
});
|
|
});
|