use std::fmt::Write;
use crate::frame::{CellColor, TerminalFrame};
use crate::locator::MatchedSpan;
use crate::region::Region;
pub fn assert_text_in_region(frame: &TerminalFrame, needle: &str, region: &Region) {
let matches = frame.find_text_in_region(needle, region);
assert!(
!matches.is_empty(),
"{}",
format_text_not_found(frame, needle, *region)
);
}
pub fn assert_not_visible(frame: &TerminalFrame, needle: &str) {
let matches = frame.find_text(needle);
assert!(
matches.is_empty(),
"Expected text '{needle}' to NOT be visible, but found {} occurrence(s):\n{}",
matches.len(),
format_span_list(&matches)
);
}
pub fn assert_match_count(frame: &TerminalFrame, needle: &str, expected_count: usize) {
let matches = frame.find_text(needle);
assert_eq!(
matches.len(),
expected_count,
"Expected '{needle}' to appear {expected_count} time(s), but found {}:\n{}",
matches.len(),
format_span_list(&matches)
);
}
pub fn assert_text_has_fg_color(frame: &TerminalFrame, needle: &str, expected_color: &CellColor) {
let matches = frame.find_text(needle);
assert!(
!matches.is_empty(),
"Cannot check color: text '{needle}' not found in frame"
);
let span = &matches[0];
assert!(
span.has_fg(expected_color),
"Text '{needle}' at ({}, {}) has foreground {:?}, expected {:?}",
span.rect.col,
span.rect.row,
span.foreground,
expected_color
);
}
pub fn assert_text_has_bg_color(frame: &TerminalFrame, needle: &str, expected_color: &CellColor) {
let matches = frame.find_text(needle);
assert!(
!matches.is_empty(),
"Cannot check color: text '{needle}' not found in frame"
);
let span = &matches[0];
assert!(
span.has_bg(expected_color),
"Text '{needle}' at ({}, {}) has background {:?}, expected {:?}",
span.rect.col,
span.rect.row,
span.background,
expected_color
);
}
pub fn assert_span_is_highlighted(frame: &TerminalFrame, needle: &str) {
let matches = frame.find_text(needle);
assert!(
!matches.is_empty(),
"Cannot check highlight: text '{needle}' not found in frame"
);
let span = &matches[0];
assert!(
span.is_highlighted(),
"Text '{needle}' at ({}, {}) is not highlighted. Style: {:?}, fg: {:?}, bg: {:?}",
span.rect.col,
span.rect.row,
span.style,
span.foreground,
span.background
);
}
pub fn assert_span_is_not_highlighted(frame: &TerminalFrame, needle: &str) {
let matches = frame.find_text(needle);
assert!(
!matches.is_empty(),
"Cannot check highlight: text '{needle}' not found in frame"
);
let span = &matches[0];
assert!(
!span.is_highlighted(),
"Text '{needle}' at ({}, {}) is highlighted but should not be. Style: {:?}, fg: {:?}, bg: \
{:?}",
span.rect.col,
span.rect.row,
span.style,
span.foreground,
span.background
);
}
fn format_text_not_found(frame: &TerminalFrame, needle: &str, region: Region) -> String {
let all_matches = frame.find_text(needle);
let region_text = frame.text_in_region(®ion);
let mut message = String::new();
let _ = writeln!(
message,
"Text '{needle}' not found in region (col:{}, row:{}, {}x{})",
region.col, region.row, region.width, region.height
);
if all_matches.is_empty() {
let _ = writeln!(message, " Text is not visible anywhere in the frame.");
} else {
let _ = writeln!(
message,
" Text found {} time(s) outside the region:",
all_matches.len()
);
for span in &all_matches {
let _ = writeln!(
message,
" - at (col:{}, row:{})",
span.rect.col, span.rect.row
);
}
}
let _ = writeln!(message, " Region content:\n{region_text}");
message
}
fn format_span_list(spans: &[MatchedSpan]) -> String {
let mut output = String::new();
for span in spans {
let _ = writeln!(
output,
" - '{}' at (col:{}, row:{}) fg:{:?} bg:{:?} style:{:?}",
span.text, span.rect.col, span.rect.row, span.foreground, span.background, span.style
);
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn assert_text_in_region_passes_when_found() {
let frame = TerminalFrame::new(80, 24, b"Hello World");
let region = Region::new(0, 0, 80, 1);
assert_text_in_region(&frame, "Hello", ®ion);
}
#[test]
#[should_panic(expected = "not found in region")]
fn assert_text_in_region_panics_when_not_found() {
let frame = TerminalFrame::new(80, 24, b"Hello World");
let region = Region::new(20, 0, 60, 1);
assert_text_in_region(&frame, "Hello", ®ion);
}
#[test]
fn assert_not_visible_passes_when_absent() {
let frame = TerminalFrame::new(80, 24, b"Hello World");
assert_not_visible(&frame, "Goodbye");
}
#[test]
#[should_panic(expected = "NOT be visible")]
fn assert_not_visible_panics_when_present() {
let frame = TerminalFrame::new(80, 24, b"Hello World");
assert_not_visible(&frame, "Hello");
}
#[test]
fn assert_match_count_passes_with_correct_count() {
let frame = TerminalFrame::new(80, 24, b"foo bar foo");
assert_match_count(&frame, "foo", 2);
}
#[test]
#[should_panic(expected = "appear 1 time(s)")]
fn assert_match_count_panics_with_wrong_count() {
let frame = TerminalFrame::new(80, 24, b"foo bar foo");
assert_match_count(&frame, "foo", 1);
}
#[test]
fn assert_span_is_highlighted_detects_bold() {
let frame = TerminalFrame::new(80, 24, b"\x1b[1mBold\x1b[0m");
assert_span_is_highlighted(&frame, "Bold");
}
#[test]
fn assert_span_is_not_highlighted_for_plain_text() {
let frame = TerminalFrame::new(80, 24, b"plain text");
assert_span_is_not_highlighted(&frame, "plain");
}
#[test]
fn assert_text_has_fg_color_passes() {
let frame = TerminalFrame::new(80, 24, b"\x1b[31mRed\x1b[0m");
assert_text_has_fg_color(&frame, "Red", &CellColor::new(128, 0, 0));
}
#[test]
#[should_panic(expected = "foreground")]
fn assert_text_has_fg_color_panics_on_mismatch() {
let frame = TerminalFrame::new(80, 24, b"\x1b[31mRed\x1b[0m");
assert_text_has_fg_color(&frame, "Red", &CellColor::new(0, 255, 0));
}
}