use crossterm::event::{KeyCode, KeyEvent};
use super::{InputMode, LogAction, LogView, RebaseMode, RebaseSource};
use crate::jj::constants;
use crate::keys;
use crate::model::{Change, ChangeId, CommitId};
fn create_test_changes() -> Vec<Change> {
vec![
Change {
change_id: ChangeId::new("abc12345".to_string()),
commit_id: CommitId::new("def67890".to_string()),
author: "user@example.com".to_string(),
timestamp: "2024-01-29".to_string(),
description: "First commit".to_string(),
is_working_copy: true,
is_empty: false,
bookmarks: vec!["main".to_string()],
graph_prefix: "@ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
Change {
change_id: ChangeId::new("xyz98765".to_string()),
commit_id: CommitId::new("uvw43210".to_string()),
author: "user@example.com".to_string(),
timestamp: "2024-01-28".to_string(),
description: "Initial commit".to_string(),
is_working_copy: false,
is_empty: false,
bookmarks: vec![],
graph_prefix: "○ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
Change {
change_id: ChangeId::new(constants::ROOT_CHANGE_ID.to_string()),
commit_id: CommitId::new("0".repeat(40)),
author: "".to_string(),
timestamp: "".to_string(),
description: "".to_string(),
is_working_copy: false,
is_empty: true,
bookmarks: vec![],
graph_prefix: "◆ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
]
}
fn press_key(view: &mut LogView, key: KeyCode) -> LogAction {
view.handle_key(KeyEvent::from(key))
}
fn type_text(view: &mut LogView, text: &str) {
for c in text.chars() {
press_key(view, KeyCode::Char(c));
}
}
fn submit(view: &mut LogView) -> LogAction {
press_key(view, keys::SUBMIT)
}
fn escape(view: &mut LogView) -> LogAction {
press_key(view, keys::ESC)
}
#[test]
fn test_log_view_new() {
let view = LogView::new();
assert!(view.changes.is_empty());
assert_eq!(view.selected_index, 0);
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_set_changes() {
let mut view = LogView::new();
let changes = create_test_changes();
view.set_changes(changes.clone());
assert_eq!(view.changes.len(), 3);
}
#[test]
fn test_navigation() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
assert_eq!(view.selected_index, 0);
view.move_down();
assert_eq!(view.selected_index, 1);
view.move_down();
assert_eq!(view.selected_index, 2);
view.move_down();
assert_eq!(view.selected_index, 2);
view.move_up();
assert_eq!(view.selected_index, 1);
view.move_to_top();
assert_eq!(view.selected_index, 0);
view.move_to_bottom();
assert_eq!(view.selected_index, 2);
}
#[test]
fn test_navigation_bounds_empty() {
let mut view = LogView::new();
view.move_down();
view.move_up();
view.move_to_top();
view.move_to_bottom();
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_selected_change() {
let mut view = LogView::new();
assert!(view.selected_change().is_none());
view.set_changes(create_test_changes());
assert!(view.selected_change().is_some());
assert_eq!(view.selected_change().unwrap().change_id, "abc12345");
view.move_down();
assert_eq!(view.selected_change().unwrap().change_id, "xyz98765");
}
#[test]
fn test_input_mode_toggle() {
let mut view = LogView::new();
assert_eq!(view.input_mode, InputMode::Normal);
view.start_revset_input();
assert_eq!(view.input_mode, InputMode::RevsetInput);
view.cancel_input();
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_handle_key_navigation() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::MOVE_DOWN);
assert_eq!(action, LogAction::None);
assert_eq!(view.selected_index, 1);
let action = press_key(&mut view, keys::MOVE_UP);
assert_eq!(action, LogAction::None);
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_handle_key_open_diff() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::OPEN_DIFF);
assert_eq!(action, LogAction::OpenDiff("def67890".to_string()));
}
#[test]
fn test_handle_key_search_input() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::SEARCH_INPUT);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::SearchInput);
type_text(&mut view, "Init");
assert_eq!(view.input_buffer, "Init");
let action = submit(&mut view);
assert_eq!(action, LogAction::None); assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
assert_eq!(view.last_search_query, Some("Init".to_string()));
assert_eq!(view.selected_index, 1); }
#[test]
fn test_handle_key_revset_input() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::REVSET_INPUT);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RevsetInput);
type_text(&mut view, "all");
assert_eq!(view.input_buffer, "all");
let action = submit(&mut view);
assert_eq!(action, LogAction::ExecuteRevset("all".to_string()));
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
assert_eq!(view.revset_history, vec!["all".to_string()]);
}
#[test]
fn test_handle_key_revset_cancel() {
let mut view = LogView::new();
view.start_revset_input();
type_text(&mut view, "te");
assert_eq!(view.input_buffer, "te");
let action = escape(&mut view);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
}
#[test]
fn test_handle_key_backspace() {
let mut view = LogView::new();
view.start_revset_input();
type_text(&mut view, "ab");
assert_eq!(view.input_buffer, "ab");
press_key(&mut view, KeyCode::Backspace);
assert_eq!(view.input_buffer, "a");
}
#[test]
fn test_set_changes_resets_selection() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 2;
view.set_changes(vec![create_test_changes()[0].clone()]);
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_first_finds_from_beginning() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 2; view.last_search_query = Some("First".to_string());
assert!(view.search_first());
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_first_no_match() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.last_search_query = Some("nonexistent".to_string());
assert!(!view.search_first());
assert_eq!(view.selected_index, 0); }
#[test]
fn test_search_next_no_query() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
assert!(!view.search_next());
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_next_finds_match() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.last_search_query = Some("Initial".to_string());
assert!(view.search_next());
assert_eq!(view.selected_index, 1);
}
#[test]
fn test_search_next_wraps_around() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 1; view.last_search_query = Some("First".to_string());
assert!(view.search_next());
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_prev_finds_match() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 2; view.last_search_query = Some("Initial".to_string());
assert!(view.search_prev());
assert_eq!(view.selected_index, 1);
}
#[test]
fn test_search_prev_wraps_around() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 0;
view.last_search_query = Some("Initial".to_string());
assert!(view.search_prev());
assert_eq!(view.selected_index, 1);
}
#[test]
fn test_search_no_match() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.last_search_query = Some("nonexistent".to_string());
assert!(!view.search_next());
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_by_author() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.last_search_query = Some("example.com".to_string());
assert!(view.search_next());
assert_eq!(view.selected_index, 1); }
#[test]
fn test_search_by_bookmark() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 1; view.last_search_query = Some("main".to_string());
assert!(view.search_next());
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_case_insensitive() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 1; view.last_search_query = Some("FIRST".to_string());
assert!(view.search_next());
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_handle_key_search_next() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.last_search_query = Some("Initial".to_string());
let action = press_key(&mut view, keys::SEARCH_NEXT);
assert_eq!(action, LogAction::None);
assert_eq!(view.selected_index, 1);
}
#[test]
fn test_handle_key_search_prev() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.selected_index = 2;
view.last_search_query = Some("First".to_string());
let action = press_key(&mut view, keys::SEARCH_PREV);
assert_eq!(action, LogAction::None);
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_search_input_stores_query() {
let mut view = LogView::new();
view.start_search_input();
type_text(&mut view, "main");
submit(&mut view);
assert_eq!(view.last_search_query, Some("main".to_string()));
}
#[test]
fn test_revset_input_does_not_store_search_query() {
let mut view = LogView::new();
view.start_revset_input();
type_text(&mut view, "all");
submit(&mut view);
assert_eq!(view.last_search_query, None);
}
#[test]
fn test_search_empty_enter_clears_query() {
let mut view = LogView::new();
view.last_search_query = Some("test".to_string());
view.start_search_input();
submit(&mut view);
assert_eq!(view.last_search_query, None);
}
#[test]
fn test_revset_empty_enter_returns_clear_action() {
let mut view = LogView::new();
view.start_revset_input();
let action = submit(&mut view);
assert_eq!(action, LogAction::ClearRevset);
}
#[test]
fn test_squash_key_enters_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::SQUASH);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::SquashSelect);
assert_eq!(
view.squash_source,
Some(("abc12345".to_string(), "def67890".to_string()))
);
}
#[test]
fn test_squash_select_cancel() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
let action = press_key(&mut view, keys::ESC);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert_eq!(view.squash_source, None);
}
#[test]
fn test_squash_select_confirm() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH); press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(matches!(
action,
LogAction::SquashInto { source, destination }
if source == "def67890" && destination == "uvw43210"
));
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_squash_into_same_revision_blocked() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
let action = press_key(&mut view, KeyCode::Enter);
assert_eq!(action, LogAction::None); assert_eq!(view.input_mode, InputMode::SquashSelect); assert_eq!(
view.squash_source,
Some(("abc12345".to_string(), "def67890".to_string()))
); }
#[test]
fn test_squash_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::SQUASH);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal); assert_eq!(view.squash_source, None);
}
#[test]
fn test_squash_select_navigation() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
assert_eq!(view.input_mode, InputMode::SquashSelect);
assert_eq!(view.selected_index, 0);
press_key(&mut view, keys::MOVE_DOWN);
assert_eq!(view.selected_index, 1);
press_key(&mut view, keys::MOVE_UP);
assert_eq!(view.selected_index, 0);
assert_eq!(view.input_mode, InputMode::SquashSelect);
}
#[test]
fn test_squash_select_ignores_other_keys() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
let action = press_key(&mut view, KeyCode::Char('d')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, KeyCode::Char('e')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, KeyCode::Char('/')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::SquashSelect);
}
#[test]
fn test_handle_key_abandon() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
assert_eq!(view.selected_change().unwrap().change_id, "abc12345");
let action = press_key(&mut view, keys::ABANDON);
assert_eq!(action, LogAction::Abandon("def67890".to_string()));
}
#[test]
fn test_handle_key_abandon_on_root() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.move_to_bottom();
assert_eq!(
view.selected_change().unwrap().change_id,
constants::ROOT_CHANGE_ID
);
let action = press_key(&mut view, keys::ABANDON);
assert_eq!(action, LogAction::Abandon("0".repeat(40)));
}
#[test]
fn test_handle_key_abandon_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::ABANDON);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_handle_key_split() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
assert_eq!(view.selected_change().unwrap().change_id, "abc12345");
let action = press_key(&mut view, keys::SPLIT);
assert_eq!(action, LogAction::Split("def67890".to_string()));
}
#[test]
fn test_handle_key_split_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::SPLIT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_handle_key_bookmark_create() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::BOOKMARK);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::BookmarkInput);
assert_eq!(view.editing_revision, Some("def67890".to_string()));
}
#[test]
fn test_handle_key_bookmark_create_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::BOOKMARK);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal); }
#[test]
fn test_bookmark_input_submit() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::BOOKMARK);
assert_eq!(view.input_mode, InputMode::BookmarkInput);
type_text(&mut view, "my-bookmark");
assert_eq!(view.input_buffer, "my-bookmark");
let action = submit(&mut view);
assert_eq!(
action,
LogAction::CreateBookmark {
revision: "def67890".to_string(),
name: "my-bookmark".to_string()
}
);
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
}
#[test]
fn test_bookmark_input_empty_submit_cancels() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::BOOKMARK);
let action = submit(&mut view);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_bookmark_input_cancel() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::BOOKMARK);
type_text(&mut view, "test");
let action = escape(&mut view);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
assert!(view.editing_revision.is_none());
}
#[test]
fn test_handle_key_bookmark_delete() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::BOOKMARK_DELETE);
assert_eq!(action, LogAction::StartBookmarkDelete);
}
#[test]
fn test_bookmark_delete_on_change_with_bookmarks() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
assert_eq!(view.selected_change().unwrap().bookmarks, vec!["main"]);
let action = press_key(&mut view, keys::BOOKMARK_DELETE);
assert_eq!(action, LogAction::StartBookmarkDelete);
}
#[test]
fn test_bookmark_delete_on_change_without_bookmarks() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.move_down();
assert!(view.selected_change().unwrap().bookmarks.is_empty());
let action = press_key(&mut view, keys::BOOKMARK_DELETE);
assert_eq!(action, LogAction::StartBookmarkDelete);
}
#[test]
fn test_rebase_mode_enter() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::REBASE);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RebaseModeSelect);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Selected { ref change_id, ref commit_id })
if change_id == "abc12345" && commit_id == "def67890"
));
}
#[test]
fn test_rebase_mode_cancel_from_mode_select() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
assert_eq!(view.input_mode, InputMode::RebaseModeSelect);
let action = press_key(&mut view, keys::ESC);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert_eq!(view.rebase_source, None);
}
#[test]
fn test_rebase_mode_cancel_from_rebase_select() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, keys::ESC);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert_eq!(view.rebase_source, None);
}
#[test]
fn test_rebase_mode_navigation() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.selected_index, 0);
press_key(&mut view, keys::MOVE_DOWN);
assert_eq!(view.selected_index, 1);
press_key(&mut view, keys::MOVE_UP);
assert_eq!(view.selected_index, 0);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
}
#[test]
fn test_rebase_action_revision_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Selected { ref change_id, .. })
if change_id == "abc12345"
));
press_key(&mut view, keys::MOVE_DOWN);
assert_eq!(view.selected_change().unwrap().change_id, "xyz98765");
let action = press_key(&mut view, KeyCode::Enter);
assert!(
matches!(action, LogAction::Rebase { source, destination, mode, .. }
if source == "def67890" && destination == "uvw43210" && mode == RebaseMode::Revision)
);
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_rebase_select_ignores_other_keys() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
let action = press_key(&mut view, KeyCode::Char('d')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, KeyCode::Char('e')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, KeyCode::Char('/')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
}
#[test]
fn test_rebase_mode_select_ignores_unrelated_keys() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
let action = press_key(&mut view, KeyCode::Char('d')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RebaseModeSelect);
let action = press_key(&mut view, KeyCode::Char('j')); assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::RebaseModeSelect);
}
#[test]
fn test_rebase_no_selection() {
let mut view = LogView::new();
view.set_changes(vec![]);
let action = press_key(&mut view, keys::REBASE);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert_eq!(view.rebase_source, None);
}
#[test]
fn test_rebase_mode_select_r_enters_revision() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
assert_eq!(view.input_mode, InputMode::RebaseModeSelect);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert_eq!(view.rebase_mode, RebaseMode::Revision);
}
#[test]
fn test_rebase_mode_select_s_enters_source() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('s'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert_eq!(view.rebase_mode, RebaseMode::Source);
}
#[test]
fn test_rebase_mode_select_a_enters_insert_after() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('A'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert_eq!(view.rebase_mode, RebaseMode::InsertAfter);
}
#[test]
fn test_rebase_mode_select_b_enters_insert_before() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('B'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert_eq!(view.rebase_mode, RebaseMode::InsertBefore);
}
#[test]
fn test_rebase_source_mode_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('s'));
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(
matches!(action, LogAction::Rebase { source, destination, mode, .. }
if source == "def67890" && destination == "uvw43210" && mode == RebaseMode::Source)
);
}
#[test]
fn test_rebase_insert_after_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('A'));
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(
matches!(action, LogAction::Rebase { source, destination, mode, .. }
if source == "def67890" && destination == "uvw43210" && mode == RebaseMode::InsertAfter)
);
}
#[test]
fn test_rebase_insert_before_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('B'));
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(
matches!(action, LogAction::Rebase { source, destination, mode, .. }
if source == "def67890" && destination == "uvw43210" && mode == RebaseMode::InsertBefore)
);
}
#[test]
fn test_rebase_self_select_blocked_all_modes() {
for mode_key in ['r', 's', 'b', 'A', 'B'] {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char(mode_key));
let action = press_key(&mut view, KeyCode::Enter);
assert_eq!(
action,
LogAction::None,
"Self-select should be blocked for mode key '{}'",
mode_key
);
assert_eq!(
view.input_mode,
InputMode::RebaseSelect,
"Should stay in RebaseSelect for mode key '{}'",
mode_key
);
}
}
#[test]
fn test_absorb_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::ABSORB);
assert!(matches!(action, LogAction::Absorb));
}
#[test]
fn test_absorb_key_works_without_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::ABSORB);
assert!(matches!(action, LogAction::Absorb));
}
#[test]
fn test_describe_key_returns_start_describe_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::DESCRIBE);
assert_eq!(action, LogAction::StartDescribe("def67890".to_string()));
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_set_describe_input_prefills_first_line() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), "First commit".to_string());
assert_eq!(view.input_mode, InputMode::DescribeInput);
assert_eq!(view.editing_revision, Some("abc12345".to_string()));
assert_eq!(view.input_buffer, "First commit");
}
#[test]
fn test_describe_input_cancel() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), "Test".to_string());
assert_eq!(view.input_mode, InputMode::DescribeInput);
view.cancel_input();
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
assert!(view.editing_revision.is_none());
}
#[test]
fn test_describe_input_escape_cancels() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), "Test description".to_string());
assert_eq!(view.input_mode, InputMode::DescribeInput);
let action = escape(&mut view);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
}
#[test]
fn test_describe_key_no_selection_returns_none() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::DESCRIBE);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_describe_input_enter_submits() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), "First commit".to_string());
assert_eq!(view.editing_revision, Some("abc12345".to_string()));
let action = submit(&mut view);
assert!(matches!(
action,
LogAction::Describe { revision, message }
if revision == "abc12345" && message == "First commit"
));
assert_eq!(view.input_mode, InputMode::Normal);
assert!(view.input_buffer.is_empty());
}
#[test]
fn test_describe_input_empty_submit_cancels() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), String::new());
let action = submit(&mut view);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_describe_input_type_and_submit() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), String::new());
type_text(&mut view, "new desc");
assert_eq!(view.input_buffer, "new desc");
let action = submit(&mut view);
assert!(matches!(
action,
LogAction::Describe { revision, message }
if revision == "abc12345" && message == "new desc"
));
}
#[test]
fn test_describe_external_key_returns_action() {
use crossterm::event::KeyModifiers;
let mut view = LogView::new();
view.set_changes(create_test_changes());
let key = KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL);
let action = view.handle_key(key);
assert_eq!(action, LogAction::DescribeExternal("def67890".to_string()));
}
#[test]
fn test_describe_external_no_selection_returns_none() {
use crossterm::event::KeyModifiers;
let mut view = LogView::new();
let key = KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL);
let action = view.handle_key(key);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_e_without_ctrl_returns_edit_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::EDIT);
assert_eq!(action, LogAction::Edit("def67890".to_string()));
}
#[test]
fn test_ctrl_e_in_search_input_mode_ignored() {
use crossterm::event::KeyModifiers;
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.start_search_input();
assert_eq!(view.input_mode, InputMode::SearchInput);
let key = KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL);
let action = view.handle_key(key);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::SearchInput);
assert!(
view.input_buffer.is_empty(),
"Ctrl+E should not add 'e' to input_buffer"
);
}
#[test]
fn test_ctrl_e_in_describe_input_mode_ignored() {
use crossterm::event::KeyModifiers;
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.set_describe_input("abc12345".to_string(), "test".to_string());
assert_eq!(view.input_mode, InputMode::DescribeInput);
let key = KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL);
let action = view.handle_key(key);
assert_eq!(action, LogAction::None);
assert_eq!(view.input_mode, InputMode::DescribeInput);
assert_eq!(
view.input_buffer, "test",
"Ctrl+E should not modify input_buffer"
);
}
#[test]
fn test_new_from_key_returns_action() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
view.selected_index = 1;
let result = view.handle_key(KeyEvent::from(KeyCode::Char('C')));
match result {
LogAction::NewChangeFrom {
revision,
display_name,
} => {
assert_eq!(revision, "uvw43210"); assert_eq!(display_name, "xyz98765"); }
_ => panic!("Expected NewChangeFrom action"),
}
}
#[test]
fn test_new_from_key_on_working_copy() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
view.selected_index = 0;
let result = view.handle_key(KeyEvent::from(KeyCode::Char('C')));
assert!(matches!(result, LogAction::NewChangeFromCurrent));
}
#[test]
fn test_new_from_key_with_bookmark() {
let mut view = LogView::default();
let mut changes = create_test_changes();
changes[1].bookmarks = vec!["feature".to_string()];
view.set_changes(changes);
view.selected_index = 1;
let result = view.handle_key(KeyEvent::from(KeyCode::Char('C')));
match result {
LogAction::NewChangeFrom { display_name, .. } => {
assert_eq!(display_name, "feature"); }
_ => panic!("Expected NewChangeFrom action"),
}
}
#[test]
fn test_new_from_no_selection() {
let mut view = LogView::default();
view.set_changes(vec![]);
let result = view.handle_key(KeyEvent::from(KeyCode::Char('C')));
assert!(matches!(result, LogAction::None));
}
#[test]
fn test_track_key_returns_start_track() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
let result = view.handle_key(KeyEvent::from(KeyCode::Char('T')));
assert!(matches!(result, LogAction::StartTrack));
}
#[test]
fn test_bookmark_jump_key_returns_start_bookmark_jump() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
let result = view.handle_key(KeyEvent::from(KeyCode::Char('\'')));
assert!(matches!(result, LogAction::StartBookmarkJump));
}
#[test]
fn test_select_change_by_id_found() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
assert_eq!(view.selected_index, 0);
let found = view.select_change_by_id("xyz98765");
assert!(found);
assert_eq!(view.selected_index, 1);
assert_eq!(view.selected_change().unwrap().change_id, "xyz98765");
}
#[test]
fn test_compare_same_revision_returns_notification() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::COMPARE);
let action = press_key(&mut view, KeyCode::Enter);
assert_eq!(action, LogAction::CompareSameRevision); assert_eq!(view.input_mode, InputMode::CompareSelect); assert_eq!(
view.compare_from,
Some(("abc12345".to_string(), "def67890".to_string()))
); }
#[test]
fn test_select_change_by_id_not_found() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
assert_eq!(view.selected_index, 0);
let found = view.select_change_by_id("nonexistent");
assert!(!found);
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_select_change_by_prefix_exact_match() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
let found = view.select_change_by_prefix("abc12345");
assert!(found);
assert_eq!(view.selected_index, 0);
assert_eq!(view.selected_change().unwrap().change_id, "abc12345");
}
#[test]
fn test_select_change_by_prefix_short_prefix() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
let found = view.select_change_by_prefix("xyz9");
assert!(found);
assert_eq!(view.selected_index, 1);
assert_eq!(view.selected_change().unwrap().change_id, "xyz98765");
}
#[test]
fn test_select_change_by_prefix_no_match() {
let mut view = LogView::default();
view.set_changes(create_test_changes());
let found = view.select_change_by_prefix("qqq");
assert!(!found);
assert_eq!(view.selected_index, 0); }
#[test]
fn test_select_change_by_prefix_empty() {
let mut view = LogView::default();
view.set_changes(vec![]);
let found = view.select_change_by_prefix("abc");
assert!(!found);
}
#[test]
fn test_next_change_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::NEXT_CHANGE);
assert_eq!(action, LogAction::NextChange);
}
#[test]
fn test_prev_change_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::PREV_CHANGE);
assert_eq!(action, LogAction::PrevChange);
}
#[test]
fn test_next_change_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::NEXT_CHANGE);
assert_eq!(action, LogAction::NextChange);
}
#[test]
fn test_prev_change_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::PREV_CHANGE);
assert_eq!(action, LogAction::PrevChange);
}
#[test]
fn test_next_prev_ignored_in_squash_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, keys::NEXT_CHANGE);
assert_eq!(action, LogAction::None);
let action = press_key(&mut view, keys::PREV_CHANGE);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_next_prev_ignored_in_rebase_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, keys::NEXT_CHANGE);
assert_eq!(action, LogAction::None);
let action = press_key(&mut view, keys::PREV_CHANGE);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_select_working_copy_found() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.move_down();
assert_eq!(view.selected_index, 1);
let found = view.select_working_copy();
assert!(found);
assert_eq!(view.selected_index, 0);
assert!(view.selected_change().unwrap().is_working_copy);
}
#[test]
fn test_select_working_copy_not_found() {
let mut view = LogView::new();
let changes = vec![
Change {
change_id: ChangeId::new("abc12345".to_string()),
commit_id: CommitId::new("def67890".to_string()),
author: "user@example.com".to_string(),
timestamp: "2024-01-29".to_string(),
description: "First commit".to_string(),
is_working_copy: false,
is_empty: false,
bookmarks: vec![],
graph_prefix: "○ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
Change {
change_id: ChangeId::new("xyz98765".to_string()),
commit_id: CommitId::new("uvw43210".to_string()),
author: "user@example.com".to_string(),
timestamp: "2024-01-28".to_string(),
description: "Second commit".to_string(),
is_working_copy: false,
is_empty: false,
bookmarks: vec![],
graph_prefix: "○ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
];
view.set_changes(changes);
view.move_down();
let found = view.select_working_copy();
assert!(!found);
assert_eq!(view.selected_index, 1);
}
#[test]
fn test_select_working_copy_already_selected() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
assert_eq!(view.selected_index, 0);
assert!(view.selected_change().unwrap().is_working_copy);
let found = view.select_working_copy();
assert!(found);
assert_eq!(view.selected_index, 0);
}
#[test]
fn test_select_working_copy_empty_changes() {
let mut view = LogView::new();
view.set_changes(vec![]);
let found = view.select_working_copy();
assert!(!found);
}
#[test]
fn test_reverse_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::LOG_REVERSE);
assert_eq!(action, LogAction::ToggleReversed);
}
#[test]
fn test_reverse_default_is_false() {
let view = LogView::new();
assert!(!view.reversed);
}
#[test]
fn test_reverse_preserves_selection() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.move_down();
assert_eq!(view.selected_change().unwrap().change_id, "xyz98765");
view.reversed = !view.reversed;
assert!(view.reversed);
let mut reversed_changes = create_test_changes();
reversed_changes.reverse();
view.set_changes(reversed_changes);
let found = view.select_change_by_id("xyz98765");
assert!(found);
assert_eq!(view.selected_change().unwrap().change_id, "xyz98765");
}
#[test]
fn test_reverse_falls_back_to_working_copy() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.move_to_bottom();
assert_eq!(
view.selected_change().unwrap().change_id,
constants::ROOT_CHANGE_ID
);
view.reversed = true;
let changes = vec![
Change {
change_id: ChangeId::new("new11111".to_string()),
commit_id: CommitId::new("com11111".to_string()),
author: "user@example.com".to_string(),
timestamp: "2024-01-30".to_string(),
description: "New change".to_string(),
is_working_copy: true,
is_empty: false,
bookmarks: vec![],
graph_prefix: "@ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
Change {
change_id: ChangeId::new("abc12345".to_string()),
commit_id: CommitId::new("def67890".to_string()),
author: "user@example.com".to_string(),
timestamp: "2024-01-29".to_string(),
description: "First commit".to_string(),
is_working_copy: false,
is_empty: false,
bookmarks: vec![],
graph_prefix: "○ ".to_string(),
is_graph_only: false,
has_conflict: false,
},
];
view.set_changes(changes);
let found = view.select_change_by_id(constants::ROOT_CHANGE_ID);
assert!(!found);
let found = view.select_working_copy();
assert!(found);
assert!(view.selected_change().unwrap().is_working_copy);
}
#[test]
fn test_diffedit_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::DIFFEDIT);
assert_eq!(action, LogAction::DiffEdit("def67890".to_string()));
}
#[test]
fn test_diffedit_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::DIFFEDIT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_diffedit_does_not_conflict_with_edit() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::EDIT);
assert_eq!(action, LogAction::Edit("def67890".to_string()));
let action = press_key(&mut view, keys::DIFFEDIT);
assert_eq!(action, LogAction::DiffEdit("def67890".to_string()));
}
#[test]
fn test_evolog_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::EVOLOG);
assert_eq!(action, LogAction::OpenEvolog("def67890".to_string()));
}
#[test]
fn test_evolog_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::EVOLOG);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_evolog_does_not_conflict_with_edit() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::EDIT);
assert_eq!(action, LogAction::Edit("def67890".to_string()));
let action = press_key(&mut view, keys::EVOLOG);
assert_eq!(action, LogAction::OpenEvolog("def67890".to_string()));
}
#[test]
fn test_diffedit_ignored_in_squash_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, keys::DIFFEDIT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_evolog_ignored_in_rebase_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, keys::EVOLOG);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_revert_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::REVERT);
assert_eq!(action, LogAction::Revert("def67890".to_string()));
}
#[test]
fn test_revert_empty_commit_returns_none() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.move_to_bottom();
assert!(view.selected_change().unwrap().is_empty);
let action = press_key(&mut view, keys::REVERT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_revert_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::REVERT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_revert_ignored_in_squash_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, keys::REVERT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_revert_ignored_in_rebase_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, keys::REVERT);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_reverse_ignored_in_special_modes() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, keys::LOG_REVERSE);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_rebase_mode_select_b_enters_branch() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
assert_eq!(view.input_mode, InputMode::RebaseModeSelect);
press_key(&mut view, KeyCode::Char('b'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert_eq!(view.rebase_mode, RebaseMode::Branch);
}
#[test]
fn test_rebase_branch_mode_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('b'));
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(
matches!(action, LogAction::Rebase { source, destination, mode, .. }
if source == "def67890" && destination == "uvw43210" && mode == RebaseMode::Branch)
);
}
#[test]
fn test_skip_emptied_toggle_in_rebase_select() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert!(!view.skip_emptied);
press_key(&mut view, KeyCode::Char('S'));
assert!(view.skip_emptied);
press_key(&mut view, KeyCode::Char('S'));
assert!(!view.skip_emptied);
}
#[test]
fn test_skip_emptied_included_in_rebase_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char('S'));
assert!(view.skip_emptied);
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(matches!(
action,
LogAction::Rebase {
skip_emptied: true,
..
}
));
assert!(!view.skip_emptied);
}
#[test]
fn test_skip_emptied_reset_on_rebase_start() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.skip_emptied = true;
press_key(&mut view, keys::REBASE);
assert!(!view.skip_emptied);
}
#[test]
fn test_skip_emptied_reset_on_cancel_from_mode_select() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
view.skip_emptied = true;
press_key(&mut view, keys::ESC);
assert!(!view.skip_emptied);
}
#[test]
fn test_skip_emptied_reset_on_cancel_from_rebase_select() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char('S'));
assert!(view.skip_emptied);
press_key(&mut view, keys::ESC);
assert!(!view.skip_emptied);
}
#[test]
fn test_skip_emptied_false_in_action_when_not_toggled() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(matches!(
action,
LogAction::Rebase {
skip_emptied: false,
..
}
));
}
#[test]
fn test_simplify_parents_toggle_in_rebase_select() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert!(!view.simplify_parents);
press_key(&mut view, KeyCode::Char('P'));
assert!(view.simplify_parents);
press_key(&mut view, KeyCode::Char('P'));
assert!(!view.simplify_parents);
}
#[test]
fn test_simplify_parents_included_in_rebase_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char('P'));
assert!(view.simplify_parents);
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(matches!(
action,
LogAction::Rebase {
simplify_parents: true,
..
}
));
assert!(!view.simplify_parents);
}
#[test]
fn test_simplify_parents_reset_on_rebase_start() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
view.simplify_parents = true;
press_key(&mut view, keys::REBASE);
assert!(!view.simplify_parents);
}
#[test]
fn test_simplify_parents_reset_on_cancel() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char('P'));
assert!(view.simplify_parents);
press_key(&mut view, keys::ESC);
assert!(!view.simplify_parents);
}
#[test]
fn test_simplify_parents_and_skip_emptied_both_on() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char('S'));
press_key(&mut view, KeyCode::Char('P'));
assert!(view.skip_emptied);
assert!(view.simplify_parents);
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(matches!(
action,
LogAction::Rebase {
skip_emptied: true,
simplify_parents: true,
..
}
));
}
#[test]
fn test_simplify_parents_key_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::SIMPLIFY_PARENTS);
assert_eq!(action, LogAction::SimplifyParents("def67890".to_string()));
}
#[test]
fn test_simplify_parents_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::SIMPLIFY_PARENTS);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_simplify_parents_ignored_in_squash_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::SQUASH);
assert_eq!(view.input_mode, InputMode::SquashSelect);
let action = press_key(&mut view, keys::SIMPLIFY_PARENTS);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_simplify_parents_ignored_in_rebase_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, keys::SIMPLIFY_PARENTS);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_parallelize_key_enters_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::PARALLELIZE);
assert_eq!(action, LogAction::StartParallelize("abc12345".to_string()));
assert_eq!(view.input_mode, InputMode::ParallelizeSelect);
assert_eq!(
view.parallelize_from,
Some(("abc12345".to_string(), "def67890".to_string()))
);
}
#[test]
fn test_parallelize_enter_returns_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::PARALLELIZE);
press_key(&mut view, KeyCode::Char('j'));
let action = press_key(&mut view, KeyCode::Enter);
assert_eq!(
action,
LogAction::Parallelize {
from: "def67890".to_string(),
to: "uvw43210".to_string()
}
);
assert_eq!(view.input_mode, InputMode::Normal);
}
#[test]
fn test_parallelize_same_revision_returns_notification() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::PARALLELIZE);
let action = press_key(&mut view, KeyCode::Enter);
assert_eq!(action, LogAction::ParallelizeSameRevision);
assert_eq!(view.input_mode, InputMode::ParallelizeSelect); assert_eq!(
view.parallelize_from,
Some(("abc12345".to_string(), "def67890".to_string()))
); }
#[test]
fn test_parallelize_esc_cancels() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::PARALLELIZE);
assert_eq!(view.input_mode, InputMode::ParallelizeSelect);
escape(&mut view);
assert_eq!(view.input_mode, InputMode::Normal);
assert_eq!(view.parallelize_from, None);
}
#[test]
fn test_parallelize_reverse_selection() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, KeyCode::Char('j'));
press_key(&mut view, keys::PARALLELIZE);
assert_eq!(
view.parallelize_from,
Some(("xyz98765".to_string(), "uvw43210".to_string()))
);
press_key(&mut view, KeyCode::Char('k'));
let action = press_key(&mut view, KeyCode::Enter);
assert_eq!(
action,
LogAction::Parallelize {
from: "uvw43210".to_string(),
to: "def67890".to_string()
}
);
}
#[test]
fn test_rebase_colon_enters_revset_input_revision() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
press_key(&mut view, KeyCode::Char(':'));
assert_eq!(view.input_mode, InputMode::RebaseRevsetInput);
}
#[test]
fn test_rebase_colon_enters_revset_input_source() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('s'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
press_key(&mut view, KeyCode::Char(':'));
assert_eq!(view.input_mode, InputMode::RebaseRevsetInput);
}
#[test]
fn test_rebase_colon_enters_revset_input_branch() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('b'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
press_key(&mut view, KeyCode::Char(':'));
assert_eq!(view.input_mode, InputMode::RebaseRevsetInput);
}
#[test]
fn test_rebase_colon_ignored_in_insert_after() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('A'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
press_key(&mut view, KeyCode::Char(':'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
}
#[test]
fn test_rebase_colon_ignored_in_insert_before() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('B'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
press_key(&mut view, KeyCode::Char(':'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
}
#[test]
fn test_rebase_revset_input_sets_source() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "A|B");
submit(&mut view);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Revset(ref s)) if s == "A|B"
));
}
#[test]
fn test_rebase_revset_input_esc_clears_revset() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "some_revset");
escape(&mut view);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Selected { ref change_id, .. }) if change_id == "abc12345"
));
}
#[test]
fn test_rebase_revset_empty_restores_source() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
submit(&mut view);
assert_eq!(view.input_mode, InputMode::RebaseSelect);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Selected { ref change_id, .. }) if change_id == "abc12345"
));
}
#[test]
fn test_rebase_revset_action_includes_flag() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "A|B");
submit(&mut view);
press_key(&mut view, keys::MOVE_DOWN);
let action = press_key(&mut view, KeyCode::Enter);
assert!(matches!(
action,
LogAction::Rebase {
use_revset: true,
..
}
));
}
#[test]
fn test_rebase_revset_reset_on_cancel() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "A|B");
submit(&mut view);
assert!(matches!(view.rebase_source, Some(RebaseSource::Revset(_))));
escape(&mut view);
assert_eq!(view.input_mode, InputMode::Normal);
assert_eq!(view.rebase_source, None);
}
#[test]
fn test_rebase_revset_esc_after_previous_revset() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "A");
submit(&mut view);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Revset(ref s)) if s == "A"
));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "B");
escape(&mut view);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Selected { ref change_id, .. }) if change_id == "abc12345"
));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
}
#[test]
fn test_rebase_revset_backspace() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
press_key(&mut view, KeyCode::Char(':'));
type_text(&mut view, "abc");
press_key(&mut view, KeyCode::Backspace);
submit(&mut view);
assert!(matches!(
view.rebase_source,
Some(RebaseSource::Revset(ref s)) if s == "ab"
));
}
#[test]
fn test_fix_key_dispatches_action() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
let action = press_key(&mut view, keys::FIX);
assert_eq!(
action,
LogAction::Fix {
revision: "def67890".to_string(),
change_id: "abc12345".to_string()
}
);
}
#[test]
fn test_fix_key_no_selection() {
let mut view = LogView::new();
let action = press_key(&mut view, keys::FIX);
assert_eq!(action, LogAction::None);
}
#[test]
fn test_fix_key_ignored_in_rebase_select_mode() {
let mut view = LogView::new();
view.set_changes(create_test_changes());
press_key(&mut view, keys::REBASE);
press_key(&mut view, KeyCode::Char('r'));
assert_eq!(view.input_mode, InputMode::RebaseSelect);
let action = press_key(&mut view, keys::FIX);
assert_eq!(action, LogAction::None);
}