1# Visual Test Plan: Agent Panel Image Rendering
2
3## ๐ฏ The Goal
4
5We want a visual regression test that **catches bugs in how `read_file` displays images**.
6
7If someone changes the code in `ReadFileTool` or the UI rendering in `thread_view.rs`, this test should fail and show us visually what changed.
8
9## โ ๏ธ Current Problem: The Test is Useless
10
11**The current test in `crates/zed/src/visual_test_runner.rs` does NOT test the real code!**
12
13Here's what it does now (WRONG):
141. Creates a `StubAgentConnection`
152. Hard-codes a fake tool call response with pre-baked image data
163. Injects that directly into `AcpThread`
174. Takes a screenshot
18
19**Why this is useless:** If you change how `ReadFileTool` produces its output (in `crates/agent/src/tools/read_file_tool.rs`), the test will still pass because it never runs that code! The test bypasses the entire tool execution pipeline.
20
21## โ
What We Actually Need
22
23The test should:
241. Create a real project with a real image file
252. Actually run the real `ReadFileTool::run()` method
263. Let the tool produce its real output via `event_stream.update_fields()`
274. Have that real output flow through to `AcpThread` and render in the UI
285. Take a screenshot of the real rendered result
29
30This way, if someone changes `ReadFileTool` or the UI rendering, the test will catch it.
31
32## ๐ Architecture Background (For Newcomers)
33
34Here's how the agent system works:
35
36### The Two "Thread" Types
37- **`Thread`** (in `crates/agent/src/thread.rs`) - Runs tools, talks to LLMs, produces events
38- **`AcpThread`** (in `crates/acp_thread/src/acp_thread.rs`) - Receives events and stores data for UI rendering
39
40### How Tools Work
411. `Thread` has registered tools (like `ReadFileTool`)
422. When a tool runs, it gets a `ToolCallEventStream`
433. The tool calls `event_stream.update_fields(...)` to send updates
444. Those updates become `ThreadEvent::ToolCallUpdate` events
455. Events flow to `AcpThread` via `handle_thread_events()` in `NativeAgentConnection`
466. `AcpThread` stores the data and the UI renders it
47
48### The Key File Locations
49- **Tool implementation:** `crates/agent/src/tools/read_file_tool.rs`
50 - Lines 163-188: Image file handling (calls `event_stream.update_fields()`)
51- **Event stream:** `crates/agent/src/thread.rs`
52 - `ToolCallEventStream::update_fields()` - sends updates
53 - `ToolCallEventStream::test()` - creates a test event stream
54- **UI rendering:** `crates/agent_ui/src/acp/thread_view.rs`
55 - `render_image_output()` - renders images in tool call output
56- **Current (broken) test:** `crates/zed/src/visual_test_runner.rs`
57 - `run_agent_thread_view_test()` - the function that needs fixing
58
59## ๐ง Implementation Plan
60
61### Option A: Direct Tool Invocation (Recommended)
62
63Run the real tool and capture its output:
64
65```rust
66// 1. Create a project with a real image file
67let fs = FakeFs::new(cx.executor());
68fs.insert_file("/project/test-image.png", EMBEDDED_TEST_IMAGE.to_vec()).await;
69let project = Project::test(fs.clone(), ["/project"], cx).await;
70
71// 2. Create the ReadFileTool (needs Thread, ActionLog)
72let action_log = cx.new(|_| ActionLog::new(project.clone()));
73// ... create Thread with project ...
74let tool = Arc::new(ReadFileTool::new(thread.downgrade(), project.clone(), action_log));
75
76// 3. Run the tool and capture events
77let (event_stream, mut event_receiver) = ToolCallEventStream::test();
78let input = ReadFileToolInput {
79 path: "project/test-image.png".to_string(),
80 start_line: None,
81 end_line: None,
82};
83tool.run(input, event_stream, cx).await?;
84
85// 4. Collect the ToolCallUpdateFields that the tool produced
86let updates = event_receiver.collect_updates();
87
88// 5. Create an AcpThread and inject the real tool output
89// ... create AcpThread ...
90acp_thread.update(cx, |thread, cx| {
91 // First create the tool call entry
92 thread.upsert_tool_call(initial_tool_call, cx)?;
93 // Then update it with the real output from the tool
94 for update in updates {
95 thread.update_tool_call(update, cx)?;
96 }
97})?;
98
99// 6. Render and screenshot
100```
101
102### Required Exports
103
104The `agent` crate needs to export these for the visual test:
105- `ReadFileTool` and `ReadFileToolInput`
106- `ToolCallEventStream::test()` (already has `#[cfg(feature = "test-support")]`)
107- `Thread` (to create the tool)
108
109Check `crates/agent/src/lib.rs` and add exports if needed.
110
111### Required Dependencies in `crates/zed/Cargo.toml`
112
113The `visual-tests` feature needs:
114```toml
115"agent/test-support" # For ToolCallEventStream::test() and tool exports
116```
117
118### Option B: Use NativeAgentConnection with Fake Model
119
120Alternatively, use the full agent flow with a fake LLM:
121
1221. Create `NativeAgentServer` with a `FakeLanguageModel`
1232. Program the fake model to return a tool call for `read_file`
1243. Let the real agent flow execute the tool
1254. The tool runs, produces output, flows through to UI
126
127This is more complex but tests more of the real code path.
128
129## ๐ Step-by-Step Implementation Checklist
130
131### Phase 1: Enable Tool Access
132- [x] Add `agent/test-support` to `visual-tests` feature in `crates/zed/Cargo.toml`
133- [x] Verify `ReadFileTool`, `ReadFileToolInput`, `ToolCallEventStream::test()` are exported
134- [x] Added additional required features: `language_model/test-support`, `fs/test-support`, `action_log`
135
136### Phase 2: Rewrite the Test
137- [x] In `run_agent_thread_view_test()`, remove the fake stub response
138- [x] Create a real temp directory with a real image file (FakeFs doesn't work in visual test runner)
139- [x] Create the real `ReadFileTool` with Thread, ActionLog, etc.
140- [x] Run the tool with `ToolCallEventStream::test()`
141- [x] Capture the `ToolCallUpdateFields` it produces
142- [x] Use the real tool output to populate the stub connection's response
143
144### Phase 3: Verify It Works
145- [x] Run `UPDATE_BASELINE=1 cargo run -p zed --bin visual_test_runner --features visual-tests`
146- [x] Check the screenshot shows the real tool output
147- [x] Intentionally break `read_file_tool.rs` (comment out `event_stream.update_fields`)
148- [x] Verified the test fails with: "ReadFileTool did not produce any content - the tool is broken!"
149- [x] Restored the code and verified test passes again
150
151## ๐งช How to Verify the Test is Actually Testing Real Code
152
153After implementing, do this sanity check:
154
1551. In `crates/agent/src/tools/read_file_tool.rs`, comment out lines 181-185:
156 ```rust
157 // event_stream.update_fields(ToolCallUpdateFields::new().content(vec![
158 // acp::ToolCallContent::Content(acp::Content::new(acp::ContentBlock::Image(
159 // acp::ImageContent::new(language_model_image.source.clone(), "image/png"),
160 // ))),
161 // ]));
162 ```
163
1642. Run the visual test - it should FAIL or produce a visibly different screenshot
165
1663. Restore the code - test should pass again
167
168If commenting out the real tool code doesn't affect the test, the test is still broken!
169
170## ๐ Files Modified
171
172| File | Change |
173|------|--------|
174| `crates/zed/Cargo.toml` | Added `agent/test-support`, `language_model/test-support`, `fs/test-support`, `action_log` to `visual-tests` feature |
175| `crates/zed/src/visual_test_runner.rs` | Rewrote `run_agent_thread_view_test()` to run the real `ReadFileTool` and capture its output |
176
177Note: No changes needed to `crates/agent/src/lib.rs` - all necessary exports were already public.
178
179## โ
Already Completed (Don't Redo These)
180
181These changes have already been made and are working:
182
1831. **`read_file` tool sends image content** - `crates/agent/src/tools/read_file_tool.rs` now calls `event_stream.update_fields()` with image content blocks (lines 181-185)
184
1852. **UI renders images** - `crates/agent_ui/src/acp/thread_view.rs` has `render_image_output()` that shows dimensions ("512ร512 PNG") and a "Go to File" button
186
1873. **Image tool calls auto-expand** - The UI automatically expands tool calls that return images
188
1894. **Visual test infrastructure exists** - The test runner, baseline comparison, etc. all work
190
191The only thing broken is that the test doesn't actually run the real tool code!
192
193## ๐ Related Code References
194
195- Tool implementation: [read_file_tool.rs](file:///Users/rtfeldman/code/zed5/crates/agent/src/tools/read_file_tool.rs)
196- Event stream: [thread.rs lines 2501-2596](file:///Users/rtfeldman/code/zed5/crates/agent/src/thread.rs#L2501-L2596)
197- UI rendering: [thread_view.rs render_image_output](file:///Users/rtfeldman/code/zed5/crates/agent_ui/src/acp/thread_view.rs#L3146-L3217)
198- Current test: [visual_test_runner.rs run_agent_thread_view_test](file:///Users/rtfeldman/code/zed5/crates/zed/src/visual_test_runner.rs#L778-L943)