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 resize(mut self, width: u32, height: u32) -> Self {
69 self.events.push(Event::Resize(width, height));
70 self
71 }
72
73 pub fn build(self) -> Vec<Event> {
74 self.events
75 }
76}
77
78impl Default for EventBuilder {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84pub struct TestBackend {
85 buffer: Buffer,
86 width: u32,
87 height: u32,
88}
89
90impl TestBackend {
91 pub fn new(width: u32, height: u32) -> Self {
92 let area = Rect::new(0, 0, width, height);
93 Self {
94 buffer: Buffer::empty(area),
95 width,
96 height,
97 }
98 }
99
100 pub fn render(&mut self, f: impl FnOnce(&mut Context)) {
102 let mut ctx = Context::new(
103 Vec::new(),
104 self.width,
105 self.height,
106 0,
107 0,
108 0,
109 Vec::new(),
110 Vec::new(),
111 false,
112 Theme::dark(),
113 None,
114 );
115 f(&mut ctx);
116 let mut tree = layout::build_tree(&ctx.commands);
117 let area = Rect::new(0, 0, self.width, self.height);
118 layout::compute(&mut tree, area);
119 self.buffer.reset();
120 layout::render(&tree, &mut self.buffer);
121 }
122
123 pub fn render_with_events(
125 &mut self,
126 events: Vec<Event>,
127 focus_index: usize,
128 prev_focus_count: usize,
129 f: impl FnOnce(&mut Context),
130 ) {
131 let mut ctx = Context::new(
132 events,
133 self.width,
134 self.height,
135 0,
136 focus_index,
137 prev_focus_count,
138 Vec::new(),
139 Vec::new(),
140 false,
141 Theme::dark(),
142 None,
143 );
144 ctx.process_focus_keys();
145 f(&mut ctx);
146 let mut tree = layout::build_tree(&ctx.commands);
147 let area = Rect::new(0, 0, self.width, self.height);
148 layout::compute(&mut tree, area);
149 self.buffer.reset();
150 layout::render(&tree, &mut self.buffer);
151 }
152
153 pub fn run_with_events(&mut self, events: Vec<Event>, f: impl FnOnce(&mut crate::Context)) {
154 self.render_with_events(events, 0, 0, f);
155 }
156
157 pub fn line(&self, y: u32) -> String {
159 let mut s = String::new();
160 for x in 0..self.width {
161 s.push_str(&self.buffer.get(x, y).symbol);
162 }
163 s.trim_end().to_string()
164 }
165
166 pub fn assert_line(&self, y: u32, expected: &str) {
168 let line = self.line(y);
169 assert_eq!(
170 line, expected,
171 "Line {y}: expected {expected:?}, got {line:?}"
172 );
173 }
174
175 pub fn assert_line_contains(&self, y: u32, expected: &str) {
177 let line = self.line(y);
178 assert!(
179 line.contains(expected),
180 "Line {y}: expected to contain {expected:?}, got {line:?}"
181 );
182 }
183
184 pub fn assert_contains(&self, expected: &str) {
186 for y in 0..self.height {
187 if self.line(y).contains(expected) {
188 return;
189 }
190 }
191 let mut all_lines = String::new();
192 for y in 0..self.height {
193 all_lines.push_str(&format!("{}: {}\n", y, self.line(y)));
194 }
195 panic!("Buffer does not contain {expected:?}.\nBuffer:\n{all_lines}");
196 }
197
198 pub fn buffer(&self) -> &Buffer {
199 &self.buffer
200 }
201
202 pub fn width(&self) -> u32 {
203 self.width
204 }
205
206 pub fn height(&self) -> u32 {
207 self.height
208 }
209}