use super::*;
fn status() -> StatusInfo {
StatusInfo {
total: 100,
matched: 42,
processed: 50,
show_spinner: false,
matcher_mode: String::new(),
multi_selection: false,
selected: 0,
current_item_idx: 7,
hscroll_offset: 3,
start: None,
inline_separator: " < ".to_string(),
}
}
#[test]
fn left_title_without_spinner_has_padding() {
let s = status();
let title = s.left_title();
assert!(title.starts_with(" "));
assert!(title.contains("42/100"));
}
#[test]
fn left_title_with_spinner_mode_progress_and_selection() {
let mut s = status();
s.show_spinner = true;
s.start = Some(Instant::now());
s.matcher_mode = "RE".to_string();
s.multi_selection = true;
s.selected = 4;
let title = s.left_title();
assert!(title.contains("42/100"));
assert!(title.contains("/RE"));
assert!(title.contains("(50%)"));
assert!(title.contains("[4]"));
}
#[test]
fn inline_separator_switches_with_spinner() {
let mut s = status();
assert_eq!(s.inline_separator_or_spinner(), " < ");
s.show_spinner = true;
s.start = Some(Instant::now());
let out = s.inline_separator_or_spinner();
assert_ne!(out, " < ");
assert!(!out.is_empty());
}
#[test]
fn inline_status_includes_mode_progress_and_selection() {
let mut s = status();
s.show_spinner = true;
s.matcher_mode = "RE".to_string();
s.multi_selection = true;
s.selected = 2;
let out = s.inline_status();
assert!(out.starts_with("42/100"));
assert!(out.contains("/RE"));
assert!(out.contains("(50%)"));
assert!(out.contains("[2]"));
}
#[test]
fn right_title_shows_index_and_hscroll() {
assert_eq!(status().right_title(), "7/3");
}
#[test]
fn input_word_navigation_and_deletion() {
let mut input = Input::default();
input.insert_str("foo bar baz");
input.move_to_end();
let deleted = input.delete_backward_word();
assert_eq!(deleted, "baz");
assert_eq!(input.value, "foo bar ");
input.move_cursor_backward_word();
input.move_cursor_forward_word();
input.move_to_end();
let head = input.delete_to_beginning();
assert_eq!(head, "foo bar ");
assert_eq!(input.value, "");
}
#[test]
fn input_switch_mode_swaps_value_and_prompt() {
let mut input = Input::default();
input.insert_str("query");
input.switch_mode();
assert_eq!(input.value, "");
input.switch_mode();
assert_eq!(input.value, "query");
}
#[test]
fn forward_word_skips_leading_separators() {
let mut input = Input::default();
input.insert_str(" ..foo bar");
input.move_cursor_to(0);
input.move_cursor_forward_word();
assert_eq!(input.value[..input.cursor_pos as usize].trim_start(), "..foo");
}
#[test]
fn backward_word_at_start_stays_at_zero() {
let mut input = Input::default();
input.insert_str("abc");
input.move_cursor_to(0);
input.move_cursor_backward_word();
assert_eq!(input.cursor_pos, 0);
}
#[test]
fn delete_backward_word_at_start_is_empty() {
let mut input = Input::default();
input.insert_str("abc");
input.move_cursor_to(0);
assert_eq!(input.delete_backward_word(), "");
assert_eq!(input.value, "abc");
}
#[test]
fn delete_backward_word_skips_trailing_punctuation() {
let mut input = Input::default();
input.insert_str("foo bar...");
input.move_to_end();
let deleted = input.delete_backward_word();
assert!(deleted.contains("bar"));
assert_eq!(input.value, "foo ");
}
#[test]
fn delete_backward_to_whitespace_skips_trailing_spaces() {
let mut input = Input::default();
input.insert_str("foo bar ");
input.move_to_end();
let deleted = input.delete_backward_to_whitespace();
assert!(deleted.contains("bar"));
assert_eq!(input.value, "foo ");
}
#[test]
fn delete_backward_to_whitespace_at_start_is_empty() {
let mut input = Input::default();
input.insert_str("abc");
input.move_cursor_to(0);
assert_eq!(input.delete_backward_to_whitespace(), "");
}
#[test]
fn delete_forward_word_removes_next_word() {
let mut input = Input::default();
input.insert_str("foo bar");
input.move_cursor_to(0);
let deleted = input.delete_forward_word();
assert!(deleted.contains("foo"));
}
#[test]
fn delete_forward_word_at_end_is_empty() {
let mut input = Input::default();
input.insert_str("foo bar");
input.move_to_end();
assert_eq!(input.delete_forward_word(), "");
assert_eq!(input.value, "foo bar");
}
#[test]
fn delete_forward_word_skips_leading_separators() {
let mut input = Input::default();
input.insert_str("foo bar");
input.move_cursor_to(3); let deleted = input.delete_forward_word();
assert_eq!(deleted, " bar");
assert_eq!(input.value, "foo");
}
#[test]
fn move_cursor_zero_offset_is_noop() {
let mut input = Input::default();
input.insert_str("abc");
input.move_cursor_to(1);
input.move_cursor(0);
assert_eq!(input.cursor_pos, 1);
}
#[test]
fn delete_at_bounds_returns_none() {
let mut input = Input::default();
assert!(input.delete(0).is_none());
input.insert_str("ab");
input.move_to_end();
assert!(input.delete(0).is_none());
}
#[test]
fn input_render_writes_prompt_and_value() {
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
let mut input = Input::default();
input.insert_str("hello");
input.status_info = Some(status());
let area = Rect::new(0, 0, 40, 3);
let mut buf = Buffer::empty(area);
input.render(area, &mut buf);
let mut text = String::new();
for y in 0..area.height {
for x in 0..area.width {
text.push_str(buf[(x, y)].symbol());
}
}
assert!(text.contains("hello"));
}