#![cfg(feature = "markdown")]
mod common;
use common::init_test_logging;
use rich_rust::prelude::*;
use rich_rust::renderables::Markdown;
use rich_rust::style::Attributes;
fn render_md_text(source: &str, width: usize) -> String {
let md = Markdown::new(source);
let segments = md.render(width);
segments.iter().map(|s| s.text.as_ref()).collect()
}
fn render_md_to_html(source: &str, width: usize) -> String {
let md = Markdown::new(source);
let console = Console::builder()
.width(width)
.force_terminal(true)
.color_system(ColorSystem::TrueColor)
.markup(false)
.build();
console.begin_capture();
console.print_renderable(&md);
console.export_html(true)
}
fn console_export_text(source: &str, width: usize) -> String {
let md = Markdown::new(source);
let console = Console::builder()
.width(width)
.force_terminal(true)
.color_system(ColorSystem::TrueColor)
.markup(false)
.build();
console.export_renderable_text(&md)
}
#[test]
fn test_md_h1_heading() {
init_test_logging();
let md = Markdown::new("# Main Title");
let segments = md.render(80);
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(text.contains("Main Title"), "H1 text should be present");
let title_seg = segments
.iter()
.find(|s| s.text.contains("Main Title"))
.expect("missing H1 segment");
let style = title_seg.style.as_ref().expect("H1 should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"H1 should be bold"
);
assert!(
style.attributes.contains(Attributes::UNDERLINE),
"H1 should be underlined"
);
}
#[test]
fn test_md_h2_heading() {
init_test_logging();
let md = Markdown::new("## Section");
let segments = md.render(80);
let title_seg = segments
.iter()
.find(|s| s.text.contains("Section"))
.expect("missing H2 segment");
let style = title_seg.style.as_ref().expect("H2 should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"H2 should be bold"
);
}
#[test]
fn test_md_h3_heading() {
init_test_logging();
let md = Markdown::new("### Subsection");
let segments = md.render(80);
let title_seg = segments
.iter()
.find(|s| s.text.contains("Subsection"))
.expect("missing H3 segment");
let style = title_seg.style.as_ref().expect("H3 should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"H3 should be bold"
);
}
#[test]
fn test_md_h4_h5_h6_headings() {
init_test_logging();
for (level, label) in [("####", "H4"), ("#####", "H5"), ("######", "H6")] {
let source = format!("{level} {label} Heading");
let text = render_md_text(&source, 80);
assert!(
text.contains(&format!("{label} Heading")),
"{label} text should be present"
);
}
}
#[test]
fn test_md_all_heading_levels() {
init_test_logging();
let source = "# H1\n## H2\n### H3\n#### H4\n##### H5\n###### H6";
let text = render_md_text(source, 80);
for label in &["H1", "H2", "H3", "H4", "H5", "H6"] {
assert!(text.contains(label), "heading {label} should be present");
}
}
#[test]
fn test_md_custom_h1_style() {
init_test_logging();
let custom_style = Style::new().italic();
let md = Markdown::new("# Custom").h1_style(custom_style);
let segments = md.render(80);
let title_seg = segments
.iter()
.find(|s| s.text.contains("Custom"))
.expect("missing segment");
let style = title_seg.style.as_ref().expect("should have style");
assert!(
style.attributes.contains(Attributes::ITALIC),
"custom H1 style should apply"
);
}
#[test]
fn test_md_unordered_list() {
init_test_logging();
let text = render_md_text("- Alpha\n- Beta\n- Gamma", 80);
assert!(text.contains("Alpha"));
assert!(text.contains("Beta"));
assert!(text.contains("Gamma"));
assert!(text.contains("•"), "default bullet should appear");
}
#[test]
fn test_md_ordered_list() {
init_test_logging();
let text = render_md_text("1. First\n2. Second\n3. Third", 80);
assert!(text.contains("First"));
assert!(text.contains("Second"));
assert!(text.contains("Third"));
assert!(text.contains("1."), "numbered prefix should appear");
assert!(text.contains("2."));
assert!(text.contains("3."));
}
#[test]
fn test_md_nested_unordered_list() {
init_test_logging();
let source = "- Outer\n - Inner\n - Deep\n- Back";
let text = render_md_text(source, 80);
assert!(text.contains("Outer"));
assert!(text.contains("Inner"));
assert!(text.contains("Deep"));
assert!(text.contains("Back"));
}
#[test]
fn test_md_task_list() {
init_test_logging();
let text = render_md_text("- [ ] Pending\n- [x] Done\n- [ ] Todo", 80);
assert!(text.contains("Pending"));
assert!(text.contains("Done"));
assert!(text.contains("☐"), "unchecked box");
assert!(text.contains("☑"), "checked box");
}
#[test]
fn test_md_task_list_checked_style() {
init_test_logging();
let md = Markdown::new("- [x] Completed");
let segments = md.render(80);
let check_seg = segments
.iter()
.find(|s| s.text.contains('☑'))
.expect("missing checkbox segment");
let style = check_seg
.style
.as_ref()
.expect("checkbox should have style");
assert!(style.color.is_some(), "checked box should have a color");
}
#[test]
fn test_md_custom_bullet_char() {
init_test_logging();
let md = Markdown::new("- Item 1\n- Item 2").bullet_char('→');
let segments = md.render(80);
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(text.contains("→"), "custom bullet should appear");
assert!(!text.contains("•"), "default bullet should not appear");
}
#[test]
fn test_md_custom_list_indent() {
init_test_logging();
let md = Markdown::new("- Outer\n - Inner").list_indent(6);
let segments = md.render(80);
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(text.contains("Outer"));
assert!(text.contains("Inner"));
}
#[test]
fn test_md_mixed_list_types() {
init_test_logging();
let source = "1. Ordered first\n2. Ordered second\n\n- Bullet first\n- Bullet second";
let text = render_md_text(source, 80);
assert!(text.contains("Ordered first"));
assert!(text.contains("1."));
assert!(text.contains("Bullet first"));
assert!(text.contains("•"));
}
#[test]
fn test_md_list_item_multi_paragraph() {
init_test_logging();
let source = "- First paragraph\n\n Second paragraph";
let text = render_md_text(source, 80);
let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
assert!(lines.len() >= 2, "should have multiple content lines");
assert!(lines[0].contains("First paragraph"));
assert!(lines[1].contains("Second paragraph"));
}
#[test]
fn test_md_inline_code() {
init_test_logging();
let text = render_md_text("Use `println!()` here.", 80);
assert!(
text.contains("println!()"),
"inline code text should appear"
);
}
#[test]
fn test_md_inline_code_style() {
init_test_logging();
let md = Markdown::new("Use `code` here.");
let segments = md.render(80);
let code_seg = segments
.iter()
.find(|s| s.text.contains("code"))
.expect("missing inline code segment");
let style = code_seg
.style
.as_ref()
.expect("inline code should have style");
assert!(style.bgcolor.is_some(), "code should have background color");
}
#[test]
fn test_md_fenced_code_block() {
init_test_logging();
let source = "```rust\nfn main() {\n println!(\"hello\");\n}\n```";
let text = render_md_text(source, 80);
assert!(text.contains("fn main"), "code block content should appear");
assert!(text.contains("println"), "code block content should appear");
}
#[test]
fn test_md_code_block_style() {
init_test_logging();
let md = Markdown::new("```\nsome code\n```");
let segments = md.render(80);
let code_seg = segments
.iter()
.find(|s| s.text.contains("some code"))
.expect("missing code block segment");
let style = code_seg
.style
.as_ref()
.expect("code block should have style");
assert!(
style.bgcolor.is_some(),
"code block should have background color"
);
}
#[test]
fn test_md_code_block_no_language() {
init_test_logging();
let source = "```\nplain code\n```";
let text = render_md_text(source, 80);
assert!(text.contains("plain code"));
}
#[test]
fn test_md_code_block_multiline() {
init_test_logging();
let source = "```\nline 1\nline 2\nline 3\n```";
let text = render_md_text(source, 80);
assert!(text.contains("line 1"));
assert!(text.contains("line 2"));
assert!(text.contains("line 3"));
}
#[test]
fn test_md_custom_code_block_style() {
init_test_logging();
let custom = Style::new().italic();
let md = Markdown::new("```\ncode\n```").code_block_style(custom);
let segments = md.render(80);
let code_seg = segments
.iter()
.find(|s| s.text.contains("code"))
.expect("missing code segment");
let style = code_seg.style.as_ref().expect("should have style");
assert!(
style.attributes.contains(Attributes::ITALIC),
"custom code block style should apply"
);
}
#[test]
fn test_md_link_default_hides_url_suffix() {
init_test_logging();
let text = render_md_text("[Click here](https://example.com)", 80);
assert!(text.contains("Click here"), "link text");
assert!(
!text.contains("example.com"),
"URL suffix should not be rendered"
);
}
#[test]
fn test_md_link_hyperlinks_disabled_shows_url_suffix() {
init_test_logging();
let md = Markdown::new("[Click here](https://example.com)").hyperlinks(false);
let segments = md.render(80);
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(text.contains("Click here"), "link text should remain");
assert!(
text.contains("example.com"),
"URL suffix should be rendered"
);
assert!(text.contains(" (https://example.com)"));
}
#[test]
fn test_md_link_style() {
init_test_logging();
let md = Markdown::new("[Example](https://example.com)");
let segments = md.render(80);
let link_seg = segments
.iter()
.find(|s| s.text.contains("Example"))
.expect("missing link segment");
let style = link_seg.style.as_ref().expect("link should have style");
assert!(
style.attributes.contains(Attributes::UNDERLINE),
"link should be underlined"
);
assert_eq!(
style.link.as_deref(),
Some("https://example.com"),
"link should carry OSC8 URL in style"
);
}
#[test]
fn test_md_multiple_links() {
init_test_logging();
let source = "[First](https://first.com) and [Second](https://second.com)";
let text = render_md_text(source, 120);
assert!(text.contains("First"));
assert!(text.contains("Second"));
assert!(!text.contains("first.com"));
assert!(!text.contains("second.com"));
}
#[test]
fn test_md_custom_link_style() {
init_test_logging();
let custom = Style::new().bold();
let md = Markdown::new("[Bold Link](https://example.com)").link_style(custom);
let segments = md.render(80);
let link_seg = segments
.iter()
.find(|s| s.text.contains("Bold Link"))
.expect("missing link segment");
let style = link_seg.style.as_ref().expect("should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"custom link style should apply"
);
}
#[test]
fn test_md_table_basic() {
init_test_logging();
let source = "| Name | Age |\n|------|-----|\n| Alice | 30 |\n| Bob | 25 |";
let text = render_md_text(source, 80);
assert!(text.contains("Name"));
assert!(text.contains("Age"));
assert!(text.contains("Alice"));
assert!(text.contains("Bob"));
assert!(text.contains("┌"), "top border");
assert!(text.contains("│"), "vertical border");
assert!(text.contains("─"), "horizontal border");
assert!(text.contains("┘"), "bottom border");
}
#[test]
fn test_md_table_alignment() {
init_test_logging();
let source = "| Left | Center | Right |\n|:-----|:------:|------:|\n| a | b | c |";
let text = render_md_text(source, 80);
assert!(text.contains("Left"));
assert!(text.contains("Center"));
assert!(text.contains("Right"));
}
#[test]
fn test_md_table_header_style() {
init_test_logging();
let md = Markdown::new("| H1 | H2 |\n|---|---|\n| a | b |");
let segments = md.render(80);
let header_seg = segments
.iter()
.find(|s| s.text.trim() == "H1" || s.text.trim() == "H2")
.expect("missing header segment");
let style = header_seg
.style
.as_ref()
.expect("table header should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"header should be bold"
);
}
#[test]
fn test_md_table_border_style() {
init_test_logging();
let md = Markdown::new("| A |\n|---|\n| x |");
let segments = md.render(80);
let border_seg = segments
.iter()
.find(|s| s.text.contains("┌") || s.text.contains("─"))
.expect("missing border segment");
assert!(border_seg.style.is_some(), "table border should have style");
}
#[test]
fn test_md_table_unicode_width() {
init_test_logging();
let source = "| Col |\n|-----|\n| 日本 |\n| abc |";
let text = render_md_text(source, 80);
let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
assert!(lines.len() >= 3, "should have header + separator + rows");
}
#[test]
fn test_md_table_many_columns() {
init_test_logging();
let source = "| A | B | C | D | E |\n|---|---|---|---|---|\n| 1 | 2 | 3 | 4 | 5 |";
let text = render_md_text(source, 120);
for col in &["A", "B", "C", "D", "E", "1", "2", "3", "4", "5"] {
assert!(text.contains(col), "column {col} should appear");
}
}
#[test]
fn test_md_blockquote() {
init_test_logging();
let text = render_md_text("> This is quoted", 80);
assert!(text.contains("This is quoted"));
assert!(text.contains("│"), "blockquote prefix");
}
#[test]
fn test_md_blockquote_multi_paragraph() {
init_test_logging();
let source = "> First paragraph\n>\n> Second paragraph";
let text = render_md_text(source, 80);
let lines: Vec<&str> = text.lines().filter(|l| !l.trim().is_empty()).collect();
assert!(lines.len() >= 2);
assert!(
lines[0].starts_with("│ "),
"first paragraph should have prefix"
);
assert!(
lines[1].starts_with("│ "),
"second paragraph should have prefix"
);
}
#[test]
fn test_md_blockquote_style() {
init_test_logging();
let md = Markdown::new("> Styled quote");
let segments = md.render(80);
let quote_seg = segments
.iter()
.find(|s| s.text.contains("Styled quote"))
.expect("missing blockquote segment");
let style = quote_seg
.style
.as_ref()
.expect("blockquote should have style");
assert!(
style.attributes.contains(Attributes::ITALIC),
"blockquote should be italic"
);
}
#[test]
fn test_md_custom_quote_style() {
init_test_logging();
let custom = Style::new().bold();
let md = Markdown::new("> Quote").quote_style(custom);
let segments = md.render(80);
let quote_seg = segments
.iter()
.find(|s| s.text.contains("Quote"))
.expect("missing segment");
let style = quote_seg.style.as_ref().expect("should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"custom quote style should apply"
);
}
#[test]
fn test_md_bold() {
init_test_logging();
let md = Markdown::new("This is **bold** text.");
let segments = md.render(80);
let bold_seg = segments
.iter()
.find(|s| s.text.contains("bold"))
.expect("missing bold segment");
let style = bold_seg.style.as_ref().expect("bold should have style");
assert!(style.attributes.contains(Attributes::BOLD));
}
#[test]
fn test_md_italic() {
init_test_logging();
let md = Markdown::new("This is *italic* text.");
let segments = md.render(80);
let italic_seg = segments
.iter()
.find(|s| s.text.contains("italic"))
.expect("missing italic segment");
let style = italic_seg.style.as_ref().expect("italic should have style");
assert!(style.attributes.contains(Attributes::ITALIC));
}
#[test]
fn test_md_strikethrough() {
init_test_logging();
let md = Markdown::new("This is ~~deleted~~ text.");
let segments = md.render(80);
let strike_seg = segments
.iter()
.find(|s| s.text.contains("deleted"))
.expect("missing strikethrough segment");
let style = strike_seg
.style
.as_ref()
.expect("strikethrough should have style");
assert!(style.attributes.contains(Attributes::STRIKE));
}
#[test]
fn test_md_bold_italic_combined() {
init_test_logging();
let md = Markdown::new("**bold *and italic***");
let segments = md.render(80);
let combined_seg = segments
.iter()
.find(|s| s.text.contains("and italic"))
.expect("missing combined segment");
let style = combined_seg
.style
.as_ref()
.expect("combined should have style");
assert!(
style.attributes.contains(Attributes::BOLD),
"should be bold"
);
assert!(
style.attributes.contains(Attributes::ITALIC),
"should be italic"
);
}
#[test]
fn test_md_custom_emphasis_style() {
init_test_logging();
let custom = Style::new().underline();
let md = Markdown::new("*custom emphasis*").emphasis_style(custom);
let segments = md.render(80);
let em_seg = segments
.iter()
.find(|s| s.text.contains("custom emphasis"))
.expect("missing segment");
let style = em_seg.style.as_ref().expect("should have style");
assert!(
style.attributes.contains(Attributes::UNDERLINE),
"custom emphasis style should apply"
);
}
#[test]
fn test_md_custom_strong_style() {
init_test_logging();
let custom = Style::new().italic();
let md = Markdown::new("**custom strong**").strong_style(custom);
let segments = md.render(80);
let strong_seg = segments
.iter()
.find(|s| s.text.contains("custom strong"))
.expect("missing segment");
let style = strong_seg.style.as_ref().expect("should have style");
assert!(
style.attributes.contains(Attributes::ITALIC),
"custom strong style should apply"
);
}
#[test]
fn test_md_horizontal_rule() {
init_test_logging();
let text = render_md_text("Above\n\n---\n\nBelow", 80);
assert!(text.contains("Above"));
assert!(text.contains("Below"));
assert!(text.contains("─"), "horizontal rule character");
}
#[test]
fn test_md_horizontal_rule_width() {
init_test_logging();
let width = 40;
let md = Markdown::new("---");
let segments = md.render(width);
let rule_seg = segments
.iter()
.find(|s| s.text.contains("─"))
.expect("missing rule segment");
let rule_len: usize = rule_seg.text.chars().filter(|c| *c == '─').count();
assert!(
rule_len >= 10,
"rule should span significant width, got {rule_len}"
);
}
#[test]
fn test_md_complex_document() {
init_test_logging();
let source = "\
# Project README
This is a **bold** introduction with *emphasis*.
## Features
- Feature one
- Feature two
- Sub-feature
- Feature three
### Code Example
```rust
fn hello() {
println!(\"world\");
}
```
> Note: This is important.
| Name | Status |
|------|--------|
| Test | Pass |
Visit [our site](https://example.com) for more.
---
That's all!
";
let text = render_md_text(source, 80);
assert!(text.contains("Project README"), "H1");
assert!(text.contains("Features"), "H2");
assert!(text.contains("Code Example"), "H3");
assert!(text.contains("bold"), "bold text");
assert!(text.contains("emphasis"), "italic text");
assert!(text.contains("Feature one"));
assert!(text.contains("Sub-feature"));
assert!(text.contains("fn hello"));
assert!(text.contains("This is important"));
assert!(text.contains("│"));
assert!(text.contains("Name"));
assert!(text.contains("Pass"));
assert!(text.contains("our site"));
assert!(!text.contains("example.com"));
assert!(text.contains("─"));
assert!(text.contains("That's all!"));
}
#[test]
fn test_md_inline_only_document() {
init_test_logging();
let source = "**Bold**, *italic*, ~~strike~~, and `code`.";
let text = render_md_text(source, 80);
assert!(text.contains("Bold"));
assert!(text.contains("italic"));
assert!(text.contains("strike"));
assert!(text.contains("code"));
}
#[test]
fn test_md_blockquote_with_content() {
init_test_logging();
let source = "> Important:\n>\n> - Item A\n> - Item B";
let text = render_md_text(source, 80);
assert!(text.contains("Important"));
assert!(text.contains("Item A"));
assert!(text.contains("Item B"));
}
#[test]
fn test_md_console_print() {
init_test_logging();
let md = Markdown::new("# Hello World\n\nParagraph text.");
let console = Console::builder()
.width(80)
.force_terminal(true)
.color_system(ColorSystem::TrueColor)
.markup(false)
.build();
console.begin_capture();
console.print_renderable(&md);
let segments = console.end_capture();
let output: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(output.contains("Hello World"));
assert!(output.contains("Paragraph text"));
}
#[test]
fn test_md_export_html() {
init_test_logging();
let html = render_md_to_html("**Bold** and *italic*", 80);
assert!(html.contains("Bold"), "HTML should contain bold text");
assert!(html.contains("italic"), "HTML should contain italic text");
assert!(html.contains("<"), "should be HTML");
}
#[test]
fn test_md_export_svg() {
init_test_logging();
let md = Markdown::new("# Title\n\nBody text.");
let console = Console::builder()
.width(80)
.force_terminal(true)
.color_system(ColorSystem::TrueColor)
.markup(false)
.build();
console.begin_capture();
console.print_renderable(&md);
let svg = console.export_svg(true);
assert!(svg.contains("<svg"), "should be SVG format");
fn strip_tags(input: &str) -> String {
let mut out = String::new();
let mut in_tag = false;
for ch in input.chars() {
match ch {
'<' => in_tag = true,
'>' => {
in_tag = false;
out.push(' ');
}
_ => {
if !in_tag {
out.push(ch);
}
}
}
}
out.split_whitespace().collect::<Vec<_>>().join(" ")
}
let visible = strip_tags(&svg);
assert!(visible.contains("Title"), "SVG should contain title text");
assert!(visible.contains("Body"), "SVG should contain body text");
}
#[test]
fn test_md_export_plain_text() {
init_test_logging();
let plain = console_export_text("# Heading\n\n**Bold** text.", 80);
assert!(plain.contains("Heading"));
assert!(plain.contains("Bold"));
assert!(plain.contains("text"));
}
#[test]
fn test_md_source_accessor() {
init_test_logging();
let md = Markdown::new("# Hello\n\nWorld");
assert_eq!(md.source(), "# Hello\n\nWorld");
}
#[test]
fn test_md_full_builder_chain() {
init_test_logging();
let md = Markdown::new("# Title\n\n*Emphasis* and **strong** with `code`.")
.h1_style(Style::new().bold())
.h2_style(Style::new().bold())
.h3_style(Style::new().bold())
.h4_style(Style::new().bold())
.emphasis_style(Style::new().italic())
.strong_style(Style::new().bold())
.code_style(Style::new().italic())
.code_block_style(Style::new().italic())
.link_style(Style::new().underline())
.quote_style(Style::new().italic())
.table_header_style(Style::new().bold())
.table_border_style(Style::new().italic())
.bullet_char('*')
.list_indent(4)
.hyperlinks(false);
let segments = md.render(80);
assert!(!segments.is_empty(), "should produce output");
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(text.contains("Title"));
}
#[test]
fn test_md_default() {
init_test_logging();
let md = Markdown::default();
assert_eq!(md.source(), "");
let segments = md.render(80);
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(
text.trim().is_empty(),
"empty source should produce empty output"
);
}
#[test]
fn test_md_clone() {
init_test_logging();
let md1 = Markdown::new("# Original");
let md2 = md1.clone();
assert_eq!(md1.source(), md2.source());
let segs1 = md1.render(80);
let segs2 = md2.render(80);
let text1: String = segs1.iter().map(|s| s.text.as_ref()).collect();
let text2: String = segs2.iter().map(|s| s.text.as_ref()).collect();
assert_eq!(text1, text2);
}
#[test]
fn test_md_debug() {
init_test_logging();
let md = Markdown::new("# Test");
let debug = format!("{:?}", md);
assert!(debug.contains("Markdown"), "Debug should contain type name");
}
#[test]
fn test_md_empty_source() {
init_test_logging();
let text = render_md_text("", 80);
assert!(text.trim().is_empty() || text.chars().all(|c| c.is_whitespace()));
}
#[test]
fn test_md_whitespace_only() {
init_test_logging();
let text = render_md_text(" \n\n \n", 80);
let _ = text;
}
#[test]
fn test_md_narrow_width() {
init_test_logging();
let text = render_md_text("# Title\n\nSome paragraph text.", 10);
assert!(text.contains("Title"));
assert!(text.contains("Some"));
}
#[test]
fn test_md_wide_width() {
init_test_logging();
let text = render_md_text("# Title\n\nBody.", 200);
assert!(text.contains("Title"));
assert!(text.contains("Body"));
}
#[test]
fn test_md_zero_width() {
init_test_logging();
let md = Markdown::new("# Hello");
let segments = md.render(0);
let text: String = segments.iter().map(|s| s.text.as_ref()).collect();
assert!(text.contains("Hello"));
}
#[test]
fn test_md_unicode_content() {
init_test_logging();
let source = "# 日本語タイトル\n\nこんにちは世界。Привет мир。";
let text = render_md_text(source, 80);
assert!(text.contains("日本語タイトル"));
assert!(text.contains("こんにちは世界"));
assert!(text.contains("Привет мир"));
}
#[test]
fn test_md_special_chars() {
init_test_logging();
let source = "Ampersand & angle <brackets> and \"quotes\".";
let text = render_md_text(source, 80);
assert!(text.contains("Ampersand"));
assert!(text.contains("&"));
}
#[test]
fn test_md_long_code_block() {
init_test_logging();
let lines: Vec<String> = (1..=50).map(|i| format!("line {i}")).collect();
let code = lines.join("\n");
let source = format!("```\n{code}\n```");
let text = render_md_text(&source, 80);
assert!(text.contains("line 1"));
assert!(text.contains("line 50"));
}
#[test]
fn test_md_deeply_nested_list() {
init_test_logging();
let source = "- Level 1\n - Level 2\n - Level 3\n - Level 4";
let text = render_md_text(source, 80);
assert!(text.contains("Level 1"));
assert!(text.contains("Level 4"));
}
#[test]
fn test_md_consecutive_headings() {
init_test_logging();
let source = "# First\n# Second\n# Third";
let text = render_md_text(source, 80);
assert!(text.contains("First"));
assert!(text.contains("Second"));
assert!(text.contains("Third"));
}
#[test]
fn test_md_soft_break() {
init_test_logging();
let source = "Line one\nLine two";
let text = render_md_text(source, 80);
assert!(text.contains("Line one"));
assert!(text.contains("Line two"));
}
#[test]
fn test_md_hard_break() {
init_test_logging();
let source = "Line one \nLine two";
let text = render_md_text(source, 80);
assert!(text.contains("Line one"));
assert!(text.contains("Line two"));
}