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