hefesto-widgets 0.7.3

Ratatui widgets for the Hefesto TUI toolkit: popups, scrollable lists, trees, text input and spinners
Documentation
use super::*;
use insta::assert_snapshot;
use ratatui::{backend::TestBackend, style::{Color, Style}, Terminal};

fn render(name: &str, list: ScrollList<'_>, state: &mut ScrollListState) {
    let backend = TestBackend::new(40, 10);
    let mut terminal = Terminal::new(backend).unwrap();
    terminal
        .draw(|f| f.render_stateful_widget(list, f.area(), state))
        .unwrap();
    assert_snapshot!(name, terminal.backend());
}

// ── state: select ──

#[test]
fn select_sets_selected() {
    let mut state = ScrollListState::default();
    state.select(Some(2));
    assert_eq!(state.list_state.selected(), Some(2));
}

#[test]
fn select_none_deselects() {
    let mut state = ScrollListState::default();
    state.select(Some(2));
    state.select(None);
    assert_eq!(state.list_state.selected(), None);
}

// ── state: next ──

#[test]
fn next_advances() {
    let mut state = ScrollListState::default();
    state.select(Some(0));
    state.next(5);
    assert_eq!(state.list_state.selected(), Some(1));
}

#[test]
fn next_stays_at_last() {
    let mut state = ScrollListState::default();
    state.select(Some(4));
    state.next(5);
    assert_eq!(state.list_state.selected(), Some(4));
}

#[test]
fn next_starts_at_zero_when_none_selected() {
    let mut state = ScrollListState::default();
    state.select(None);
    state.next(5);
    assert_eq!(state.list_state.selected(), Some(0));
}

// ── state: previous ──

#[test]
fn previous_decrements() {
    let mut state = ScrollListState::default();
    state.select(Some(2));
    state.previous();
    assert_eq!(state.list_state.selected(), Some(1));
}

#[test]
fn previous_clamps_at_zero() {
    let mut state = ScrollListState::default();
    state.select(Some(0));
    state.previous();
    assert_eq!(state.list_state.selected(), Some(0));
}

// ── state: last ──

#[test]
fn last_selects_last_index() {
    let mut state = ScrollListState::default();
    state.last(5);
    assert_eq!(state.list_state.selected(), Some(4));
}

#[test]
fn last_with_zero_items() {
    let mut state = ScrollListState::default();
    state.last(0);
    assert_eq!(state.list_state.selected(), Some(0));
}

// ── snapshots ──

fn short_list() -> Vec<String> {
    vec!["alpha".into(), "beta".into(), "gamma".into()]
}

fn long_list() -> Vec<String> {
    (0..20).map(|i| format!("item_{}", i)).collect()
}

#[test]
fn snapshot_default() {
    let list = ScrollList::new(short_list());
    let mut state = ScrollListState::default();
    state.select(Some(0));
    render("scroll_list_default", list, &mut state);
}

#[test]
fn snapshot_styled() {
    let items: Vec<(String, Style)> = short_list()
        .into_iter()
        .map(|s| (s, Style::new().fg(Color::Cyan)))
        .collect();
    let list = ScrollList::new_styled(items);
    let mut state = ScrollListState::default();
    state.select(Some(1));
    render("scroll_list_styled", list, &mut state);
}

#[test]
fn snapshot_with_borders() {
    let list = ScrollList::new(long_list())
        .borders(Borders::ALL);
    let mut state = ScrollListState::default();
    state.select(Some(0));
    render("scroll_list_with_borders", list, &mut state);
}

#[test]
fn snapshot_with_scrollbar() {
    let list = ScrollList::new(long_list())
        .borders(Borders::ALL);
    let mut state = ScrollListState::default();
    state.select(Some(15));
    render("scroll_list_scrolled", list, &mut state);
}

#[test]
fn snapshot_no_highlight_symbol() {
    let list = ScrollList::new(short_list())
        .highlight_symbol("");
    let mut state = ScrollListState::default();
    state.select(Some(0));
    render("scroll_list_no_symbol", list, &mut state);
}