#![allow(missing_docs)]
use html_to_markdown_rs::options::{OutputFormat, TierStrategy};
use html_to_markdown_rs::{ConversionOptions, ConversionResult, WarningKind};
fn convert_with_options(html: &str, options: ConversionOptions) -> ConversionResult {
html_to_markdown_rs::convert(html, Some(options)).expect("conversion should not fail")
}
fn assert_depth_warning(result: &ConversionResult, max_depth: usize) {
assert_eq!(result.warnings.len(), 1, "expected exactly one depth warning");
let warning = &result.warnings[0];
assert_eq!(warning.kind, WarningKind::DepthLimitExceeded);
assert_eq!(
warning.message,
format!("DOM traversal reached the effective depth limit of {max_depth}; deeper nodes were skipped.")
);
}
fn nested_divs(depth: usize, inner: &str) -> String {
let mut html = inner.to_string();
for _ in 0..depth {
html = format!("<div>{html}</div>");
}
html
}
#[test]
fn test_max_depth_none_converts_reasonably_nested_content() {
let mut html = String::from("<p>deep</p>");
for _ in 0..32 {
html = format!("<div>{html}</div>");
}
let options = ConversionOptions {
extract_metadata: false,
max_depth: None,
..Default::default()
};
let result = convert_with_options(&html, options);
let content = result.content.as_deref().unwrap_or_default();
assert!(
content.contains("deep"),
"Deeply nested text should be present when max_depth is None. Got:\n{content}"
);
assert!(result.warnings.is_empty());
}
#[test]
fn test_max_depth_truncates_at_limit() {
let html = "<div><p>shallow</p><div><p>deep</p></div></div>";
let options = ConversionOptions {
extract_metadata: false,
max_depth: Some(3),
..Default::default()
};
let result = convert_with_options(html, options);
let content = result.content.as_deref().unwrap_or_default();
assert!(
content.contains("shallow"),
"Content at depth < max_depth should be present. Got:\n{content}"
);
assert!(
!content.contains("deep"),
"Content at depth >= max_depth should be absent. Got:\n{content}"
);
assert_depth_warning(&result, 3);
}
#[test]
fn test_max_depth_can_be_raised_above_native_default_issue_434() {
let html = nested_divs(90, "<p>deep-content</p>");
let options = ConversionOptions {
extract_metadata: false,
max_depth: Some(256),
..Default::default()
};
let result = convert_with_options(&html, options);
assert!(
result.content.as_deref().unwrap_or_default().contains("deep-content"),
"Raising max_depth above 64 must recover content nested deeper than the native default."
);
assert!(
result.warnings.is_empty(),
"No DepthLimitExceeded warning expected when nothing is truncated: {:?}",
result.warnings
);
}
#[test]
fn test_depth_truncation_emits_warning_issue_434() {
let html = nested_divs(90, "<p>deep-content</p>");
let options = ConversionOptions {
extract_metadata: false,
max_depth: None, ..Default::default()
};
let result = convert_with_options(&html, options);
assert!(
!result.content.as_deref().unwrap_or_default().contains("deep-content"),
"Content nested beyond the native default should be truncated."
);
assert_depth_warning(&result, 64);
}
#[test]
fn test_max_depth_warns_once_for_multiple_truncated_subtrees() {
let html = "<div><div><p>first</p></div><div><p>second</p></div></div>";
let options = ConversionOptions {
extract_metadata: false,
max_depth: Some(2),
..Default::default()
};
let result = convert_with_options(html, options);
let content = result.content.as_deref().unwrap_or_default();
assert!(!content.contains("first"));
assert!(!content.contains("second"));
assert_depth_warning(&result, 2);
}
#[test]
fn test_max_depth_zero_produces_empty() {
let html = "<p>hello</p>";
let options = ConversionOptions {
extract_metadata: false,
max_depth: Some(0),
..Default::default()
};
let result = convert_with_options(html, options);
let content = result.content.as_deref().unwrap_or_default();
assert!(
content.trim().is_empty(),
"max_depth: Some(0) should produce no output. Got:\n{content}"
);
assert_depth_warning(&result, 0);
}
#[test]
fn test_tier2_default_limit_truncates_and_warns() {
let html = format!("{}leaf{}", "<div>".repeat(65), "</div>".repeat(65));
let options = ConversionOptions {
extract_metadata: false,
max_depth: None,
tier_strategy: TierStrategy::Tier2,
..Default::default()
};
let result = convert_with_options(&html, options);
let content = result.content.as_deref().unwrap_or_default();
assert!(!content.contains("leaf"));
assert_depth_warning(&result, 64);
}
#[test]
fn test_tier2_default_limit_boundary_does_not_warn() {
let html = format!("{}leaf{}", "<div>".repeat(63), "</div>".repeat(63));
let options = ConversionOptions {
extract_metadata: false,
max_depth: None,
tier_strategy: TierStrategy::Tier2,
..Default::default()
};
let result = convert_with_options(&html, options);
assert!(result.content.as_deref().unwrap_or_default().contains("leaf"));
assert!(result.warnings.is_empty());
}
#[test]
fn test_plain_text_default_limit_truncates_and_warns() {
let html = format!("{}leaf{}", "<div>".repeat(65), "</div>".repeat(65));
let options = ConversionOptions {
extract_metadata: false,
max_depth: None,
output_format: OutputFormat::Plain,
..Default::default()
};
let result = convert_with_options(&html, options);
assert!(!result.content.as_deref().unwrap_or_default().contains("leaf"));
assert_depth_warning(&result, 64);
}
#[test]
fn test_max_depth_below_limit_does_not_warn() {
let options = ConversionOptions {
extract_metadata: false,
max_depth: Some(3),
..Default::default()
};
let result = convert_with_options("<div><p>safe</p></div>", options);
assert!(result.content.as_deref().unwrap_or_default().contains("safe"));
assert!(result.warnings.is_empty());
}