mod common;
#[cfg(unix)]
use common::{Terminal, has_askpass};
#[test]
#[cfg(unix)]
fn test_pty_readline() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line("cat('hello'); readline('input> ')")
.expect("Should send readline command");
terminal.expect("hello").expect("Should see cat output");
terminal
.expect("input> ")
.expect("Should see readline prompt");
terminal
.send_line("user_answer")
.expect("Should send readline input");
terminal
.expect(r#""user_answer""#)
.expect("readline should return the input");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_askpass() {
if !has_askpass() {
eprintln!("Skipping test_pty_askpass: askpass package not installed");
return;
}
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line("requireNamespace('askpass', quietly = TRUE)")
.expect("Should check askpass");
terminal
.expect("TRUE")
.expect("askpass package should be available");
terminal
.send_line(r#"askpass::askpass(paste0("Enter ", "password: "))"#)
.expect("Should send askpass command");
terminal
.expect("Enter password:")
.expect("Should see askpass prompt");
terminal
.send_line("secret_answer")
.expect("Should send askpass input");
terminal
.expect(r#""secret_answer""#)
.expect("askpass should return the input");
terminal.wait_for_prompt().expect("Should return to prompt");
let output = terminal.get_output().expect("Should get output");
let bare_occurrences = output.matches("secret_answer").count();
let quoted_occurrences = output.matches(r#""secret_answer""#).count();
assert_eq!(
bare_occurrences, quoted_occurrences,
"Password was echoed in plaintext.\n\
bare={bare_occurrences}, quoted={quoted_occurrences}\n\
output:\n{output}",
);
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_shell_mode() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal.send_line(":shell").expect("Should send :shell");
terminal
.expect("Shell mode enabled")
.expect("Should show shell mode message");
terminal
.expect("] $")
.expect("Should show shell mode prompt");
terminal
.send_line("echo hello")
.expect("Should send shell command");
terminal.expect("hello").expect("Should see shell output");
terminal.send_line(":r").expect("Should send :r");
terminal
.expect("Returned to R mode")
.expect("Should show R mode message");
terminal.send_line("42").expect("Should send R expression");
terminal.expect("[1] 42").expect("Should evaluate R code");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_system_command() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":system echo test_system_output")
.expect("Should send :system");
terminal
.expect("test_system_output")
.expect("Should see system output");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal.send_line("100").expect("Should send R expression");
terminal
.clear_and_expect("[1] 100")
.expect("Should evaluate R code");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_shell_mode_ctrl_c_exit() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal.send_line(":shell").expect("Should send :shell");
terminal
.clear_and_expect("Shell mode enabled")
.expect("Should show shell mode message");
terminal.clear_buffer().expect("Should clear buffer");
terminal.send_interrupt().expect("Should send Ctrl+C");
terminal
.expect("Returned to R mode")
.expect("Should return to R mode");
terminal.send_line("200").expect("Should send R expression");
terminal
.clear_and_expect("[1] 200")
.expect("Should evaluate R code");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_help_browser() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal.send_line(":h").expect("Should send :h command");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal
.expect("Help Search")
.expect("Should show help browser header");
terminal
.send("\x1b")
.expect("Should send Esc to exit browser");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal.send_line("42").expect("Should send R expression");
terminal
.clear_and_expect("[1] 42")
.expect("Should evaluate R code after browser exit");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_history_schema_pager() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":history schema")
.expect("Should send :history schema");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal
.line(0)
.assert_contains("History Schema")
.expect("Should show pager header");
terminal.send("q").expect("Should send q to exit pager");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal.send_line("42").expect("Should send R expression");
terminal
.clear_and_expect("[1] 42")
.expect("Should evaluate R code after pager exit");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_history_schema_pager_copy() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":history schema")
.expect("Should send :history schema");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal
.line(0)
.assert_contains("History Schema")
.expect("Should show pager header");
terminal.send("c").expect("Should send c to copy");
std::thread::sleep(std::time::Duration::from_millis(200));
terminal
.line(23)
.assert_contains("Copied R example to clipboard")
.expect("Should show copy feedback message");
terminal.send("q").expect("Should send q to exit pager");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_history_schema_pager_mouse_scroll() {
let mut terminal = Terminal::spawn_with_args(&["--no-auto-match", "--no-completion"])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":history schema")
.expect("Should send :history schema");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal
.line(0)
.assert_contains("History Schema")
.expect("Should show pager header");
terminal
.line(0)
.assert_contains("[1/")
.expect("Should show position 1 initially");
terminal
.send("\x1b[<65;10;10M")
.expect("Should send mouse scroll down");
std::thread::sleep(std::time::Duration::from_millis(200));
terminal
.line(0)
.assert_contains("[2/")
.expect("Should show position 2 after scroll down");
terminal
.send("\x1b[<64;10;10M")
.expect("Should send mouse scroll up");
std::thread::sleep(std::time::Duration::from_millis(200));
terminal
.line(0)
.assert_contains("[1/")
.expect("Should show position 1 after scroll up");
terminal.send("q").expect("Should send q to exit pager");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_history_browser_persists_across_sessions() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let history_dir = temp_dir.path().to_string_lossy().to_string();
{
let mut terminal = Terminal::spawn_with_args(&[
"--no-auto-match",
"--no-completion",
"--history-dir",
&history_dir,
])
.expect("Failed to spawn arf (session 1)");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line("unique_test_value_12345 <- 999")
.expect("Should send assignment");
terminal
.clear_and_expect("unique_test_value_12345")
.expect("Should see variable name");
terminal.wait_for_prompt().expect("Should show prompt");
terminal.quit().expect("Should quit cleanly (session 1)");
}
{
let mut terminal = Terminal::spawn_with_args(&[
"--no-auto-match",
"--no-completion",
"--history-dir",
&history_dir,
])
.expect("Failed to spawn arf (session 2)");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":history browse")
.expect("Should send :history browse");
terminal
.expect("History Browser")
.expect("Should show history browser header");
terminal
.expect("unique_test_value_12345")
.expect("Should see command from session 1 in history browser");
terminal.send("q").expect("Should send q to exit browser");
terminal
.clear_and_expect("> ")
.expect("Should return to prompt after browser exit");
terminal.quit().expect("Should quit cleanly (session 2)");
}
}
#[test]
#[cfg(unix)]
fn test_pty_history_browser() {
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let history_dir = temp_dir.path().to_string_lossy().to_string();
let mut terminal = Terminal::spawn_with_args(&[
"--no-auto-match",
"--no-completion",
"--history-dir",
&history_dir,
])
.expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line("1 + 1")
.expect("Should send R expression");
terminal
.clear_and_expect("[1] 2")
.expect("Should evaluate R code");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line("print('hello')")
.expect("Should send print command");
terminal
.clear_and_expect("[1] \"hello\"")
.expect("Should print hello");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":history browse")
.expect("Should send :history browse");
terminal
.expect("History Browser")
.expect("Should show history browser header");
terminal
.expect("[R]")
.expect("Should show R mode indicator");
terminal.send("q").expect("Should send q to exit browser");
terminal
.clear_and_expect("> ")
.expect("Should return to prompt after browser exit");
terminal.send_line("42").expect("Should send R expression");
terminal
.clear_and_expect("[1] 42")
.expect("Should evaluate R code after browser exit");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line(":history browse")
.expect("Should send :history browse again");
terminal
.expect("History Browser")
.expect("Should show history browser header on second open");
terminal.send("q").expect("Should send q to exit browser");
terminal
.clear_and_expect("> ")
.expect("Should return to prompt after second browser exit");
terminal.send_line("99").expect("Should send R expression");
terminal
.clear_and_expect("[1] 99")
.expect("Should evaluate R code after second browser exit");
terminal.quit().expect("Should quit cleanly");
}