Add sendImages E2E tests for DaemonClient

Add two E2E tests that verify image attachment support in sendMessage():
- "sends message with image attachment to Claude agent" - tests single image
- "sends message with multiple image attachments" - tests multiple images

Tests use minimal 1x1 PNG base64 and verify:
- Server logs image attachments received
- Agent processes message and completes turn
- Assistant response is generated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2025-12-25 18:24:29 +07:00
co-authored by Claude Opus 4.5
parent 8a614aac58
commit 7b8d032bbc
2 changed files with 142 additions and 4 deletions
@@ -1504,4 +1504,134 @@ describe("daemon E2E", () => {
60000 // 1 minute timeout
);
});
describe("sendImages", () => {
// Minimal 1x1 red PNG image encoded in base64
// This is a valid PNG that can be decoded by image processing libraries
const MINIMAL_PNG_BASE64 =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";
test(
"sends message with image attachment to Claude agent",
async () => {
const cwd = tmpCwd();
// Create Claude agent
const agent = await ctx.client.createAgent({
provider: "claude",
cwd,
title: "Image Test Agent",
});
expect(agent.id).toBeTruthy();
expect(agent.provider).toBe("claude");
// Send message with image attachment
await ctx.client.sendMessage(
agent.id,
"I'm sending you an image. Describe what information you received about the image attachment. Reply with a single short sentence.",
{
images: [
{
data: MINIMAL_PNG_BASE64,
mimeType: "image/png",
},
],
}
);
// Wait for agent to complete
const finalState = await ctx.client.waitForAgentIdle(agent.id, 120000);
expect(finalState.status).toBe("idle");
expect(finalState.lastError).toBeUndefined();
// Verify stream events show the agent processed the message
const queue = ctx.client.getMessageQueue();
const streamEvents = queue.filter(
(m) => m.type === "agent_stream" && m.payload.agentId === agent.id
);
// Should have received stream events
expect(streamEvents.length).toBeGreaterThan(0);
// Verify turn completed successfully
const hasTurnCompleted = streamEvents.some(
(m) =>
m.type === "agent_stream" && m.payload.event.type === "turn_completed"
);
expect(hasTurnCompleted).toBe(true);
// Verify there was an assistant message in the timeline
const hasAssistantMessage = streamEvents.some((m) => {
if (m.type !== "agent_stream" || m.payload.event.type !== "timeline") {
return false;
}
const item = m.payload.event.item;
return item.type === "assistant_message" && item.text.length > 0;
});
expect(hasAssistantMessage).toBe(true);
// Cleanup
await ctx.client.deleteAgent(agent.id);
rmSync(cwd, { recursive: true, force: true });
},
180000 // 3 minute timeout for Claude API call
);
test(
"sends message with multiple image attachments",
async () => {
const cwd = tmpCwd();
// Create Claude agent
const agent = await ctx.client.createAgent({
provider: "claude",
cwd,
title: "Multi-Image Test Agent",
});
expect(agent.id).toBeTruthy();
// Send message with two image attachments
await ctx.client.sendMessage(
agent.id,
"I'm sending you two images. How many image attachments are mentioned in the context? Reply with just a number.",
{
images: [
{
data: MINIMAL_PNG_BASE64,
mimeType: "image/png",
},
{
data: MINIMAL_PNG_BASE64,
mimeType: "image/jpeg",
},
],
}
);
// Wait for agent to complete
const finalState = await ctx.client.waitForAgentIdle(agent.id, 120000);
expect(finalState.status).toBe("idle");
expect(finalState.lastError).toBeUndefined();
// Verify turn completed
const queue = ctx.client.getMessageQueue();
const hasTurnCompleted = queue.some(
(m) =>
m.type === "agent_stream" &&
m.payload.agentId === agent.id &&
m.payload.event.type === "turn_completed"
);
expect(hasTurnCompleted).toBe(true);
// Cleanup
await ctx.client.deleteAgent(agent.id);
rmSync(cwd, { recursive: true, force: true });
},
180000 // 3 minute timeout for Claude API call
);
});
});