83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
// A file tool call must show what it streamed even when it carries no
|
|
// path: the path only names the file in the block title, so a call that
|
|
// streams its body first (or a same-named tool with other arg names) used
|
|
// to render an empty block.
|
|
|
|
import ChatMessageToolCallBlockEditFile from '$lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageToolCall/ChatMessageToolCallBlockEditFile.svelte';
|
|
import ChatMessageToolCallBlockWriteFile from '$lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageToolCall/ChatMessageToolCallBlockWriteFile.svelte';
|
|
import { AgenticSectionType, BuiltInTool } from '$lib/enums';
|
|
import type { AgenticSection } from '$lib/types';
|
|
import { tick } from 'svelte';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { render } from 'vitest-browser-svelte';
|
|
|
|
const WRITE_FILE_BODY = 'body streamed without a path';
|
|
const EDIT_FILE_NEW_TEXT = 'replacement text';
|
|
|
|
function makeSection(toolName: BuiltInTool, toolArgs: string): AgenticSection {
|
|
return {
|
|
content: '',
|
|
toolArgs,
|
|
toolName,
|
|
toolResult: undefined,
|
|
type: AgenticSectionType.TOOL_CALL
|
|
};
|
|
}
|
|
|
|
describe('file tool calls render their body without a path', () => {
|
|
it('write_file shows the content it streamed', async () => {
|
|
render(ChatMessageToolCallBlockWriteFile, {
|
|
isStreaming: true,
|
|
open: true,
|
|
section: makeSection(BuiltInTool.SERVER_WRITE_FILE, `{"content":"${WRITE_FILE_BODY}"`)
|
|
});
|
|
|
|
await tick();
|
|
|
|
expect(document.body.textContent).toContain(WRITE_FILE_BODY);
|
|
});
|
|
|
|
it('edit_file shows the diff it streamed', async () => {
|
|
render(ChatMessageToolCallBlockEditFile, {
|
|
isStreaming: true,
|
|
open: true,
|
|
section: makeSection(
|
|
BuiltInTool.SERVER_EDIT_FILE,
|
|
`{"edits":[{"old_text":"stale text","new_text":"${EDIT_FILE_NEW_TEXT}"}]}`
|
|
)
|
|
});
|
|
|
|
await tick();
|
|
|
|
expect(document.body.textContent).toContain(EDIT_FILE_NEW_TEXT);
|
|
expect(document.body.textContent).toContain('stale text');
|
|
});
|
|
|
|
it('write_file falls back to the raw args when no known content key is present', async () => {
|
|
render(ChatMessageToolCallBlockWriteFile, {
|
|
isStreaming: false,
|
|
open: true,
|
|
section: makeSection(BuiltInTool.SERVER_WRITE_FILE, '{"contents":"x"}')
|
|
});
|
|
|
|
await tick();
|
|
|
|
expect(document.body.textContent).toContain('contents');
|
|
});
|
|
|
|
it('write_file renders an empty code block for a finished empty write', async () => {
|
|
// content of '' used to fall through to the raw-args fallback
|
|
const section = makeSection(BuiltInTool.SERVER_WRITE_FILE, '{"content":""}');
|
|
|
|
section.toolResult = '{"result":"File written","bytes":0}';
|
|
|
|
render(ChatMessageToolCallBlockWriteFile, { isStreaming: false, open: true, section });
|
|
|
|
await tick();
|
|
|
|
expect(document.querySelector('pre code.hljs')).toBeTruthy();
|
|
expect(document.body.textContent).not.toContain('"content"');
|
|
expect(document.body.textContent).toMatch(/0\s*bytes/);
|
|
});
|
|
});
|