use crossterm::style::Color;
use htui::html::{css_parse, html_parse};
use htui::layout::layout::TermMap;
use htui::tui::graph_api::{FontStyle, TermLayout};
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
}
fn setup(html: &str, css: &str) -> (htui::tdom::node::NodeId, htui::tdom::node::NodeArena) {
let mut html_parser = html_parse::Parser::new(html);
let (root_id, mut arena, embedded) = html_parser.parse();
let combined = if css.is_empty() {
embedded
} else if embedded.is_empty() {
css.to_string()
} else {
format!("{embedded}\n{css}")
};
if !combined.trim().is_empty() {
let mut css_parser = css_parse::Parser::new(&combined);
let rules = css_parser.parse();
css_parse::css_into_node(&mut arena, root_id, &rules);
}
(root_id, arena)
}
const COLS: u16 = 80;
const ROWS: u16 = 24;
fn mock_term_map() -> TermMap {
TermMap {
columns: COLS,
rows: ROWS,
}
}
fn canvas_string(tl: &TermLayout) -> String {
tl.layout.iter().map(|b| b.word.as_str()).collect()
}
fn first_cell_with(tl: &TermLayout, word: &str) -> Option<usize> {
tl.layout.iter().position(|b| b.word == word)
}
#[test]
fn test_em_renders_text_content() {
init();
let (root_id, arena) = setup("<p>plain <em>italic</em> tail</p>", "");
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let canvas = canvas_string(&tl);
assert!(canvas.contains("plain"), "pre-em text must render");
assert!(canvas.contains("italic"), "em text must render");
assert!(canvas.contains("tail"), "post-em text must render");
}
#[test]
fn test_em_default_italic_applies() {
init();
let (root_id, arena) = setup("<p><em>word</em></p>", "");
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let idx = first_cell_with(&tl, "w").expect("em text must render");
let cell = &tl.layout[idx];
assert!(
cell.style.contains(FontStyle::ITALIC),
"default <em> rendering must be italic (got style {:?})",
cell.style
);
}
#[test]
fn test_em_default_italic_does_not_leak_outside_em() {
init();
let (root_id, arena) = setup("<p>xxx <em>foo</em> yyy</p>", "");
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let em_idx = first_cell_with(&tl, "f").expect("em text must render");
let plain_idx = first_cell_with(&tl, "x").expect("pre-em text must render");
assert!(tl.layout[em_idx].style.contains(FontStyle::ITALIC));
assert!(
!tl.layout[plain_idx].style.contains(FontStyle::ITALIC),
"text outside <em> must not inherit the UA italic flag (got {:?})",
tl.layout[plain_idx].style
);
}
#[test]
fn test_em_font_style_normal_disables_italic() {
init();
let (root_id, arena) = setup("<p><em>word</em></p>", "em { font-style: normal; }");
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let idx = first_cell_with(&tl, "w").expect("em text must render");
let cell = &tl.layout[idx];
assert!(
!cell.style.contains(FontStyle::ITALIC),
"`em {{ font-style: normal }}` must suppress the UA italic default (got {:?})",
cell.style
);
}
#[test]
fn test_em_color_override_applies() {
init();
let (root_id, arena) = setup("<p><em>word</em></p>", "em { color: magenta; }");
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let idx = first_cell_with(&tl, "w").expect("em text must render");
let cell = &tl.layout[idx];
assert_eq!(
cell.fcolor,
Some(Color::Rgb {
r: 255,
g: 0,
b: 255
}),
"`em {{ color: magenta }}` must paint the cell magenta (got {:?})",
cell.fcolor
);
assert!(
cell.style.contains(FontStyle::ITALIC),
"color override must not affect the italic UA default"
);
}
#[test]
fn test_em_nested_in_p_renders_correctly() {
init();
let (root_id, arena) = setup(
"<p>Another paragraph with <em>inline</em> elements (em tags are flattened).</p>",
"",
);
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let canvas = canvas_string(&tl);
assert!(
canvas.contains("inline"),
"em text must survive in the rendered canvas"
);
let em_idx = first_cell_with(&tl, "l").expect("em text must render");
assert!(tl.layout[em_idx].style.contains(FontStyle::ITALIC));
let another_idx = first_cell_with(&tl, "A").expect("pre-em text must render");
assert!(!tl.layout[another_idx].style.contains(FontStyle::ITALIC));
}
#[test]
fn test_em_inside_div_paint_layer() {
init();
let (root_id, arena) = setup("<div>before <em>italic-here</em> after</div>", "");
let term_map = mock_term_map();
let mut tl = TermLayout::new(&term_map, 1.0);
tl.draw(&arena, root_id, &term_map);
let idx = first_cell_with(&tl, "i").expect("em text must render");
assert!(
tl.layout[idx].style.contains(FontStyle::ITALIC),
"<em> inside <div> must receive the UA italic default (got {:?})",
tl.layout[idx].style
);
}