larpshell 0.2.3

Ctrl+C then Ctrl+V is simply too much work. Just let an LLM rule your terminal!!
use crate::confirmation::{
    ConfirmPromptMode, ConfirmResult, KeyEvent, ResponseStyle, confirm_from_reader,
    display_command, display_explanation, display_message, display_response, parse_key_from_reader,
    style_html_tags_for_test, style_message_markup_for_test,
};

#[test]
fn display_command_single_line_returns_one() {
    assert_eq!(display_command("echo hi"), 1);
}

#[test]
fn display_command_multiline_returns_n_plus_one() {
    assert_eq!(display_command("echo hi\necho bye"), 3);
    assert_eq!(display_command("a\nb\nc"), 4);
}

#[test]
fn display_message_single_line_returns_one() {
    assert_eq!(display_message("done"), 1);
}

#[test]
fn display_message_multiline_returns_line_count() {
    assert_eq!(display_message("line one\nline two\nline three"), 3);
}

#[test]
fn display_response_delegates_to_message_renderer() {
    assert_eq!(display_response("done", ResponseStyle::Message), 1);
}

#[test]
fn display_explanation_single_line_returns_one() {
    assert_eq!(display_explanation("pipes stdout to a file"), 1);
}

#[test]
fn display_explanation_multiline_returns_line_count() {
    assert_eq!(display_explanation("line one\nline two\nline three"), 3);
}

#[test]
fn style_html_tags_converts_bold() {
    let result = style_html_tags_for_test("<b>hello</b>", true);
    assert_eq!(result, "\x1b[1mhello\x1b[22m");
}

#[test]
fn style_html_tags_converts_italic() {
    let result = style_html_tags_for_test("<i>hello</i>", true);
    assert_eq!(result, "\x1b[3mhello\x1b[23m");
}

#[test]
fn style_html_tags_converts_underline() {
    let result = style_html_tags_for_test("<u>hello</u>", true);
    assert_eq!(result, "\x1b[4mhello\x1b[24m");
}

#[test]
fn style_html_tags_strips_when_no_color() {
    let result = style_html_tags_for_test("<b>bold</b> and <i>italic</i>", false);
    assert_eq!(result, "bold and italic");
}

#[test]
fn style_message_markup_converts_supported_markdown() {
    let result = style_message_markup_for_test(
        "- **bold** `code` *italic* [label](https://example.com)",
        true,
    );
    assert_eq!(
        result,
        "\x1b[1mbold\x1b[22m \x1b[7mcode\x1b[27m \x1b[3mitalic\x1b[23m label"
    );
}

#[test]
fn style_message_markup_strips_unsupported_markdown_when_no_color() {
    let result = style_message_markup_for_test(
        "> ## heading\n1. [x] **bold** and ~~gone~~ with [label](https://example.com)",
        false,
    );
    assert_eq!(result, "heading\nbold and gone with label");
}

#[test]
fn style_message_markup_drops_rules_and_fences() {
    let result = style_message_markup_for_test("---\n```\nbody\n~~~", false);
    assert_eq!(result, "\n\nbody\n");
}

#[test]
fn parse_key_from_reader_maps_enter() {
    let mut input = std::io::Cursor::new(b"\n");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::Enter));
}

#[test]
fn parse_key_from_reader_maps_carriage_return() {
    let mut input = std::io::Cursor::new(b"\r");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::Enter));
}

#[test]
fn parse_key_from_reader_maps_char_y() {
    let mut input = std::io::Cursor::new(b"y");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::Char('y')
    ));
}

#[test]
fn parse_key_from_reader_maps_char_uppercase_y() {
    let mut input = std::io::Cursor::new(b"Y");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::Char('Y')
    ));
}

#[test]
fn parse_key_from_reader_maps_char_e() {
    let mut input = std::io::Cursor::new(b"e");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::Char('e')
    ));
}

#[test]
fn parse_key_from_reader_maps_char_n() {
    let mut input = std::io::Cursor::new(b"n");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::Char('n')
    ));
}

