test: cover tool call hydration preview

This commit is contained in:
Mohamed Boudra
2025-11-15 17:33:56 +01:00
parent af7d7cedb0
commit e26219c967
4 changed files with 136 additions and 18 deletions
+13 -14
View File
@@ -26,15 +26,9 @@ import { baseColors, theme } from "@/styles/theme";
import { Colors } from "@/constants/theme";
import * as Clipboard from "expo-clipboard";
import type { TodoEntry } from "@/types/stream";
import {
extractCommandDetails,
extractEditEntries,
extractReadEntries,
type CommandDetails,
type EditEntry,
type ReadEntry,
} from "@/utils/tool-call-parsers";
import type { CommandDetails, EditEntry, ReadEntry } from "@/utils/tool-call-parsers";
import { DiffViewer } from "./diff-viewer";
import { resolveToolCallPreview } from "./tool-call-preview";
interface UserMessageProps {
message: string;
@@ -1010,12 +1004,17 @@ export const ToolCall = memo(function ToolCall({
parsedCommandDetails,
onOpenDetails,
}: ToolCallProps) {
const fallbackEditEntries = useMemo(() => extractEditEntries(args, result), [args, result]);
const editEntries = parsedEditEntries ?? fallbackEditEntries;
const fallbackReadEntries = useMemo(() => extractReadEntries(result, args), [args, result]);
const readEntries = parsedReadEntries ?? fallbackReadEntries;
const fallbackCommandDetails = useMemo(() => extractCommandDetails(args, result), [args, result]);
const commandDetails = parsedCommandDetails ?? fallbackCommandDetails;
const { editEntries, readEntries, commandDetails } = useMemo(
() =>
resolveToolCallPreview({
args,
result,
parsedEditEntries,
parsedReadEntries,
parsedCommandDetails,
}),
[args, result, parsedEditEntries, parsedReadEntries, parsedCommandDetails]
);
const primaryEditEntry = editEntries[0];
const primaryReadEntry = readEntries[0];
@@ -0,0 +1,74 @@
import { describe, expect, it } from "vitest";
import { resolveToolCallPreview } from "./tool-call-preview";
import type { CommandDetails, EditEntry, ReadEntry } from "@/utils/tool-call-parsers";
describe("resolveToolCallPreview", () => {
it("prefers parsed hydration payloads when provided", () => {
const parsedEdits: EditEntry[] = [
{
filePath: "README.md",
diffLines: [
{ type: "header", content: "@@ -1 +1 @@" },
{ type: "remove", content: "-Old" },
{ type: "add", content: "+New" },
],
},
];
const parsedReads: ReadEntry[] = [
{
filePath: "README.md",
content: "# Hydrated\nFinal text\n",
},
];
const parsedCommand: CommandDetails = {
command: "ls",
cwd: "/tmp/hydrated",
output: "README.md\npackages\n",
exitCode: 0,
};
const preview = resolveToolCallPreview({
parsedEditEntries: parsedEdits,
parsedReadEntries: parsedReads,
parsedCommandDetails: parsedCommand,
});
expect(preview.editEntries).toBe(parsedEdits);
expect(preview.readEntries).toBe(parsedReads);
expect(preview.commandDetails).toBe(parsedCommand);
});
it("falls back to derived parser output when hydration metadata is missing", () => {
const args = {
type: "mcp_tool_use",
id: "call_fallback",
name: "apply_patch",
server: "editor",
input: {
file_path: "README.md",
patch: "*** Begin Patch\n*** Update File: README.md\n@@\n-Old\n+New\n*** End Patch",
},
};
const result = {
changes: [
{
file_path: "README.md",
previous_content: "Old\n",
content: "New\n",
},
],
};
const preview = resolveToolCallPreview({
args,
result,
});
expect(preview.editEntries[0]?.diffLines.length).toBeGreaterThan(0);
expect(
preview.editEntries[0]?.diffLines.some((line) => line.content.includes("+New"))
).toBe(true);
expect(preview.commandDetails).toBeNull();
});
});
@@ -0,0 +1,40 @@
import {
extractCommandDetails,
extractEditEntries,
extractReadEntries,
type CommandDetails,
type EditEntry,
type ReadEntry,
} from "@/utils/tool-call-parsers";
export type ToolCallPreviewSource = {
args?: unknown;
result?: unknown;
parsedEditEntries?: EditEntry[] | undefined;
parsedReadEntries?: ReadEntry[] | undefined;
parsedCommandDetails?: CommandDetails | null | undefined;
};
export type ToolCallPreview = {
editEntries: EditEntry[];
readEntries: ReadEntry[];
commandDetails: CommandDetails | null | undefined;
};
export function resolveToolCallPreview({
args,
result,
parsedEditEntries,
parsedReadEntries,
parsedCommandDetails,
}: ToolCallPreviewSource): ToolCallPreview {
const fallbackEditEntries = extractEditEntries(args, result);
const fallbackReadEntries = extractReadEntries(result, args);
const fallbackCommandDetails = extractCommandDetails(args, result);
return {
editEntries: parsedEditEntries ?? fallbackEditEntries,
readEntries: parsedReadEntries ?? fallbackReadEntries,
commandDetails: parsedCommandDetails ?? fallbackCommandDetails,
};
}