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