use justerm_core::{Engine, SelectionType, Side, TermEvent};
fn row_text(e: &Engine, row: usize) -> String {
e.viewport_line(row)
.iter()
.map(|c| c.c())
.collect::<String>()
.trim_end()
.to_string()
}
#[test]
fn drops_history_and_leaves_the_screen() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4\r\nl5");
assert_eq!(e.scrollback_len(), 2, "precondition");
e.feed(b"\x1b[3J");
assert_eq!(e.scrollback_len(), 0);
assert_eq!(
(0..3).map(|r| row_text(&e, r)).collect::<Vec<_>>(),
["l3", "l4", "l5"]
);
assert_eq!((e.cursor().row, e.cursor().col), (2, 2));
assert_eq!(e.accessible_text().trim_end(), "l3\nl4\nl5");
}
#[test]
fn returns_a_scrolled_view_to_the_bottom() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4\r\nl5");
e.scroll_up(2);
e.selection_begin(0, 0, Side::Left, SelectionType::Char);
e.selection_extend(2, 1, Side::Right);
e.feed(b"\x1b[3J");
assert_eq!(e.frame().display_offset, 0);
assert_eq!(row_text(&e, 0), "l3");
assert_eq!(e.selection_text().as_deref(), Some("l3"));
assert_eq!(e.selection_range().len(), 1);
}
#[test]
fn a_selection_end_anywhere_in_history_clamps_to_the_new_top() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4\r\nl5"); e.scroll_up(2);
e.selection_begin(1, 1, Side::Left, SelectionType::Char); e.selection_extend(2, 1, Side::Right);
e.feed(b"\x1b[3J");
assert_eq!(e.selection_text().as_deref(), Some("l3"));
}
#[test]
fn a_selection_wholly_in_history_is_cleared() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4\r\nl5"); e.scroll_up(2);
e.selection_begin(1, 0, Side::Left, SelectionType::Char); e.selection_extend(1, 1, Side::Right);
e.feed(b"\x1b[3J");
assert_eq!(e.selection_text(), None);
}
#[test]
fn a_scrolled_view_is_repainted() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4\r\nl5");
e.scroll_up(2);
e.reset_damage();
e.feed(b"\x1b[3J");
assert_eq!(e.frame().kind, justerm_core::FrameKind::Full);
}
#[test]
fn disposes_the_marks_in_history_and_shifts_the_rest() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3");
let old = e.add_marker(0); e.feed(b"\r\nl4\r\nl5"); let live = e.add_marker(1); e.drain_events();
let before = e.marker_index();
e.feed(b"\x1b[3J");
let disposed: Vec<_> = e
.drain_events()
.into_iter()
.filter_map(|ev| match ev {
TermEvent::MarkerDisposed(id) => Some(id),
_ => None,
})
.collect();
assert_eq!(disposed, vec![old]);
let after = e.marker_index();
assert_eq!(after.evicted_total - before.evicted_total, 2);
assert_eq!(after.epoch, before.epoch);
assert_eq!(after.markers.len(), 1);
assert_eq!((after.markers[0].id, after.markers[0].line), (live, 1));
}
#[test]
fn drops_the_tracked_points_in_history_and_shifts_the_rest() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4\r\nl5");
let gone = e.track_point(1, 0); let kept = e.track_point(3, 1);
e.feed(b"\x1b[3J");
assert_eq!(e.tracked_point(gone), None);
assert_eq!(e.tracked_point(kept), Some((1, 1)));
}
#[test]
fn drops_the_search_highlights() {
let mut e = Engine::new(10, 3);
e.feed(b"ab\r\nab\r\nab\r\nab");
let hits = e.search("ab");
e.set_search_highlights(hits);
e.feed(b"\x1b[3J");
assert!(e.frame().overlay.matches.is_empty());
}
#[test]
fn is_quiet_with_no_history() {
let mut e = Engine::new(10, 3);
e.feed(b"l1");
e.reset_damage();
e.feed(b"\x1b[3J");
assert!(e.frame().spans.is_empty());
}
#[test]
fn on_the_alt_screen_drops_the_primary_history() {
let mut e = Engine::new(10, 3);
e.feed(b"l1\r\nl2\r\nl3\r\nl4"); e.feed(b"\x1b[?1049h\x1b[2;1Htui");
let point = e.track_point(e.scrollback_len() + 1, 0);
let mark = e.add_marker(1);
e.feed(b"\x1b[3J");
assert_eq!(row_text(&e, 1), "tui");
assert_eq!(e.tracked_point(point), Some((1, 0)));
let index = e.marker_index();
assert_eq!(index.markers.len(), 1);
assert_eq!((index.markers[0].id, index.markers[0].line), (mark, 1));
e.feed(b"\x1b[?1049l");
assert_eq!(e.scrollback_len(), 0);
}