use molt::prelude::*;
fn wide_tree(count: usize) -> Element {
ul().children_from((0..count).map(|i| li().add_class("item").text(format!("Item {i}"))))
}
#[test]
fn compact_output_size_is_stable() {
let rendered = wide_tree(1_000).render();
let expected: usize = 9
+ (0..1_000)
.map(|i| format!(r#"<li class="item">Item {i}</li>"#).len())
.sum::<usize>();
assert_eq!(
rendered.len(),
expected,
"compact render emitted unexpected bytes"
);
}
#[test]
fn pretty_output_adds_exactly_one_line_per_node() {
let rendered = wide_tree(1_000).render_pretty();
assert_eq!(rendered.lines().count(), 1_002);
}
#[test]
fn rendering_is_stable_across_repeated_calls() {
let tree = wide_tree(500);
let first = tree.render();
for _ in 0..10 {
assert_eq!(tree.render(), first, "render is not deterministic");
}
}
const VERY_DEEP: usize = 100_000;
fn deep_tree(depth: usize) -> Element {
let mut node = p().text("bottom");
for _ in 0..depth {
node = div().child(node);
}
node
}
#[test]
fn very_deep_nesting_renders_compact() {
assert_eq!(
deep_tree(VERY_DEEP).render().matches("<div>").count(),
VERY_DEEP
);
}
#[test]
fn very_deep_nesting_renders_pretty() {
let options = RenderOptions::pretty().with_indent("");
let rendered = deep_tree(VERY_DEEP).render_with(&options);
assert_eq!(rendered.lines().count(), VERY_DEEP * 2 + 1);
}
#[test]
fn dropping_a_very_deep_tree_does_not_abort() {
drop(deep_tree(VERY_DEEP));
}
#[test]
fn very_deep_fragments_render_and_drop() {
let mut node = Node::from(p().text("bottom"));
for _ in 0..VERY_DEEP {
node = Node::fragment([node]);
}
let root = div().child(node);
assert!(root.render().ends_with("<p>bottom</p></div>"));
drop(root);
}
#[test]
fn nesting_to_the_documented_depth_renders() {
let node = deep_tree(256);
assert!(node.render().len() > 256);
assert!(node.render_pretty().lines().count() > 256);
}
#[test]
fn a_document_with_a_very_deep_body_renders_in_both_modes() {
let page = molt::Document::new(Some("en"))
.head_children([title().text("deep")])
.body_children([deep_tree(VERY_DEEP)]);
assert_eq!(
page.render_compact().matches("<div>").count(),
VERY_DEEP,
"compact"
);
let pretty = page.render_with(&RenderOptions::pretty().with_indent(""));
assert_eq!(pretty.lines().count(), VERY_DEEP * 2 + 9, "pretty");
let mut streamed = Vec::new();
page.render_to(&mut streamed, &RenderOptions::compact())
.expect("written");
assert_eq!(streamed, page.render_compact().into_bytes());
}
#[test]
fn auditing_a_very_deep_tree_does_not_abort() {
use molt::accessibility::audit;
let mut node = Node::from(img().attr("src", "/a.png"));
for _ in 0..VERY_DEEP {
node = Node::from(div().child(node));
}
node = Node::fragment([node, Node::from(img().attr("src", "/b.png"))]);
let issues = audit(&node);
assert_eq!(issues.len(), 2);
assert!(!node.is_empty());
}
#[test]
fn a_chain_of_empty_fragments_is_empty_without_recursing() {
let mut node = Node::text("");
for _ in 0..VERY_DEEP {
node = Node::fragment([node]);
}
assert!(node.is_empty());
std::thread::Builder::new()
.stack_size(512 * 1024 * 1024)
.spawn(move || drop(node))
.expect("spawned")
.join()
.expect("dropped");
}
#[allow(clippy::manual_assert_eq)]
#[test]
fn cloning_and_comparing_a_very_deep_tree_does_not_abort() {
let tree = deep_tree(VERY_DEEP);
let copy = tree.clone();
assert!(tree == copy);
assert_eq!(copy.render().matches("<div>").count(), VERY_DEEP);
let mut different = deep_tree(VERY_DEEP);
different = div().child(different);
assert!(tree != different);
let other_leaf = {
let mut node = p().text("other");
for _ in 0..VERY_DEEP {
node = div().child(node);
}
node
};
assert!(tree != other_leaf);
}
#[allow(clippy::manual_assert_eq)]
#[test]
fn cloning_and_comparing_deep_fragments_does_not_abort() {
let mut node = Node::from(p().text("bottom"));
for _ in 0..VERY_DEEP {
node = Node::fragment([node, Node::text("x")]);
}
let root = div().child(node);
let copy = root.clone();
assert!(root == copy);
assert_eq!(copy.render(), root.render());
}
#[test]
fn hashing_a_very_deep_tree_does_not_abort() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let hash = |tree: &Element| {
let mut hasher = DefaultHasher::new();
tree.hash(&mut hasher);
hasher.finish()
};
let tree = deep_tree(VERY_DEEP);
assert_eq!(hash(&tree), hash(&tree.clone()));
assert_ne!(hash(&tree), hash(&div().child(tree.clone())));
}
#[test]
fn displaying_a_very_deep_tree_does_not_abort() {
assert_eq!(
deep_tree(VERY_DEEP).to_string().matches("<div>").count(),
VERY_DEEP
);
}