hefesto-widgets 0.6.7

Ratatui widgets for the Hefesto TUI toolkit: popups, scrollable lists, trees, text input and spinners
Documentation
use std::collections::HashSet;

use super::*;
use insta::assert_snapshot;
use ratatui::{backend::TestBackend, Terminal};

fn render_stateful(name: &str, tree: Tree<'_>, state: &mut TreeState) {
    let backend = TestBackend::new(60, 12);
    let mut terminal = Terminal::new(backend).unwrap();
    terminal
        .draw(|f| f.render_stateful_widget(tree, f.area(), state))
        .unwrap();
    assert_snapshot!(name, terminal.backend());
}

fn sample_nodes() -> Vec<TreeNode<'static>> {
    vec![
        TreeNode {
            text: Line::from("src"),
            children: vec![
                TreeNode {
                    text: Line::from("main.rs"),
                    children: vec![],
                    id: 2,
                },
                TreeNode {
                    text: Line::from("lib.rs"),
                    children: vec![],
                    id: 3,
                },
            ],
            id: 1,
        },
        TreeNode {
            text: Line::from("Cargo.toml"),
            children: vec![],
            id: 4,
        },
        TreeNode {
            text: Line::from("docs"),
            children: vec![
                TreeNode {
                    text: Line::from("guide.md"),
                    children: vec![],
                    id: 6,
                },
            ],
            id: 5,
        },
    ]
}

// ── visible_count ──

#[test]
fn visible_count_zero_when_empty() {
    let tree = Tree::new(vec![]);
    assert_eq!(tree.visible_count(&HashSet::new()), 0);
}

#[test]
fn visible_count_root_nodes() {
    let tree = Tree::new(sample_nodes());
    assert_eq!(tree.visible_count(&HashSet::new()), 3);
}

#[test]
fn visible_count_expanded_shows_children() {
    let tree = Tree::new(sample_nodes());
    let mut expanded = HashSet::new();
    expanded.insert(1);
    assert_eq!(tree.visible_count(&expanded), 5);
}

#[test]
fn visible_count_all_expanded() {
    let tree = Tree::new(sample_nodes());
    let expanded = HashSet::from([1, 5]);
    assert_eq!(tree.visible_count(&expanded), 6);
}

// ── toggle ──

#[test]
fn toggle_adds_when_absent() {
    let mut state = TreeState::default();
    state.toggle(1);
    assert!(state.expanded.contains(&1));
}

#[test]
fn toggle_removes_when_present() {
    let mut state = TreeState { expanded: HashSet::from([1]), ..TreeState::default() };
    state.toggle(1);
    assert!(!state.expanded.contains(&1));
}

// ── visible_index_of ──

#[test]
fn visible_index_of_root() {
    let nodes = sample_nodes();
    assert_eq!(TreeState::visible_index_of(&TreeState::default(), &nodes, 1), Some(0));
}

#[test]
fn visible_index_of_second_root() {
    let nodes = sample_nodes();
    assert_eq!(TreeState::visible_index_of(&TreeState::default(), &nodes, 4), Some(1));
}

#[test]
fn visible_index_of_child_when_collapsed() {
    let nodes = sample_nodes();
    assert_eq!(TreeState::visible_index_of(&TreeState::default(), &nodes, 2), None);
}

#[test]
fn visible_index_of_child_when_expanded() {
    let nodes = sample_nodes();
    let expanded = HashSet::from([1]);
    let state = TreeState { expanded, ..TreeState::default() };
    assert_eq!(TreeState::visible_index_of(&state, &nodes, 2), Some(1));
}

#[test]
fn visible_index_of_missing_id() {
    let nodes = sample_nodes();
    assert_eq!(TreeState::visible_index_of(&TreeState::default(), &nodes, 99), None);
}

// ── snapshots ──

#[test]
fn snapshot_collapsed() {
    let tree = Tree::new(sample_nodes());
    let mut state = TreeState::default();
    render_stateful("tree_collapsed", tree, &mut state);
}

#[test]
fn snapshot_expanded_root() {
    let tree = Tree::new(sample_nodes());
    let mut state = TreeState { expanded: HashSet::from([1]), ..TreeState::default() };
    render_stateful("tree_expanded_root", tree, &mut state);
}

#[test]
fn snapshot_all_expanded() {
    let tree = Tree::new(sample_nodes());
    let mut state = TreeState { expanded: HashSet::from([1, 5]), ..TreeState::default() };
    render_stateful("tree_all_expanded", tree, &mut state);
}

#[test]
fn snapshot_with_borders() {
    let tree = Tree::new(sample_nodes())
        .borders(ratatui::widgets::Borders::ALL);
    let mut state = TreeState { expanded: HashSet::from([1]), ..TreeState::default() };
    render_stateful("tree_with_borders", tree, &mut state);
}

#[test]
fn snapshot_custom_icons() {
    let tree = Tree::new(sample_nodes())
        .expand_icon("[+] ")
        .collapse_icon("[-] ")
        .leaf_icon(" *  ");
    let mut state = TreeState { expanded: HashSet::from([1, 5]), ..TreeState::default() };
    render_stateful("tree_custom_icons", tree, &mut state);
}