use crate::common::harness::EditorTestHarness;
use crossterm::event::{KeyCode, KeyModifiers};
#[test]
fn test_paste_replaces_selection() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("hello world").unwrap();
harness.assert_buffer_content("hello world");
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
for _ in 0..6 {
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
}
for _ in 0..5 {
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
}
let primary = harness.editor().active_state().cursors.primary();
assert_eq!(primary.position, 11, "Cursor should be at end of 'world'");
assert_eq!(
primary.anchor,
Some(6),
"Anchor should be at start of 'world'"
);
harness
.editor_mut()
.set_clipboard_for_test("universe".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("hello universe");
}
#[test]
fn test_paste_with_multiple_cursors() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("aaa\nbbb\nccc").unwrap();
harness.assert_buffer_content("aaa\nbbb\nccc");
harness
.send_key(KeyCode::Home, KeyModifiers::CONTROL)
.unwrap();
harness.editor_mut().add_cursor_below();
harness.editor_mut().add_cursor_below();
assert_eq!(harness.editor().active_state().cursors.count(), 3);
harness.editor_mut().set_clipboard_for_test("X".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
let content = harness.get_buffer_content().unwrap();
let x_count = content.matches('X').count();
assert_eq!(
x_count, 3,
"Should have 3 X's (one per cursor), got {}. Buffer:\n{}",
x_count, content
);
harness.assert_buffer_content("Xaaa\nXbbb\nXccc");
}
#[test]
fn test_paste_replaces_multiple_selections() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("foo bar foo baz foo").unwrap();
harness.assert_buffer_content("foo bar foo baz foo");
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
harness.editor_mut().add_cursor_at_next_match();
harness.render().unwrap();
harness.editor_mut().add_cursor_at_next_match();
harness.render().unwrap();
assert_eq!(harness.editor().active_state().cursors.count(), 3);
harness
.editor_mut()
.set_clipboard_for_test("XXX".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("XXX bar XXX baz XXX");
}
#[test]
fn test_paste_undo_is_atomic() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("hello").unwrap();
harness.assert_buffer_content("hello");
harness
.editor_mut()
.set_clipboard_for_test(" world".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("hello world");
harness
.send_key(KeyCode::Char('z'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("hello");
harness
.send_key(KeyCode::Char('y'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("hello world");
}
#[test]
fn test_multi_cursor_paste_undo_is_atomic() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("aaa\nbbb\nccc").unwrap();
harness.assert_buffer_content("aaa\nbbb\nccc");
harness
.send_key(KeyCode::Home, KeyModifiers::CONTROL)
.unwrap();
harness.editor_mut().add_cursor_below();
harness.editor_mut().add_cursor_below();
assert_eq!(harness.editor().active_state().cursors.count(), 3);
harness.editor_mut().set_clipboard_for_test("X".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
let content = harness.get_buffer_content().unwrap();
let x_count = content.matches('X').count();
assert_eq!(x_count, 3, "Should have 3 X's. Buffer:\n{}", content);
harness
.send_key(KeyCode::Char('z'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
let content_after_undo = harness.get_buffer_content().unwrap();
let x_count_after_undo = content_after_undo.matches('X').count();
assert_eq!(
x_count_after_undo, 0,
"Single undo should remove all X's. Buffer:\n{}",
content_after_undo
);
harness.assert_buffer_content("aaa\nbbb\nccc");
}
#[test]
fn test_paste_with_selection_undo_is_atomic() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("hello world").unwrap();
harness.assert_buffer_content("hello world");
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
for _ in 0..6 {
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
}
for _ in 0..5 {
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
}
harness
.editor_mut()
.set_clipboard_for_test("universe".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("hello universe");
harness
.send_key(KeyCode::Char('z'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("hello world");
harness
.send_key(KeyCode::Char('y'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("hello universe");
}
#[test]
fn test_paste_multiline_text() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.assert_buffer_content("");
harness
.editor_mut()
.set_clipboard_for_test("line1\nline2\nline3".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("line1\nline2\nline3");
harness
.send_key(KeyCode::Char('z'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("");
}
#[test]
fn test_paste_at_end_of_line() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("hello").unwrap();
harness.assert_buffer_content("hello");
harness
.editor_mut()
.set_clipboard_for_test(" world".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("hello world");
}
#[test]
fn test_paste_in_middle() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("helloworld").unwrap();
harness.assert_buffer_content("helloworld");
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
for _ in 0..5 {
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
}
harness.editor_mut().set_clipboard_for_test(" ".to_string());
harness.editor_mut().paste_for_test();
harness.render().unwrap();
harness.assert_buffer_content("hello world");
}
#[test]
fn test_external_paste_goes_to_prompt() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("buffer content").unwrap();
harness.assert_buffer_content("buffer content");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command:");
harness.editor_mut().paste_text("pasted text".to_string());
harness.render().unwrap();
harness.assert_screen_contains("Command: pasted text");
harness.assert_buffer_content("buffer content");
}
#[test]
fn test_external_paste_in_open_file_prompt() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.send_key(KeyCode::Char('o'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Open file:");
harness
.editor_mut()
.paste_text("/path/to/file.txt".to_string());
harness.render().unwrap();
harness.assert_screen_contains("/path/to/file.txt");
}
#[test]
fn test_external_paste_appends_to_prompt() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("hello ").unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: hello ");
harness.editor_mut().paste_text("world".to_string());
harness.render().unwrap();
harness.assert_screen_contains("Command: hello world");
}
#[test]
fn test_ctrl_v_paste_in_prompt() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.editor_mut()
.set_clipboard_for_test("clipboard content".to_string());
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command:");
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: clipboard content");
}
#[test]
fn test_prompt_copy_paste_workflow() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.editor_mut().set_clipboard_for_test("".to_string());
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("copy me").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('c'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
harness
.send_key(KeyCode::Delete, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command:");
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: copy me");
}
#[test]
fn test_prompt_cut_paste_workflow() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.editor_mut().set_clipboard_for_test("".to_string());
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("cut me").unwrap();
harness.process_async_and_render().unwrap();
harness.assert_screen_contains("Command: cut me");
harness
.send_key(KeyCode::Char('x'), KeyModifiers::CONTROL)
.unwrap();
harness.process_async_and_render().unwrap();
let screen = harness.screen_to_string();
assert!(
screen.contains("Command:") && !screen.contains("cut me"),
"Prompt should be empty after cut. Screen:\n{}",
screen
);
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.process_async_and_render().unwrap();
harness.assert_screen_contains("Command: cut me");
}
#[test]
fn test_prompt_copy_selection() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.editor_mut().set_clipboard_for_test("".to_string());
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("hello world").unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
for _ in 0..5 {
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
}
harness
.send_key(KeyCode::Char('c'), KeyModifiers::CONTROL)
.unwrap();
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: hello");
let screen = harness.screen_to_string();
let prompt_content = screen
.lines()
.find(|line| line.contains("Command:"))
.unwrap_or("");
assert!(
!prompt_content.contains("world"),
"Should only paste selected text 'hello', not 'world'. Line: {}",
prompt_content
);
}
#[test]
fn test_prompt_cut_selection() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.editor_mut().set_clipboard_for_test("".to_string());
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("hello world").unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
for _ in 0..6 {
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
}
harness
.send_key(KeyCode::Char('x'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: world");
harness.send_key(KeyCode::Esc, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: hello ");
}
#[test]
fn test_prompt_paste_replaces_selection() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.editor_mut()
.set_clipboard_for_test("replaced".to_string());
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("hello world").unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Home, KeyModifiers::NONE).unwrap();
for _ in 0..6 {
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
}
for _ in 0..5 {
harness
.send_key(KeyCode::Right, KeyModifiers::SHIFT)
.unwrap();
}
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command: hello replaced");
}
#[test]
fn test_external_paste_replaces_prompt_selection() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.type_text("old text").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('a'), KeyModifiers::CONTROL)
.unwrap();
harness.editor_mut().paste_text("new text".to_string());
harness.render().unwrap();
harness.assert_screen_contains("Command: new text");
let screen = harness.screen_to_string();
let prompt_line = screen
.lines()
.find(|l| l.contains("Command:"))
.unwrap_or("");
assert!(
!prompt_line.contains("old"),
"Old text should be replaced. Line: {}",
prompt_line
);
}
#[test]
fn test_paste_in_search_prompt() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.type_text("find this text").unwrap();
harness
.editor_mut()
.set_clipboard_for_test("this".to_string());
harness
.send_key(KeyCode::Char('f'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Search:");
harness
.send_key(KeyCode::Char('v'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Search: this");
}
#[test]
fn test_paste_crlf_text_normalized() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.assert_buffer_content("");
harness
.editor_mut()
.paste_text("line1\r\nline2\r\nline3".to_string());
harness.render().unwrap();
harness.assert_buffer_content("line1\nline2\nline3");
}
#[test]
fn test_paste_cr_only_text_normalized() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.assert_buffer_content("");
harness
.editor_mut()
.paste_text("line1\rline2\rline3".to_string());
harness.render().unwrap();
harness.assert_buffer_content("line1\nline2\nline3");
}
#[test]
fn test_paste_into_crlf_buffer() {
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("crlf_paste_test.txt");
std::fs::write(&file_path, "existing\r\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::End, KeyModifiers::CONTROL)
.unwrap();
harness.editor_mut().paste_text("new\nlines".to_string());
harness.render().unwrap();
let content = harness.get_buffer_content().unwrap();
assert!(
content.contains("\r\n"),
"Pasted text should use CRLF in CRLF buffer"
);
assert!(
content.contains("existing\r\nnew\r\nlines"),
"Content should be: {:?}",
content
);
}
#[test]
fn test_paste_mixed_line_endings() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.editor_mut()
.paste_text("crlf\r\ncr\rlf\n".to_string());
harness.render().unwrap();
harness.assert_buffer_content("crlf\ncr\nlf\n");
}
#[test]
fn test_paste_crlf_into_prompt() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_screen_contains("Command:");
harness
.editor_mut()
.paste_text("line1\r\nline2".to_string());
harness.render().unwrap();
harness.assert_screen_contains("line1");
}