use egui::{Pos2, Rect};
use par_term::pane::mouse::MouseState;
use par_term::selection::{Selection, SelectionMode};
use par_term::tab_bar_ui::TabBarUI;
type TabId = u64;
#[test]
fn test_mouse_state_defaults_button_not_pressed() {
let state = MouseState::test_new();
assert!(
!state.test_button_pressed(),
"button_pressed must default to false"
);
assert!(
!state.test_is_selecting(),
"is_selecting must default to false"
);
}
#[test]
fn test_mouse_state_button_press_sets_flag() {
let mut state = MouseState::test_new();
state.test_set_button_pressed(true);
assert!(state.test_button_pressed());
}
#[test]
fn test_mouse_state_release_clears_flags() {
let mut state = MouseState::test_new();
state.test_set_button_pressed(true);
state.test_set_is_selecting(true);
state.test_set_selection(Some(Selection::new(
(0, 0),
(5, 0),
SelectionMode::Normal,
0,
)));
state.test_set_button_pressed(false);
state.test_set_is_selecting(false);
assert!(
!state.test_button_pressed(),
"button_pressed must be cleared on release"
);
assert!(
!state.test_is_selecting(),
"is_selecting must be cleared on release"
);
}
#[test]
fn test_mouse_state_release_clears_even_with_active_selection() {
let mut state = MouseState::test_new();
state.test_set_button_pressed(true);
state.test_set_is_selecting(true);
state.test_set_selection(Some(Selection::new(
(0, 0),
(10, 2),
SelectionMode::Normal,
0,
)));
state.test_set_click_pixel_position(Some((100.0, 200.0)));
state.test_set_click_position(Some((5, 3)));
state.test_set_click_count(1);
state.test_set_button_pressed(false);
state.test_set_is_selecting(false);
assert!(!state.test_button_pressed());
assert!(!state.test_is_selecting());
assert!(state.test_selection().is_some());
}
#[test]
fn test_drag_selection_requires_button_pressed() {
let mut state = MouseState::test_new();
state.test_set_click_pixel_position(Some((100.0, 200.0)));
state.test_set_click_position(Some((5, 3)));
state.test_set_click_count(1);
assert!(!state.test_button_pressed());
}
#[test]
fn test_button_pressed_stuck_causes_false_selection() {
let mut state = MouseState::test_new();
state.test_set_button_pressed(true);
state.test_set_click_pixel_position(Some((100.0, 200.0)));
state.test_set_click_position(Some((5, 3)));
state.test_set_click_count(1);
assert!(state.test_button_pressed()); state.test_set_button_pressed(false); assert!(!state.test_button_pressed()); }
#[test]
fn test_tab_at_logical_pos_no_rects() {
let ui = TabBarUI::new();
assert!(ui.tab_at_logical_pos(Pos2::new(10.0, 10.0)).is_none());
}
#[test]
fn test_tab_at_logical_pos_hits_tab() {
let mut ui = TabBarUI::new();
let tab1: TabId = 1;
let tab2: TabId = 2;
let tab3: TabId = 3;
ui.test_set_tab_rects(vec![
(
tab1,
Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(100.0, 30.0)),
),
(
tab2,
Rect::from_min_max(Pos2::new(104.0, 0.0), Pos2::new(204.0, 30.0)),
),
(
tab3,
Rect::from_min_max(Pos2::new(208.0, 0.0), Pos2::new(308.0, 30.0)),
),
]);
assert_eq!(ui.tab_at_logical_pos(Pos2::new(50.0, 15.0)), Some(tab1));
assert_eq!(ui.tab_at_logical_pos(Pos2::new(150.0, 15.0)), Some(tab2));
assert_eq!(ui.tab_at_logical_pos(Pos2::new(250.0, 15.0)), Some(tab3));
}
#[test]
fn test_tab_at_logical_pos_miss_between_tabs() {
let mut ui = TabBarUI::new();
let tab1: TabId = 1;
let tab2: TabId = 2;
ui.test_set_tab_rects(vec![
(
tab1,
Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(100.0, 30.0)),
),
(
tab2,
Rect::from_min_max(Pos2::new(104.0, 0.0), Pos2::new(204.0, 30.0)),
),
]);
assert!(ui.tab_at_logical_pos(Pos2::new(102.0, 15.0)).is_none());
}
#[test]
fn test_tab_at_logical_pos_edge_boundaries() {
let mut ui = TabBarUI::new();
let tab1: TabId = 1;
let tab2: TabId = 2;
ui.test_set_tab_rects(vec![
(
tab1,
Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(100.0, 30.0)),
),
(
tab2,
Rect::from_min_max(Pos2::new(104.0, 0.0), Pos2::new(204.0, 30.0)),
),
]);
assert_eq!(ui.tab_at_logical_pos(Pos2::new(0.0, 0.0)), Some(tab1));
assert_eq!(ui.tab_at_logical_pos(Pos2::new(99.99, 29.99)), Some(tab1));
assert_eq!(ui.tab_at_logical_pos(Pos2::new(100.0, 15.0)), Some(tab1));
assert!(ui.tab_at_logical_pos(Pos2::new(102.0, 15.0)).is_none());
assert_eq!(ui.tab_at_logical_pos(Pos2::new(104.0, 0.0)), Some(tab2));
}
#[test]
fn test_tab_at_logical_pos_above_and_below() {
let mut ui = TabBarUI::new();
let tab1: TabId = 1;
ui.test_set_tab_rects(vec![(
tab1,
Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(100.0, 30.0)),
)]);
assert!(ui.tab_at_logical_pos(Pos2::new(50.0, -1.0)).is_none());
assert_eq!(ui.tab_at_logical_pos(Pos2::new(50.0, 30.0)), Some(tab1));
assert!(ui.tab_at_logical_pos(Pos2::new(50.0, 30.01)).is_none());
assert_eq!(ui.tab_at_logical_pos(Pos2::new(50.0, 15.0)), Some(tab1));
}
#[test]
fn test_regression_drag_from_terminal_release_in_tab_bar() {
let mut mouse = MouseState::test_new();
mouse.test_set_button_pressed(true);
mouse.test_set_click_pixel_position(Some((500.0, 400.0)));
mouse.test_set_click_position(Some((50, 20)));
mouse.test_set_click_count(1);
mouse.test_set_button_pressed(false);
mouse.test_set_is_selecting(false);
assert!(
!mouse.test_button_pressed(),
"button_pressed must be false — no accidental drag selection"
);
assert!(!mouse.test_is_selecting());
}
#[test]
fn test_regression_tab_click_dropped_when_egui_stale() {
let mut ui = TabBarUI::new();
let target_tab: TabId = 5;
ui.test_set_tab_rects(vec![
(
3,
Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(100.0, 30.0)),
),
(
target_tab,
Rect::from_min_max(Pos2::new(104.0, 0.0), Pos2::new(204.0, 30.0)),
),
(
7,
Rect::from_min_max(Pos2::new(208.0, 0.0), Pos2::new(308.0, 30.0)),
),
]);
let scale_factor: f32 = 2.0; let physical_x = 154.0 * scale_factor; let physical_y = 15.0 * scale_factor;
let logical_pos = egui::pos2(physical_x / scale_factor, physical_y / scale_factor);
let found = ui.tab_at_logical_pos(logical_pos);
assert_eq!(
found,
Some(target_tab),
"tab_at_logical_pos must find the clicked tab for fallback switching"
);
let pending_switch = found;
assert_eq!(pending_switch, Some(target_tab));
}
#[test]
fn test_regression_normal_tab_click_not_affected() {
let mut ui = TabBarUI::new();
let tab1: TabId = 1;
ui.test_set_tab_rects(vec![(
tab1,
Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(100.0, 30.0)),
)]);
let pending: Option<TabId> = None;
assert!(
pending.is_none(),
"No fallback needed for normal egui clicks"
);
}
#[test]
fn test_mouse_state_divider_drag_cleared_on_release() {
let mut state = MouseState::test_new();
state.test_set_dragging_divider(Some(2));
state.test_set_button_pressed(true);
state.test_set_button_pressed(false);
assert!(!state.test_button_pressed());
if !state.test_button_pressed() {
state.test_set_dragging_divider(None);
}
assert!(state.test_dragging_divider().is_none());
}