use rumdl_lib::lint_context::LintContext;
use rumdl_lib::rule::Rule;
use rumdl_lib::rules::MD027MultipleSpacesBlockquote;
#[test]
fn test_md027_valid() {
let rule = MD027MultipleSpacesBlockquote;
let content = "> Quote\n> Another line\n> Third line\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_md027_invalid() {
let rule = MD027MultipleSpacesBlockquote;
let content = "> Quote\n> Another line\n> Third line\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result[0].line, 1);
assert_eq!(result[1].line, 2);
assert_eq!(result[2].line, 3);
}
#[test]
fn test_md027_mixed() {
let rule = MD027MultipleSpacesBlockquote;
let content = "> Quote\n> Another line\n> Third line\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result[0].line, 2);
}
#[test]
fn test_md027_fix() {
let rule = MD027MultipleSpacesBlockquote;
let content = "> Quote\n> Another line\n> Third line\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.fix(&ctx).unwrap();
assert_eq!(result, "> Quote\n> Another line\n> Third line\n");
}
#[test]
fn test_md027_html_block_no_blank_after_closing_div() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<div>\ncontent\n</div>\n> After div no blank\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(
result.is_empty(),
"Content after closing tag (no blank line) should be in HTML block, got {} warnings",
result.len()
);
}
#[test]
fn test_md027_html_block_with_blank_after_closing_div() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<div>\ncontent\n</div>\n\n> After div with blank\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 1, "Blockquote after blank line should be flagged");
assert_eq!(result[0].line, 5);
}
#[test]
fn test_md027_table_with_gt_symbols() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<table>\n<tr>\n<td>> Cell content</td>\n</tr>\n</table>\n> After table\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(
result.is_empty(),
"Content in table and after (no blank) should not be flagged, got {} warnings",
result.len()
);
}
#[test]
fn test_md027_nested_html_blocks() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<div>\n<table>\n> In nested HTML\n</table>\n> Still in div\n</div>\n> After div\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(
result.is_empty(),
"All lines should be in HTML block, got {} warnings",
result.len()
);
}
#[test]
fn test_md027_multiple_html_blocks() {
let rule = MD027MultipleSpacesBlockquote;
let content = r#"<div>
content
</div>
> No blank after div1
<article>
more
</article>
> Blank after article - should flag
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 1, "Only blockquote after blank line should flag");
assert_eq!(result[0].line, 10);
}
#[test]
fn test_md027_html5_media_elements() {
let rule = MD027MultipleSpacesBlockquote;
let content = r#"<figure>
> Figure caption
</figure>
> After figure
<video>
> Video description
</video>
> After video
"#;
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(
result.is_empty(),
"Content in HTML5 media elements should not flag, got {} warnings",
result.len()
);
}
#[test]
fn test_md027_self_closing_tags() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<br/>\n> After self-closing\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert_eq!(result.len(), 1, "Content after self-closing tag should flag");
}
#[test]
fn test_md027_style_tag_allows_blanks() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<style>\n.class {\n color: red;\n}\n\n.other {\n color: blue;\n}\n</style>\n> After style\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(result.is_empty(), "Content after style tag should not flag");
}
#[test]
fn test_md027_fix_idempotent() {
let rule = MD027MultipleSpacesBlockquote;
let cases = vec![
"> Two spaces\n> Three spaces\n",
"> Two spaces\n> Normal\n> Four spaces\n",
" > Indented with multiple spaces\n",
"> - Item\n> continuation\n",
"> \n",
"> Normal content\n",
];
for content in cases {
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_once = rule.fix(&ctx).unwrap();
let ctx2 = LintContext::new(&fixed_once, rumdl_lib::config::MarkdownFlavor::Standard, None);
let fixed_twice = rule.fix(&ctx2).unwrap();
assert_eq!(fixed_once, fixed_twice, "fix() not idempotent for input: {content:?}");
}
}
#[test]
fn test_md027_all_warnings_have_fixes() {
let rule = MD027MultipleSpacesBlockquote;
let cases = vec![
"> Two spaces\n",
"> Three spaces\n",
"> Four spaces\n",
" > Indented\n",
"> \n",
];
for content in cases {
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let warnings = rule.check(&ctx).unwrap();
for w in &warnings {
let line = w.line;
let column = w.column;
assert!(
w.fix.is_some(),
"Warning at line {line} col {column} has no fix for input: {content:?}"
);
}
}
}
#[test]
fn test_md027_check_fix_roundtrip() {
use rumdl_lib::utils::fix_utils::apply_warning_fixes;
let rule = MD027MultipleSpacesBlockquote;
let cases = vec![
"> Two spaces\n> Three spaces\n",
"> Two spaces\n> Normal\n> Four spaces\n",
" > Indented with multiple spaces\n",
"> \n",
"> Normal content\n",
"> Two spaces",
];
for content in cases {
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let warnings = rule.check(&ctx).unwrap();
let fix_result = rule.fix(&ctx).unwrap();
let check_then_fix = apply_warning_fixes(content, &warnings).unwrap();
assert_eq!(
fix_result, check_then_fix,
"fix() and apply_warning_fixes(check()) differ for input: {content:?}"
);
}
}
#[test]
fn test_md027_script_tag_allows_blanks() {
let rule = MD027MultipleSpacesBlockquote;
let content = "<script>\nfunction test() {\n return true;\n}\n\nconsole.log();\n</script>\n> After script\n";
let ctx = LintContext::new(content, rumdl_lib::config::MarkdownFlavor::Standard, None);
let result = rule.check(&ctx).unwrap();
assert!(result.is_empty(), "Content after script tag should not flag");
}