#[test]
fn parse_key_from_reader_maps_ctrl_c() {
    let mut input = std::io::Cursor::new(b"\x03");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::CtrlC));
}

#[test]
fn parse_key_from_reader_maps_backspace() {
    let mut input = std::io::Cursor::new(b"\x7f");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::Backspace
    ));
}

#[test]
fn parse_key_from_reader_maps_arrow_up_escape_sequence() {
    let mut input = std::io::Cursor::new(b"\x1b[A");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::ArrowUp
    ));
}

#[test]
fn parse_key_from_reader_maps_arrow_down_escape_sequence() {
    let mut input = std::io::Cursor::new(b"\x1b[B");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::Other));
}

#[test]
fn parse_key_from_reader_maps_arrow_right_escape_sequence() {
    let mut input = std::io::Cursor::new(b"\x1b[C");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::Right));
}

#[test]
fn parse_key_from_reader_maps_arrow_left_escape_sequence() {
    let mut input = std::io::Cursor::new(b"\x1b[D");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::Left));
}

#[test]
fn parse_key_from_reader_maps_delete_key() {
    let mut input = std::io::Cursor::new(b"\x1b[3~");
    assert!(matches!(
        parse_key_from_reader(&mut input),
        KeyEvent::Delete
    ));
}

#[test]
fn parse_key_from_reader_maps_eof() {
    let mut input = std::io::Cursor::new(b"");
    assert!(matches!(parse_key_from_reader(&mut input), KeyEvent::Eof));
}

#[test]
fn confirm_from_reader_with_explain_on_enter_returns_yes() {
    let mut keys = vec![KeyEvent::Enter].into_iter();
    let result = confirm_from_reader(
        || keys.next().unwrap(),
        ConfirmPromptMode::WithExplain,
        1,
        0,
    );
    assert!(matches!(result, ConfirmResult::Yes));
}

#[test]
fn confirm_from_reader_with_explain_on_y_returns_yes() {
    let mut keys = vec![KeyEvent::Char('y')].into_iter();
    let result = confirm_from_reader(
        || keys.next().unwrap(),
        ConfirmPromptMode::WithExplain,
        1,
        0,
    );
    assert!(matches!(result, ConfirmResult::Yes));
}

#[test]
fn confirm_from_reader_with_explain_on_e_returns_explain() {
    let mut keys = vec![KeyEvent::Char('e')].into_iter();
    let result = confirm_from_reader(
        || keys.next().unwrap(),
        ConfirmPromptMode::WithExplain,
        1,
        0,
    );
    assert!(matches!(result, ConfirmResult::Explain));
}

#[test]
fn confirm_from_reader_with_explain_on_n_returns_cancel() {
    let mut keys = vec![KeyEvent::Char('n')].into_iter();
    let result = confirm_from_reader(
        || keys.next().unwrap(),
        ConfirmPromptMode::WithExplain,
        1,
        0,
    );
    assert!(matches!(result, ConfirmResult::Cancel));
}

#[test]
fn confirm_from_reader_on_arrow_up_returns_edit() {
    let mut keys = vec![KeyEvent::ArrowUp].into_iter();
    let result = confirm_from_reader(|| keys.next().unwrap(), ConfirmPromptMode::Simple, 1, 1);
    assert!(matches!(result, ConfirmResult::Edit));
}

#[test]
fn confirm_from_reader_on_eof_returns_no() {
    let mut keys = vec![KeyEvent::Eof].into_iter();
    let result = confirm_from_reader(|| keys.next().unwrap(), ConfirmPromptMode::Simple, 1, 0);
    assert!(matches!(result, ConfirmResult::No));
}

#[test]
fn confirm_from_reader_ignores_e_in_simple_mode() {
    let mut keys = vec![KeyEvent::Char('e'), KeyEvent::Enter].into_iter();
    let result = confirm_from_reader(|| keys.next().unwrap(), ConfirmPromptMode::Simple, 1, 0);
    assert!(matches!(result, ConfirmResult::Yes));
}