use super::*;
use proptest::prelude::*;
#[test]
fn test_new_selection_state() {
let state = SelectionState::new();
assert!(state.get_selected().is_none());
assert!(!state.is_navigation_active());
}
#[test]
fn test_select_index() {
let mut state = SelectionState::new();
state.select_index(2);
assert_eq!(state.get_selected(), Some(2));
assert!(!state.is_navigation_active()); }
#[test]
fn test_clear_selection() {
let mut state = SelectionState::new();
state.select_index(2);
state.navigation_active = true;
state.clear_selection();
assert!(state.get_selected().is_none());
assert!(!state.is_navigation_active());
}
#[test]
fn test_navigate_next_from_none() {
let mut state = SelectionState::new();
state.navigate_next(5);
assert_eq!(state.get_selected(), Some(0));
assert!(state.is_navigation_active());
}
#[test]
fn test_navigate_next_stops_at_last() {
let mut state = SelectionState::new();
state.selected_index = Some(4);
state.navigate_next(5);
assert_eq!(state.get_selected(), Some(4)); assert!(state.is_navigation_active());
}
#[test]
fn test_navigate_previous_from_none() {
let mut state = SelectionState::new();
state.navigate_previous(5);
assert_eq!(state.get_selected(), Some(4)); assert!(state.is_navigation_active());
}
#[test]
fn test_navigate_previous_stops_at_first() {
let mut state = SelectionState::new();
state.selected_index = Some(0);
state.navigate_previous(5);
assert_eq!(state.get_selected(), Some(0)); assert!(state.is_navigation_active());
}
#[test]
fn test_navigate_with_zero_suggestions() {
let mut state = SelectionState::new();
state.navigate_next(0);
assert!(state.get_selected().is_none());
assert!(!state.is_navigation_active());
state.navigate_previous(0);
assert!(state.get_selected().is_none());
assert!(!state.is_navigation_active());
}
#[test]
fn test_update_layout_calculates_positions() {
let mut state = SelectionState::new();
let heights = vec![3, 5, 2, 4];
state.update_layout(heights.clone(), 10);
assert_eq!(state.viewport_height, 10);
assert_eq!(state.suggestion_heights, heights);
assert_eq!(state.suggestion_y_positions, vec![0, 3, 8, 10]);
}
#[test]
fn test_scroll_offset_getter() {
let mut state = SelectionState::new();
assert_eq!(state.scroll_offset_u16(), 0);
state.scroll_offset = 5;
assert_eq!(state.scroll_offset_u16(), 5);
}
#[test]
fn test_clear_layout() {
let mut state = SelectionState::new();
state.scroll_offset = 10;
state.viewport_height = 20;
state.suggestion_y_positions = vec![0, 5, 10];
state.suggestion_heights = vec![3, 4, 2];
state.clear_layout();
assert_eq!(state.scroll_offset, 0);
assert_eq!(state.viewport_height, 0);
assert!(state.suggestion_y_positions.is_empty());
assert!(state.suggestion_heights.is_empty());
}
#[test]
fn test_scroll_down_when_selection_below_viewport() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5], 10);
state.scroll_offset = 0;
state.selected_index = Some(2);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 5);
}
#[test]
fn test_scroll_up_when_selection_above_viewport() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5], 10);
state.scroll_offset = 10; state.selected_index = Some(0);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 0);
}
#[test]
fn test_no_scroll_when_selection_visible() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5], 10);
state.scroll_offset = 0; state.selected_index = Some(1);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 0);
}
#[test]
fn test_navigate_next_scrolls_to_selection() {
let mut state = SelectionState::new();
state.update_layout(vec![8, 8, 8, 8, 8], 10);
state.scroll_offset = 0;
state.selected_index = Some(0);
state.navigate_next(5);
assert_eq!(state.selected_index, Some(1));
assert_eq!(state.scroll_offset, 6);
}
#[test]
fn test_navigate_previous_scrolls_to_selection() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5, 5], 10);
state.scroll_offset = 15; state.selected_index = Some(4);
state.navigate_previous(5);
assert_eq!(state.selected_index, Some(3));
assert_eq!(state.scroll_offset, 15);
}
#[test]
fn test_navigate_next_at_last_stays_in_place() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5], 10);
state.scroll_offset = 10;
state.selected_index = Some(3);
state.navigate_next(4);
assert_eq!(state.selected_index, Some(3));
assert_eq!(state.scroll_offset, 10);
}
#[test]
fn test_navigate_previous_at_first_stays_in_place() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5], 10);
state.scroll_offset = 0;
state.selected_index = Some(0);
state.navigate_previous(4);
assert_eq!(state.selected_index, Some(0));
assert_eq!(state.scroll_offset, 0);
}
use crate::scroll::Scrollable;
#[test]
fn test_scrollable_scroll_view_down() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5, 5], 10);
state.scroll_view_down(3);
assert_eq!(Scrollable::scroll_offset(&state), 3);
state.scroll_view_down(5);
assert_eq!(Scrollable::scroll_offset(&state), 8);
}
#[test]
fn test_scrollable_scroll_view_down_clamped() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5, 5], 10);
state.scroll_view_down(100);
assert_eq!(Scrollable::scroll_offset(&state), 15);
}
#[test]
fn test_scrollable_scroll_view_up() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5, 5], 10);
state.scroll_offset = 10;
state.scroll_view_up(3);
assert_eq!(Scrollable::scroll_offset(&state), 7);
state.scroll_view_up(4);
assert_eq!(Scrollable::scroll_offset(&state), 3);
}
#[test]
fn test_scrollable_scroll_view_up_clamped() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5, 5], 10);
state.scroll_offset = 5;
state.scroll_view_up(10);
assert_eq!(Scrollable::scroll_offset(&state), 0);
}
#[test]
fn test_scrollable_max_scroll() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5, 5], 10);
assert_eq!(state.max_scroll(), 15);
state.update_layout(vec![3, 5], 10);
assert_eq!(state.max_scroll(), 0);
}
#[test]
fn test_scrollable_viewport_size() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5], 15);
assert_eq!(state.viewport_size(), 15);
}
#[test]
fn test_scrollable_content_fits_in_viewport() {
let mut state = SelectionState::new();
state.update_layout(vec![3, 3], 10);
assert_eq!(state.max_scroll(), 0);
state.scroll_view_down(5);
assert_eq!(Scrollable::scroll_offset(&state), 0); }
#[test]
fn test_last_selection_maintains_correct_spacing() {
let mut state = SelectionState::new();
state.update_layout(vec![3, 3, 2], 5);
assert_eq!(state.suggestion_y_positions, vec![0, 3, 6]);
state.selected_index = Some(2);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 3);
state.selected_index = Some(1);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 3);
state.selected_index = Some(0);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 0);
state.selected_index = Some(1);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 1);
state.selected_index = Some(2);
state.ensure_selected_visible();
assert_eq!(state.scroll_offset, 3);
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_navigation_stops_at_boundaries(suggestion_count in 1usize..20) {
let mut state = SelectionState::new();
state.selected_index = Some(suggestion_count - 1);
state.navigate_next(suggestion_count);
prop_assert_eq!(
state.get_selected(),
Some(suggestion_count - 1),
"Navigating next from last suggestion ({}) should stay at last",
suggestion_count - 1
);
let mut state = SelectionState::new();
state.selected_index = Some(0);
state.navigate_previous(suggestion_count);
prop_assert_eq!(
state.get_selected(),
Some(0),
"Navigating previous from suggestion 0 should stay at 0"
);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_navigation_movement(
suggestion_count in 1usize..20,
current_index in 0usize..20
) {
prop_assume!(current_index < suggestion_count);
let mut state = SelectionState::new();
state.selected_index = Some(current_index);
state.navigate_next(suggestion_count);
let expected_next = std::cmp::min(current_index + 1, suggestion_count - 1);
prop_assert_eq!(
state.get_selected(),
Some(expected_next),
"Navigate next from {} with {} suggestions should go to {}",
current_index, suggestion_count, expected_next
);
prop_assert!(
state.is_navigation_active(),
"Navigation should be active after navigate_next"
);
let mut state = SelectionState::new();
state.selected_index = Some(current_index);
state.navigate_previous(suggestion_count);
let expected_prev = current_index.saturating_sub(1);
prop_assert_eq!(
state.get_selected(),
Some(expected_prev),
"Navigate previous from {} with {} suggestions should go to {}",
current_index, suggestion_count, expected_prev
);
prop_assert!(
state.is_navigation_active(),
"Navigation should be active after navigate_previous"
);
}
}
#[test]
fn test_hover_initial_state() {
let state = SelectionState::new();
assert!(state.get_hovered().is_none());
}
#[test]
fn test_set_hovered() {
let mut state = SelectionState::new();
state.set_hovered(Some(2));
assert_eq!(state.get_hovered(), Some(2));
}
#[test]
fn test_clear_hover() {
let mut state = SelectionState::new();
state.set_hovered(Some(2));
state.clear_hover();
assert!(state.get_hovered().is_none());
}
#[test]
fn test_suggestion_at_y_first_suggestion() {
let mut state = SelectionState::new();
state.update_layout(vec![3, 3, 3], 10);
assert_eq!(state.suggestion_at_y(0), Some(0));
assert_eq!(state.suggestion_at_y(1), Some(0));
assert_eq!(state.suggestion_at_y(2), Some(0));
}
#[test]
fn test_suggestion_at_y_second_suggestion() {
let mut state = SelectionState::new();
state.update_layout(vec![3, 3, 3], 10);
assert_eq!(state.suggestion_at_y(3), Some(1));
assert_eq!(state.suggestion_at_y(4), Some(1));
assert_eq!(state.suggestion_at_y(5), Some(1));
}
#[test]
fn test_suggestion_at_y_third_suggestion() {
let mut state = SelectionState::new();
state.update_layout(vec![3, 3, 3], 10);
assert_eq!(state.suggestion_at_y(6), Some(2));
assert_eq!(state.suggestion_at_y(7), Some(2));
assert_eq!(state.suggestion_at_y(8), Some(2));
}
#[test]
fn test_suggestion_at_y_beyond_content() {
let mut state = SelectionState::new();
state.update_layout(vec![3, 3, 3], 10);
assert!(state.suggestion_at_y(9).is_none());
assert!(state.suggestion_at_y(100).is_none());
}
#[test]
fn test_suggestion_at_y_with_scroll_offset() {
let mut state = SelectionState::new();
state.update_layout(vec![5, 5, 5, 5], 10);
state.scroll_offset = 5;
assert_eq!(state.suggestion_at_y(0), Some(1));
assert_eq!(state.suggestion_at_y(5), Some(2));
assert_eq!(state.suggestion_at_y(10), Some(3));
}
#[test]
fn test_suggestion_at_y_empty_layout() {
let state = SelectionState::new();
assert!(state.suggestion_at_y(0).is_none());
assert!(state.suggestion_at_y(5).is_none());
}