use crate::utils::extract_react_marker;
#[test]
fn react_bare_directive() {
let (text, emoji) = extract_react_marker("<<react:👍>>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_directive_with_text() {
let (text, emoji) = extract_react_marker("Sure thing! <<react:✅>>");
assert_eq!(text, "Sure thing!");
assert_eq!(emoji.as_deref(), Some("✅"));
}
#[test]
fn react_multiple_directives_uses_first() {
let (text, emoji) = extract_react_marker("<<react:👍>> and <<react:❤️>>");
assert_eq!(text, "and");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_whitespace_trimmed() {
let (text, emoji) = extract_react_marker(" <<react:🔥>> ");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("🔥"));
}
#[test]
fn react_with_surrounding_newlines() {
let (text, emoji) = extract_react_marker("\n\n<<react:🔥>>\n\n");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("🔥"));
}
#[test]
fn react_embedded_in_middle() {
let (text, emoji) = extract_react_marker("Hello <<react:👋>> world");
assert_eq!(text, "Hello world");
assert_eq!(emoji.as_deref(), Some("👋"));
}
#[test]
fn react_only_react_with_no_extra_text() {
let (text, emoji) = extract_react_marker("<<react:✅>>");
assert!(text.trim().is_empty());
assert_eq!(emoji.as_deref(), Some("✅"));
}
#[test]
fn react_compound_emoji_accepted() {
let (text, emoji) = extract_react_marker("<<react:👍🏽>>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍🏽"));
}
#[test]
fn react_escaped_single_backslash_prefix() {
let (text, emoji) = extract_react_marker(r"<\react:👍>>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_escaped_double_backslash_prefix() {
let (text, emoji) = extract_react_marker(r"<\\react:👍>>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_single_angle_prefix() {
let (text, emoji) = extract_react_marker("<react:👍>>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_escaped_prefix_with_text() {
let (text, emoji) = extract_react_marker(r"All set <\react:✅>>");
assert_eq!(text, "All set");
assert_eq!(emoji.as_deref(), Some("✅"));
}
#[test]
fn react_mangled_prefix_word_payload_stays() {
let (text, emoji) = extract_react_marker(r"<\react:emoji>>");
assert_eq!(text, r"<\react:emoji>>");
assert!(emoji.is_none());
}
#[test]
fn react_mangled_prefix_in_code_span_untouched() {
let (text, emoji) = extract_react_marker(r"use `<\react:👍>>` to react");
assert_eq!(text, r"use `<\react:👍>>` to react");
assert!(emoji.is_none());
}
#[test]
fn react_no_directive() {
let (text, emoji) = extract_react_marker("Just a normal message.");
assert_eq!(text, "Just a normal message.");
assert!(emoji.is_none());
}
#[test]
fn react_malformed_no_closing() {
let (text, emoji) = extract_react_marker("<<react:👍");
assert_eq!(text, "<<react:👍");
assert!(emoji.is_none());
}
#[test]
fn react_xml_close_tag_terminator() {
let (text, emoji) = extract_react_marker("<<react:👍</react>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_xml_close_tag_with_text() {
let (text, emoji) = extract_react_marker("All set <<react:✅</react>");
assert_eq!(text, "All set");
assert_eq!(emoji.as_deref(), Some("✅"));
}
#[test]
fn react_bare_single_bracket_terminator() {
let (text, emoji) = extract_react_marker("<<react:🔥>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("🔥"));
}
#[test]
fn react_double_bracket_preferred_over_single() {
let (text, emoji) = extract_react_marker("<<react:👍>> done");
assert_eq!(text, "done");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_xml_close_word_payload_stays() {
let (text, emoji) = extract_react_marker("<<react:emoji</react>");
assert_eq!(text, "<<react:emoji</react>");
assert!(emoji.is_none());
}
#[test]
fn react_bare_terminator_in_code_span_untouched() {
let (text, emoji) = extract_react_marker("use `<<react:👍>` to react");
assert_eq!(text, "use `<<react:👍>` to react");
assert!(emoji.is_none());
}
#[test]
fn react_keywordless_double_bracket_fires() {
let (text, emoji) = extract_react_marker("<<✅>>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("✅"));
}
#[test]
fn react_keywordless_with_text() {
let (text, emoji) = extract_react_marker("Done <<🔥>>");
assert_eq!(text, "Done");
assert_eq!(emoji.as_deref(), Some("🔥"));
}
#[test]
fn react_keywordless_bare_terminator_fires() {
let (text, emoji) = extract_react_marker("<<👍>");
assert_eq!(text, "");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_keywordless_single_bracket_stays_text() {
let (text, emoji) = extract_react_marker("<✅>");
assert_eq!(text, "<✅>");
assert!(emoji.is_none());
}
#[test]
fn react_keywordless_word_payload_stays_text() {
let (text, emoji) = extract_react_marker("<<word>>");
assert_eq!(text, "<<word>>");
assert!(emoji.is_none());
}
#[test]
fn react_keywordless_in_code_span_untouched() {
let (text, emoji) = extract_react_marker("use `<<✅>>` to react");
assert_eq!(text, "use `<<✅>>` to react");
assert!(emoji.is_none());
}
#[test]
fn react_keywordless_empty_payload_stays_text() {
let (text, emoji) = extract_react_marker("<<>>");
assert_eq!(text, "<<>>");
assert!(emoji.is_none());
}
#[test]
fn react_word_payload_stays_in_prose() {
let (text, emoji) = extract_react_marker("the leak where <<react:emoji>> showed as raw text");
assert_eq!(text, "the leak where <<react:emoji>> showed as raw text");
assert!(emoji.is_none());
}
#[test]
fn react_hello_payload_not_extracted() {
let (text, emoji) = extract_react_marker("<<react:hello>>");
assert_eq!(text, "<<react:hello>>");
assert!(emoji.is_none());
}
#[test]
fn react_empty_payload_not_extracted() {
let (text, emoji) = extract_react_marker("<<react:>>");
assert_eq!(text, "<<react:>>");
assert!(emoji.is_none());
}
#[test]
fn react_marker_in_code_span_untouched() {
let (text, emoji) = extract_react_marker("use `<<react:👍>>` to react");
assert_eq!(text, "use `<<react:👍>>` to react");
assert!(emoji.is_none());
}
#[test]
fn react_directive_after_closed_code_span_still_extracts() {
let (text, emoji) = extract_react_marker("see `code` here <<react:👍>>");
assert_eq!(text, "see `code` here");
assert_eq!(emoji.as_deref(), Some("👍"));
}
#[test]
fn react_long_payload_rejected() {
let (text, emoji) = extract_react_marker("<<react:🔥🔥🔥🔥🔥🔥🔥🔥🔥>>");
assert_eq!(text, "<<react:🔥🔥🔥🔥🔥🔥🔥🔥🔥>>");
assert!(emoji.is_none());
}
#[test]
fn lenient_extractor_fires_a_code_span_marker() {
use crate::utils::{extract_react_marker, extract_react_marker_lenient};
let leaky = "So I should reply with only `<<react:🙏>>`.";
let (strict_text, strict_emoji) = extract_react_marker(leaky);
assert_eq!(
strict_emoji, None,
"strict leaves a code-span marker as prose"
);
assert!(strict_text.contains("<<react:🙏>>"));
let (lenient_text, lenient_emoji) = extract_react_marker_lenient(leaky);
assert_eq!(lenient_emoji.as_deref(), Some("🙏"));
assert!(
!lenient_text.contains("<<react:"),
"lenient strips the marker: {lenient_text:?}"
);
let (_t, none) = extract_react_marker_lenient("see `<<react:emoji>>` in the docs");
assert_eq!(none, None);
}
#[test]
fn react_recovered_from_orphan_fence_single_bracket() {
let raw = "<```html\n<react:✅>> Done — all receipts verified in the last run:\n\nbody stays put\n```>";
let (text, emoji) = extract_react_marker(raw);
assert_eq!(emoji.as_deref(), Some("✅"));
assert!(!text.contains("```"), "fence lines must go: {text:?}");
assert!(!text.contains("<react:"), "marker must go: {text:?}");
assert!(text.contains("body stays put"));
}
#[test]
fn react_recovered_from_orphan_fence_double_bracket() {
let raw = "<```html\n<<react:💯>>\n\nChecked before arguing, and empirically right.\n```>";
let (text, emoji) = extract_react_marker(raw);
assert_eq!(emoji.as_deref(), Some("💯"));
assert!(!text.contains("```"));
assert!(text.contains("empirically right"));
}
#[test]
fn react_recovered_from_bare_orphan_fence() {
let raw = "<```\n<<react:🔥>>\nplain body";
let (text, emoji) = extract_react_marker(raw);
assert_eq!(emoji.as_deref(), Some("🔥"));
assert!(!text.contains("```"));
assert!(text.contains("plain body"));
}
#[test]
fn react_recovered_when_marker_shares_the_fence_line() {
let raw = "<```html <<react:👀>> visible text";
let (text, emoji) = extract_react_marker(raw);
assert_eq!(emoji.as_deref(), Some("👀"));
assert!(!text.contains("```html"));
assert!(text.contains("visible text"));
}
#[test]
fn react_recovery_strips_paired_trailing_fence_only() {
let clean = "<<react:👍>> normal turn\n```";
let (text, emoji) = extract_react_marker(clean);
assert_eq!(emoji.as_deref(), Some("👍"));
assert!(text.contains("normal turn"), "{text:?}");
}
#[test]
fn react_no_recovery_for_prose_then_fenced_example() {
let docs = "Use fences like this:\n```html\n<<react:✅>>\n```\nto react.";
let (text, emoji) = extract_react_marker(docs);
assert_eq!(emoji, None);
assert!(
text.contains("<<react:✅>>"),
"example stays as text: {text:?}"
);
}
#[test]
fn react_no_recovery_for_word_payload_in_orphan_fence() {
let raw = "<```html\n<react:hello>> not an emoji";
let (text, emoji) = extract_react_marker(raw);
assert_eq!(emoji, None);
assert_eq!(text, raw);
}