mdv 3.0.0

Terminal Markdown Viewer
Documentation
use super::*;

#[test]
fn attached_footnotes_follow_paragraphs() {
    let temp_file = NamedTempFile::new().unwrap();
    fs::write(
        &temp_file,
        "First paragraph with note[^first].\n\nSecond paragraph with note[^second].\n\n[^first]: One\n[^second]: Two\n",
    )
    .unwrap();

    let mut cmd = mdv_cmd();
    cmd.arg("--no-colors")
        .arg("--cols")
        .arg("80")
        .arg("--footnote-style")
        .arg("attached")
        .arg(temp_file.path());

    let output = cmd.output().expect("run mdv with attached footnotes");
    assert!(output.status.success());
    let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");

    let separator_count = stdout
        .lines()
        .filter(|line| line.trim_start().starts_with(''))
        .count();
    assert_eq!(
        separator_count, 4,
        "each paragraph footnote block should have opening and closing separators: {}",
        stdout
    );
    let first_pos = stdout
        .find("First paragraph with note[^first].")
        .expect("first paragraph present");
    let separator_after_first = stdout[first_pos..]
        .find('')
        .map(|offset| offset + first_pos)
        .expect("separator after first paragraph");
    let second_pos = stdout
        .find("Second paragraph with note[^second].")
        .expect("second paragraph present");
    assert!(
        separator_after_first < second_pos,
        "first separator should appear before second paragraph: {}",
        stdout
    );
    assert!(
        !stdout.contains("[^first]: One"),
        "raw definitions should be stripped"
    );
}

#[test]
fn footnotes_have_single_blank_line_after_block_elements() {
    let temp_file = NamedTempFile::new().unwrap();
    fs::write(
        &temp_file,
        "Intro\n\n```\ncode\n```\n\ntext with note[^a]\n\n[^a]: One\n",
    )
    .unwrap();

    let mut cmd = mdv_cmd();
    cmd.arg("--no-colors")
        .arg("--cols")
        .arg("80")
        .arg(temp_file.path());

    let output = cmd.output().expect("run mdv with block before footnotes");
    assert!(output.status.success());
    let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");

    let lines: Vec<&str> = stdout.lines().collect();
    let separator_idx = lines
        .iter()
        .position(|line| line.trim_start().starts_with(''))
        .expect("separator line present");
    let prev_content_idx = (0..separator_idx)
        .rfind(|idx| !lines[*idx].trim().is_empty())
        .expect("content before separator");
    let blank_lines = separator_idx.saturating_sub(prev_content_idx + 1);
    assert_eq!(
        blank_lines, 1,
        "expected exactly one blank line before footnotes, got {}: {}",
        blank_lines, stdout
    );
}

#[test]
fn attached_footnotes_render_inside_list_items() {
    let temp_file = NamedTempFile::new().unwrap();
    fs::write(&temp_file, "- item with note[^a]\n\n[^a]: one\n").unwrap();

    let mut cmd = mdv_cmd();
    cmd.arg("--no-colors")
        .arg("--cols")
        .arg("80")
        .arg("--footnote-style")
        .arg("attached")
        .arg(temp_file.path());

    let output = cmd
        .output()
        .expect("run mdv with attached footnotes inside list");
    assert!(output.status.success());
    let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");

    let separator_lines: Vec<_> = stdout
        .lines()
        .filter(|line| line.trim_start().starts_with(''))
        .collect();
    assert_eq!(
        separator_lines.len(),
        2,
        "expected opening and closing separators: {}",
        stdout
    );
    assert!(
        stdout.contains("[^a] one"),
        "footnote content should render inline: {}",
        stdout
    );
}

#[test]
fn attached_footnotes_render_after_tables() {
    let temp_file = NamedTempFile::new().unwrap();
    fs::write(
        &temp_file,
        "| A | B |\n| - | - |\n| foo[^a] | bar |\n\n[^a]: alpha\n",
    )
    .unwrap();

    let mut cmd = mdv_cmd();
    cmd.arg("--no-colors")
        .arg("--cols")
        .arg("40")
        .arg("--footnote-style")
        .arg("attached")
        .arg(temp_file.path());

    let output = cmd.output().expect("run mdv with table footnotes");
    assert!(output.status.success());
    let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");

    let separator_lines: Vec<_> = stdout
        .lines()
        .filter(|line| line.trim_start().starts_with(''))
        .collect();
    assert!(
        !separator_lines.is_empty(),
        "attached table footnotes should render with separators: {}",
        stdout
    );
    assert!(
        stdout.contains("[^a] alpha"),
        "footnote body should render after table: {}",
        stdout
    );
}

#[test]
fn footnotes_leave_single_blank_line_after_block() {
    let temp_file = NamedTempFile::new().unwrap();
    fs::write(&temp_file, "Line with note[^a]\n\n[^a]: A\n\nNext block\n").unwrap();

    let mut cmd = mdv_cmd();
    cmd.arg("--no-colors")
        .arg("--cols")
        .arg("40")
        .arg("--footnote-style")
        .arg("attached")
        .arg(temp_file.path());

    let output = cmd.output().expect("run mdv with spacing check");
    assert!(output.status.success());
    let stdout = String::from_utf8(output.stdout).expect("stdout is utf-8");

    let lines: Vec<&str> = stdout.lines().collect();
    let separator_idx = lines
        .iter()
        .rposition(|line| line.trim_start().starts_with(''))
        .expect("separator present");
    let next_content_idx = lines
        .iter()
        .skip(separator_idx + 1)
        .position(|line| !line.trim().is_empty())
        .map(|offset| separator_idx + 1 + offset)
        .expect("next content present");

    let blank_lines = next_content_idx.saturating_sub(separator_idx + 1);
    assert_eq!(
        blank_lines, 1,
        "expected exactly one blank line after footnotes: {}",
        stdout
    );
}