lv-tui 0.4.0

A reactive TUI framework for Rust
Documentation
/// Integration tests for Tree widget (RFC 60).
use lv_tui::prelude::*;
use lv_tui::widgets::{Tree, TreeNode};

fn has_text(buf: &Buffer, text: &str) -> bool {
    let all: String = buf.cells.iter().map(|c| c.symbol.as_str()).collect();
    all.contains(text)
}

fn make_node(label: &str) -> TreeNode {
    TreeNode { label: Text::from(label), children: vec![], expanded: false }
}

fn make_expanded(label: &str, children: Vec<TreeNode>) -> TreeNode {
    TreeNode { label: Text::from(label), children, expanded: true }
}

// ── Basic rendering ────────────────────────────────────────────

#[test]
fn tree_renders_root_nodes() {
    let nodes = vec![make_node("root1"), make_node("root2")];
    let pilot = Pilot::new(Tree::new(nodes), 30, 10);

    assert!(has_text(pilot.frame(), "root1"), "should render first root");
    assert!(has_text(pilot.frame(), "root2"), "should render second root");
}

#[test]
fn tree_expanded_shows_children() {
    let nodes = vec![make_expanded("parent", vec![
        make_node("child1"),
        make_node("child2"),
    ])];
    let pilot = Pilot::new(Tree::new(nodes), 30, 10);

    assert!(has_text(pilot.frame(), "parent"), "parent visible");
    assert!(has_text(pilot.frame(), "child1"), "child visible when expanded");
    assert!(has_text(pilot.frame(), "child2"), "second child visible");
}

#[test]
fn tree_collapsed_hides_children() {
    let nodes = vec![TreeNode {
        label: Text::from("parent"),
        children: vec![make_node("hidden")],
        expanded: false,
    }];
    let pilot = Pilot::new(Tree::new(nodes), 30, 10);

    assert!(has_text(pilot.frame(), "parent"), "parent visible");
    assert!(!has_text(pilot.frame(), "hidden"), "child hidden when collapsed");
}

// ── Keyboard navigation ────────────────────────────────────────

#[test]
fn tree_down_moves_selection() {
    let nodes = vec![
        make_node("A"),
        make_node("B"),
        make_node("C"),
    ];
    let mut pilot = Pilot::new(Tree::new(nodes).show_guides(false), 30, 10);

    // Press Down to select second node
    pilot.press(Key::Down).unwrap();
    pilot.press(Key::Down).unwrap();
    // C should still be rendered (nothing changes visually for selection)
    assert!(has_text(pilot.frame(), "C"), "all nodes still rendered");
}

#[test]
fn tree_enter_toggles_expand() {
    let nodes = vec![TreeNode {
        label: Text::from("folder"),
        children: vec![make_node("file")],
        expanded: false,
    }];
    let mut pilot = Pilot::new(Tree::new(nodes), 30, 10);

    // Initially hidden
    assert!(!has_text(pilot.frame(), "file"), "file hidden when collapsed");

    // Press Enter to expand (selects root first, then toggles)
    pilot.press(Key::Enter).unwrap();
    assert!(has_text(pilot.frame(), "file"), "file visible after expand");
}

// ── Guide lines ────────────────────────────────────────────────

#[test]
fn tree_shows_guides() {
    let nodes = vec![make_expanded("root", vec![
        make_node("leaf"),
    ])];
    let pilot = Pilot::new(Tree::new(nodes).show_guides(true), 30, 10);

    // Guide characters should appear
    assert!(has_text(pilot.frame(), "") || has_text(pilot.frame(), ""),
        "guides should be visible");
}

// ── Collapse all / nested ──────────────────────────────────────

#[test]
fn tree_nested_expand_collapse() {
    let nodes = vec![make_expanded("A", vec![
        make_expanded("B", vec![
            make_node("C"),
        ]),
    ])];
    let pilot = Pilot::new(Tree::new(nodes), 30, 10);

    assert!(has_text(pilot.frame(), "C"), "nested C visible");
}

#[test]
fn tree_deeply_nested() {
    let nodes = vec![make_expanded("1", vec![
        make_expanded("2", vec![
            make_expanded("3", vec![
                make_node("4"),
            ]),
        ]),
    ])];
    let pilot = Pilot::new(Tree::new(nodes), 30, 10);
    assert!(has_text(pilot.frame(), "4"), "deeply nested leaf visible");
}