use markdown2pdf::markdown::*;
use super::common::parse;
fn first_html_block(tokens: &[Token]) -> Option<String> {
tokens.iter().find_map(|t| match t {
Token::HtmlBlock(s) => Some(s.clone()),
_ => None,
})
}
fn html_blocks(tokens: &[Token]) -> Vec<String> {
tokens
.iter()
.filter_map(|t| match t {
Token::HtmlBlock(s) => Some(s.clone()),
_ => None,
})
.collect()
}
#[test]
fn div_simple() {
let input = "<div>\nbody\n</div>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn table_simple() {
let input = "<table>\n<tr><td>x</td></tr>\n</table>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn paragraph_p_tag() {
let input = "<p>body</p>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn hr_tag() {
let input = "<hr>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn heading_tags_h1_through_h6() {
for n in 1..=6 {
let input = format!("<h{}>title</h{}>\n", n, n);
let tokens = parse(&input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input.as_str()));
}
}
#[test]
fn case_insensitive_tag_name() {
let input = "<DIV CLASS=\"foo\">\nbody\n</DIV>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn incomplete_opener_still_triggers() {
let input = "<div id=\"foo\"\n*hi*\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn opener_with_multiline_attributes() {
let input = "<div id=\"foo\"\n class=\"bar\">\n</div>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn opener_with_attribute_value_split_across_lines() {
let input = "<div id=\"foo\" class=\"bar\n baz\">\n</div>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn opener_with_invalid_attribute_chars_still_triggers() {
let input = "<div *???-&&&-<---\n*foo*\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn blank_line_terminates_block() {
let tokens = parse("<div>\n*foo*\n\n*bar*\n");
let blocks = html_blocks(&tokens);
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0], "<div>\n*foo*\n");
assert!(tokens.iter().any(|t| matches!(t, Token::Emphasis { .. })));
}
#[test]
fn blank_lines_inside_split_into_multiple_blocks_with_paragraphs_between() {
let tokens = parse("<div>\n\n*Emphasized*\n\n</div>\n");
let blocks = html_blocks(&tokens);
assert_eq!(blocks.len(), 2);
assert_eq!(blocks[0], "<div>\n");
assert_eq!(blocks[1], "</div>\n");
assert!(tokens.iter().any(|t| matches!(t, Token::Emphasis { .. })));
}
#[test]
fn no_blank_lines_keeps_everything_inside_block() {
let input = "<div>\n*foo*\n</div>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
assert!(!tokens.iter().any(|t| matches!(t, Token::Emphasis { .. })));
}
#[test]
fn closer_then_text_outside_block() {
let input = "<div>\nbar\n</div>\n*foo*\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
assert!(!tokens.iter().any(|t| matches!(t, Token::Emphasis { .. })));
}
#[test]
fn can_interrupt_paragraph() {
let tokens = parse("Foo\n<div>\nbar\n</div>\n");
let blocks = html_blocks(&tokens);
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0], "<div>\nbar\n</div>\n");
assert!(Token::collect_all_text(&tokens).contains("Foo"));
}
#[test]
fn block_with_one_space_indent() {
let input = " <div>\nbody\n</div>\n";
let block = first_html_block(&parse(input)).expect("expected HtmlBlock");
assert!(block.starts_with(" <div>"), "got {:?}", block);
}
#[test]
fn block_with_two_space_indent_then_body_blank_then_indented_code() {
let tokens = parse(" <div>\n\n <div>\n");
let blocks = html_blocks(&tokens);
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0], " <div>\n");
assert!(tokens.iter().any(|t| matches!(t, Token::Code { block: true, .. })));
}
#[test]
fn four_space_indent_opener_is_code_block() {
let tokens = parse(" <div>\nbody\n</div>\n");
assert!(tokens.iter().any(|t| matches!(t, Token::Code { block: true, .. })));
let blocks = html_blocks(&tokens);
assert_eq!(blocks.len(), 1);
assert!(blocks[0].contains("</div>"));
}
#[test]
fn block_inside_blockquote() {
let tokens = parse("> <div>\n> body\n");
let Some(Token::BlockQuote(body)) = tokens.first() else {
panic!("expected BlockQuote, got {:?}", tokens);
};
assert!(body.iter().any(|t| matches!(t, Token::HtmlBlock(_))));
}
#[test]
fn many_back_to_back_blocks_with_blank_separators() {
let tokens = parse("<table>\n\n<tr>\n\n<td>\nHi\n</td>\n\n</tr>\n\n</table>\n");
let blocks = html_blocks(&tokens);
assert_eq!(blocks.len(), 5);
}
#[test]
fn complete_element_on_one_line() {
let input = "<div><a href=\"bar\">*foo*</a></div>\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
assert!(!tokens.iter().any(|t| matches!(t, Token::Emphasis { .. })));
}
#[test]
fn standalone_closer_opens_block() {
let input = "</div>\n*foo*\n";
let tokens = parse(input);
assert_eq!(first_html_block(&tokens).as_deref(), Some(input));
}
#[test]
fn non_whitelist_tag_does_not_open_block_here() {
let tokens = parse("<a href=\"x\">\nbody\n</a>\n");
assert!(first_html_block(&tokens).is_some());
}