1use crate::buffer::Buffer;
2use crate::context::Context;
3use crate::event::{Event, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseKind};
4use crate::layout;
5use crate::rect::Rect;
6use crate::style::Theme;
7
8pub struct EventBuilder {
9 events: Vec<Event>,
10}
11
12impl EventBuilder {
13 pub fn new() -> Self {
14 Self { events: Vec::new() }
15 }
16
17 pub fn key(mut self, c: char) -> Self {
18 self.events.push(Event::Key(KeyEvent {
19 code: KeyCode::Char(c),
20 modifiers: KeyModifiers::NONE,
21 }));
22 self
23 }
24
25 pub fn key_code(mut self, code: KeyCode) -> Self {
26 self.events.push(Event::Key(KeyEvent {
27 code,
28 modifiers: KeyModifiers::NONE,
29 }));
30 self
31 }
32
33 pub fn key_with(mut self, code: KeyCode, modifiers: KeyModifiers) -> Self {
34 self.events.push(Event::Key(KeyEvent { code, modifiers }));
35 self
36 }
37
38 pub fn click(mut self, x: u32, y: u32) -> Self {
39 self.events.push(Event::Mouse(MouseEvent {
40 kind: MouseKind::Down(MouseButton::Left),
41 x,
42 y,
43 modifiers: KeyModifiers::NONE,
44 }));
45 self
46 }
47
48 pub fn scroll_up(mut self, x: u32, y: u32) -> Self {
49 self.events.push(Event::Mouse(MouseEvent {
50 kind: MouseKind::ScrollUp,
51 x,
52 y,
53 modifiers: KeyModifiers::NONE,
54 }));
55 self
56 }
57
58 pub fn scroll_down(mut self, x: u32, y: u32) -> Self {
59 self.events.push(Event::Mouse(MouseEvent {
60 kind: MouseKind::ScrollDown,
61 x,
62 y,
63 modifiers: KeyModifiers::NONE,
64 }));
65 self
66 }
67
68 pub fn paste(mut self, text: impl Into<String>) -> Self {
69 self.events.push(Event::Paste(text.into()));
70 self
71 }
72
73 pub fn resize(mut self, width: u32, height: u32) -> Self {
74 self.events.push(Event::Resize(width, height));
75 self
76 }
77
78 pub fn build(self) -> Vec<Event> {
79 self.events
80 }
81}
82
83impl Default for EventBuilder {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89pub struct TestBackend {
90 buffer: Buffer,
91 width: u32,
92 height: u32,
93}
94
95impl TestBackend {
96 pub fn new(width: u32, height: u32) -> Self {
97 let area = Rect::new(0, 0, width, height);
98 Self {
99 buffer: Buffer::empty(area),
100 width,
101 height,
102 }
103 }
104
105 pub fn render(&mut self, f: impl FnOnce(&mut Context)) {
107 let mut ctx = Context::new(
108 Vec::new(),
109 self.width,
110 self.height,
111 0,
112 0,
113 0,
114 Vec::new(),
115 Vec::new(),
116 false,
117 Theme::dark(),
118 None,
119 );
120 f(&mut ctx);
121 let mut tree = layout::build_tree(&ctx.commands);
122 let area = Rect::new(0, 0, self.width, self.height);
123 layout::compute(&mut tree, area);
124 self.buffer.reset();
125 layout::render(&tree, &mut self.buffer);
126 }
127
128 pub fn render_with_events(
130 &mut self,
131 events: Vec<Event>,
132 focus_index: usize,
133 prev_focus_count: usize,
134 f: impl FnOnce(&mut Context),
135 ) {
136 let mut ctx = Context::new(
137 events,
138 self.width,
139 self.height,
140 0,
141 focus_index,
142 prev_focus_count,
143 Vec::new(),
144 Vec::new(),
145 false,
146 Theme::dark(),
147 None,
148 );
149 ctx.process_focus_keys();
150 f(&mut ctx);
151 let mut tree = layout::build_tree(&ctx.commands);
152 let area = Rect::new(0, 0, self.width, self.height);
153 layout::compute(&mut tree, area);
154 self.buffer.reset();
155 layout::render(&tree, &mut self.buffer);
156 }
157
158 pub fn run_with_events(&mut self, events: Vec<Event>, f: impl FnOnce(&mut crate::Context)) {
159 self.render_with_events(events, 0, 0, f);
160 }
161
162 pub fn line(&self, y: u32) -> String {
164 let mut s = String::new();
165 for x in 0..self.width {
166 s.push_str(&self.buffer.get(x, y).symbol);
167 }
168 s.trim_end().to_string()
169 }
170
171 pub fn assert_line(&self, y: u32, expected: &str) {
173 let line = self.line(y);
174 assert_eq!(
175 line, expected,
176 "Line {y}: expected {expected:?}, got {line:?}"
177 );
178 }
179
180 pub fn assert_line_contains(&self, y: u32, expected: &str) {
182 let line = self.line(y);
183 assert!(
184 line.contains(expected),
185 "Line {y}: expected to contain {expected:?}, got {line:?}"
186 );
187 }
188
189 pub fn assert_contains(&self, expected: &str) {
191 for y in 0..self.height {
192 if self.line(y).contains(expected) {
193 return;
194 }
195 }
196 let mut all_lines = String::new();
197 for y in 0..self.height {
198 all_lines.push_str(&format!("{}: {}\n", y, self.line(y)));
199 }
200 panic!("Buffer does not contain {expected:?}.\nBuffer:\n{all_lines}");
201 }
202
203 pub fn buffer(&self) -> &Buffer {
204 &self.buffer
205 }
206
207 pub fn width(&self) -> u32 {
208 self.width
209 }
210
211 pub fn height(&self) -> u32 {
212 self.height
213 }
214}