Markdown parser fork with extended syntax for personal use.
at main 497 lines 17 kB view raw
1use markdown::{ 2 mdast::{Definition, LinkReference, Node, Paragraph, ReferenceKind, Root, Text}, 3 message, to_html, to_html_with_options, to_mdast, 4 unist::Position, 5 CompileOptions, Constructs, Options, ParseOptions, 6}; 7use pretty_assertions::assert_eq; 8 9#[test] 10fn link_reference() -> Result<(), message::Message> { 11 let danger = Options { 12 compile: CompileOptions { 13 allow_dangerous_html: true, 14 allow_dangerous_protocol: true, 15 ..Default::default() 16 }, 17 ..Default::default() 18 }; 19 20 assert_eq!( 21 to_html("[bar]: /url \"title\"\n\n[foo][bar]"), 22 "<p><a href=\"/url\" title=\"title\">foo</a></p>", 23 "should support link references" 24 ); 25 26 assert_eq!( 27 to_html("[ref]: /uri\n\n[link [foo [bar]]][ref]"), 28 "<p><a href=\"/uri\">link [foo [bar]]</a></p>", 29 "should support balanced brackets in link references" 30 ); 31 32 assert_eq!( 33 to_html("[ref]: /uri\n\n[link \\[bar][ref]"), 34 "<p><a href=\"/uri\">link [bar</a></p>", 35 "should support escaped brackets in link references" 36 ); 37 38 assert_eq!( 39 to_html("[ref]: /uri\n\n[link *foo **bar** `#`*][ref]"), 40 "<p><a href=\"/uri\">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>", 41 "should support content in link references" 42 ); 43 44 assert_eq!( 45 to_html("[ref]: /uri\n\n[![moon](moon.jpg)][ref]"), 46 "<p><a href=\"/uri\"><img src=\"moon.jpg\" alt=\"moon\" /></a></p>", 47 "should support images in link references" 48 ); 49 50 assert_eq!( 51 to_html("[ref]: /uri\n\n[foo [bar](/uri)][ref]"), 52 "<p>[foo <a href=\"/uri\">bar</a>]<a href=\"/uri\">ref</a></p>", 53 "should not support links in link references" 54 ); 55 56 assert_eq!( 57 to_html("[ref]: /uri\n\n[foo *bar [baz][ref]*][ref]"), 58 "<p>[foo <em>bar <a href=\"/uri\">baz</a></em>]<a href=\"/uri\">ref</a></p>", 59 "should not support deep links in link references" 60 ); 61 62 assert_eq!( 63 to_html("[ref]: /uri\n\n*[foo*][ref]"), 64 "<p>*<a href=\"/uri\">foo*</a></p>", 65 "should prefer link references over emphasis (1)" 66 ); 67 68 assert_eq!( 69 to_html("[ref]: /uri\n\n[foo *bar][ref]"), 70 "<p><a href=\"/uri\">foo *bar</a></p>", 71 "should prefer link references over emphasis (2)" 72 ); 73 74 assert_eq!( 75 to_html_with_options("[ref]: /uri\n\n[foo <bar attr=\"][ref]\">", &danger)?, 76 "<p>[foo <bar attr=\"][ref]\"></p>", 77 "should prefer HTML over link references" 78 ); 79 80 assert_eq!( 81 to_html("[ref]: /uri\n\n[foo`][ref]`"), 82 "<p>[foo<code>][ref]</code></p>", 83 "should prefer code over link references" 84 ); 85 86 assert_eq!( 87 to_html("[ref]: /uri\n\n[foo<http://example.com/?search=][ref]>"), 88 "<p>[foo<a href=\"http://example.com/?search=%5D%5Bref%5D\">http://example.com/?search=][ref]</a></p>", 89 "should prefer autolinks over link references" 90 ); 91 92 assert_eq!( 93 to_html("[bar]: /url \"title\"\n\n[foo][BaR]"), 94 "<p><a href=\"/url\" title=\"title\">foo</a></p>", 95 "should match references to definitions case-insensitively" 96 ); 97 98 assert_eq!( 99 to_html("[ТОЛПОЙ]: /url\n\n[Толпой][Толпой] is a Russian word."), 100 "<p><a href=\"/url\">Толпой</a> is a Russian word.</p>", 101 "should match references to definitions w/ unicode case-folding" 102 ); 103 104 assert_eq!( 105 to_html("[Foo\n bar]: /url\n\n[Baz][Foo bar]"), 106 "<p><a href=\"/url\">Baz</a></p>", 107 "should match references to definitions w/ collapsing" 108 ); 109 110 assert_eq!( 111 to_html("[bar]: /url \"title\"\n\n[foo] [bar]"), 112 "<p>[foo] <a href=\"/url\" title=\"title\">bar</a></p>", 113 "should not support whitespace between label and reference (1)" 114 ); 115 116 assert_eq!( 117 to_html("[bar]: /url \"title\"\n\n[foo]\n[bar]"), 118 "<p>[foo]\n<a href=\"/url\" title=\"title\">bar</a></p>", 119 "should not support whitespace between label and reference (2)" 120 ); 121 122 assert_eq!( 123 to_html("[foo]: /url1\n\n[foo]: /url2\n\n[bar][foo]"), 124 "<p><a href=\"/url1\">bar</a></p>", 125 "should prefer earlier definitions" 126 ); 127 128 assert_eq!( 129 to_html("[foo!]: /url\n\n[bar][foo\\!]"), 130 "<p>[bar][foo!]</p>", 131 "should not match references to definitions w/ escapes" 132 ); 133 134 assert_eq!( 135 to_html("[ref[]: /uri\n\n[foo][ref[]"), 136 "<p>[ref[]: /uri</p>\n<p>[foo][ref[]</p>", 137 "should not support references w/ brackets (1)" 138 ); 139 140 assert_eq!( 141 to_html("[ref[bar]]: /uri\n\n[foo][ref[bar]]"), 142 "<p>[ref[bar]]: /uri</p>\n<p>[foo][ref[bar]]</p>", 143 "should not support references w/ brackets (2)" 144 ); 145 146 assert_eq!( 147 to_html("[[[foo]]]: /url\n\n[[[foo]]]"), 148 "<p>[[[foo]]]: /url</p>\n<p>[[[foo]]]</p>", 149 "should not support references w/ brackets (3)" 150 ); 151 152 assert_eq!( 153 to_html("[ref\\[]: /uri\n\n[foo][ref\\[]"), 154 "<p><a href=\"/uri\">foo</a></p>", 155 "should match references to definitions w/ matching escapes" 156 ); 157 158 assert_eq!( 159 to_html("[bar\\\\]: /uri\n\n[bar\\\\]"), 160 "<p><a href=\"/uri\">bar\\</a></p>", 161 "should support escapes" 162 ); 163 164 assert_eq!( 165 to_html("[]: /uri\n\n[]"), 166 "<p>[]: /uri</p>\n<p>[]</p>", 167 "should not support empty references" 168 ); 169 170 assert_eq!( 171 to_html("[\n ]: /uri\n\n[\n ]"), 172 "<p>[\n]: /uri</p>\n<p>[\n]</p>", 173 "should not support blank references" 174 ); 175 176 assert_eq!( 177 to_html("[foo]: /url \"title\"\n\n[foo][]"), 178 "<p><a href=\"/url\" title=\"title\">foo</a></p>", 179 "should support collaped references" 180 ); 181 182 assert_eq!( 183 to_html("[*foo* bar]: /url \"title\"\n\n[*foo* bar][]"), 184 "<p><a href=\"/url\" title=\"title\"><em>foo</em> bar</a></p>", 185 "should support content in collaped references" 186 ); 187 188 assert_eq!( 189 to_html("[foo]: /url \"title\"\n\n[Foo][]"), 190 "<p><a href=\"/url\" title=\"title\">Foo</a></p>", 191 "should match references to definitions case-insensitively" 192 ); 193 194 assert_eq!( 195 to_html("[foo]: /url \"title\"\n\n[foo] \n[]"), 196 "<p><a href=\"/url\" title=\"title\">foo</a>\n[]</p>", 197 "should not support whitespace between label and collaped reference" 198 ); 199 200 assert_eq!( 201 to_html("[foo]: /url \"title\"\n\n[foo]"), 202 "<p><a href=\"/url\" title=\"title\">foo</a></p>", 203 "should support shortcut references" 204 ); 205 206 assert_eq!( 207 to_html("[*foo* bar]: /url \"title\"\n\n[*foo* bar]"), 208 "<p><a href=\"/url\" title=\"title\"><em>foo</em> bar</a></p>", 209 "should support content in shortcut references (1)" 210 ); 211 212 assert_eq!( 213 to_html("[*foo* bar]: /url \"title\"\n\n[[*foo* bar]]"), 214 "<p>[<a href=\"/url\" title=\"title\"><em>foo</em> bar</a>]</p>", 215 "should support content in shortcut references (2)" 216 ); 217 218 assert_eq!( 219 to_html("[foo]: /url\n\n[[bar [foo]"), 220 "<p>[[bar <a href=\"/url\">foo</a></p>", 221 "should support content in shortcut references (3)" 222 ); 223 224 assert_eq!( 225 to_html("[foo]: /url \"title\"\n\n[Foo]"), 226 "<p><a href=\"/url\" title=\"title\">Foo</a></p>", 227 "should match shortcut references to definitions case-insensitively" 228 ); 229 230 assert_eq!( 231 to_html("[foo]: /url\n\n[foo] bar"), 232 "<p><a href=\"/url\">foo</a> bar</p>", 233 "should support whitespace after a shortcut reference" 234 ); 235 236 assert_eq!( 237 to_html("[foo]: /url \"title\"\n\n\\[foo]"), 238 "<p>[foo]</p>", 239 "should “support” an escaped shortcut reference" 240 ); 241 242 assert_eq!( 243 to_html("[foo*]: /url\n\n*[foo*]"), 244 "<p>*<a href=\"/url\">foo*</a></p>", 245 "should prefer shortcut references over emphasis" 246 ); 247 248 assert_eq!( 249 to_html("[foo]: /url1\n[bar]: /url2\n\n[foo][bar]"), 250 "<p><a href=\"/url2\">foo</a></p>", 251 "should prefer full references over shortcut references" 252 ); 253 254 assert_eq!( 255 to_html("[foo]: /url1\n\n[foo][]"), 256 "<p><a href=\"/url1\">foo</a></p>", 257 "should prefer collapsed references over shortcut references" 258 ); 259 260 assert_eq!( 261 to_html("[foo]: /url\n\n[foo]()"), 262 "<p><a href=\"\">foo</a></p>", 263 "should prefer resources over shortcut references (1)" 264 ); 265 266 assert_eq!( 267 to_html("[foo]: /url \"title\"\n\n[foo]()"), 268 "<p><a href=\"\">foo</a></p>", 269 "should prefer resources over shortcut references (2)" 270 ); 271 272 assert_eq!( 273 to_html("[foo]: /url1\n\n[foo](not a link)"), 274 "<p><a href=\"/url1\">foo</a>(not a link)</p>", 275 "should support shortcut references when followed by nonconforming resources" 276 ); 277 278 assert_eq!( 279 to_html("[baz]: /url\n\n[foo][bar][baz]"), 280 "<p>[foo]<a href=\"/url\">bar</a></p>", 281 "stable/unstable (1)" 282 ); 283 284 assert_eq!( 285 to_html("[baz]: /url1\n[bar]: /url2\n\n[foo][bar][baz]"), 286 "<p><a href=\"/url2\">foo</a><a href=\"/url1\">baz</a></p>", 287 "stable/unstable (2)" 288 ); 289 290 assert_eq!( 291 to_html("[baz]: /url1\n[foo]: /url2\n\n[foo][bar][baz]"), 292 "<p>[foo]<a href=\"/url1\">bar</a></p>", 293 "stable/unstable (3)" 294 ); 295 296 // Extra 297 // This matches most implimentations, but is not strictly according to spec. 298 // See: <https://github.com/commonmark/commonmark-spec/issues/653> 299 assert_eq!( 300 to_html("[x]: /url\n\n[x][ ], [x][\t], [x][\n], [x][]"), 301 "<p>[x][ ], [x][\t], [x][\n], <a href=\"/url\">x</a></p>", 302 "should not support whitespace-only full references" 303 ); 304 305 // See also: <https://github.com/commonmark/commonmark-spec/issues/616> 306 assert_eq!( 307 to_html("[+]: example.com\n[\\;]: example.com\n\nWill it link? [\\+], [;]"), 308 "<p>Will it link? [+], [;]</p>", 309 "should not support mismatched character escapes in shortcuts" 310 ); 311 312 assert_eq!( 313 to_html("[©]: example.com\n[&amp;]: example.com\n\nWill it link? [&copy;], [&]"), 314 "<p>Will it link? [©], [&amp;]</p>", 315 "should not support mismatched character references in shortcuts" 316 ); 317 318 assert_eq!( 319 to_html("[+]: example.com\n[\\;]: example.com\n\nWill it link? [\\+][], [;][]"), 320 "<p>Will it link? [+][], [;][]</p>", 321 "should not support mismatched character escapes in collapsed" 322 ); 323 324 assert_eq!( 325 to_html("[©]: example.com\n[&amp;]: example.com\n\nWill it link? [&copy;][], [&][]"), 326 "<p>Will it link? [©][], [&amp;][]</p>", 327 "should not support mismatched character references in collapsed" 328 ); 329 330 assert_eq!( 331 to_html("[+]: example.com\n[\\;]: example.com\n\nWill it link? [a][ \\+ ], [b][ ; ]"), 332 "<p>Will it link? [a][ + ], [b][ ; ]</p>", 333 "should not support mismatched character escapes in fulls" 334 ); 335 336 assert_eq!( 337 to_html("[©]: example.com\n[&amp;]: example.com\n\nWill it link? [a][ &copy; ], [b][ & ]"), 338 "<p>Will it link? [a][ © ], [b][ &amp; ]</p>", 339 "should not support mismatched character references in fulls" 340 ); 341 342 assert_eq!( 343 to_html( 344 "[*f*][] 345[&semi;][] 346[\\;][] 347[;][] 348[*f*&semi;][] 349[*f*\\;][] 350[*f*;][] 351 352[*f*]: alpha 353[&semi;]: bravo 354[\\;]: charlie 355[;]: delta 356[*f*&semi;]: echo 357[*f*\\;]: foxtrot 358[*f*;]: golf" 359 ), 360 "<p><a href=\"alpha\"><em>f</em></a> 361<a href=\"bravo\">;</a> 362<a href=\"charlie\">;</a> 363<a href=\"delta\">;</a> 364<a href=\"echo\"><em>f</em>;</a> 365<a href=\"foxtrot\"><em>f</em>;</a> 366<a href=\"golf\"><em>f</em>;</a></p> 367", 368 "should properly handle labels w/ character references and -escapes, and phrasing" 369 ); 370 371 // 999 `x` characters. 372 let max = "x".repeat(999); 373 374 assert_eq!( 375 to_html(format!("[{}]: a\n[y][{}]", max, max).as_str()), 376 "<p><a href=\"a\">y</a></p>", 377 "should support 999 characters in a reference" 378 ); 379 380 assert_eq!( 381 to_html(format!("[{}x]: a\n[y][{}x]", max, max).as_str()), 382 format!("<p>[{}x]: a\n[y][{}x]</p>", max, max), 383 "should not support 1000 characters in a reference" 384 ); 385 386 assert_eq!( 387 to_html("[x] missing-colon\n\nWill it link? [x]"), 388 "<p>[x] missing-colon</p>\n<p>Will it link? [x]</p>", 389 "should not fail on a missing colon in a definition" 390 ); 391 392 assert_eq!( 393 to_html_with_options( 394 "[x]()", 395 &Options { 396 parse: ParseOptions { 397 constructs: Constructs { 398 label_start_link: false, 399 ..Default::default() 400 }, 401 ..Default::default() 402 }, 403 ..Default::default() 404 } 405 )?, 406 "<p>[x]()</p>", 407 "should support turning off label start (link)" 408 ); 409 410 assert_eq!( 411 to_html_with_options( 412 "[x]()", 413 &Options { 414 parse: ParseOptions { 415 constructs: Constructs { 416 label_end: false, 417 ..Default::default() 418 }, 419 ..Default::default() 420 }, 421 ..Default::default() 422 } 423 )?, 424 "<p>[x]()</p>", 425 "should support turning off label end" 426 ); 427 428 assert_eq!( 429 to_mdast("[x]: y\n\na [x] b [x][] c [d][x] e.", &Default::default())?, 430 Node::Root(Root { 431 children: vec![ 432 Node::Definition(Definition { 433 identifier: "x".into(), 434 label: Some("x".into()), 435 url: "y".into(), 436 title: None, 437 position: Some(Position::new(1, 1, 0, 1, 7, 6)) 438 }), 439 Node::Paragraph(Paragraph { 440 children: vec![ 441 Node::Text(Text { 442 value: "a ".into(), 443 position: Some(Position::new(3, 1, 8, 3, 3, 10)) 444 }), 445 Node::LinkReference(LinkReference { 446 reference_kind: ReferenceKind::Shortcut, 447 identifier: "x".into(), 448 label: Some("x".into()), 449 children: vec![Node::Text(Text { 450 value: "x".into(), 451 position: Some(Position::new(3, 4, 11, 3, 5, 12)) 452 }),], 453 position: Some(Position::new(3, 3, 10, 3, 6, 13)) 454 }), 455 Node::Text(Text { 456 value: " b ".into(), 457 position: Some(Position::new(3, 6, 13, 3, 9, 16)) 458 }), 459 Node::LinkReference(LinkReference { 460 reference_kind: ReferenceKind::Collapsed, 461 identifier: "x".into(), 462 label: Some("x".into()), 463 children: vec![Node::Text(Text { 464 value: "x".into(), 465 position: Some(Position::new(3, 10, 17, 3, 11, 18)) 466 }),], 467 position: Some(Position::new(3, 9, 16, 3, 14, 21)) 468 }), 469 Node::Text(Text { 470 value: " c ".into(), 471 position: Some(Position::new(3, 14, 21, 3, 17, 24)) 472 }), 473 Node::LinkReference(LinkReference { 474 reference_kind: ReferenceKind::Full, 475 identifier: "x".into(), 476 label: Some("x".into()), 477 children: vec![Node::Text(Text { 478 value: "d".into(), 479 position: Some(Position::new(3, 18, 25, 3, 19, 26)) 480 }),], 481 position: Some(Position::new(3, 17, 24, 3, 23, 30)) 482 }), 483 Node::Text(Text { 484 value: " e.".into(), 485 position: Some(Position::new(3, 23, 30, 3, 26, 33)) 486 }), 487 ], 488 position: Some(Position::new(3, 1, 8, 3, 26, 33)) 489 }), 490 ], 491 position: Some(Position::new(1, 1, 0, 3, 26, 33)) 492 }), 493 "should support link (reference) as `LinkReference`s in mdast" 494 ); 495 496 Ok(()) 497}