use super::*;
use crate::test_utils::test_helpers::test_app;
use proptest::prelude::*;
#[test]
fn test_strip_ansi_codes_no_codes() {
assert_eq!(strip_ansi_codes("hello world"), "hello world");
}
#[test]
fn test_strip_ansi_codes_simple_color() {
assert_eq!(strip_ansi_codes("\x1b[31mhello\x1b[0m"), "hello");
}
#[test]
fn test_strip_ansi_codes_multiple_colors() {
assert_eq!(
strip_ansi_codes("\x1b[1;31mbold red\x1b[0m normal"),
"bold red normal"
);
}
#[test]
fn test_strip_ansi_codes_empty_string() {
assert_eq!(strip_ansi_codes(""), "");
}
#[test]
fn test_strip_ansi_codes_only_escape_sequences() {
assert_eq!(strip_ansi_codes("\x1b[31m\x1b[0m"), "");
}
#[test]
fn test_strip_ansi_codes_preserves_newlines() {
assert_eq!(
strip_ansi_codes("\x1b[32mline1\x1b[0m\nline2"),
"line1\nline2"
);
}
#[test]
fn test_strip_ansi_codes_osc_sequence() {
assert_eq!(strip_ansi_codes("\x1b]0;title\x07text"), "text");
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_ansi_stripping_preserves_non_ansi_content(
text in "[^\x1b]*"
) {
let result = strip_ansi_codes(&text);
prop_assert_eq!(
result, text,
"Text without ANSI codes should be unchanged"
);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_ansi_stripping_removes_all_escape_sequences(
prefix in "[a-zA-Z0-9 ]{0,20}",
suffix in "[a-zA-Z0-9 ]{0,20}",
ansi_params in "[0-9;]{0,10}",
ansi_letter in "[A-Za-z]",
) {
let text_with_ansi = format!(
"{}\x1b[{}{}{}",
prefix,
ansi_params,
ansi_letter,
suffix
);
let result = strip_ansi_codes(&text_with_ansi);
prop_assert!(
!result.contains('\x1b'),
"Stripped text should not contain escape character. Input: {:?}, Output: {:?}",
text_with_ansi,
result
);
prop_assert_eq!(
result,
format!("{}{}", prefix, suffix),
"Stripped text should be prefix + suffix"
);
}
}
#[test]
fn test_copy_query_rejects_empty() {
let mut app = test_app("{}");
let result = copy_query(&mut app, ClipboardBackend::Osc52);
assert!(!result, "Empty query should be rejected");
assert!(
app.notification.current().is_none(),
"No notification for rejected copy"
);
}
#[test]
fn test_copy_result_rejects_empty_without_cache() {
let mut app = test_app("{}");
let query_state = app.query.as_mut().unwrap();
query_state.result = Ok(String::new());
query_state.last_successful_result_unformatted = None;
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(!result, "Empty result without cache should be rejected");
assert!(
app.notification.current().is_none(),
"No notification for rejected copy"
);
}
#[test]
fn test_copy_result_rejects_ansi_only_without_cache() {
let mut app = test_app("{}");
let query_state = app.query.as_mut().unwrap();
query_state.result = Ok("\x1b[31m\x1b[0m".to_string());
query_state.last_successful_result_unformatted = None;
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(!result, "ANSI-only result without cache should be rejected");
assert!(
app.notification.current().is_none(),
"No notification for rejected copy"
);
}
#[test]
fn test_copy_result_rejects_error_without_cache() {
let mut app = test_app("{}");
let query_state = app.query.as_mut().unwrap();
query_state.result = Err("some error".to_string());
query_state.last_successful_result_unformatted = None;
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(!result, "Error result without cache should be rejected");
assert!(
app.notification.current().is_none(),
"No notification for rejected copy"
);
}
#[test]
fn test_copy_query_accepts_non_empty() {
let mut app = test_app("{}");
app.input.textarea.insert_str(".foo");
let result = copy_query(&mut app, ClipboardBackend::Osc52);
assert!(result, "Non-empty query should be accepted");
assert_eq!(
app.notification.current_message(),
Some("Copied query!"),
"Notification should be shown for successful copy"
);
}
#[test]
fn test_copy_result_accepts_non_empty() {
let mut app = test_app("{}");
app.query.as_mut().unwrap().result = Ok(r#"{"key": "value"}"#.to_string());
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(result, "Non-empty result should be accepted");
assert_eq!(
app.notification.current_message(),
Some("Copied result!"),
"Notification should be shown for successful copy"
);
}
#[test]
fn test_copy_result_uses_cached_when_result_is_error() {
use std::sync::Arc;
let mut app = test_app(r#"{"value": 42}"#);
let query_state = app.query.as_mut().unwrap();
let cached_result = r#"{"cached": "data"}"#;
query_state.last_successful_result_unformatted = Some(Arc::new(cached_result.to_string()));
query_state.result = Err("syntax error".to_string());
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(
result,
"Copy should succeed with cached result even when current result is error"
);
assert_eq!(
app.notification.current_message(),
Some("Copied result!"),
"Notification should be shown"
);
}
#[test]
fn test_copy_result_uses_cached_when_result_is_null() {
use std::sync::Arc;
let mut app = test_app(r#"{"name": "test", "value": 42}"#);
let query_state = app.query.as_mut().unwrap();
let cached_result = r#"{"value": 42}"#;
query_state.last_successful_result_unformatted = Some(Arc::new(cached_result.to_string()));
query_state.result = Ok("null\n".to_string());
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(
result,
"Copy should succeed with cached result even when current result is null"
);
assert_eq!(
app.notification.current_message(),
Some("Copied result!"),
"Notification should be shown"
);
}
#[test]
fn test_strip_ansi_osc_sequence_with_st_terminator() {
assert_eq!(strip_ansi_codes("\x1b]0;title\x1b\\text"), "text");
}
#[test]
fn test_strip_ansi_simple_escape() {
assert_eq!(strip_ansi_codes("\x1b(Btext"), "Btext");
}
#[test]
fn test_strip_ansi_lone_escape_at_end() {
assert_eq!(strip_ansi_codes("text\x1b"), "text");
}
#[test]
fn test_handle_clipboard_key_ctrl_y() {
let mut app = test_app(r#"{"test": "data"}"#);
app.input.textarea.insert_str(".test");
let key = KeyEvent::new(KeyCode::Char('y'), KeyModifiers::CONTROL);
let result = handle_clipboard_key(&mut app, key, ClipboardBackend::Osc52);
assert!(result);
}
#[test]
fn test_copy_focused_content_results_pane() {
use crate::app::Focus;
let mut app = test_app(r#"{"test": "data"}"#);
app.focus = Focus::ResultsPane;
let result = handle_yank_key(&mut app, ClipboardBackend::Osc52);
assert!(result);
}
#[test]
fn test_copy_result_when_query_none() {
let mut app = test_app(r#"{"test": "data"}"#);
app.query = None;
app.focus = crate::app::Focus::ResultsPane;
let result = copy_focused_content(&mut app, ClipboardBackend::Osc52);
assert!(!result);
}
#[test]
fn test_copy_result_when_result_empty() {
use std::sync::Arc;
let mut app = test_app(r#"{"test": "data"}"#);
app.focus = crate::app::Focus::ResultsPane;
if let Some(ref mut query_state) = app.query {
query_state.last_successful_result_unformatted = Some(Arc::new(String::new()));
}
let result = copy_result(&mut app, ClipboardBackend::Osc52);
assert!(!result);
}