use super::*;
#[test]
fn summary_line_collapses_only_spilled_results() {
let line = spill_summary_line("l1\nl2\nl3\nl4\nl5", 3, 80).expect("5 > 3 collapses");
assert_eq!(line, "▲ 5 lines hidden [/spill N raises this view] · l5");
let line = spill_summary_line("l1\nl2\nl3\nerror: boom\n\n", 3, 80).unwrap();
assert!(line.contains("error: boom"), "{line}");
assert_eq!(spill_summary_line("a\nb\nc", 3, 80), None);
assert_eq!(spill_summary_line("a\nb\nc\nd\ne", 0, 80), None);
}
#[test]
fn summary_line_fits_narrow_terminals() {
let wide = format!("l1\nl2\nl3\n{}", "x".repeat(300));
let line = spill_summary_line(&wide, 3, 60).unwrap();
assert!(
line.chars().count() <= 60,
"one visual row on an 60-col terminal: {} chars",
line.chars().count()
);
assert!(line.contains('…'), "a cut tail is marked: {line}");
assert!(line.contains("/spill N raises this view"), "{line}");
let line = spill_summary_line(&wide, 3, 20).unwrap();
assert_eq!(line, "▲ 4 lines hidden [/spill N raises this view]");
}
#[test]
fn summary_mode_commits_the_marker_not_the_excerpt() {
let mut display = super::ToolDisplay::new(Vec::new(), false, 80, 3, true);
display.result("l1\nl2\nl3\nl4\nl5");
let out = String::from_utf8(display.writer).unwrap();
assert!(
out.contains("▲ 5 lines hidden [/spill N raises this view] · l5"),
"the marker committed: {out:?}"
);
assert!(
!out.contains("▒ l3"),
"no excerpt rows in summary mode: {out:?}"
);
let mut display = super::ToolDisplay::new(Vec::new(), false, 80, 3, true);
display.result("a\nb");
let out = String::from_utf8(display.writer).unwrap();
assert!(
out.contains("▒ a"),
"fitting results keep the full render: {out:?}"
);
}
#[test]
fn collapse_uses_wrapped_row_accounting_like_the_excerpt() {
let long = format!("{}\nshort tail\n", "x".repeat(200));
assert!(super::spills_past(&long, 3, 20), "wrapped rows spill");
assert!(
super::spill_summary_line(&long, 3, 20).is_some(),
"summary engages on wrapped spill (logical-line count would say no)"
);
let excerpt = super::spill_view_lines(&long, 3, 20);
assert!(
excerpt[0].starts_with('▲'),
"excerpt path truncates the same input: {excerpt:?}"
);
let fits = "a\nb\n";
assert!(!super::spills_past(fits, 3, 80));
assert!(super::spill_summary_line(fits, 3, 80).is_none());
}
#[test]
fn every_spill_row_reapplies_the_theme_dim_color() {
let mut out = Vec::new();
ToolDisplay::new(&mut out, true, 80, 3, false).result("one\ntwo\nthree\nfour\nfive");
let rendered = String::from_utf8(out).unwrap();
let expected = crate::tty::theme::active().ansi(crate::tty::theme::Role::Spill);
for row in rendered
.lines()
.filter(|line| line.contains('▒') || line.contains('▓') || line.contains('▲'))
{
assert!(row.contains(&expected), "row lost dim style: {row:?}");
}
}
#[test]
fn wrapped_spill_rows_keep_their_gutter_and_content() {
let body = "ordered plan and arguments which must wrap within the spill boundary";
let rows = super::bounded_spill_rows(&format!("▒ {body}"), 24);
assert!(rows.len() > 1);
for row in &rows {
assert!(crate::tty::width::str_width(row) <= 24, "{row:?}");
}
assert!(rows[1..].iter().all(|r| r.starts_with(" ")));
assert_eq!(
rows.iter()
.map(|r| r.chars().skip(2).collect::<String>())
.collect::<String>(),
body
);
}
#[test]
fn colored_tool_output_cannot_break_spill_wrapping_or_style() {
let plain = "▒ a long line that must wrap neatly";
let colored = "▒ \x1b[38;2;10;20;30ma long line\x1b[0m that must wrap neatly";
assert_eq!(
super::bounded_spill_rows(colored, 14),
super::bounded_spill_rows(plain, 14)
);
}