use super::*;
use crate::render::context::RenderContext;
#[test]
fn chips_are_found_where_they_are_drawn() {
let bar = HintBar::from_ctx(&RenderContext::for_test())
.screen(Context::Export)
.key("Enter")
.push(crate::render::footer::registry_hint(
Context::Global,
"Ctrl+Q",
));
let drawn = render_to_string(&bar, 40);
let chips = bar.chips_in(Rect::new(0, 0, 40, 1));
assert_eq!(chips.len(), 2);
for ((rect, key), label) in chips.iter().zip(["Export", "Quit"]) {
let text: String = drawn
.chars()
.skip(rect.x as usize)
.take(rect.width as usize)
.collect();
assert_eq!(text, format!(" {key} {label}"));
}
assert_eq!(bar.chips_in(Rect::new(0, 0, 18, 1)).len(), 1);
}
fn render_to_string(bar: &HintBar, width: u16) -> String {
let area = Rect::new(0, 0, width, 1);
let mut buf = Buffer::empty(area);
bar.render(area, &mut buf);
(0..width)
.map(|x| buf[(x, 0)].symbol().to_string())
.collect()
}
fn bar() -> HintBar {
HintBar::from_ctx(&RenderContext::for_test())
.screen(Context::Export)
.key("Enter")
.key("Tab")
.key("Esc")
}
#[test]
fn chips_read_key_then_label_in_order() {
let out = render_to_string(&bar(), 60);
let positions: Vec<usize> = ["Enter", "Export", "Tab", "Next", "Esc", "Cancel"]
.iter()
.map(|word| out.find(word).unwrap_or_else(|| panic!("{word} missing")))
.collect();
assert!(
positions.windows(2).all(|pair| pair[0] < pair[1]),
"chips out of order: {out:?}"
);
}
#[test]
fn a_tight_bar_drops_whole_chips_from_the_right() {
let full = bar().width_in(u16::MAX);
for width in 1..full {
let out = render_to_string(&bar(), width);
for (key, label) in [("Enter", "Export"), ("Tab", "Next"), ("Esc", "Cancel")] {
assert_eq!(
out.contains(key),
out.contains(label),
"chip {key}/{label} was clipped at width {width}: {out:?}"
);
}
if out.contains("Cancel") {
assert!(out.contains("Next") && out.contains("Export"), "{out:?}");
}
if out.contains("Next") {
assert!(out.contains("Export"), "{out:?}");
}
}
let out = render_to_string(&bar(), full);
assert!(out.contains("Cancel"), "everything fits at {full}: {out:?}");
}
#[test]
fn the_escape_chip_outlives_lighter_chips() {
let weighted = || {
HintBar::from_ctx(&RenderContext::for_test())
.screen(Context::Export)
.key("Enter")
.weight(2)
.key("Tab")
.weight(1)
.key("Esc")
.weight(3)
};
let full = weighted().width_in(u16::MAX);
let out = render_to_string(&weighted(), full - 1);
assert!(
out.contains("Export") && out.contains("Cancel") && !out.contains("Next"),
"Tab is the chip that yields: {out:?}"
);
let narrow = render_to_string(&weighted(), 16);
assert!(
narrow.contains("Cancel") && !narrow.contains("Export"),
"Esc goes last: {narrow:?}"
);
}
#[test]
fn a_flush_bar_keeps_a_chip_that_ends_at_the_edge() {
let full = bar().width_in(u16::MAX);
let tight = full - 2;
assert!(!render_to_string(&bar(), tight).contains("Cancel"));
let area = Rect::new(0, 0, tight, 1);
let mut buf = Buffer::empty(area);
bar().render_flush(area, &mut buf);
let out: String = (0..tight).map(|x| buf[(x, 0)].symbol()).collect();
assert!(out.ends_with("Cancel"), "{out:?}");
assert_eq!(bar().flush_width_in(tight), tight);
let area = Rect::new(0, 0, tight - 1, 1);
let mut buf = Buffer::empty(area);
bar().render_flush(area, &mut buf);
let out: String = (0..tight - 1).map(|x| buf[(x, 0)].symbol()).collect();
assert!(!out.contains("Esc") && !out.contains("Cancel"), "{out:?}");
}
#[test]
fn the_key_carries_the_chip_background_and_the_label_does_not() {
let area = Rect::new(0, 0, 40, 1);
let mut buf = Buffer::empty(area);
let bar = bar();
bar.render(area, &mut buf);
let out = render_to_string(&bar, 40);
let key_x = out.find("Enter").unwrap() as u16;
let label_x = out.find("Export").unwrap() as u16;
assert_ne!(
buf[(key_x, 0)].bg,
buf[(label_x, 0)].bg,
"key and label share a background, so there is no chip"
);
}
#[test]
fn accent_marks_one_label_and_changes_no_text() {
let plain = render_to_string(&bar(), 60);
let accented_bar = bar().accent("Tab");
assert_eq!(plain, render_to_string(&accented_bar, 60));
let area = Rect::new(0, 0, 60, 1);
let mut plain_buf = Buffer::empty(area);
bar().render(area, &mut plain_buf);
let mut accent_buf = Buffer::empty(area);
accented_bar.render(area, &mut accent_buf);
let changed: Vec<u16> = (0..60)
.filter(|&x| plain_buf[(x, 0)].fg != accent_buf[(x, 0)].fg)
.collect();
assert!(!changed.is_empty(), "the accent did nothing");
let label_at = plain.find("Next").unwrap() as u16;
let chunk = (label_at - 1)..(label_at + "Next".len() as u16 + 2);
for x in &changed {
assert!(chunk.contains(x), "column {x} is outside the Next label");
}
}