- Add word-level highlighting using LCS algorithm for edit diffs - Match diff colors to explorer sidebar (GitHub-style rgba backgrounds) - Use discriminated union for tool call parsing (shell, edit, generic) - Add horizontal scroll for bash/command output instead of wrapping - Remove status badge from bottom sheet header - Fix sheet snap points (60%/95%) and disable dynamic sizing - Add min-width to diff viewer for full-width backgrounds 🤖 Generated with [Claude Code](https://claude.com/claude-code)
161 lines
5.7 KiB
TypeScript
161 lines
5.7 KiB
TypeScript
import { describe, test, expect } from "vitest";
|
|
import {
|
|
extractKeyValuePairs,
|
|
parseToolCallDisplay,
|
|
type ToolCallDisplay,
|
|
} from "./tool-call-parsers";
|
|
|
|
describe("tool-call-parsers - real runtime shapes", () => {
|
|
// Real data captured from Claude agent test: "shows the command inside pending tool calls"
|
|
// Run: npx vitest run claude-agent.test.ts -t "shows the command"
|
|
|
|
test("bash tool call - input shape", () => {
|
|
// REAL shape from Claude SDK timeline event (status: pending/completed)
|
|
const bashInput = {
|
|
command: "pwd",
|
|
description: "Print working directory",
|
|
};
|
|
|
|
const pairs = extractKeyValuePairs(bashInput);
|
|
expect(pairs).toContainEqual({ key: "command", value: "pwd" });
|
|
expect(pairs).toContainEqual({ key: "description", value: "Print working directory" });
|
|
});
|
|
|
|
test("bash tool call - output shape (completed)", () => {
|
|
// REAL shape from Claude SDK timeline event (status: completed)
|
|
// NOTE: output already has type: "command" discriminator!
|
|
const bashOutput = {
|
|
type: "command",
|
|
command: "pwd",
|
|
output: "/private/var/folders/xl/kkk9drfd3ms_t8x7rmy4z6900000gn/T/claude-agent-e2e-9tnmUm",
|
|
};
|
|
|
|
const pairs = extractKeyValuePairs(bashOutput);
|
|
expect(pairs).toContainEqual({ key: "type", value: "command" });
|
|
expect(pairs).toContainEqual({ key: "command", value: "pwd" });
|
|
expect(pairs).toContainEqual({ key: "output", value: expect.stringContaining("claude-agent") });
|
|
});
|
|
});
|
|
|
|
describe("parseToolCallDisplay", () => {
|
|
test("parses completed bash tool call into shell type", () => {
|
|
const input = { command: "pwd", description: "Print working directory" };
|
|
const result = { type: "command", command: "pwd", output: "/some/path" };
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("shell");
|
|
if (display.type === "shell") {
|
|
expect(display.command).toBe("pwd");
|
|
expect(display.output).toBe("/some/path");
|
|
}
|
|
});
|
|
|
|
test("parses pending bash tool call into shell type with empty output", () => {
|
|
// When tool is pending, we have input but no result yet
|
|
const input = { command: "pwd", description: "Print working directory" };
|
|
const result = undefined;
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("shell");
|
|
if (display.type === "shell") {
|
|
expect(display.command).toBe("pwd");
|
|
expect(display.output).toBe("");
|
|
}
|
|
});
|
|
|
|
test("handles command as array", () => {
|
|
const input = { command: ["git", "status"] };
|
|
const result = { type: "command", output: "On branch main" };
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("shell");
|
|
if (display.type === "shell") {
|
|
expect(display.command).toBe("git status");
|
|
expect(display.output).toBe("On branch main");
|
|
}
|
|
});
|
|
|
|
test("parses non-command tool call into generic type", () => {
|
|
const input = { file_path: "/some/file.txt" };
|
|
const result = { content: "file contents here", lineCount: 42 };
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("generic");
|
|
if (display.type === "generic") {
|
|
expect(display.input).toContainEqual({ key: "file_path", value: "/some/file.txt" });
|
|
expect(display.output).toContainEqual({ key: "content", value: "file contents here" });
|
|
expect(display.output).toContainEqual({ key: "lineCount", value: "42" });
|
|
}
|
|
});
|
|
|
|
test("handles file_write output as generic", () => {
|
|
const input = { file_path: "/some/file.txt", content: "new content" };
|
|
const result = { type: "file_write", filePath: "/some/file.txt" };
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("generic");
|
|
});
|
|
|
|
test("handles undefined input and result gracefully", () => {
|
|
const display: ToolCallDisplay = parseToolCallDisplay(undefined, undefined);
|
|
expect(display.type).toBe("generic");
|
|
if (display.type === "generic") {
|
|
expect(display.input).toEqual([]);
|
|
expect(display.output).toEqual([]);
|
|
}
|
|
});
|
|
|
|
test("parses edit tool call into edit type with old_string/new_string", () => {
|
|
const input = {
|
|
file_path: "/some/file.txt",
|
|
old_string: "const foo = 1;",
|
|
new_string: "const foo = 2;",
|
|
};
|
|
const result = {
|
|
type: "file_edit",
|
|
filePath: "/some/file.txt",
|
|
};
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("edit");
|
|
if (display.type === "edit") {
|
|
expect(display.filePath).toBe("/some/file.txt");
|
|
expect(display.oldString).toBe("const foo = 1;");
|
|
expect(display.newString).toBe("const foo = 2;");
|
|
}
|
|
});
|
|
|
|
test("parses edit tool call with old_str/new_str variants", () => {
|
|
const input = {
|
|
file_path: "/some/file.txt",
|
|
old_str: "line 1",
|
|
new_str: "line 2",
|
|
};
|
|
const result = undefined;
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, result);
|
|
expect(display.type).toBe("edit");
|
|
if (display.type === "edit") {
|
|
expect(display.filePath).toBe("/some/file.txt");
|
|
expect(display.oldString).toBe("line 1");
|
|
expect(display.newString).toBe("line 2");
|
|
}
|
|
});
|
|
|
|
test("parses pending edit tool call (no result yet)", () => {
|
|
const input = {
|
|
file_path: "/some/file.txt",
|
|
old_string: "old content",
|
|
new_string: "new content",
|
|
};
|
|
|
|
const display: ToolCallDisplay = parseToolCallDisplay(input, undefined);
|
|
expect(display.type).toBe("edit");
|
|
if (display.type === "edit") {
|
|
expect(display.filePath).toBe("/some/file.txt");
|
|
expect(display.oldString).toBe("old content");
|
|
expect(display.newString).toBe("new content");
|
|
}
|
|
});
|
|
});
|