use super::*; use ParseOpt as PO; #[test] fn test_parse_simple() { let buf = "**abcd** rest"; let (t, r) = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong).unwrap(); assert_eq!(t, MdTree::Strong("abcd")); assert_eq!(r, b" rest"); // Escaping should fail let buf = r"**abcd\** rest"; let res = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong); assert!(res.is_none()); } #[test] fn test_parse_comment() { let opt = PO::TrimNoEsc; let buf = "rest"; let (t, r) = parse_simple_pat(buf.as_bytes(), CMT_S, CMT_E, opt, MdTree::Comment).unwrap(); assert_eq!(t, MdTree::Comment("foobar!")); assert_eq!(r, b"rest"); let buf = r"rest"; let (t, r) = parse_simple_pat(buf.as_bytes(), CMT_S, CMT_E, opt, MdTree::Comment).unwrap(); assert_eq!(t, MdTree::Comment(r"foobar! \")); assert_eq!(r, b"rest"); } #[test] fn test_parse_heading() { let buf1 = "# Top level\nrest"; let (t, r) = parse_heading(buf1.as_bytes()).unwrap(); assert_eq!(t, MdTree::Heading(1, vec![MdTree::PlainText("Top level")].into())); assert_eq!(r, b"\nrest"); let buf1 = "# Empty"; let (t, r) = parse_heading(buf1.as_bytes()).unwrap(); assert_eq!(t, MdTree::Heading(1, vec![MdTree::PlainText("Empty")].into())); assert_eq!(r, b""); // Combo let buf2 = "### Top `level` _woo_\nrest"; let (t, r) = parse_heading(buf2.as_bytes()).unwrap(); assert_eq!( t, MdTree::Heading( 3, vec![ MdTree::PlainText("Top "), MdTree::CodeInline("level"), MdTree::PlainText(" "), MdTree::Emphasis("woo"), ] .into() ) ); assert_eq!(r, b"\nrest"); } #[test] fn test_parse_code_inline() { let buf1 = "`abcd` rest"; let (t, r) = parse_codeinline(buf1.as_bytes()).unwrap(); assert_eq!(t, MdTree::CodeInline("abcd")); assert_eq!(r, b" rest"); // extra backticks, newline let buf2 = "```ab\ncd``` rest"; let (t, r) = parse_codeinline(buf2.as_bytes()).unwrap(); assert_eq!(t, MdTree::CodeInline("ab\ncd")); assert_eq!(r, b" rest"); // test no escaping let buf3 = r"`abcd\` rest"; let (t, r) = parse_codeinline(buf3.as_bytes()).unwrap(); assert_eq!(t, MdTree::CodeInline(r"abcd\")); assert_eq!(r, b" rest"); } #[test] fn test_parse_code_block() { let buf1 = "```rust\ncode\ncode\n```\nleftovers"; let (t, r) = parse_codeblock(buf1.as_bytes()); assert_eq!(t, MdTree::CodeBlock { txt: "code\ncode", lang: Some("rust") }); assert_eq!(r, b"\nleftovers"); let buf2 = "`````\ncode\ncode````\n`````\nleftovers"; let (t, r) = parse_codeblock(buf2.as_bytes()); assert_eq!(t, MdTree::CodeBlock { txt: "code\ncode````", lang: None }); assert_eq!(r, b"\nleftovers"); } #[test] fn test_parse_link() { let simple = "[see here](docs.rs) other"; let (t, r) = parse_any_link(simple.as_bytes(), false).unwrap(); assert_eq!(t, MdTree::Link { disp: "see here", link: "docs.rs" }); assert_eq!(r, b" other"); let simple_toplevel = "[see here](docs.rs) other"; let (t, r) = parse_any_link(simple_toplevel.as_bytes(), true).unwrap(); assert_eq!(t, MdTree::Link { disp: "see here", link: "docs.rs" }); assert_eq!(r, b" other"); let reference = "[see here] other"; let (t, r) = parse_any_link(reference.as_bytes(), true).unwrap(); assert_eq!(t, MdTree::RefLink { disp: "see here", id: None }); assert_eq!(r, b" other"); let reference_full = "[see here][docs-rs] other"; let (t, r) = parse_any_link(reference_full.as_bytes(), false).unwrap(); assert_eq!(t, MdTree::RefLink { disp: "see here", id: Some("docs-rs") }); assert_eq!(r, b" other"); let reference_def = "[see here]: docs.rs\nother"; let (t, r) = parse_any_link(reference_def.as_bytes(), true).unwrap(); assert_eq!(t, MdTree::LinkDef { id: "see here", link: "docs.rs" }); assert_eq!(r, b"\nother"); } const IND1: &str = r"test standard ind ind2 not ind"; const IND2: &str = r"test end of stream 1 2 "; const IND3: &str = r"test empty lines 1 2 not ind"; #[test] fn test_indented_section() { let (t, r) = get_indented_section(IND1.as_bytes()); assert_eq!(str::from_utf8(t).unwrap(), "test standard\n ind\n ind2"); assert_eq!(str::from_utf8(r).unwrap(), "\nnot ind"); let (txt, rest) = get_indented_section(IND2.as_bytes()); assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n 1\n 2"); assert_eq!(str::from_utf8(rest).unwrap(), "\n"); let (txt, rest) = get_indented_section(IND3.as_bytes()); assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n 1\n 2"); assert_eq!(str::from_utf8(rest).unwrap(), "\n\nnot ind"); } const HBT: &str = r"# Heading content"; #[test] fn test_heading_breaks() { let expected = vec![ MdTree::Heading(1, vec![MdTree::PlainText("Heading")].into()), MdTree::PlainText("content"), ] .into(); let res = entrypoint(HBT); assert_eq!(res, expected); } const NL1: &str = r"start end"; const NL2: &str = r"start end"; const NL3: &str = r"start end"; #[test] fn test_newline_breaks() { let expected = vec![MdTree::PlainText("start"), MdTree::ParagraphBreak, MdTree::PlainText("end")].into(); for (idx, check) in [NL1, NL2, NL3].iter().enumerate() { let res = entrypoint(check); assert_eq!(res, expected, "failed {idx}"); } } const WRAP: &str = "plain _italics italics_"; #[test] fn test_wrap_pattern() { let expected = vec![ MdTree::PlainText("plain "), MdTree::Emphasis("italics"), MdTree::Emphasis(" "), MdTree::Emphasis("italics"), ] .into(); let res = entrypoint(WRAP); assert_eq!(res, expected); } const WRAP_NOTXT: &str = r"_italics_ **bold**"; #[test] fn test_wrap_notxt() { let expected = vec![MdTree::Emphasis("italics"), MdTree::PlainText(" "), MdTree::Strong("bold")].into(); let res = entrypoint(WRAP_NOTXT); assert_eq!(res, expected); } const MIXED_LIST: &str = r"start - _italics item_ - **bold item** second line [link1](foobar1) third line [link2][link-foo] - :crab: extra indent end [link-foo]: foobar2 "; #[test] fn test_list() { let expected = vec![ MdTree::PlainText("start"), MdTree::ParagraphBreak, MdTree::UnorderedListItem(vec![MdTree::Emphasis("italics item")].into()), MdTree::LineBreak, MdTree::UnorderedListItem( vec![ MdTree::Strong("bold item"), MdTree::PlainText(" second line "), MdTree::Link { disp: "link1", link: "foobar1" }, MdTree::PlainText(" third line "), MdTree::Link { disp: "link2", link: "foobar2" }, ] .into(), ), MdTree::LineBreak, MdTree::UnorderedListItem( vec![MdTree::PlainText("🦀"), MdTree::PlainText(" extra indent")].into(), ), MdTree::ParagraphBreak, MdTree::PlainText("end"), ] .into(); let res = entrypoint(MIXED_LIST); assert_eq!(res, expected); } const SMOOSHED: &str = r#" start ### heading 1. ordered item ```rust println!("Hello, world!"); ``` `inline` ``end`` "#; #[test] fn test_without_breaks() { let expected = vec![ MdTree::PlainText("start"), MdTree::ParagraphBreak, MdTree::Heading(3, vec![MdTree::PlainText("heading")].into()), MdTree::OrderedListItem(1, vec![MdTree::PlainText("ordered item")].into()), MdTree::ParagraphBreak, MdTree::CodeBlock { txt: r#"println!("Hello, world!");"#, lang: Some("rust") }, MdTree::ParagraphBreak, MdTree::CodeInline("inline"), MdTree::PlainText(" "), MdTree::CodeInline("end"), ] .into(); let res = entrypoint(SMOOSHED); assert_eq!(res, expected); } const CODE_STARTLINE: &str = r#" start `code` middle `more code` end "#; #[test] fn test_code_at_start() { let expected = vec![ MdTree::PlainText("start"), MdTree::PlainText(" "), MdTree::CodeInline("code"), MdTree::PlainText(" "), MdTree::PlainText("middle"), MdTree::PlainText(" "), MdTree::CodeInline("more code"), MdTree::PlainText(" "), MdTree::PlainText("end"), ] .into(); let res = entrypoint(CODE_STARTLINE); assert_eq!(res, expected); }