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

use crate::{Badge, BadgeAnchor, BadgeStack};

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

// ── resolve_rect ──

#[test]
fn resolve_rect_delegates_to_popup() {
    let popup = TextInputPopup::new()
        .width(PopupSize::Fixed(30))
        .height(PopupSize::Fixed(10));
    let state = TextInputState::default();
    let rect = popup.resolve_rect(Rect::new(0, 0, 100, 50), &state);
    assert_eq!(rect.width, 30);
    assert_eq!(rect.height, 10);
}

#[test]
fn resolve_rect_clamps_when_larger_than_area() {
    let popup = TextInputPopup::new()
        .width(PopupSize::Fixed(100))
        .height(PopupSize::Fixed(50));
    let state = TextInputState::default();
    let rect = popup.resolve_rect(Rect::new(0, 0, 50, 20), &state);
    assert_eq!(rect.width, 50);
    assert_eq!(rect.height, 20);
}

#[test]
fn resolve_rect_auto_uses_auto_height() {
    let popup = TextInputPopup::new();
    let state = TextInputState::default();
    let auto_h = popup.auto_height(&state, Rect::new(0, 0, 100, 50));
    // 1 row + no borders + 2 popup borders = 3
    assert_eq!(auto_h, 3);
    let rect = popup.resolve_rect(Rect::new(0, 0, 100, 50), &state);
    assert_eq!(rect.height, 3);
}

#[test]
fn resolve_rect_auto_centers_correctly() {
    let popup = TextInputPopup::new();
    let state = TextInputState::default();
    let rect = popup.resolve_rect(Rect::new(0, 0, 100, 50), &state);
    // height = 3, width = 80% of 100 = 80
    assert_eq!(rect.x, 10);
    assert_eq!(rect.y, 23); // (50 - 3) / 2
}

// ── snapshots ──

#[test]
fn snapshot_default() {
    let popup = TextInputPopup::new();
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_default", popup, &mut state);
}

#[test]
fn snapshot_with_title() {
    let popup = TextInputPopup::new().title("Ingrese texto");
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_with_title", popup, &mut state);
}

#[test]
fn snapshot_with_placeholder() {
    let popup = TextInputPopup::new()
        .placeholder("Escribir aqui...");
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_with_placeholder", popup, &mut state);
}

#[test]
fn snapshot_with_content() {
    let popup = TextInputPopup::new();
    let mut state = TextInputState {
        content: "Hola mundo".to_string(),
        cursor: 10,
    };
    render_stateful("text_input_popup_with_content", popup, &mut state);
}

#[test]
fn snapshot_with_cursor_middle() {
    let popup = TextInputPopup::new();
    let mut state = TextInputState {
        content: "abcdef".to_string(),
        cursor: 3,
    };
    render_stateful("text_input_popup_cursor_middle", popup, &mut state);
}

#[test]
fn snapshot_bg_color() {
    let popup = TextInputPopup::new()
        .rows(1)
        .bg_color(Color::Blue);
    let mut state = TextInputState {
        content: "fondo azul".to_string(),
        cursor: 10,
    };
    render_stateful("text_input_popup_bg_color", popup, &mut state);
}

#[test]
fn snapshot_input_bg_color() {
    let popup = TextInputPopup::new()
        .rows(1)
        .input_bg_color(Color::DarkGray);
    let mut state = TextInputState {
        content: "input gris".to_string(),
        cursor: 9,
    };
    render_stateful("text_input_popup_input_bg_color", popup, &mut state);
}

#[test]
fn snapshot_bg_color_both() {
    let popup = TextInputPopup::new()
        .rows(1)
        .bg_color(Color::Blue)
        .input_bg_color(Color::DarkGray);
    let mut state = TextInputState {
        content: "ambos fondos".to_string(),
        cursor: 12,
    };
    render_stateful("text_input_popup_bg_color_both", popup, &mut state);
}

#[test]
fn snapshot_border_color() {
    let popup = TextInputPopup::new()
        .border_color(Color::Red);
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_border_color", popup, &mut state);
}

#[test]
fn snapshot_no_border() {
    let popup = TextInputPopup::new()
        .border_type(BorderType::None);
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_no_border", popup, &mut state);
}

#[test]
fn snapshot_with_origin() {
    let popup = TextInputPopup::new().origin(5, 5);
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_with_origin", popup, &mut state);
}

#[test]
fn snapshot_with_header() {
    let popup = TextInputPopup::new().header();
    let mut state = TextInputState::default();
    render_stateful("text_input_popup_with_header", popup, &mut state);
}

// ── badges integration ──

#[test]
fn snapshot_with_badges() {
    let badges = BadgeStack::new()
        .push(Badge::new(" main", Color::Green).anchor(BadgeAnchor::TopLeft));
    let popup = TextInputPopup::new()
        .title("Name")
        .badges(badges);
    let mut state = TextInputState::default();
    render_stateful("text_input_with_badges", popup, &mut state);
}