use crate::common::harness::EditorTestHarness;
use crossterm::event::{KeyCode, KeyModifiers};
use tempfile::TempDir;
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_to_new_buffer() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("unsorted.txt");
std::fs::write(&file_path, "cherry\napple\nbanana\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness.assert_buffer_content("cherry\napple\nbanana\n");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("sort").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("apple\nbanana\ncherry\n");
harness.assert_screen_contains("Shell output");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_replace_buffer() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("unsorted.txt");
std::fs::write(&file_path, "cherry\napple\nbanana\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness.assert_buffer_content("cherry\napple\nbanana\n");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command (replace)").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("sort").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("apple\nbanana\ncherry\n");
harness.assert_screen_contains("unsorted.txt");
}
#[test]
#[ignore = "Selection-based shell commands require more complex test setup"]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_on_selection() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("mixed.txt");
std::fs::write(&file_path, "header\ncherry\napple\nbanana\nfooter\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('v'), KeyModifiers::SHIFT)
.unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.type_text("shell command (replace)").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.type_text("sort").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("header\napple\nbanana\ncherry\nfooter\n");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_failure() {
let mut harness = EditorTestHarness::with_temp_project(120, 24).unwrap();
let project_dir = harness.project_dir().unwrap();
let file_path = project_dir.join("test.txt");
std::fs::write(&file_path, "some content\n").unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("false").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("some content\n");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_tr_transform() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("lowercase.txt");
std::fs::write(&file_path, "hello world\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command (replace)").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("tr a-z A-Z").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("HELLO WORLD\n");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_replace_undo() {
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.render().unwrap();
harness.type_text("original content").unwrap();
harness.render().unwrap();
harness.assert_buffer_content("original content");
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command (replace)").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("tr a-z A-Z").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("ORIGINAL CONTENT");
harness
.send_key(KeyCode::Char('z'), KeyModifiers::CONTROL)
.unwrap();
harness.render().unwrap();
harness.assert_buffer_content("original content");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_cat_identity() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.txt");
std::fs::write(&file_path, "line 1\nline 2\nline 3\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("cat").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("line 1\nline 2\nline 3\n");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_wc() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("words.txt");
std::fs::write(&file_path, "one two three\nfour five\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("wc -w").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_screen_contains("5");
}
#[test]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_replace_preserves_cursor_position() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.txt");
std::fs::write(&file_path, "hello world\nfoo bar\nbaz qux\n").unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let cursor_pos_before = harness.editor().active_state().cursors.primary().position;
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command (replace)").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("tr a-z A-Z").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.assert_buffer_content("HELLO WORLD\nFOO BAR\nBAZ QUX\n");
let cursor_pos_after = harness.editor().active_state().cursors.primary().position;
assert_eq!(
cursor_pos_before, cursor_pos_after,
"Cursor position should be preserved after shell command replace"
);
}
#[test]
#[ignore = "Flaky test - timing issues with shell command execution"]
#[cfg_attr(not(unix), ignore = "Shell commands require Unix-like environment")]
fn test_shell_command_replace_clamps_cursor_when_buffer_shrinks() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("long.txt");
std::fs::write(
&file_path,
"This is a very long line of text\nAnother line\nYet another line\n",
)
.unwrap();
let mut harness = EditorTestHarness::new(80, 24).unwrap();
harness.open_file(&file_path).unwrap();
harness.render().unwrap();
harness.send_key(KeyCode::Down, KeyModifiers::NONE).unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness
.send_key(KeyCode::Right, KeyModifiers::NONE)
.unwrap();
harness.render().unwrap();
let cursor_pos_before = harness.editor().active_state().cursors.primary().position;
assert!(
cursor_pos_before > 20,
"Cursor should be in the middle of the buffer"
);
harness
.send_key(KeyCode::Char('p'), KeyModifiers::CONTROL)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("shell command (replace)").unwrap();
harness.render().unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt().unwrap();
harness.type_text("echo short").unwrap();
harness
.send_key(KeyCode::Enter, KeyModifiers::NONE)
.unwrap();
harness.wait_for_prompt_closed().unwrap();
harness.wait_for_screen_contains("short").unwrap();
harness.assert_buffer_content("short\n");
let cursor_pos_after = harness.editor().active_state().cursors.primary().position;
let new_buffer_len = "short\n".len();
assert!(
cursor_pos_after <= new_buffer_len,
"Cursor should be clamped to new buffer length"
);
}