mod support; use moli_html2md::{Converter, Options}; use support::{Tree, rendered_html}; fn markdown_with(html: &str, options: Options) -> String { let dom = Tree::parse(html); let before = dom.clone(); let result = Converter::new(options).convert(&dom, dom.root); assert_eq!(dom, before); result } fn markdown(html: &str) -> String { markdown_with(html, Options::default()) } #[test] fn ordered_lists_use_start_and_sequential_numbers() { for (attributes, start) in [ ("", 1), ("reversed", 1), ("start='4'", 4), ("reversed start='4'", 4), ] { let html = format!( "
  1. first
  2. second
  3. third
" ); let result = markdown(&html); assert_eq!( result, format!( "{start}. first\n{}. second\n{}. third", start + 1, start + 2 ) ); let start_attribute = if start == 1 { String::new() } else { format!(" start=\"{start}\"") }; assert_eq!( rendered_html(&result), format!( "\n
  • first
  • \n
  • second
  • \n
  • third
  • \n\n" ) ); } } #[test] fn nested_lists_keep_independent_start_numbers() { assert_eq!( markdown( "
    1. outer
      1. inner
      2. next
    2. tail
    " ), "4. outer\n 9. inner\n 10. next\n5. tail" ); } #[test] fn code_languages_preserve_punctuation_and_unicode() { for language in ["text/x-rust", "中文", "C#", "objective-c++", "foo:bar"] { let html = format!("
    x
    "); let result = markdown(&html); assert_eq!(result, format!("```{language}\nx\n```")); assert_eq!( rendered_html(&result), format!("
    x\n
    \n") ); } } #[test] fn code_languages_preserve_literal_backslashes_and_entities() { for (language, expected) in [ ("f\\*oo", "f\\*oo"), ("literal©", "literal©"), ] { let result = markdown(&format!( "
    x
    " )); assert_eq!( rendered_html(&result), format!("
    x\n
    \n") ); } } #[test] fn language_backticks_use_a_tilde_fence_that_contains_the_entire_code() { let result = markdown("
    first\n~~~\nlast\n
    "); assert_eq!(result, "~~~~a`b\nfirst\n~~~\nlast\n~~~~"); assert_eq!( rendered_html(&result), "
    first\n~~~\nlast\n
    \n" ); } #[test] fn fallback_language_stays_on_the_fence_line() { for ending in ["\n", "\r\n", "\r"] { let result = markdown_with( "
    x
    ", Options { default_code_language: Some(format!("text/x-rust{ending}```{ending}outside")), ..Options::default() }, ); assert_eq!(result, "```text/x-rust\nx\n```"); } } #[test] fn list_spacing_uses_converted_blocks_including_wrapped_blocks() { for (body, expected) in [ ("

    Heading

    ", "- ## Heading\n\n- tail"), ("intro
    ", "- intro\n\n ---\n\n- tail"), ("
    one
    ", "- one\n\n- tail"), ("

    one

    ", "- one\n\n- tail"), ] { let result = markdown(&format!("")); assert_eq!(result, expected, "{body}"); assert!(rendered_html(&result).contains("

    tail

    "), "{body}"); } } #[test] fn nested_list_blocks_do_not_make_outer_items_loose() { for (inner, expected) in [ ( "
  • one
  • two
  • ", "- outer\n - one\n - two\n- tail", ), ( "
  • one

  • two

  • ", "- outer\n - one\n\n - two\n- tail", ), ] { let result = markdown(&format!( "" )); assert_eq!(result, expected); assert!(rendered_html(&result).contains("
  • tail
  • ")); } assert_eq!( markdown(""), "- outer\n\n - inner\n\n- tail" ); } #[test] fn omitted_subtrees_do_not_affect_list_spacing() { let html = ""; assert_eq!( markdown_with( html, Options { max_depth: 4, ..Options::default() } ), "- one\n- two" ); assert_eq!( markdown(""), "- one\n- two" ); } #[test] fn emphasis_uses_markdown_between_adjacent_links() { for (tag, marker, html_tag) in [ ("b", "**", "strong"), ("em", "*", "em"), ("del", "~~", "del"), ] { for label in ["Hacker News", "News!"] { let result = markdown(&format!( "<{tag}>{label}new" )); assert_eq!( result, format!("{marker}[{label}](news){marker}[new](newest)") ); assert_eq!( rendered_html(&result), format!( "

    <{html_tag}>{label}new

    \n" ) ); } } assert_eq!( markdown("News!new"), "**News!**[new](newest)" ); } #[test] fn emphasized_links_keep_html_at_intraword_boundaries() { for tag in ["strong", "em"] { for suffix in [" after", "after", "next"] { let result = markdown(&format!( "before<{tag}>text{suffix}" )); let suffix_markdown = if suffix.starts_with('<') { "[next](/next)" } else { suffix }; let suffix_html = if suffix.starts_with('<') { "next" } else { suffix }; assert_eq!( result, format!("before<{tag}>[text](/){suffix_markdown}") ); assert_eq!( rendered_html(&result), format!("

    before<{tag}>text{suffix_html}

    \n") ); } let result = markdown(&format!("<{tag}>textafter")); assert_eq!(result, format!("<{tag}>[text](/)after")); } }