use righter::config::Config;
use righter::editor::document::Document;
use righter::editor::Editor;
use righter::input::mode::Mode;
fn editor_with_text(text: &str) -> Editor {
let mut doc = Document::new_empty();
doc.rope = ropey::Rope::from_str(text);
Editor::new(doc)
}
#[test]
fn document_new_empty() {
let doc = Document::new_empty();
assert_eq!(doc.line_count(), 2);
assert_eq!(doc.line_len(0), 0);
assert!(!doc.modified);
assert!(doc.path.is_none());
}
#[test]
fn document_line_count() {
let mut doc = Document::new_empty();
doc.rope = ropey::Rope::from_str("hello\nworld\n");
assert_eq!(doc.line_count(), 3); }
#[test]
fn document_insert_char() {
let mut doc = Document::new_empty();
doc.insert_char(righter::editor::selection::Position { row: 0, col: 0 }, 'A');
assert_eq!(doc.rope.to_string(), "A\n");
assert!(doc.modified);
}
#[test]
fn document_delete_char_backward() {
let mut doc = Document::new_empty();
doc.rope = ropey::Rope::from_str("AB\n");
let pos = righter::editor::selection::Position { row: 0, col: 1 };
let new_pos = doc.delete_char_backward(pos);
assert!(new_pos.is_some());
assert_eq!(new_pos.unwrap().col, 0);
assert_eq!(doc.rope.to_string(), "B\n");
}
#[test]
fn document_delete_char_backward_at_start() {
let mut doc = Document::new_empty();
let pos = righter::editor::selection::Position { row: 0, col: 0 };
let new_pos = doc.delete_char_backward(pos);
assert!(new_pos.is_none());
}
#[test]
fn document_insert_newline() {
let mut doc = Document::new_empty();
doc.rope = ropey::Rope::from_str("hello\n");
doc.insert_newline(righter::editor::selection::Position { row: 0, col: 3 });
assert_eq!(doc.line_count(), 3);
assert_eq!(doc.rope.line(0).to_string(), "hel\n");
assert_eq!(doc.rope.line(1).to_string(), "lo\n");
}
#[test]
fn document_file_name_no_path() {
let doc = Document::new_empty();
assert_eq!(doc.file_name(), "[No Name]");
}
#[test]
fn editor_move_right() {
let mut editor = editor_with_text("hello\n");
editor.mode = Mode::Normal;
assert_eq!(editor.cursor.col, 0);
editor.move_right();
assert_eq!(editor.cursor.col, 1);
}
#[test]
fn editor_move_right_clamps_at_line_end() {
let mut editor = editor_with_text("ab\n");
editor.mode = Mode::Normal;
editor.move_right(); editor.move_right(); assert_eq!(editor.cursor.col, 1);
}
#[test]
fn editor_move_down() {
let mut editor = editor_with_text("line1\nline2\n");
editor.view.height = 20;
assert_eq!(editor.cursor.row, 0);
editor.move_down();
assert_eq!(editor.cursor.row, 1);
}
#[test]
fn editor_move_up() {
let mut editor = editor_with_text("line1\nline2\n");
editor.view.height = 20;
editor.cursor.row = 1;
editor.move_up();
assert_eq!(editor.cursor.row, 0);
}
#[test]
fn editor_move_line_start_end() {
let mut editor = editor_with_text(" hello\n");
editor.cursor.col = 3;
editor.move_line_start();
assert_eq!(editor.cursor.col, 0);
editor.move_line_end();
assert!(editor.cursor.col > 0);
}
#[test]
fn editor_move_first_non_blank() {
let mut editor = editor_with_text(" hello\n");
editor.move_first_non_blank();
assert_eq!(editor.cursor.col, 2);
}
#[test]
fn editor_goto_top_bottom() {
let mut editor = editor_with_text("a\nb\nc\nd\n");
editor.view.height = 20;
editor.goto_bottom();
assert_eq!(editor.cursor.row, 4);
editor.goto_top();
assert_eq!(editor.cursor.row, 0);
}
#[test]
fn editor_insert_char() {
let mut editor = editor_with_text("\n");
editor.mode = Mode::Insert;
editor.insert_char('X');
assert_eq!(editor.document.rope.line(0).to_string(), "X\n");
assert_eq!(editor.cursor.col, 1);
}
#[test]
fn editor_delete_line() {
let mut editor = editor_with_text("aaa\nbbb\nccc\n");
editor.view.height = 20;
let orig_count = editor.document.line_count();
editor.delete_line();
assert_eq!(editor.document.line_count(), orig_count - 1);
assert_eq!(editor.document.rope.line(0).to_string(), "bbb\n");
}
#[test]
fn editor_delete_line_single_line() {
let mut editor = editor_with_text("only\n");
editor.view.height = 20;
editor.delete_line();
assert!(editor.document.line_count() >= 1);
}
#[test]
fn editor_join_lines() {
let mut editor = editor_with_text("hello\nworld\n");
editor.view.height = 20;
editor.join_lines();
assert_eq!(editor.document.rope.line(0).to_string(), "hello world\n");
}
#[test]
fn editor_join_lines_last_line_noop() {
let mut editor = editor_with_text("only\n");
editor.view.height = 20;
editor.cursor.row = 0;
editor.join_lines();
assert_eq!(editor.document.line_count(), 1);
}
#[test]
fn editor_indent_line() {
let mut editor = editor_with_text("hello\n");
editor.indent_line();
assert!(editor.document.rope.line(0).to_string().starts_with(" "));
}
#[test]
fn editor_dedent_line() {
let mut editor = editor_with_text(" hello\n");
editor.dedent_line();
assert_eq!(editor.document.rope.line(0).to_string(), "hello\n");
}
#[test]
fn editor_dedent_no_indent() {
let mut editor = editor_with_text("hello\n");
editor.dedent_line();
assert_eq!(editor.document.rope.line(0).to_string(), "hello\n");
}
#[test]
fn editor_replace_char() {
let mut editor = editor_with_text("abc\n");
editor.replace_char('X');
assert_eq!(editor.document.rope.line(0).to_string(), "Xbc\n");
}
#[test]
fn editor_undo_redo() {
let mut editor = editor_with_text("original\n");
editor.view.height = 20;
editor.enter_insert_mode();
editor.insert_char('Z');
assert!(editor.document.rope.to_string().contains('Z'));
editor.mode = Mode::Normal;
editor.undo();
assert_eq!(editor.document.rope.to_string(), "original\n");
editor.redo();
assert!(editor.document.rope.to_string().contains('Z'));
}
#[test]
fn editor_enter_insert_mode() {
let mut editor = editor_with_text("hello\n");
assert_eq!(editor.mode, Mode::Normal);
editor.enter_insert_mode();
assert_eq!(editor.mode, Mode::Insert);
}
#[test]
fn editor_enter_visual_mode() {
let mut editor = editor_with_text("hello\n");
editor.enter_visual_mode();
assert_eq!(editor.mode, Mode::Visual);
assert!(editor.visual_anchor.is_some());
}
#[test]
fn editor_enter_command_mode() {
let mut editor = editor_with_text("hello\n");
editor.enter_command_mode();
assert_eq!(editor.mode, Mode::Command);
assert!(editor.command_buffer.is_empty());
}
#[test]
fn config_default_values() {
let config = Config::default();
assert_eq!(config.tab_width, 4);
assert_eq!(config.scroll_off, 5);
assert!(!config.wrap);
assert_eq!(config.gui_font_size, 14.0);
assert!(config.gui_font_family.is_none());
}
#[test]
fn config_load_nonexistent_file() {
let result = Config::load_from_path(std::path::Path::new("/nonexistent/config.json"));
assert!(result.warning.is_none());
assert_eq!(result.config.tab_width, 4); }
#[test]
fn config_load_invalid_json() {
let dir = std::env::temp_dir().join("righter_test_config");
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("bad.json");
std::fs::write(&path, "not valid json{{{").unwrap();
let result = Config::load_from_path(&path);
assert!(result.warning.is_some());
assert!(result.warning.unwrap().contains("parse error"));
let _ = std::fs::remove_file(&path);
}
#[test]
fn config_load_partial_json() {
let dir = std::env::temp_dir().join("righter_test_config2");
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("partial.json");
std::fs::write(&path, r#"{"scroll_off": 10}"#).unwrap();
let result = Config::load_from_path(&path);
assert!(result.warning.is_none());
assert_eq!(result.config.scroll_off, 10);
assert_eq!(result.config.tab_width, 4); let _ = std::fs::remove_file(&path);
}
#[test]
fn config_load_font_family() {
let dir = std::env::temp_dir().join("righter_test_config3");
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("font.json");
std::fs::write(&path, r#"{"font_family": "Menlo"}"#).unwrap();
let result = Config::load_from_path(&path);
assert_eq!(result.config.gui_font_family.as_deref(), Some("Menlo"));
let _ = std::fs::remove_file(&path);
}
#[test]
fn editor_move_word_forward() {
let mut editor = editor_with_text("hello world\n");
editor.move_word_forward();
assert!(editor.cursor.col > 0);
assert_eq!(editor.cursor.col, 6);
}
#[test]
fn editor_move_word_backward() {
let mut editor = editor_with_text("hello world\n");
editor.cursor.col = 8; editor.move_word_backward();
assert_eq!(editor.cursor.col, 6); }