use ghostscope_ui::action::CursorDirection;
use ghostscope_ui::components::command_panel::InputHandler;
use ghostscope_ui::model::panel_state::{
CommandPanelState, InteractionMode, LineType, StaticTextLine,
};
fn create_command_mode_state() -> CommandPanelState {
CommandPanelState {
mode: InteractionMode::Command,
static_lines: vec![
StaticTextLine {
content: "First line of text".to_string(),
line_type: LineType::Response,
history_index: None,
response_type: None,
styled_content: None,
},
StaticTextLine {
content: "Second line with more content".to_string(),
line_type: LineType::Response,
history_index: None,
response_type: None,
styled_content: None,
},
StaticTextLine {
content: "Third line".to_string(),
line_type: LineType::Response,
history_index: None,
response_type: None,
styled_content: None,
},
StaticTextLine {
content: "".to_string(), line_type: LineType::Response,
history_index: None,
response_type: None,
styled_content: None,
},
StaticTextLine {
content: "Final line here".to_string(),
line_type: LineType::Response,
history_index: None,
response_type: None,
styled_content: None,
},
],
command_cursor_line: 0,
command_cursor_column: 0,
..Default::default()
}
}
#[cfg(test)]
mod command_mode_cursor_tests {
use super::*;
#[test]
fn test_basic_horizontal_movement() {
let mut state = create_command_mode_state();
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.command_cursor_column, 1);
assert_eq!(state.command_cursor_line, 0);
for _ in 0..5 {
InputHandler::move_cursor(&mut state, CursorDirection::Right);
}
assert_eq!(state.command_cursor_column, 6);
InputHandler::move_cursor(&mut state, CursorDirection::Left);
assert_eq!(state.command_cursor_column, 5);
for _ in 0..10 {
InputHandler::move_cursor(&mut state, CursorDirection::Left);
}
assert_eq!(state.command_cursor_column, 0); }
#[test]
fn test_horizontal_boundary_checking() {
let mut state = create_command_mode_state();
let first_line_len = state.static_lines[0].content.len();
for _ in 0..50 {
InputHandler::move_cursor(&mut state, CursorDirection::Right);
}
assert_eq!(state.command_cursor_column, first_line_len);
state.command_cursor_line = 3; state.command_cursor_column = 0;
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.command_cursor_column, 0); }
#[test]
fn test_vertical_movement() {
let mut state = create_command_mode_state();
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 1);
assert_eq!(state.command_cursor_column, 0);
for _ in 0..10 {
InputHandler::move_cursor(&mut state, CursorDirection::Down);
}
assert_eq!(state.command_cursor_line, 4);
InputHandler::move_cursor(&mut state, CursorDirection::Up);
assert_eq!(state.command_cursor_line, 3);
for _ in 0..10 {
InputHandler::move_cursor(&mut state, CursorDirection::Up);
}
assert_eq!(state.command_cursor_line, 0); }
#[test]
fn test_column_adjustment_on_vertical_movement() {
let mut state = create_command_mode_state();
state.command_cursor_line = 1;
InputHandler::move_cursor(&mut state, CursorDirection::End);
let long_line_end = state.command_cursor_column;
assert_eq!(long_line_end, 29);
InputHandler::move_cursor(&mut state, CursorDirection::Up);
assert_eq!(state.command_cursor_line, 0);
assert_eq!(
state.command_cursor_column,
state.static_lines[0].content.len()
);
state.command_cursor_column = 15;
state.command_cursor_line = 0;
InputHandler::move_cursor(&mut state, CursorDirection::Down);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 2);
assert_eq!(state.command_cursor_column, 10);
}
#[test]
fn test_home_and_end_movement() {
let mut state = create_command_mode_state();
state.command_cursor_column = 5;
InputHandler::move_cursor(&mut state, CursorDirection::Home);
assert_eq!(state.command_cursor_column, 0);
assert_eq!(state.command_cursor_line, 0);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(
state.command_cursor_column,
state.static_lines[0].content.len()
);
state.command_cursor_line = 2;
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(
state.command_cursor_column,
state.static_lines[2].content.len()
);
state.command_cursor_line = 3; state.command_cursor_column = 5; InputHandler::move_cursor(&mut state, CursorDirection::Home);
assert_eq!(state.command_cursor_column, 0);
}
#[test]
fn test_complex_navigation_sequence() {
let mut state = create_command_mode_state();
assert_eq!(
(state.command_cursor_line, state.command_cursor_column),
(0, 0)
);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.command_cursor_column, 18);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 1);
assert_eq!(state.command_cursor_column, 18);
for _ in 0..5 {
InputHandler::move_cursor(&mut state, CursorDirection::Right);
}
assert_eq!(state.command_cursor_column, 23);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 2);
assert_eq!(state.command_cursor_column, 10);
InputHandler::move_cursor(&mut state, CursorDirection::Home);
assert_eq!(state.command_cursor_column, 0);
InputHandler::move_cursor(&mut state, CursorDirection::Up);
assert_eq!(state.command_cursor_line, 1);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.command_cursor_column, 29);
}
#[test]
fn test_movement_preserves_mode() {
let mut state = create_command_mode_state();
let movements = vec![
CursorDirection::Right,
CursorDirection::Down,
CursorDirection::Left,
CursorDirection::Up,
CursorDirection::Home,
CursorDirection::End,
];
for direction in movements {
InputHandler::move_cursor(&mut state, direction);
assert_eq!(
state.mode,
InteractionMode::Command,
"Mode should remain Command after {direction:?} movement"
);
}
}
#[test]
fn test_empty_lines_navigation() {
let mut state = create_command_mode_state();
state.command_cursor_line = 3;
state.command_cursor_column = 0;
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.command_cursor_column, 0);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.command_cursor_column, 0);
state.command_cursor_column = 5; InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 4);
assert_eq!(
state.command_cursor_column,
5.min(state.static_lines[4].content.len())
);
}
#[test]
fn test_navigation_with_single_line() {
let mut state = CommandPanelState {
mode: InteractionMode::Command,
static_lines: vec![StaticTextLine {
content: "Only line".to_string(),
line_type: LineType::Response,
history_index: None,
response_type: None,
styled_content: None,
}],
command_cursor_line: 0,
command_cursor_column: 0,
..Default::default()
};
InputHandler::move_cursor(&mut state, CursorDirection::Up);
assert_eq!(state.command_cursor_line, 0);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 0);
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.command_cursor_column, 1);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.command_cursor_column, 9);
}
#[test]
fn test_navigation_with_no_lines() {
let mut state = CommandPanelState {
mode: InteractionMode::Command,
static_lines: vec![], command_cursor_line: 0,
command_cursor_column: 0,
..Default::default()
};
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.command_cursor_column, 0);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.command_cursor_line, 0);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.command_cursor_column, 0);
}
}
#[cfg(test)]
mod input_mode_cursor_tests {
use super::*;
#[test]
fn test_input_mode_horizontal_movement() {
let mut state = CommandPanelState {
mode: InteractionMode::Input,
input_text: "test input text".to_string(),
cursor_position: 0,
..Default::default()
};
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.cursor_position, 1);
for _ in 0..4 {
InputHandler::move_cursor(&mut state, CursorDirection::Right);
}
assert_eq!(state.cursor_position, 5);
InputHandler::move_cursor(&mut state, CursorDirection::Left);
assert_eq!(state.cursor_position, 4);
InputHandler::move_cursor(&mut state, CursorDirection::Home);
assert_eq!(state.cursor_position, 0);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.cursor_position, 15); }
#[test]
fn test_input_mode_boundary_checking() {
let mut state = CommandPanelState {
mode: InteractionMode::Input,
input_text: "short".to_string(),
cursor_position: 0,
..Default::default()
};
InputHandler::move_cursor(&mut state, CursorDirection::Left);
assert_eq!(state.cursor_position, 0);
state.cursor_position = 5;
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.cursor_position, 5); }
#[test]
fn test_unicode_cursor_movement() {
let mut state = CommandPanelState {
mode: InteractionMode::Input,
input_text: "Hello 世界 Test".to_string(),
cursor_position: 0,
..Default::default()
};
for i in 1..=6 {
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.cursor_position, i);
}
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.cursor_position, 7);
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.cursor_position, 8); }
#[test]
fn test_word_deletion_movement() {
let mut state = CommandPanelState {
mode: InteractionMode::Input,
input_text: "one two three four".to_string(),
cursor_position: 18,
..Default::default()
};
InputHandler::delete_previous_word(&mut state);
assert_eq!(state.input_text, "one two three ");
assert_eq!(state.cursor_position, 14);
InputHandler::delete_previous_word(&mut state);
assert_eq!(state.input_text, "one two ");
assert_eq!(state.cursor_position, 8);
state.cursor_position = 4; InputHandler::delete_previous_word(&mut state);
assert_eq!(state.input_text, "two ");
assert_eq!(state.cursor_position, 0);
}
}
#[cfg(test)]
mod script_editor_cursor_tests {
use super::*;
use ghostscope_ui::model::panel_state::ScriptCache;
fn create_script_state() -> CommandPanelState {
CommandPanelState {
mode: InteractionMode::ScriptEditor,
script_cache: Some(ScriptCache {
target: "test_function".to_string(),
original_command: "trace test_function".to_string(),
selected_index: None,
lines: vec![
"First line".to_string(),
"Second line here".to_string(),
"".to_string(), "Fourth line content".to_string(),
"Last line".to_string(),
],
cursor_line: 0,
cursor_col: 0,
status: ghostscope_ui::model::panel_state::ScriptStatus::Draft,
saved_scripts: std::collections::HashMap::new(),
}),
..Default::default()
}
}
#[test]
fn test_script_editor_horizontal_movement() {
let mut state = create_script_state();
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.script_cache.as_ref().unwrap().cursor_col, 1);
InputHandler::move_cursor(&mut state, CursorDirection::End);
assert_eq!(state.script_cache.as_ref().unwrap().cursor_col, 10);
InputHandler::move_cursor(&mut state, CursorDirection::Home);
assert_eq!(state.script_cache.as_ref().unwrap().cursor_col, 0);
}
#[test]
fn test_script_editor_vertical_movement() {
let mut state = create_script_state();
if let Some(ref mut script) = state.script_cache {
script.cursor_col = 5;
}
InputHandler::move_cursor(&mut state, CursorDirection::Down);
let script = state.script_cache.as_ref().unwrap();
assert_eq!(script.cursor_line, 1);
assert_eq!(script.cursor_col, 5);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
let script = state.script_cache.as_ref().unwrap();
assert_eq!(script.cursor_line, 2);
assert_eq!(script.cursor_col, 0);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
let script = state.script_cache.as_ref().unwrap();
assert_eq!(script.cursor_line, 3);
assert_eq!(script.cursor_col, 0); }
#[test]
fn test_script_editor_column_preservation() {
let mut state = create_script_state();
if let Some(ref mut script) = state.script_cache {
script.cursor_line = 1;
}
InputHandler::move_cursor(&mut state, CursorDirection::End);
let long_col = state.script_cache.as_ref().unwrap().cursor_col;
assert_eq!(long_col, 16);
InputHandler::move_cursor(&mut state, CursorDirection::Up);
let script = state.script_cache.as_ref().unwrap();
assert_eq!(script.cursor_line, 0);
assert_eq!(script.cursor_col, 10);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
InputHandler::move_cursor(&mut state, CursorDirection::Down);
let script = state.script_cache.as_ref().unwrap();
assert_eq!(script.cursor_line, 2); assert_eq!(script.cursor_col, 0);
}
#[test]
fn test_script_editor_boundary_checking() {
let mut state = create_script_state();
InputHandler::move_cursor(&mut state, CursorDirection::Up);
assert_eq!(state.script_cache.as_ref().unwrap().cursor_line, 0);
if let Some(ref mut script) = state.script_cache {
script.cursor_line = 4;
}
InputHandler::move_cursor(&mut state, CursorDirection::Down);
assert_eq!(state.script_cache.as_ref().unwrap().cursor_line, 4);
InputHandler::move_cursor(&mut state, CursorDirection::End);
let end_col = state.script_cache.as_ref().unwrap().cursor_col;
InputHandler::move_cursor(&mut state, CursorDirection::Right);
assert_eq!(state.script_cache.as_ref().unwrap().cursor_col, end_col);
}
}