ratto 0.3.1

Ratatui-powered terminal primitives for shell dashboards: flicker-free repaints, progress bars, prompts, and portable time tools
use assert_cmd::Command;

fn rat() -> Command {
    let mut cmd = Command::cargo_bin("rat").expect("rat binary builds");
    cmd.env_remove("NO_COLOR");
    cmd
}

#[test]
fn once_prints_child_stdout_and_exits_zero() {
    rat()
        .args(["watch", "--once", "--", "echo", "hi"])
        .assert()
        .success()
        .stdout(predicates::str::contains("hi"));
}

#[test]
fn piped_output_has_no_movement_or_cursor_escapes() {
    let assert = rat()
        .args(["watch", "--once", "--", "echo", "hi"])
        .assert()
        .success();
    let stdout = String::from_utf8_lossy(&assert.get_output().stdout).into_owned();
    for escape in ["\x1b[?25l", "\x1b[?2026", "\x1b[0J"] {
        assert!(!stdout.contains(escape), "found {escape:?} in {stdout:?}");
    }
}

#[test]
fn child_failure_does_not_fail_the_watch() {
    rat()
        .args(["watch", "--once", "--", "sh", "-c", "exit 3"])
        .assert()
        .success();
}

#[test]
fn missing_command_is_a_usage_error() {
    rat().args(["watch", "--once"]).assert().code(2);
}

#[test]
fn child_ansi_passes_through_verbatim() {
    rat()
        .args(["watch", "--once", "--", "printf", "\\033[31mred\\033[0m"])
        .assert()
        .success()
        .stdout(predicates::str::contains("\x1b[31mred\x1b[0m"));
}

#[test]
fn title_line_is_prepended() {
    rat()
        .env("NO_COLOR", "1")
        .args([
            "watch",
            "--once",
            "--title",
            "My Dashboard",
            "--",
            "echo",
            "body",
        ])
        .assert()
        .success()
        .stdout(predicates::str::contains("My Dashboard"));
}

#[test]
fn bad_interval_is_an_error() {
    rat()
        .args(["watch", "-n", "bogus", "--once", "--", "echo", "hi"])
        .assert()
        .code(1);
}

#[test]
fn spawn_failure_in_once_mode_fails_loudly() {
    rat()
        .args(["watch", "--once", "--", "definitely-no-such-binary-xyz"])
        .assert()
        .code(1)
        .stderr(predicates::str::contains("definitely-no-such-binary-xyz"));
}

#[test]
fn shell_mode_runs_through_sh() {
    rat()
        .args(["watch", "--once", "--shell", "--", "echo $((6 * 7))"])
        .assert()
        .success()
        .stdout(predicates::str::contains("42"));
}

#[test]
fn clear_flag_stays_silent_when_piped() {
    let assert = rat()
        .args(["watch", "--clear", "--once", "--", "echo", "hi"])
        .assert()
        .success();
    let stdout = String::from_utf8_lossy(&assert.get_output().stdout).into_owned();
    assert!(!stdout.contains("\x1b[2J"), "piped output must stay plain");
    assert!(stdout.contains("hi"));
}

#[test]
fn child_stderr_passes_through_when_piped() {
    rat()
        .args([
            "watch",
            "--once",
            "--",
            "sh",
            "-c",
            "echo out; echo err >&2",
        ])
        .assert()
        .success()
        .stdout(predicates::str::contains("out"))
        .stderr(predicates::str::contains("err"));
}

#[test]
fn child_stderr_stays_off_stdout_when_piped() {
    let assert = rat()
        .args(["watch", "--once", "--", "sh", "-c", "echo err >&2"])
        .assert()
        .success();
    let stdout = String::from_utf8_lossy(&assert.get_output().stdout).into_owned();
    assert!(
        !stdout.contains("err"),
        "stderr leaked into stdout: {stdout:?}"
    );
}