#![allow(clippy::print_stderr)]
#![allow(clippy::unwrap_used)]
use super::tree::{default_container_config, ContainerConfig};
use super::*;
#[test]
fn wrap_empty() {
assert_eq!(wrap_lines("", 10), vec![""]);
}
#[test]
fn wrap_fits() {
assert_eq!(wrap_lines("hello", 10), vec!["hello"]);
}
#[test]
fn wrap_word_boundary() {
assert_eq!(wrap_lines("hello world", 7), vec!["hello", "world"]);
}
#[test]
fn wrap_multiple_words() {
assert_eq!(
wrap_lines("one two three four", 9),
vec!["one two", "three", "four"]
);
}
#[test]
fn wrap_long_word() {
assert_eq!(wrap_lines("abcdefghij", 4), vec!["abcd", "efgh", "ij"]);
}
#[test]
fn wrap_zero_width() {
assert_eq!(wrap_lines("hello", 0), vec!["hello"]);
}
#[test]
fn wrap_lines_single_word_exact_fit() {
assert_eq!(wrap_lines("hello", 5), vec!["hello"]);
}
#[test]
fn wrap_lines_cjk_wide_chars() {
assert_eq!(wrap_lines("日本語文字", 4), vec!["日本", "語文", "字"]);
}
#[test]
fn wrap_lines_cjk_with_space() {
assert_eq!(wrap_lines("日本 hello", 5), vec!["日本", "hello"]);
}
#[test]
fn wrap_lines_consecutive_spaces_collapse() {
assert_eq!(wrap_lines("a b c", 10), vec!["a b c"]);
}
#[test]
fn wrap_lines_combining_mark_preserved() {
let input = "e\u{0301}llo";
assert_eq!(wrap_lines(input, 10), vec![input]);
}
#[test]
fn wrap_lines_single_wide_char_over_width() {
assert_eq!(wrap_lines("日", 1), vec!["日"]);
}
#[test]
fn wrap_lines_only_spaces() {
assert_eq!(wrap_lines(" ", 5), vec![""]);
}
#[test]
fn wrap_segments_empty_returns_single_empty_line() {
let segs: Vec<(String, Style)> = Vec::new();
assert_eq!(
wrap_segments(&segs, 10),
vec![Vec::<(String, Style)>::new()]
);
}
#[test]
fn wrap_segments_zero_width_returns_single_empty_line() {
let segs = vec![("hello".to_string(), Style::new())];
assert_eq!(wrap_segments(&segs, 0), vec![Vec::<(String, Style)>::new()]);
}
#[test]
fn wrap_segments_single_style_greedy_break() {
let style = Style::new();
let segs = vec![("hello world foo bar".to_string(), style)];
let lines = wrap_segments(&segs, 9);
let joined: Vec<String> = lines
.iter()
.map(|l| l.iter().map(|(t, _)| t.as_str()).collect())
.collect();
assert_eq!(joined, vec!["hello", "world", "foo bar"]);
}
#[test]
fn wrap_segments_mixed_styles_preserve_run_boundaries() {
let s1 = Style::new();
let s2 = Style::new().fg(Color::Red);
let segs = vec![("abc".to_string(), s1), ("def".to_string(), s2)];
let lines = wrap_segments(&segs, 10);
assert_eq!(lines.len(), 1);
assert_eq!(lines[0].len(), 2);
assert_eq!(lines[0][0].0, "abc");
assert_eq!(lines[0][1].0, "def");
assert_eq!(lines[0][0].1, s1);
assert_eq!(lines[0][1].1, s2);
}
#[test]
fn wrap_segments_trailing_whitespace_trimmed() {
let s1 = Style::new();
let s2 = Style::new().fg(Color::Red);
let segs = vec![("abc ".to_string(), s1), (" ".to_string(), s2)];
let lines = wrap_segments(&segs, 20);
assert_eq!(lines, vec![vec![("abc".to_string(), s1)]]);
}
#[test]
fn wrap_segments_break_on_space_with_style_change() {
let s1 = Style::new();
let s2 = Style::new().fg(Color::Red);
let segs = vec![("abc ".to_string(), s1), ("defgh".to_string(), s2)];
let lines = wrap_segments(&segs, 5);
assert_eq!(lines.len(), 2);
assert_eq!(lines[0], vec![("abc".to_string(), s1)]);
assert_eq!(lines[1], vec![("defgh".to_string(), s2)]);
}
#[test]
fn wrap_segments_cjk_with_mixed_styles() {
let s1 = Style::new();
let s2 = Style::new().fg(Color::Red);
let segs = vec![("日本".to_string(), s1), ("語".to_string(), s2)];
let lines = wrap_segments(&segs, 4);
assert_eq!(lines.len(), 2);
assert_eq!(lines[0], vec![("日本".to_string(), s1)]);
assert_eq!(lines[1], vec![("語".to_string(), s2)]);
}
#[test]
fn diagnostic_demo_layout() {
use crate::rect::Rect;
use crate::style::{Align, Border, Constraints, Justify, Margin, Padding, Style};
let mut root = LayoutNode::container(
Direction::Column,
ContainerConfig {
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 0,
},
);
let mut outer_container = LayoutNode::container(
Direction::Column,
ContainerConfig {
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: Some(Border::Rounded),
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::all(1),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 1,
},
);
outer_container.children.push(LayoutNode::text(
"header".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
outer_container.children.push(LayoutNode::text(
"separator".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
let mut inner_container = LayoutNode::container(
Direction::Column,
ContainerConfig {
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 1,
},
);
inner_container.children.push(LayoutNode::text(
"content1".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
inner_container.children.push(LayoutNode::text(
"content2".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
inner_container.children.push(LayoutNode::text(
"content3".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
outer_container.children.push(inner_container);
outer_container.children.push(LayoutNode::text(
"separator2".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
outer_container.children.push(LayoutNode::text(
"footer".to_string(),
Style::new(),
0,
Align::Start,
(None, false, false),
Margin::default(),
Constraints::default(),
));
root.children.push(outer_container);
compute(&mut root, Rect::new(0, 0, 80, 50));
eprintln!("\n=== DIAGNOSTIC LAYOUT TEST ===");
eprintln!("Root node:");
eprintln!(" pos: {:?}, size: {:?}", root.pos, root.size);
let outer = &root.children[0];
eprintln!("\nOuter bordered container (grow:1):");
eprintln!(" pos: {:?}, size: {:?}", outer.pos, outer.size);
let inner = &outer.children[2];
eprintln!("\nInner container (grow:1, simulates scrollable):");
eprintln!(" pos: {:?}, size: {:?}", inner.pos, inner.size);
eprintln!("\nAll children of outer container:");
for (i, child) in outer.children.iter().enumerate() {
eprintln!(" [{}] pos: {:?}, size: {:?}", i, child.pos, child.size);
}
assert_eq!(root.size, (80, 50));
assert_eq!(outer.size, (80, 50));
let expected_inner_height = 50 - 2 - 2 - 4;
assert_eq!(inner.size.1, expected_inner_height as u32);
let expected_inner_y = 1 + 1 + 1 + 1;
assert_eq!(inner.pos.1, expected_inner_y as u32);
}
#[test]
fn collect_focus_rects_from_markers() {
use Style;
let commands = vec![
Command::FocusMarker(0),
Command::Text {
content: "input1".into(),
cursor_offset: None,
style: Style::new(),
grow: 0,
align: Align::Start,
wrap: false,
truncate: false,
margin: Default::default(),
constraints: Default::default(),
},
Command::FocusMarker(1),
Command::Text {
content: "input2".into(),
cursor_offset: None,
style: Style::new(),
grow: 0,
align: Align::Start,
wrap: false,
truncate: false,
margin: Default::default(),
constraints: Default::default(),
},
];
let mut tree = build_tree(commands);
let area = crate::rect::Rect::new(0, 0, 40, 10);
compute(&mut tree, area);
let fd = collect_all(&tree);
assert_eq!(fd.focus_rects.len(), 2);
assert_eq!(fd.focus_rects[0].0, 0);
assert_eq!(fd.focus_rects[1].0, 1);
assert!(fd.focus_rects[0].1.width > 0);
assert!(fd.focus_rects[1].1.width > 0);
assert_ne!(fd.focus_rects[0].1.y, fd.focus_rects[1].1.y);
}
#[test]
fn focus_marker_tags_container() {
use crate::style::{Border, Style};
let commands = vec![
Command::FocusMarker(0),
Command::BeginContainer(Box::new(BeginContainerArgs {
direction: Direction::Column,
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: Some(Border::Single),
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Default::default(),
constraints: Default::default(),
title: None,
grow: 0,
group_name: None,
})),
Command::Text {
content: "inside".into(),
cursor_offset: None,
style: Style::new(),
grow: 0,
align: Align::Start,
wrap: false,
truncate: false,
margin: Default::default(),
constraints: Default::default(),
},
Command::EndContainer,
];
let mut tree = build_tree(commands);
let area = crate::rect::Rect::new(0, 0, 40, 10);
compute(&mut tree, area);
let fd = collect_all(&tree);
assert_eq!(fd.focus_rects.len(), 1);
assert_eq!(fd.focus_rects[0].0, 0);
assert!(fd.focus_rects[0].1.width >= 8);
assert!(fd.focus_rects[0].1.height >= 3);
}
#[test]
fn wrapped_text_cache_reused_for_same_width() {
let mut node = LayoutNode::text(
"alpha beta gamma".to_string(),
Style::new(),
0,
Align::Start,
(None, true, false),
Margin::default(),
Constraints::default(),
);
let height_a = node.min_height_for_width(6);
let first_ptr = node.cached_wrapped.as_ref().map(Vec::as_ptr).unwrap();
let height_b = node.min_height_for_width(6);
let second_ptr = node.cached_wrapped.as_ref().map(Vec::as_ptr).unwrap();
assert_eq!(height_a, height_b);
assert_eq!(first_ptr, second_ptr);
assert_eq!(node.cached_wrap_width, Some(6));
}
#[test]
fn collect_all_clips_raw_draw_to_scroll_viewport() {
let mut root = LayoutNode::container(Direction::Column, default_container_config());
let mut scroll = LayoutNode::container(Direction::Column, default_container_config());
scroll.is_scrollable = true;
scroll.pos = (0, 0);
scroll.size = (20, 4);
scroll.scroll_offset = 2;
let mut raw = LayoutNode::raw_draw(7, Constraints::default(), 0, Margin::default(), None, None);
raw.pos = (1, 3);
raw.size = (6, 3);
scroll.children.push(raw);
root.children.push(scroll);
let rects: Vec<_> = collect_all(&root)
.raw_draw_rects
.into_iter()
.map(|r| (r.draw_id, r.rect, r.top_clip_rows, r.original_height))
.collect();
assert_eq!(
rects,
vec![(7, crate::rect::Rect::new(1, 1, 6, 3), 0, 3)],
"collect_all must clip RawDraw rect into the scroll viewport, \
leave top_clip_rows = 0 when the image top is already inside \
the viewport, and report the unclipped original height"
);
}
#[test]
fn group_names_share_arc_across_focus_descendants() {
use std::sync::Arc;
const N_GROUPS: usize = 50;
const FOCUSES_PER_GROUP: usize = 3;
let mut commands: Vec<Command> = Vec::new();
let mut focus_id = 0usize;
for i in 0..N_GROUPS {
commands.push(Command::BeginContainer(Box::new(BeginContainerArgs {
direction: Direction::Column,
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 0,
group_name: Some(Arc::from(format!("group-{i}").as_str())),
})));
for _ in 0..FOCUSES_PER_GROUP {
commands.push(Command::FocusMarker(focus_id));
commands.push(Command::Text {
content: format!("row {focus_id}"),
cursor_offset: None,
style: Style::new(),
grow: 0,
align: Align::Start,
wrap: false,
truncate: false,
margin: Margin::default(),
constraints: Constraints::default(),
});
focus_id += 1;
}
commands.push(Command::EndContainer);
}
let mut tree = build_tree(commands);
let area = crate::rect::Rect::new(0, 0, 80, (N_GROUPS * FOCUSES_PER_GROUP) as u32 + 4);
compute(&mut tree, area);
let fd = collect_all(&tree);
assert_eq!(
fd.group_rects.len(),
N_GROUPS,
"one group rect per container"
);
for (i, (name, _rect)) in fd.group_rects.iter().enumerate() {
assert_eq!(name.as_ref(), format!("group-{i}"));
}
assert_eq!(
fd.focus_groups.len(),
N_GROUPS * FOCUSES_PER_GROUP,
"one focus slot per focusable"
);
for g in 0..N_GROUPS {
let base = g * FOCUSES_PER_GROUP;
let first = fd.focus_groups[base]
.as_ref()
.unwrap_or_else(|| panic!("focus slot {base} missing group"));
assert_eq!(first.as_ref(), format!("group-{g}"));
for k in 1..FOCUSES_PER_GROUP {
let slot = fd.focus_groups[base + k]
.as_ref()
.unwrap_or_else(|| panic!("focus slot {} missing group", base + k));
assert!(
Arc::ptr_eq(first, slot),
"focus slots within group-{g} must share the same Arc allocation"
);
}
}
}
#[test]
fn flexbox_row_many_children_overflow_scratch() {
const N: usize = 32;
let mut commands: Vec<Command> = Vec::new();
commands.push(Command::BeginContainer(Box::new(BeginContainerArgs {
direction: Direction::Row,
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 0,
group_name: None,
})));
for i in 0..N {
commands.push(Command::Text {
content: format!("c{i}"),
cursor_offset: None,
style: Style::new(),
grow: 1,
align: Align::Start,
wrap: false,
truncate: false,
margin: Default::default(),
constraints: Default::default(),
});
}
commands.push(Command::EndContainer);
let mut tree = build_tree(commands);
compute(&mut tree, crate::rect::Rect::new(0, 0, 200, 4));
let row = &tree.children[0];
assert_eq!(row.children.len(), N);
let mut prev_end = 0u32;
for child in &row.children {
assert!(child.pos.0 >= prev_end, "children must not overlap");
prev_end = child.pos.0 + child.size.0;
}
assert!(prev_end <= 200);
}
#[test]
fn flexbox_column_many_children_overflow_scratch() {
const N: usize = 20;
let mut commands: Vec<Command> = Vec::new();
commands.push(Command::BeginContainer(Box::new(BeginContainerArgs {
direction: Direction::Column,
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 0,
group_name: None,
})));
for i in 0..N {
commands.push(Command::Text {
content: format!("row{i}"),
cursor_offset: None,
style: Style::new(),
grow: 0,
align: Align::Start,
wrap: false,
truncate: false,
margin: Default::default(),
constraints: Default::default(),
});
}
commands.push(Command::EndContainer);
let mut tree = build_tree(commands);
compute(&mut tree, crate::rect::Rect::new(0, 0, 20, 50));
let col = &tree.children[0];
assert_eq!(col.children.len(), N);
let mut prev_end = 0u32;
for child in &col.children {
assert!(
child.pos.1 >= prev_end,
"children must not overlap vertically"
);
prev_end = child.pos.1 + child.size.1;
}
assert!(prev_end <= 50);
}
#[test]
fn flexbox_grow_with_max_width_no_gap() {
use crate::style::{Align, Constraints, Justify, Margin, Padding};
let commands = vec![
Command::BeginContainer(Box::new(BeginContainerArgs {
direction: Direction::Row,
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: crate::style::Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 0,
group_name: None,
})),
Command::Text {
content: "A".into(),
cursor_offset: None,
style: crate::style::Style::new(),
grow: 1,
align: Align::Start,
wrap: false,
truncate: false,
margin: Margin::default(),
constraints: Constraints::default().max_w(10),
},
Command::Text {
content: "B".into(),
cursor_offset: None,
style: crate::style::Style::new(),
grow: 1,
align: Align::Start,
wrap: false,
truncate: false,
margin: Margin::default(),
constraints: Constraints::default(),
},
Command::EndContainer,
];
let mut tree = build_tree(commands);
compute(&mut tree, crate::rect::Rect::new(0, 0, 40, 4));
let row = &tree.children[0];
assert_eq!(row.children.len(), 2);
let c0 = &row.children[0];
let c1 = &row.children[1];
assert_eq!(
c0.size.0, 10,
"child 0 width should be clamped to max_width=10"
);
assert_eq!(
c1.pos.0,
c0.pos.0 + c0.size.0,
"child 1 x must equal child 0 x + child 0 width (no gap)"
);
}
#[test]
fn flexbox_column_grow_with_max_height_no_gap() {
use crate::style::{Align, Constraints, Justify, Margin, Padding};
let commands = vec![
Command::BeginContainer(Box::new(BeginContainerArgs {
direction: Direction::Column,
gap: 0,
align: Align::Start,
align_self: None,
justify: Justify::Start,
border: None,
border_sides: BorderSides::all(),
border_style: crate::style::Style::new(),
bg_color: None,
padding: Padding::default(),
margin: Margin::default(),
constraints: Constraints::default(),
title: None,
grow: 1,
group_name: None,
})),
Command::Text {
content: "A".into(),
cursor_offset: None,
style: crate::style::Style::new(),
grow: 1,
align: Align::Start,
wrap: false,
truncate: false,
margin: Margin::default(),
constraints: Constraints::default().max_h(5),
},
Command::Text {
content: "B".into(),
cursor_offset: None,
style: crate::style::Style::new(),
grow: 1,
align: Align::Start,
wrap: false,
truncate: false,
margin: Margin::default(),
constraints: Constraints::default(),
},
Command::EndContainer,
];
let mut tree = build_tree(commands);
compute(&mut tree, crate::rect::Rect::new(0, 0, 20, 20));
let col = &tree.children[0];
assert_eq!(col.children.len(), 2);
let c0 = &col.children[0];
let c1 = &col.children[1];
assert_eq!(
c0.size.1, 5,
"child 0 height should be clamped to max_height=5"
);
assert_eq!(
c1.pos.1,
c0.pos.1 + c0.size.1,
"child 1 y must equal child 0 y + child 0 height (no gap)"
);
}
#[test]
fn collect_all_keeps_scroll_invariant_with_nested_scrollables() {
let mut root = LayoutNode::container(Direction::Column, default_container_config());
let mut outer = LayoutNode::container(Direction::Column, default_container_config());
outer.is_scrollable = true;
outer.pos = (0, 0);
outer.size = (40, 20);
let mut inner = LayoutNode::container(Direction::Column, default_container_config());
inner.is_scrollable = true;
inner.pos = (1, 1);
inner.size = (38, 18);
outer.children.push(inner);
let mut sibling = LayoutNode::container(Direction::Column, default_container_config());
sibling.is_scrollable = true;
sibling.pos = (0, 25);
sibling.size = (40, 10);
root.children.push(outer);
root.children.push(sibling);
let fd = collect_all(&root);
assert_eq!(
fd.scroll_infos.len(),
fd.scroll_rects.len(),
"scroll_infos and scroll_rects must always have equal length \
(collect_all_inner branch merge invariant)"
);
assert_eq!(
fd.scroll_infos.len(),
3,
"expected 3 scrollable nodes (outer, inner, sibling)"
);
}
#[test]
fn raw_draw_constructor_matches_inline_literal_shape() {
let constraints = Constraints::default().min_w(7).min_h(2);
let margin = Margin {
top: 1,
right: 2,
bottom: 3,
left: 4,
};
let node = LayoutNode::raw_draw(42, constraints, 5, margin, Some(11), Some(13));
assert!(matches!(node.kind, NodeKind::RawDraw(42)));
assert_eq!(node.grow, 5);
assert_eq!(node.margin.top, 1);
assert_eq!(node.margin.right, 2);
assert_eq!(node.margin.bottom, 3);
assert_eq!(node.margin.left, 4);
assert_eq!(node.constraints.min_width, Some(7));
assert_eq!(node.constraints.min_height, Some(2));
assert_eq!(node.size, (7, 2), "size must seed from constraints minima");
assert_eq!(node.pos, (0, 0));
assert_eq!(node.focus_id, Some(11));
assert_eq!(node.interaction_id, Some(13));
assert!(node.content.is_none());
assert!(node.cursor_offset.is_none());
assert_eq!(node.align, Align::Start);
assert!(node.align_self.is_none());
assert_eq!(node.justify, Justify::Start);
assert!(!node.wrap);
assert!(!node.truncate);
assert_eq!(node.gap, 0);
assert!(node.border.is_none());
assert!(node.bg_color.is_none());
assert!(node.title.is_none());
assert!(node.children.is_empty());
assert!(!node.is_scrollable);
assert_eq!(node.scroll_offset, 0);
assert_eq!(node.content_height, 0);
assert!(node.cached_wrap_width.is_none());
assert!(node.cached_wrapped.is_none());
assert!(node.segments.is_none());
assert!(node.cached_wrapped_segments.is_none());
assert!(node.link_url.is_none());
assert!(node.group_name.is_none());
assert!(node.overlays.is_empty());
}
fn build_deep_node(depth: usize) -> LayoutNode {
let mut root = LayoutNode::container(Direction::Column, default_container_config());
let mut cursor = &mut root;
for _ in 0..depth {
cursor.children.push(LayoutNode::container(
Direction::Column,
default_container_config(),
));
cursor = cursor.children.last_mut().unwrap();
}
root
}
#[test]
#[should_panic(expected = "layout tree depth exceeds 512")]
fn compute_panics_at_depth_guard() {
let mut node = build_deep_node(514);
compute(&mut node, crate::rect::Rect::new(0, 0, 80, 24));
}
#[test]
#[should_panic(expected = "layout tree depth exceeds 512")]
fn collect_all_panics_at_depth_guard() {
let mut node = build_deep_node(514);
fn populate_sizes(n: &mut LayoutNode) {
n.size = (10, 10);
for c in &mut n.children {
populate_sizes(c);
}
}
populate_sizes(&mut node);
let _ = collect_all(&node);
}
#[test]
#[should_panic(expected = "layout tree depth exceeds 512")]
fn render_panics_at_depth_guard() {
let mut node = build_deep_node(514);
fn populate_sizes(n: &mut LayoutNode) {
n.size = (10, 10);
n.pos = (0, 0);
for c in &mut n.children {
populate_sizes(c);
}
}
populate_sizes(&mut node);
let mut buf = crate::buffer::Buffer::empty(crate::rect::Rect::new(0, 0, 80, 24));
super::render::render(&node, &mut buf);
}