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, Terminal};

fn render(name: &str, input: TextInput<'_>, state: &mut TextInputState) {
    let backend = TestBackend::new(20, 3);
    let mut terminal = Terminal::new(backend).unwrap();
    terminal
        .draw(|f| f.render_stateful_widget(input, f.area(), state))
        .unwrap();
    assert_snapshot!(name, terminal.backend());
}

// ── state: insert_char ──

#[test]
fn insert_char_appends_at_cursor() {
    let mut s = TextInputState::default();
    s.insert_char('a');
    s.insert_char('b');
    assert_eq!(s.content, "ab");
    assert_eq!(s.cursor, 2);
}

#[test]
fn insert_char_in_middle() {
    let mut s = TextInputState { content: "ac".into(), cursor: 1 };
    s.insert_char('b');
    assert_eq!(s.content, "abc");
    assert_eq!(s.cursor, 2);
}

#[test]
fn insert_char_handles_multibyte() {
    let mut s = TextInputState::default();
    s.insert_char('ñ');
    assert_eq!(s.cursor, 2);
}

// ── state: delete ──

#[test]
fn delete_before_removes_previous_char() {
    let mut s = TextInputState { content: "abc".into(), cursor: 3 };
    s.delete_before();
    assert_eq!(s.content, "ab");
    assert_eq!(s.cursor, 2);
}

#[test]
fn delete_before_at_start_does_nothing() {
    let mut s = TextInputState::default();
    s.delete_before();
    assert_eq!(s.content, "");
    assert_eq!(s.cursor, 0);
}

#[test]
fn delete_at_removes_next_char() {
    let mut s = TextInputState { content: "abc".into(), cursor: 0 };
    s.delete_at();
    assert_eq!(s.content, "bc");
    assert_eq!(s.cursor, 0);
}

// ── state: cursor movement ──

#[test]
fn cursor_left_decrements() {
    let mut s = TextInputState { content: "abc".into(), cursor: 2 };
    s.cursor_left();
    assert_eq!(s.cursor, 1);
}

#[test]
fn cursor_right_increments() {
    let mut s = TextInputState { content: "abc".into(), cursor: 0 };
    s.cursor_right();
    assert_eq!(s.cursor, 1);
}

#[test]
fn cursor_home_sets_to_zero() {
    let mut s = TextInputState { content: "abc".into(), cursor: 2 };
    s.cursor_home();
    assert_eq!(s.cursor, 0);
}

#[test]
fn cursor_end_sets_to_len() {
    let mut s = TextInputState { content: "abc".into(), cursor: 0 };
    s.cursor_end();
    assert_eq!(s.cursor, 3);
}

// ── helper: required_height ──

#[test]
fn required_height_one_row_no_borders() {
    let input = TextInput::new().borders(ratatui::widgets::Borders::NONE);
    assert_eq!(input.required_height(), 1);
}

#[test]
fn required_height_one_row_with_bottom_border() {
    let input = TextInput::new().borders(ratatui::widgets::Borders::BOTTOM);
    assert_eq!(input.required_height(), 2);
}

#[test]
fn required_height_three_rows() {
    let input = TextInput::new()
        .rows(3)
        .borders(ratatui::widgets::Borders::NONE);
    assert_eq!(input.required_height(), 3);
}

// ── snapshots ──

#[test]
fn snapshot_default() {
    let mut state = TextInputState::default();
    render("text_input_default", TextInput::new(), &mut state);
}

#[test]
fn snapshot_with_content() {
    let mut state = TextInputState { content: "hello".into(), cursor: 2 };
    render("text_input_with_content", TextInput::new(), &mut state);
}

#[test]
fn snapshot_with_placeholder() {
    let mut state = TextInputState::default();
    render("text_input_with_placeholder", TextInput::new().placeholder("Buscar..."), &mut state);
}

#[test]
fn snapshot_no_border() {
    let mut state = TextInputState::default();
    render("text_input_no_border", TextInput::new().border_type(BorderType::None), &mut state);
}