Skip to main content

lv_tui/
testbuffer.rs

1//! Test utilities for asserting component render output.
2
3use crate::buffer::Buffer;
4use crate::component::Component;
5use crate::geom::{Rect, Size};
6use crate::render::RenderCx;
7use crate::style::Color;
8
9/// A buffer wrapper for testing component render output.
10pub struct TestBuffer {
11    pub buffer: Buffer,
12}
13
14impl TestBuffer {
15    /// Creates a test buffer of the given size.
16    pub fn new(width: u16, height: u16) -> Self {
17        Self { buffer: Buffer::new(Size { width, height }) }
18    }
19
20    /// Renders a component into this buffer.
21    pub fn render<C: Component>(&mut self, component: &C) {
22        let rect = Rect { x: 0, y: 0, width: self.buffer.size.width, height: self.buffer.size.height };
23        let style = component.style();
24        let mut cx = RenderCx::new(rect, &mut self.buffer, style);
25        component.render(&mut cx);
26    }
27
28    /// Asserts that the cell at (x, y) contains the expected text.
29    #[track_caller]
30    pub fn assert_text(&self, x: u16, y: u16, expected: &str) {
31        let index = y as usize * self.buffer.size.width as usize + x as usize;
32        let cell = &self.buffer.cells[index];
33        assert_eq!(cell.symbol, expected,
34            "cell at ({}, {}) expected {:?}, got {:?}", x, y, expected, cell.symbol);
35    }
36
37    /// Asserts that a line starting from x=0 matches the expected text.
38    /// Spaces at the end of rendered output are trimmed before comparison.
39    #[track_caller]
40    pub fn assert_line(&self, y: u16, expected: &str) {
41        let mut actual = String::new();
42        for x in 0..self.buffer.size.width {
43            let index = y as usize * self.buffer.size.width as usize + x as usize;
44            actual.push_str(&self.buffer.cells[index].symbol);
45        }
46        let actual = actual.trim_end();
47        assert_eq!(actual, expected,
48            "line {} expected {:?}, got {:?}", y, expected, actual);
49    }
50
51    /// Asserts entire buffer lines match expected strings (trailing spaces trimmed).
52    #[track_caller]
53    pub fn assert_lines(&self, expected: &[&str]) {
54        for (y, exp) in expected.iter().enumerate() {
55            self.assert_line(y as u16, exp);
56        }
57    }
58
59    /// Returns the foreground color of the cell at (x, y).
60    pub fn cell_fg(&self, x: u16, y: u16) -> Option<Color> {
61        let index = y as usize * self.buffer.size.width as usize + x as usize;
62        self.buffer.cells[index].style.fg
63    }
64
65    /// Returns the background color of the cell at (x, y).
66    pub fn cell_bg(&self, x: u16, y: u16) -> Option<Color> {
67        let index = y as usize * self.buffer.size.width as usize + x as usize;
68        self.buffer.cells[index].style.bg
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use crate::widgets::{Checkbox, Label};
76
77    #[test]
78    fn test_label_renders_text() {
79        let mut tb = TestBuffer::new(20, 1);
80        tb.render(&Label::new("hello"));
81        tb.assert_text(0, 0, "h");
82        tb.assert_line(0, "hello");
83    }
84
85    #[test]
86    fn test_checkbox_unchecked() {
87        let mut tb = TestBuffer::new(20, 1);
88        tb.render(&Checkbox::new("opt"));
89        tb.assert_text(1, 0, " "); // unchecked bracket
90    }
91
92    #[test]
93    fn test_checkbox_checked() {
94        let mut tb = TestBuffer::new(20, 1);
95        tb.render(&Checkbox::new("opt").checked());
96        tb.assert_text(1, 0, "✓");
97    }
98}