mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-14 00:47:14 +00:00
2cfe82a47e
- Simplify `assert_buffer_eq!` logic. - Deprecate `assert_buffer_eq!`. - Introduce `TestBackend::assert_buffer_lines`. Also simplify many tests involving buffer comparisons. For the deprecation, just use `assert_eq` instead of `assert_buffer_eq`: ```diff -assert_buffer_eq!(actual, expected); +assert_eq!(actual, expected); ``` --- I noticed `assert_buffer_eq!` creating no test coverage reports and looked into this macro. First I simplified it. Then I noticed a bunch of `assert_eq!(buffer, …)` and other indirect usages of this macro (like `TestBackend::assert_buffer`). The good thing here is that it's mainly used in tests so not many changes to the library code.
381 lines
12 KiB
Rust
381 lines
12 KiB
Rust
use ratatui::{
|
|
backend::TestBackend,
|
|
buffer::Buffer,
|
|
layout::Rect,
|
|
style::{Color, Style},
|
|
symbols,
|
|
text::Line,
|
|
widgets::{Block, Borders, HighlightSpacing, List, ListItem, ListState},
|
|
Terminal,
|
|
};
|
|
use rstest::rstest;
|
|
|
|
#[test]
|
|
fn list_should_shows_the_length() {
|
|
let items = vec![
|
|
ListItem::new("Item 1"),
|
|
ListItem::new("Item 2"),
|
|
ListItem::new("Item 3"),
|
|
];
|
|
let list = List::new(items);
|
|
assert_eq!(list.len(), 3);
|
|
assert!(!list.is_empty());
|
|
|
|
let empty_list = List::default();
|
|
assert_eq!(empty_list.len(), 0);
|
|
assert!(empty_list.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn widgets_list_should_highlight_the_selected_item() {
|
|
let backend = TestBackend::new(10, 3);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
let mut state = ListState::default();
|
|
state.select(Some(1));
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let items = vec![
|
|
ListItem::new("Item 1"),
|
|
ListItem::new("Item 2"),
|
|
ListItem::new("Item 3"),
|
|
];
|
|
let list = List::new(items)
|
|
.highlight_style(Style::default().bg(Color::Yellow))
|
|
.highlight_symbol(">> ");
|
|
f.render_stateful_widget(list, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
#[rustfmt::skip]
|
|
let mut expected = Buffer::with_lines([
|
|
" Item 1 ",
|
|
">> Item 2 ",
|
|
" Item 3 ",
|
|
]);
|
|
for x in 0..10 {
|
|
expected.get_mut(x, 1).set_bg(Color::Yellow);
|
|
}
|
|
terminal.backend().assert_buffer(&expected);
|
|
}
|
|
|
|
#[test]
|
|
fn widgets_list_should_highlight_the_selected_item_wide_symbol() {
|
|
let backend = TestBackend::new(10, 3);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
let mut state = ListState::default();
|
|
|
|
let wide_symbol = "▶ ";
|
|
|
|
state.select(Some(1));
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let items = vec![
|
|
ListItem::new("Item 1"),
|
|
ListItem::new("Item 2"),
|
|
ListItem::new("Item 3"),
|
|
];
|
|
let list = List::new(items)
|
|
.highlight_style(Style::default().bg(Color::Yellow))
|
|
.highlight_symbol(wide_symbol);
|
|
f.render_stateful_widget(list, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
#[rustfmt::skip]
|
|
let mut expected = Buffer::with_lines([
|
|
" Item 1 ",
|
|
"▶ Item 2 ",
|
|
" Item 3 ",
|
|
]);
|
|
for x in 0..10 {
|
|
expected.get_mut(x, 1).set_bg(Color::Yellow);
|
|
}
|
|
terminal.backend().assert_buffer(&expected);
|
|
}
|
|
|
|
#[test]
|
|
fn widgets_list_should_truncate_items() {
|
|
struct TruncateTestCase<'a> {
|
|
selected: Option<usize>,
|
|
items: Vec<ListItem<'a>>,
|
|
expected: Buffer,
|
|
}
|
|
|
|
let backend = TestBackend::new(10, 2);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
|
|
let cases = [
|
|
// An item is selected
|
|
TruncateTestCase {
|
|
selected: Some(0),
|
|
items: vec![
|
|
ListItem::new("A very long line"),
|
|
ListItem::new("A very long line"),
|
|
],
|
|
expected: Buffer::with_lines([
|
|
format!(">> A ve{} ", symbols::line::VERTICAL),
|
|
format!(" A ve{} ", symbols::line::VERTICAL),
|
|
]),
|
|
},
|
|
// No item is selected
|
|
TruncateTestCase {
|
|
selected: None,
|
|
items: vec![
|
|
ListItem::new("A very long line"),
|
|
ListItem::new("A very long line"),
|
|
],
|
|
expected: Buffer::with_lines([
|
|
format!("A very {} ", symbols::line::VERTICAL),
|
|
format!("A very {} ", symbols::line::VERTICAL),
|
|
]),
|
|
},
|
|
];
|
|
for case in cases {
|
|
let mut state = ListState::default();
|
|
state.select(case.selected);
|
|
terminal
|
|
.draw(|f| {
|
|
let list = List::new(case.items.clone())
|
|
.block(Block::new().borders(Borders::RIGHT))
|
|
.highlight_symbol(">> ");
|
|
f.render_stateful_widget(list, Rect::new(0, 0, 8, 2), &mut state);
|
|
})
|
|
.unwrap();
|
|
terminal.backend().assert_buffer(&case.expected);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn widgets_list_should_clamp_offset_if_items_are_removed() {
|
|
let backend = TestBackend::new(10, 4);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
let mut state = ListState::default();
|
|
|
|
// render with 6 items => offset will be at 2
|
|
state.select(Some(5));
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let items = vec![
|
|
ListItem::new("Item 0"),
|
|
ListItem::new("Item 1"),
|
|
ListItem::new("Item 2"),
|
|
ListItem::new("Item 3"),
|
|
ListItem::new("Item 4"),
|
|
ListItem::new("Item 5"),
|
|
];
|
|
let list = List::new(items).highlight_symbol(">> ");
|
|
f.render_stateful_widget(list, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
terminal.backend().assert_buffer_lines([
|
|
" Item 2 ",
|
|
" Item 3 ",
|
|
" Item 4 ",
|
|
">> Item 5 ",
|
|
]);
|
|
|
|
// render again with 1 items => check offset is clamped to 1
|
|
state.select(Some(1));
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let items = vec![ListItem::new("Item 3")];
|
|
let list = List::new(items).highlight_symbol(">> ");
|
|
f.render_stateful_widget(list, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
terminal.backend().assert_buffer_lines([
|
|
" Item 3 ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
]);
|
|
}
|
|
|
|
#[test]
|
|
fn widgets_list_should_display_multiline_items() {
|
|
let backend = TestBackend::new(10, 6);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
let mut state = ListState::default();
|
|
state.select(Some(1));
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let items = vec![
|
|
ListItem::new(vec![Line::from("Item 1"), Line::from("Item 1a")]),
|
|
ListItem::new(vec![Line::from("Item 2"), Line::from("Item 2b")]),
|
|
ListItem::new(vec![Line::from("Item 3"), Line::from("Item 3c")]),
|
|
];
|
|
let list = List::new(items)
|
|
.highlight_style(Style::default().bg(Color::Yellow))
|
|
.highlight_symbol(">> ");
|
|
f.render_stateful_widget(list, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
let mut expected = Buffer::with_lines([
|
|
" Item 1 ",
|
|
" Item 1a",
|
|
">> Item 2 ",
|
|
" Item 2b",
|
|
" Item 3 ",
|
|
" Item 3c",
|
|
]);
|
|
for x in 0..10 {
|
|
expected.get_mut(x, 2).set_bg(Color::Yellow);
|
|
expected.get_mut(x, 3).set_bg(Color::Yellow);
|
|
}
|
|
terminal.backend().assert_buffer(&expected);
|
|
}
|
|
|
|
#[test]
|
|
fn widgets_list_should_repeat_highlight_symbol() {
|
|
let backend = TestBackend::new(10, 6);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
let mut state = ListState::default();
|
|
state.select(Some(1));
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let items = vec![
|
|
ListItem::new(vec![Line::from("Item 1"), Line::from("Item 1a")]),
|
|
ListItem::new(vec![Line::from("Item 2"), Line::from("Item 2b")]),
|
|
ListItem::new(vec![Line::from("Item 3"), Line::from("Item 3c")]),
|
|
];
|
|
let list = List::new(items)
|
|
.highlight_style(Style::default().bg(Color::Yellow))
|
|
.highlight_symbol(">> ")
|
|
.repeat_highlight_symbol(true);
|
|
f.render_stateful_widget(list, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
let mut expected = Buffer::with_lines([
|
|
" Item 1 ",
|
|
" Item 1a",
|
|
">> Item 2 ",
|
|
">> Item 2b",
|
|
" Item 3 ",
|
|
" Item 3c",
|
|
]);
|
|
for x in 0..10 {
|
|
expected.get_mut(x, 2).set_bg(Color::Yellow);
|
|
expected.get_mut(x, 3).set_bg(Color::Yellow);
|
|
}
|
|
terminal.backend().assert_buffer(&expected);
|
|
}
|
|
|
|
#[test]
|
|
fn widget_list_should_not_ignore_empty_string_items() {
|
|
let backend = TestBackend::new(6, 4);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
terminal
|
|
.draw(|f| {
|
|
let items = vec![
|
|
ListItem::new("Item 1"),
|
|
ListItem::new(""),
|
|
ListItem::new(""),
|
|
ListItem::new("Item 4"),
|
|
];
|
|
|
|
let list = List::new(items)
|
|
.style(Style::default())
|
|
.highlight_style(Style::default());
|
|
|
|
f.render_widget(list, f.size());
|
|
})
|
|
.unwrap();
|
|
terminal
|
|
.backend()
|
|
.assert_buffer_lines(["Item 1", "", "", "Item 4"]);
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::none_when_selected(None, HighlightSpacing::WhenSelected, [
|
|
"┌─────────────┐",
|
|
"│Item 1 │",
|
|
"│Item 1a │",
|
|
"│Item 2 │",
|
|
"│Item 2b │",
|
|
"│Item 3 │",
|
|
"│Item 3c │",
|
|
"└─────────────┘",
|
|
])]
|
|
#[case::none_always(None, HighlightSpacing::Always, [
|
|
"┌─────────────┐",
|
|
"│ Item 1 │",
|
|
"│ Item 1a │",
|
|
"│ Item 2 │",
|
|
"│ Item 2b │",
|
|
"│ Item 3 │",
|
|
"│ Item 3c │",
|
|
"└─────────────┘",
|
|
])]
|
|
#[case::none_never(None, HighlightSpacing::Never, [
|
|
"┌─────────────┐",
|
|
"│Item 1 │",
|
|
"│Item 1a │",
|
|
"│Item 2 │",
|
|
"│Item 2b │",
|
|
"│Item 3 │",
|
|
"│Item 3c │",
|
|
"└─────────────┘",
|
|
])]
|
|
#[case::first_when_selected(Some(0), HighlightSpacing::WhenSelected, [
|
|
"┌─────────────┐",
|
|
"│>> Item 1 │",
|
|
"│ Item 1a │",
|
|
"│ Item 2 │",
|
|
"│ Item 2b │",
|
|
"│ Item 3 │",
|
|
"│ Item 3c │",
|
|
"└─────────────┘",
|
|
])]
|
|
#[case::first_always(Some(0), HighlightSpacing::Always, [
|
|
"┌─────────────┐",
|
|
"│>> Item 1 │",
|
|
"│ Item 1a │",
|
|
"│ Item 2 │",
|
|
"│ Item 2b │",
|
|
"│ Item 3 │",
|
|
"│ Item 3c │",
|
|
"└─────────────┘",
|
|
])]
|
|
#[case::first_never(Some(0), HighlightSpacing::Never, [
|
|
"┌─────────────┐",
|
|
"│Item 1 │",
|
|
"│Item 1a │",
|
|
"│Item 2 │",
|
|
"│Item 2b │",
|
|
"│Item 3 │",
|
|
"│Item 3c │",
|
|
"└─────────────┘",
|
|
])]
|
|
fn widgets_list_enable_always_highlight_spacing<'line, Lines>(
|
|
#[case] selected: Option<usize>,
|
|
#[case] space: HighlightSpacing,
|
|
#[case] expected: Lines,
|
|
) where
|
|
Lines: IntoIterator,
|
|
Lines::Item: Into<Line<'line>>,
|
|
{
|
|
let mut state = ListState::default().with_selected(selected);
|
|
let backend = TestBackend::new(15, 8);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
terminal
|
|
.draw(|f| {
|
|
let size = f.size();
|
|
let table = List::new(vec![
|
|
ListItem::new(vec![Line::from("Item 1"), Line::from("Item 1a")]),
|
|
ListItem::new(vec![Line::from("Item 2"), Line::from("Item 2b")]),
|
|
ListItem::new(vec![Line::from("Item 3"), Line::from("Item 3c")]),
|
|
])
|
|
.block(Block::bordered())
|
|
.highlight_symbol(">> ")
|
|
.highlight_spacing(space);
|
|
f.render_stateful_widget(table, size, &mut state);
|
|
})
|
|
.unwrap();
|
|
terminal
|
|
.backend()
|
|
.assert_buffer(&Buffer::with_lines(expected));
|
|
}
|