use crate::common::harness::EditorTestHarness;
use crossterm::event::{KeyCode, KeyModifiers};
#[test]
fn test_open_settings_modal() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Settings");
harness.open_settings().unwrap();
harness.assert_screen_contains("Settings");
}
#[test]
fn test_close_settings_with_escape() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.assert_screen_contains("Settings");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Settings");
}
#[test]
fn test_settings_navigation() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_search() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('t'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('h'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('e'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('m'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('e'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_help_overlay() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('?'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Keyboard Shortcuts");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Settings");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_search_text_displays() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Type to search");
harness
.send_key(KeyCode::Char('t'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('a'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('b'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("tab");
let screen = harness.screen_to_string();
assert!(
screen.contains(" of ") || screen.contains("results"),
"Should show result count indicator"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_toggle_shows_modified() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_confirmation_dialog_shows_changes() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Unsaved Changes");
harness.assert_screen_contains("You have unsaved changes");
harness.assert_screen_contains("check_for_updates");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_confirmation_dialog_button_navigation() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Save and Exit ]");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Discard ]");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Cancel ]");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Unsaved Changes");
harness.assert_screen_contains("Settings");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_settings_selection_indicator() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("> Active Keybinding Map"),
"Focus indicator '>' should appear before focused item in settings panel. Screen:\n{}",
screen
);
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("> Check For Updates"),
"Focus indicator '>' should move to Check For Updates. Screen:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_number_increment() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("501");
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Left, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_number_decrement() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness.send_key(KeyCode::Left, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("499");
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_settings_dropdown_cycle() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "theme".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let initial_screen = harness.screen_to_string();
let has_dark = initial_screen.contains("dark");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
if has_dark {
harness.assert_screen_contains("modified");
}
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_settings_dropdown_increment() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "theme".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let initial_screen = harness.screen_to_string();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let new_screen = harness.screen_to_string();
if initial_screen != new_screen {
harness.assert_screen_contains("modified");
}
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_settings_scrolling() {
let mut harness = EditorTestHarness::new(100, 25).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let initial_screen = harness.screen_to_string();
for _ in 0..15 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
let scrolled_screen = harness.screen_to_string();
assert_ne!(
initial_screen, scrolled_screen,
"Screen should change after scrolling down"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_scrollbar_visible() {
let mut harness = EditorTestHarness::new(100, 25).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let has_scrollbar = (40..100).any(|col| harness.has_scrollbar_at_column(col));
assert!(
has_scrollbar,
"Settings panel should have a visible scrollbar (checked columns 40-99)"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_search_jump_scrolls() {
let mut harness = EditorTestHarness::new(100, 25).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "wrap".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Wrap");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_search_result_click_navigates() {
let mut harness = EditorTestHarness::new(100, 30).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "tab".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("Tab Size");
let screen = harness.screen_to_string();
let result_pos = screen
.lines()
.enumerate()
.find_map(|(row, line)| line.find("Tab Size").map(|col| (col as u16, row as u16)))
.expect("Should find Tab Size in search results");
harness.mouse_click(result_pos.0 + 2, result_pos.1).unwrap();
harness.render().unwrap();
assert!(
!harness.screen_to_string().contains("Type to search"),
"Search mode should be closed after clicking a result"
);
harness.assert_screen_contains("Tab Size");
harness.assert_screen_contains("Editor");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
#[ignore] fn test_settings_theme_dropdown_cycle() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "theme".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Theme");
let initial_screen = harness.screen_to_string();
let has_high_contrast = initial_screen.contains("high-contrast");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let after_enter = harness.screen_to_string();
if has_high_contrast {
assert!(
!after_enter.contains("high-contrast") || after_enter.contains("modified"),
"Theme should change after pressing Enter, but it stayed the same"
);
}
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let after_right = harness.screen_to_string();
assert!(
after_right.contains("modified"),
"Theme dropdown should cycle with Right arrow and show modified indicator"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_settings_from_terminal_mode_captures_input() {
use portable_pty::{native_pty_system, PtySize};
if native_pty_system()
.openpty(PtySize {
rows: 1,
cols: 1,
pixel_width: 0,
pixel_height: 0,
})
.is_err()
{
eprintln!("Skipping test: PTY not available");
return;
}
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.editor_mut().open_terminal();
harness.render().unwrap();
assert!(
harness.editor().is_terminal_mode(),
"Should be in terminal mode after opening terminal"
);
harness.open_settings().unwrap();
harness.assert_screen_contains("Settings");
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap(); harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Quick Suggestions");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
if harness.screen_to_string().contains("Unsaved Changes") {
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
}
#[test]
fn test_settings_footer_buttons_keyboard_accessible() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.assert_screen_contains("Settings");
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ User ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Reset ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Save ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Cancel ]");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Unsaved Changes");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Settings");
}
#[test]
fn test_settings_change_theme_and_save() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
let initial_theme = harness.editor().theme().name.clone();
harness.open_settings().unwrap();
assert!(
harness.editor().is_settings_open(),
"Settings should be open after Ctrl+,"
);
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "theme".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('s'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Settings should be closed after saving"
);
let new_theme = harness.editor().theme().name.clone();
assert_ne!(
new_theme, initial_theme,
"Theme should have changed after saving. Was: {}, Now: {}",
initial_theme, new_theme
);
}
#[test]
fn test_settings_descriptions_render_properly() {
let mut harness = EditorTestHarness::new(120, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap(); harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
!screen.contains(" hether") && !screen.contains("|hether"), "Description should not be cut mid-word (found 'hether' at start of word)"
);
assert!(
!screen.contains(" oll interval"), "Description should not be cut mid-word (found 'oll interval')"
);
assert!(
!screen.contains(" yntax "), "Description should not be cut mid-word"
);
assert!(
screen.contains("completion")
|| screen.contains("Completion")
|| screen.contains("suggest"),
"Should show completion-related description (first visible section)"
);
assert!(
screen.contains("Enter") || screen.contains("trigger") || screen.contains("suggestions"),
"Description containing completion behavior should be visible"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_consumes_global_shortcuts() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
harness.open_settings().unwrap();
assert!(
harness.editor().is_settings_open(),
"Settings should be open"
);
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
harness.editor().is_settings_open(),
"Settings should still be open after Ctrl+P - shortcut should be consumed"
);
harness.assert_screen_not_contains("Command Palette");
harness
.send_key(KeyCode::Char('q'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
harness.editor().is_settings_open(),
"Settings should still be open after Ctrl+Q - shortcut should be consumed"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
#[ignore] fn test_map_control_add_new_shows_text_input() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "keybinding maps".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("[+] Add new");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "vim".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("vim");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("vim");
harness.assert_screen_contains("[+] Add new");
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Unsaved Changes");
harness.assert_screen_contains("keybinding_maps");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_settings_file_explorer_width_shows_percent_suffix() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
assert_eq!(
harness.config().file_explorer.width,
fresh::config::ExplorerWidth::Percent(30),
);
harness.open_settings().unwrap();
for _ in 0..4 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
for _ in 0..7 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("Width");
harness.assert_screen_contains("30%");
}
#[test]
fn test_settings_file_explorer_width_applies_live() {
use fresh::config::ExplorerWidth;
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness
.send_key(KeyCode::Char('e'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_file_explorer().unwrap();
harness.render().unwrap();
assert!(harness.editor().file_explorer_visible());
let before = find_settings_explorer_border_col(&harness) + 1;
assert_eq!(
before, 30,
"baseline: default 30% should render 30 cols on a 100-col terminal"
);
harness.open_settings().unwrap();
for _ in 0..4 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
for _ in 0..7 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("Width");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
for c in "24".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('s'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Ctrl+S should close the settings dialog after saving"
);
assert_eq!(
harness.config().file_explorer.width,
ExplorerWidth::Columns(24),
"Settings save path must write Columns(24) to config"
);
let after = find_settings_explorer_border_col(&harness) + 1;
assert_eq!(
after, 24,
"explorer panel should re-render at 24 columns immediately after saving settings.\nScreen:\n{}",
harness.screen_to_string()
);
}
fn find_settings_explorer_border_col(harness: &EditorTestHarness) -> u16 {
for row in 0..40u16 {
let text = harness.get_row_text(row);
for (i, ch) in text.chars().enumerate() {
if ch == '┐' {
return i as u16;
}
}
}
for row in (0..40u16).rev() {
let text = harness.get_row_text(row);
for (i, ch) in text.chars().enumerate() {
if ch == '┘' {
return i as u16;
}
}
}
panic!(
"Could not find file explorer border on screen.\nScreen:\n{}",
harness.screen_to_string()
);
}
#[test]
fn test_settings_file_explorer_toggles_propagate_to_runtime() {
let mut harness = EditorTestHarness::with_temp_project(120, 40).unwrap();
harness.editor_mut().focus_file_explorer();
harness.wait_for_file_explorer().unwrap();
assert!(!harness
.editor()
.file_explorer()
.unwrap()
.ignore_patterns()
.show_hidden());
assert!(!harness
.editor()
.file_explorer()
.unwrap()
.ignore_patterns()
.show_gitignored());
harness.open_settings().unwrap();
for _ in 0..4 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
for _ in 0..4 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Settings should be closed after saving"
);
assert!(harness.config().file_explorer.show_hidden);
assert!(harness.config().file_explorer.show_gitignored);
let patterns = harness.editor().file_explorer().unwrap().ignore_patterns();
assert!(
patterns.show_hidden(),
"live IgnorePatterns.show_hidden was not propagated from Settings save"
);
assert!(
patterns.show_gitignored(),
"live IgnorePatterns.show_gitignored was not propagated from Settings save"
);
}
#[test]
fn test_number_input_enter_editing_mode() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
for c in "750".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("750");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_number_input_escape_cancels_editing() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
for c in "999".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("999");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_number_input_cursor_navigation() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Char('1'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("1500");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_number_input_backspace() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.send_key(KeyCode::End, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Backspace, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("50");
harness
.send_key(KeyCode::Backspace, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("5");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_loads_saved_values_on_reopen() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
let initial_value = harness.config().editor.tab_size;
assert_eq!(initial_value, 4, "Initial tab_size should be 4");
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "tab size".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("4");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("5");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Settings should be closed after saving"
);
let saved_value = harness.config().editor.tab_size;
assert_eq!(saved_value, 5, "tab_size should be 5 after saving");
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "tab size".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("5");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_number_input_enter_selects_all_text() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "hover delay".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("500");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "100".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("100");
harness.assert_screen_not_contains("500100");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_category_selection_indicator_visible() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
let screen = harness.screen_to_string();
assert!(
screen
.lines()
.any(|l| l.contains(">") && l.contains("General") && l.find(">") < l.find("General")),
"Expected '>' indicator on General category when focused. Screen: {}",
screen
);
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.lines().any(|l| l.contains(">")
&& l.contains("Clipboard")
&& l.find(">") < l.find("Clipboard")),
"Expected '>' indicator on Clipboard category when focused. Screen: {}",
screen
);
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
let has_focused_clipboard = screen.lines().any(|l| {
if let (Some(gt_pos), Some(cb_pos)) = (l.find("> "), l.find("Clipboard")) {
gt_pos < cb_pos
} else {
false
}
});
assert!(
!has_focused_clipboard,
"Clipboard should not have '>' indicator when categories panel is unfocused. Screen: {}",
screen
);
harness.assert_screen_contains("Clipboard");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_ctrl_s_saves_settings() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
assert!(!harness.config().check_for_updates);
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness
.send_key(KeyCode::Char('s'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Settings should be closed after Ctrl+S"
);
assert!(
harness.config().check_for_updates,
"check_for_updates should be true after saving"
);
}
#[test]
fn test_entry_dialog_focus_indicator() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
for _ in 0..11 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
let screen = harness.screen_to_string();
if !screen.contains("[Enter to edit]") {
for _ in 0..5 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Edit Value");
harness.assert_screen_contains("Key:");
let screen = harness.screen_to_string();
assert!(
screen.contains("> Auto Close") || screen.contains(">● Auto Close"),
"Focus indicator '>' should appear before Auto Close. Screen:\n{}",
screen
);
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("> Auto Indent") || screen.contains(">● Auto Indent"),
"Focus indicator '>' should appear before Auto Indent. Screen:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_entry_dialog_add_new_textlist_item() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
for _ in 0..10 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Edit Value");
for _ in 0..3 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("[+] Add new");
let before_add = harness.screen_to_string();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "test_ext".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness.assert_screen_contains("test_ext");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let after_add = harness.screen_to_string();
assert_ne!(
before_add, after_add,
"Screen should change after adding item"
);
harness.assert_screen_contains("test_ext");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_entry_dialog_delete_textlist_item() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
for _ in 0..10 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Edit Value");
let mut attempts = 0;
loop {
harness.render().unwrap();
let screen = harness.screen_to_string();
if screen.contains("> Extensions") || screen.contains(">● Extensions") {
break;
}
let lines: Vec<&str> = screen.lines().collect();
let mut found_near = false;
for (i, line) in lines.iter().enumerate() {
if line.contains(">") && !line.contains("Extensions") {
for offset in 1..=3 {
if i >= offset && lines[i - offset].contains("Extensions:") {
found_near = true;
break;
}
}
}
if found_near {
break;
}
}
if found_near {
break;
}
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
attempts += 1;
assert!(
attempts < 100,
"Could not find Extensions section after {} Down presses.\nScreen:\n{}",
attempts,
screen
);
}
harness.assert_screen_contains("[x]");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "to_delete".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("to_delete");
harness.send_key(KeyCode::Up, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let before_delete = harness.screen_to_string();
assert!(
before_delete.contains("to_delete"),
"Item should be visible before delete"
);
harness
.send_key(KeyCode::Delete, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let after_delete = harness.screen_to_string();
assert!(
!after_delete.contains("to_delete"),
"Item should be removed after Delete key"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_toggle_persists_after_save_and_reopen() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
assert!(
!harness.config().check_for_updates,
"check_for_updates should be false in test harness"
);
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("> Check For Updates") && screen.contains(": [ ]"),
"Check For Updates should be focused and unchecked. Screen:\n{}",
screen
);
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains(">● Check For Updates") && screen.contains(": [✓]"),
"Check For Updates should now be checked (with modified indicator). Screen:\n{}",
screen
);
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Settings should be closed after save"
);
assert!(
harness.config().check_for_updates,
"check_for_updates should be true after save"
);
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("Check For Updates") && screen.contains(": [✓]"),
"BUG #474: After save and reopen, Check For Updates should still be checked [✓], \
but it shows the original value [ ]. Screen:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_line_numbers_config_applied_to_new_buffers() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("1 │"),
"Initial buffer should show line numbers by default"
);
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "line numbers".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('s'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
!harness.config().editor.line_numbers,
"line_numbers should be false after saving"
);
harness
.send_key(KeyCode::Char('n'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
!screen.contains("1 │") && !screen.contains("2 │"),
"New buffer should not show line numbers when config.editor.line_numbers=false. Screen:\n{}",
screen
);
}
#[test]
fn test_line_wrap_config_applied_to_new_buffers() {
let mut harness = EditorTestHarness::new(80, 40).unwrap();
harness.render().unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "line wrap".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('s'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
assert!(
!harness.config().editor.line_wrap,
"line_wrap should be false after saving"
);
harness
.send_key(KeyCode::Char('n'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
let long_text = "X".repeat(100);
for c in long_text.chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
let screen = harness.screen_to_string();
let lines: Vec<&str> = screen.lines().collect();
let line2_content = lines.get(3).unwrap_or(&""); assert!(
!line2_content.contains("X"),
"Long line should not wrap when config.editor.line_wrap=false. Line 2: '{}'. Screen:\n{}",
line2_content,
screen
);
}
fn navigate_to_lsp_json_editor(harness: &mut EditorTestHarness) {
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.type_text("lsp").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Lsp");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Edit Value");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
loop {
harness.render().unwrap();
let screen = harness.screen_to_string();
if screen.contains("> Initialization Options")
|| screen.contains(">● Initialization Options")
{
break;
}
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
}
#[test]
fn test_json_editor_delete_key_works() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
navigate_to_lsp_json_editor(&mut harness);
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Enter:Newline");
harness.type_text("ABC").unwrap();
harness.render().unwrap();
harness.assert_screen_contains("ABCnull");
for _ in 0..3 {
harness.send_key(KeyCode::Left, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Delete, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("BCnull") && !screen.contains("ABCnull"),
"Delete key should remove character at cursor. Expected 'BCnull', got:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_json_editor_home_end_keys_work() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
navigate_to_lsp_json_editor(&mut harness);
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.type_text("XYZ").unwrap();
harness.render().unwrap();
harness.assert_screen_contains("XYZnull");
harness.send_key(KeyCode::End, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('B'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("XYZnullB");
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('A'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("AXYZnullB");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_json_editor_ctrl_a_selects_all() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
navigate_to_lsp_json_editor(&mut harness);
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.type_text("OLD").unwrap();
harness.render().unwrap();
harness.assert_screen_contains("OLDnull");
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.type_text("NEW").unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("NEW") && !screen.contains("OLDnull") && !screen.contains("OLD"),
"Ctrl+A should select all, then typing should replace. Expected only 'NEW', got:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_json_editor_ctrl_c_copies_selected_text() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
harness.editor_mut().set_clipboard_for_test("".to_string());
navigate_to_lsp_json_editor(&mut harness);
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Enter:Newline");
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("HELLO").unwrap();
harness.render().unwrap();
harness.assert_screen_contains("HELLO");
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('c'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
let clipboard_content = harness.editor_mut().clipboard_content_for_test();
assert!(
clipboard_content.contains("HELLO"),
"Ctrl+C should copy selected JSON text to clipboard. Clipboard content: {:?}",
clipboard_content
);
}
#[test]
fn test_settings_edit_button_visible() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.assert_screen_contains("[ Edit ]");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_edit_button_keyboard_navigation() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ User ]");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Reset ]");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Save ]");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Cancel ]");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Edit ]");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_edit_button_opens_config_file() {
let mut harness = EditorTestHarness::new(120, 40).unwrap();
harness.open_settings().unwrap();
assert!(
harness.editor().is_settings_open(),
"Settings should be open"
);
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); harness.render().unwrap();
harness.assert_screen_contains(">[ Edit ]");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
assert!(
!harness.editor().is_settings_open(),
"Settings should be closed after Edit"
);
harness.assert_screen_contains("config.json");
harness.assert_screen_contains("Editing User config");
}
#[test]
fn test_settings_edit_button_blocked_with_pending_changes() {
let mut harness = EditorTestHarness::new(140, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap(); for _ in 0..4 {
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
assert!(
harness.editor().is_settings_open(),
"Settings should still be open when Edit is blocked due to pending changes"
);
harness.assert_screen_contains("Save or discard pending changes");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_map_add_new_button_clickable_with_mouse() {
let mut harness = EditorTestHarness::new(120, 45).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "keybinding maps".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.wait_until(|h| h.screen_to_string().contains("[+] Add new"))
.unwrap();
let screen = harness.screen_to_string();
let add_new_pos = screen
.lines()
.enumerate()
.find_map(|(row, line)| line.find("[+] Add new").map(|col| (col as u16, row as u16)))
.expect("Should find [+] Add new on screen");
harness
.mouse_click(add_new_pos.0 + 2, add_new_pos.1)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_lsp_map_has_add_new_button() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.type_text("lsp").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Lsp");
let screen = harness.screen_to_string();
assert!(
screen.contains("[+] Add new"),
"LSP map should show '[+] Add new' button since it doesn't have x-no-add.\n\
The LSP section should allow users to add new language server configurations.\n\
Screen contents:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_languages_map_has_add_new_button() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.type_text("languages").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Languages");
let screen = harness.screen_to_string();
assert!(
screen.contains("> Languages"),
"Focus should be on Languages section. Screen:\n{}",
screen
);
for _ in 0..30 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
if screen.contains("[+] Add new") && screen.contains("Languages") {
break;
}
}
let screen = harness.screen_to_string();
assert!(
screen.contains("[+] Add new"),
"Languages map should show '[+] Add new' button after scrolling to the end.\n\
The Languages section should allow users to add new language configurations.\n\
Full screen:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_lsp_map_add_new_button_click_opens_dialog() {
let mut harness = EditorTestHarness::new(120, 50).unwrap();
harness.render().unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.type_text("lsp").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Lsp");
for _ in 0..30 {
let screen = harness.screen_to_string();
if screen.contains("[+] Add new") && screen.contains("Lsp") {
break;
}
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
}
harness.assert_screen_contains("[+] Add new");
let screen = harness.screen_to_string();
let lines: Vec<&str> = screen.lines().collect();
let add_new_pos = lines
.iter()
.enumerate()
.find_map(|(row, line)| line.find("[+] Add new").map(|col| (col as u16, row as u16)))
.expect("Should find [+] Add new after scrolling to it");
eprintln!("Clicking at ({}, {})", add_new_pos.0 + 2, add_new_pos.1);
harness
.mouse_click(add_new_pos.0 + 2, add_new_pos.1)
.unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
eprintln!("Screen after click:\n{}", screen);
assert!(
screen.contains("Enter:Add") || screen.contains("[") && screen.contains("]"),
"Clicking '[+] Add new' on LSP map should start text input mode for key name.\n\
The screen should show a text input field or 'Enter:Add' help text.\n\
Screen contents:\n{}",
screen
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_map_entry_navigation_scrolls_to_focused_entry() {
let mut harness = EditorTestHarness::new(120, 30).unwrap();
harness.render().unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness.type_text("languages").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("Languages"),
"Should show Languages section. Screen:\n{}",
screen
);
assert!(
screen.contains("[Enter to edit]") || screen.contains("[+] Add new"),
"After jumping to Languages, the first entry should show '[Enter to edit]'.\n\
This indicates the Map control's focus state is properly set.\n\
Screen contents:\n{}",
screen
);
for i in 0..15 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
let has_focused_entry = screen.contains("[Enter to edit]");
let has_add_new_focused = screen.contains("[+] Add new");
assert!(
has_focused_entry || has_add_new_focused,
"After pressing Down {} times, the focused entry should be visible.\n\
Expected to see '[Enter to edit]' for a focused language entry or \n\
'[+] Add new' for the add-new row, but neither was found.\n\
This indicates the view didn't scroll to keep the focused entry visible.\n\
Screen contents:\n{}",
i + 1,
screen
);
}
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_settings_search_results_scroll() {
let mut harness = EditorTestHarness::new(80, 20).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Char('e'), KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let screen_before = harness.screen_to_string();
for _ in 0..15 {
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
let screen_after = harness.screen_to_string();
assert_ne!(
screen_before, screen_after,
"Screen should change after scrolling through search results"
);
for _ in 0..15 {
harness.send_key(KeyCode::Up, KeyModifiers::NONE).unwrap();
}
harness.render().unwrap();
let screen_back = harness.screen_to_string();
assert_eq!(
screen_before, screen_back,
"Screen should return to original state after scrolling back up"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_usability_backtab_backward_navigation() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::BackTab, KeyModifiers::SHIFT)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ User ]");
harness
.send_key(KeyCode::BackTab, KeyModifiers::SHIFT)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::BackTab, KeyModifiers::SHIFT)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::BackTab, KeyModifiers::SHIFT)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Edit ]");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_usability_footer_tab_visits_all_buttons() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ User ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Reset ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Save ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Cancel ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Edit ]");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains(">[ Edit ]");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_usability_left_arrow_to_categories() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Left, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ User ]");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_usability_dropdown_no_left_right_change() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "theme".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let before = harness.screen_to_string();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let after = harness.screen_to_string();
assert_eq!(
before, after,
"Right arrow should not change dropdown value"
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_usability_rulers_integer_array_no_data_loss() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "rulers".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Rulers");
let screen = harness.screen_to_string();
assert!(
!screen.contains("[Enter to edit JSON]"),
"Rulers should NOT render as JSON editor. Screen:\n{}",
screen
);
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
for c in "80".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("80");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("80");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
if harness.screen_to_string().contains("Unsaved Changes") {
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
}
#[test]
fn test_usability_descriptions_always_full() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
let truncation_markers: Vec<&str> = screen
.lines()
.filter(|line| {
let trimmed = line.trim();
trimmed.ends_with("...")
&& !trimmed.ends_with("e.g., ...")
&& !trimmed.ends_with("etc...")
&& trimmed.len() > 10
})
.collect();
assert!(
truncation_markers.len() <= 1,
"Descriptions should not be truncated. Found {} lines ending with '...': {:?}",
truncation_markers.len(),
truncation_markers
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_usability_entry_dialog_button_focus_indicator() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "languages".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let mut has_focused_button = false;
for _ in 0..60 {
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
let screen = harness.screen_to_string();
if screen.contains("> [ Save ]")
|| screen.contains("> [ Delete ]")
|| screen.contains("> [ Cancel ]")
{
has_focused_button = true;
break;
}
if screen.contains("[ Save ]") || screen.contains("[ Cancel ]") {
for line in screen.lines() {
if (line.contains("[ Save ]") || line.contains("[ Cancel ]")) && line.contains(">")
{
has_focused_button = true;
break;
}
}
if has_focused_button {
break;
}
}
}
assert!(
has_focused_button,
"Entry dialog buttons should show > focus indicator after Tab cycling. Screen:\n{}",
harness.screen_to_string()
);
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
}
#[test]
fn test_discard_dialog_does_not_persist_on_reopen() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Unsaved Changes");
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains(">[ Discard ]");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Settings");
harness.assert_screen_not_contains("Unsaved Changes");
harness.open_settings().unwrap();
harness.assert_screen_contains("Settings");
harness.assert_screen_not_contains("Unsaved Changes");
}
#[test]
fn test_reset_button_shows_confirmation_dialog() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Reset All Changes");
harness.assert_screen_contains("check_for_updates");
harness.assert_screen_contains("Reset");
harness.assert_screen_contains("Cancel");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Reset All Changes");
harness.assert_screen_contains("Settings");
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
}
#[test]
fn test_reset_dialog_confirm_discards_changes() {
let mut harness = EditorTestHarness::new(100, 40).unwrap();
harness.open_settings().unwrap();
harness
.send_key(KeyCode::Char('/'), KeyModifiers::NONE)
.unwrap();
for c in "check".chars() {
harness
.send_key(KeyCode::Char(c), KeyModifiers::NONE)
.unwrap();
}
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("modified");
harness.send_key(KeyCode::Tab, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Reset All Changes");
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Reset All Changes");
harness.assert_screen_contains("Settings");
harness.assert_screen_not_contains("modified");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness.assert_screen_not_contains("Unsaved Changes");
}