1use crate::buffer::Buffer;
8use crate::context::Context;
9use crate::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseKind};
10use crate::layout;
11use crate::rect::Rect;
12use crate::style::Theme;
13
14pub struct EventBuilder {
33 events: Vec<Event>,
34}
35
36impl EventBuilder {
37 pub fn new() -> Self {
39 Self { events: Vec::new() }
40 }
41
42 pub fn key(mut self, c: char) -> Self {
44 self.events.push(Event::Key(KeyEvent {
45 code: KeyCode::Char(c),
46 modifiers: KeyModifiers::NONE,
47 }));
48 self
49 }
50
51 pub fn key_code(mut self, code: KeyCode) -> Self {
53 self.events.push(Event::Key(KeyEvent {
54 code,
55 modifiers: KeyModifiers::NONE,
56 }));
57 self
58 }
59
60 pub fn key_with(mut self, code: KeyCode, modifiers: KeyModifiers) -> Self {
62 self.events.push(Event::Key(KeyEvent { code, modifiers }));
63 self
64 }
65
66 pub fn click(mut self, x: u32, y: u32) -> Self {
68 self.events.push(Event::Mouse(MouseEvent {
69 kind: MouseKind::Down(MouseButton::Left),
70 x,
71 y,
72 modifiers: KeyModifiers::NONE,
73 }));
74 self
75 }
76
77 pub fn scroll_up(mut self, x: u32, y: u32) -> Self {
79 self.events.push(Event::Mouse(MouseEvent {
80 kind: MouseKind::ScrollUp,
81 x,
82 y,
83 modifiers: KeyModifiers::NONE,
84 }));
85 self
86 }
87
88 pub fn scroll_down(mut self, x: u32, y: u32) -> Self {
90 self.events.push(Event::Mouse(MouseEvent {
91 kind: MouseKind::ScrollDown,
92 x,
93 y,
94 modifiers: KeyModifiers::NONE,
95 }));
96 self
97 }
98
99 pub fn paste(mut self, text: impl Into<String>) -> Self {
101 self.events.push(Event::Paste(text.into()));
102 self
103 }
104
105 pub fn resize(mut self, width: u32, height: u32) -> Self {
107 self.events.push(Event::Resize(width, height));
108 self
109 }
110
111 pub fn build(self) -> Vec<Event> {
113 self.events
114 }
115}
116
117impl Default for EventBuilder {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123pub struct TestBackend {
142 buffer: Buffer,
143 width: u32,
144 height: u32,
145}
146
147impl TestBackend {
148 pub fn new(width: u32, height: u32) -> Self {
150 let area = Rect::new(0, 0, width, height);
151 Self {
152 buffer: Buffer::empty(area),
153 width,
154 height,
155 }
156 }
157
158 pub fn render(&mut self, f: impl FnOnce(&mut Context)) {
160 let mut ctx = Context::new(
161 Vec::new(),
162 self.width,
163 self.height,
164 0,
165 0,
166 0,
167 Vec::new(),
168 Vec::new(),
169 Vec::new(),
170 false,
171 Theme::dark(),
172 None,
173 false,
174 );
175 f(&mut ctx);
176 let mut tree = layout::build_tree(&ctx.commands);
177 let area = Rect::new(0, 0, self.width, self.height);
178 layout::compute(&mut tree, area);
179 self.buffer.reset();
180 layout::render(&tree, &mut self.buffer);
181 }
182
183 pub fn render_with_events(
185 &mut self,
186 events: Vec<Event>,
187 focus_index: usize,
188 prev_focus_count: usize,
189 f: impl FnOnce(&mut Context),
190 ) {
191 let mut ctx = Context::new(
192 events,
193 self.width,
194 self.height,
195 0,
196 focus_index,
197 prev_focus_count,
198 Vec::new(),
199 Vec::new(),
200 Vec::new(),
201 false,
202 Theme::dark(),
203 None,
204 false,
205 );
206 ctx.process_focus_keys();
207 f(&mut ctx);
208 let mut tree = layout::build_tree(&ctx.commands);
209 let area = Rect::new(0, 0, self.width, self.height);
210 layout::compute(&mut tree, area);
211 self.buffer.reset();
212 layout::render(&tree, &mut self.buffer);
213 }
214
215 pub fn run_with_events(&mut self, events: Vec<Event>, f: impl FnOnce(&mut crate::Context)) {
217 self.render_with_events(events, 0, 0, f);
218 }
219
220 pub fn line(&self, y: u32) -> String {
222 let mut s = String::new();
223 for x in 0..self.width {
224 s.push_str(&self.buffer.get(x, y).symbol);
225 }
226 s.trim_end().to_string()
227 }
228
229 pub fn assert_line(&self, y: u32, expected: &str) {
231 let line = self.line(y);
232 assert_eq!(
233 line, expected,
234 "Line {y}: expected {expected:?}, got {line:?}"
235 );
236 }
237
238 pub fn assert_line_contains(&self, y: u32, expected: &str) {
240 let line = self.line(y);
241 assert!(
242 line.contains(expected),
243 "Line {y}: expected to contain {expected:?}, got {line:?}"
244 );
245 }
246
247 pub fn assert_contains(&self, expected: &str) {
249 for y in 0..self.height {
250 if self.line(y).contains(expected) {
251 return;
252 }
253 }
254 let mut all_lines = String::new();
255 for y in 0..self.height {
256 all_lines.push_str(&format!("{}: {}\n", y, self.line(y)));
257 }
258 panic!("Buffer does not contain {expected:?}.\nBuffer:\n{all_lines}");
259 }
260
261 pub fn buffer(&self) -> &Buffer {
263 &self.buffer
264 }
265
266 pub fn width(&self) -> u32 {
268 self.width
269 }
270
271 pub fn height(&self) -> u32 {
273 self.height
274 }
275
276 pub fn to_string_trimmed(&self) -> String {
281 let mut lines = Vec::with_capacity(self.height as usize);
282 for y in 0..self.height {
283 lines.push(self.line(y));
284 }
285 while lines.last().is_some_and(|l| l.is_empty()) {
286 lines.pop();
287 }
288 lines.join("\n")
289 }
290}
291
292impl std::fmt::Display for TestBackend {
293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 write!(f, "{}", self.to_string_trimmed())
295 }
296}