Markdown parser fork with extended syntax for personal use.
1use markdown::{
2 mdast::{Image, Link, Node, Paragraph, Root, Text},
3 message, to_html, to_html_with_options, to_mdast,
4 unist::Position,
5 CompileOptions, Options,
6};
7use pretty_assertions::assert_eq;
8
9#[test]
10fn link_resource() -> 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("[link](/uri \"title\")"),
22 "<p><a href=\"/uri\" title=\"title\">link</a></p>",
23 "should support links"
24 );
25
26 assert_eq!(
27 to_html("[link](/uri)"),
28 "<p><a href=\"/uri\">link</a></p>",
29 "should support links w/o title"
30 );
31
32 assert_eq!(
33 to_html("[link]()"),
34 "<p><a href=\"\">link</a></p>",
35 "should support links w/o destination"
36 );
37
38 assert_eq!(
39 to_html("[link](<>)"),
40 "<p><a href=\"\">link</a></p>",
41 "should support links w/ empty enclosed destination"
42 );
43
44 assert_eq!(
45 to_html("[link](/my uri)"),
46 "<p>[link](/my uri)</p>",
47 "should not support links w/ spaces in destination"
48 );
49
50 assert_eq!(
51 to_html("[link](</my uri>)"),
52 "<p><a href=\"/my%20uri\">link</a></p>",
53 "should support links w/ spaces in enclosed destination"
54 );
55
56 assert_eq!(
57 to_html("[link](foo\nbar)"),
58 "<p>[link](foo\nbar)</p>",
59 "should not support links w/ line endings in destination"
60 );
61
62 assert_eq!(
63 to_html_with_options("[link](<foo\nbar>)", &danger)?,
64 "<p>[link](<foo\nbar>)</p>",
65 "should not support links w/ line endings in enclosed destination"
66 );
67
68 assert_eq!(
69 to_html("[a](<b)c>)"),
70 "<p><a href=\"b)c\">a</a></p>",
71 "should support links w/ closing parens in destination"
72 );
73
74 assert_eq!(
75 to_html("[link](<foo\\>)"),
76 "<p>[link](<foo>)</p>",
77 "should not support links w/ enclosed destinations w/o end"
78 );
79
80 assert_eq!(
81 to_html_with_options("[a](<b)c\n[a](<b)c>\n[a](<b>c)", &danger)?,
82 "<p>[a](<b)c\n[a](<b)c>\n[a](<b>c)</p>",
83 "should not support links w/ unmatched enclosed destinations"
84 );
85
86 assert_eq!(
87 to_html("[link](\\(foo\\))"),
88 "<p><a href=\"(foo)\">link</a></p>",
89 "should support links w/ destinations w/ escaped parens"
90 );
91
92 assert_eq!(
93 to_html("[link](foo(and(bar)))"),
94 "<p><a href=\"foo(and(bar))\">link</a></p>",
95 "should support links w/ destinations w/ balanced parens"
96 );
97
98 assert_eq!(
99 to_html("[link](foo\\(and\\(bar\\))"),
100 "<p><a href=\"foo(and(bar)\">link</a></p>",
101 "should support links w/ destinations w/ escaped parens"
102 );
103
104 assert_eq!(
105 to_html("[link](<foo(and(bar)>)"),
106 "<p><a href=\"foo(and(bar)\">link</a></p>",
107 "should support links w/ enclosed destinations w/ parens"
108 );
109
110 assert_eq!(
111 to_html_with_options("[link](foo\\)\\:)", &danger)?,
112 "<p><a href=\"foo):\">link</a></p>",
113 "should support links w/ escapes in destinations"
114 );
115
116 assert_eq!(
117 to_html("[link](#fragment)"),
118 "<p><a href=\"#fragment\">link</a></p>",
119 "should support links w/ destinations to fragments"
120 );
121
122 assert_eq!(
123 to_html("[link](http://example.com#fragment)"),
124 "<p><a href=\"http://example.com#fragment\">link</a></p>",
125 "should support links w/ destinations to URLs w/ fragments"
126 );
127
128 assert_eq!(
129 to_html("[link](http://example.com?foo=3#frag)"),
130 "<p><a href=\"http://example.com?foo=3#frag\">link</a></p>",
131 "should support links w/ destinations to URLs w/ search and fragments"
132 );
133
134 assert_eq!(
135 to_html("[link](foo\\bar)"),
136 "<p><a href=\"foo%5Cbar\">link</a></p>",
137 "should not support non-punctuation character escapes in links"
138 );
139
140 assert_eq!(
141 to_html("[link](foo%20bä)"),
142 "<p><a href=\"foo%20b%C3%A4\">link</a></p>",
143 "should support character references in links"
144 );
145
146 assert_eq!(
147 to_html("[link](\"title\")"),
148 "<p><a href=\"%22title%22\">link</a></p>",
149 "should not support links w/ only a title"
150 );
151
152 assert_eq!(
153 to_html("[link](/url \"title\")"),
154 "<p><a href=\"/url\" title=\"title\">link</a></p>",
155 "should support titles w/ double quotes"
156 );
157
158 assert_eq!(
159 to_html("[link](/url 'title')"),
160 "<p><a href=\"/url\" title=\"title\">link</a></p>",
161 "should support titles w/ single quotes"
162 );
163
164 assert_eq!(
165 to_html("[link](/url (title))"),
166 "<p><a href=\"/url\" title=\"title\">link</a></p>",
167 "should support titles w/ parens"
168 );
169
170 assert_eq!(
171 to_html("[link](/url \"title \\\""\")"),
172 "<p><a href=\"/url\" title=\"title ""\">link</a></p>",
173 "should support character references and escapes in titles"
174 );
175
176 assert_eq!(
177 to_html("[link](/url \"title\")"),
178 "<p><a href=\"/url%C2%A0%22title%22\">link</a></p>",
179 "should not support unicode whitespace between destination and title"
180 );
181
182 assert_eq!(
183 to_html("[link](/url \"title \"and\" title\")"),
184 "<p>[link](/url "title "and" title")</p>",
185 "should not support nested balanced quotes in title"
186 );
187
188 assert_eq!(
189 to_html("[link](/url 'title \"and\" title')"),
190 "<p><a href=\"/url\" title=\"title "and" title\">link</a></p>",
191 "should support the other quotes in titles"
192 );
193
194 assert_eq!(
195 to_html("[link]( /uri\n \"title\" )"),
196 "<p><a href=\"/uri\" title=\"title\">link</a></p>",
197 "should support whitespace around destination and title (1)"
198 );
199
200 assert_eq!(
201 to_html("[link](\t\n/uri \"title\")"),
202 "<p><a href=\"/uri\" title=\"title\">link</a></p>",
203 "should support whitespace around destination and title (2)"
204 );
205
206 assert_eq!(
207 to_html("[link](/uri \"title\"\t\n)"),
208 "<p><a href=\"/uri\" title=\"title\">link</a></p>",
209 "should support whitespace around destination and title (3)"
210 );
211
212 assert_eq!(
213 to_html("[link] (/uri)"),
214 "<p>[link] (/uri)</p>",
215 "should not support whitespace between label and resource"
216 );
217
218 assert_eq!(
219 to_html("[link [foo [bar]]](/uri)"),
220 "<p><a href=\"/uri\">link [foo [bar]]</a></p>",
221 "should support balanced brackets"
222 );
223
224 assert_eq!(
225 to_html("[link] bar](/uri)"),
226 "<p>[link] bar](/uri)</p>",
227 "should not support unbalanced brackets (1)"
228 );
229
230 assert_eq!(
231 to_html("[link [bar](/uri)"),
232 "<p>[link <a href=\"/uri\">bar</a></p>",
233 "should not support unbalanced brackets (2)"
234 );
235
236 assert_eq!(
237 to_html("[link \\[bar](/uri)"),
238 "<p><a href=\"/uri\">link [bar</a></p>",
239 "should support characer escapes"
240 );
241
242 assert_eq!(
243 to_html("[link *foo **bar** `#`*](/uri)"),
244 "<p><a href=\"/uri\">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>",
245 "should support content"
246 );
247
248 assert_eq!(
249 to_html("[](/uri)"),
250 "<p><a href=\"/uri\"><img src=\"moon.jpg\" alt=\"moon\" /></a></p>",
251 "should support an image as content"
252 );
253
254 assert_eq!(
255 to_html("[foo [bar](/uri)](/uri)"),
256 "<p>[foo <a href=\"/uri\">bar</a>](/uri)</p>",
257 "should not support links in links (1)"
258 );
259
260 assert_eq!(
261 to_html("[foo *[bar [baz](/uri)](/uri)*](/uri)"),
262 "<p>[foo <em>[bar <a href=\"/uri\">baz</a>](/uri)</em>](/uri)</p>",
263 "should not support links in links (2)"
264 );
265
266 assert_eq!(
267 to_html("](uri2)](uri3)"),
268 "<p><img src=\"uri3\" alt=\"[foo](uri2)\" /></p>",
269 "should not support links in links (3)"
270 );
271
272 assert_eq!(
273 to_html("*[foo*](/uri)"),
274 "<p>*<a href=\"/uri\">foo*</a></p>",
275 "should prefer links over emphasis (1)"
276 );
277
278 assert_eq!(
279 to_html("[foo *bar](baz*)"),
280 "<p><a href=\"baz*\">foo *bar</a></p>",
281 "should prefer links over emphasis (2)"
282 );
283
284 assert_eq!(
285 to_html_with_options("[foo <bar attr=\"](baz)\">", &danger)?,
286 "<p>[foo <bar attr=\"](baz)\"></p>",
287 "should prefer HTML over links"
288 );
289
290 assert_eq!(
291 to_html("[foo`](/uri)`"),
292 "<p>[foo<code>](/uri)</code></p>",
293 "should prefer code over links"
294 );
295
296 assert_eq!(
297 to_html("[foo<http://example.com/?search=](uri)>"),
298 "<p>[foo<a href=\"http://example.com/?search=%5D(uri)\">http://example.com/?search=](uri)</a></p>",
299 "should prefer autolinks over links"
300 );
301
302 assert_eq!(
303 to_html("[foo<http://example.com/?search=](uri)>"),
304 "<p>[foo<a href=\"http://example.com/?search=%5D(uri)\">http://example.com/?search=](uri)</a></p>",
305 "should prefer autolinks over links"
306 );
307
308 // Extra
309 assert_eq!(
310 to_html("[]()"),
311 "<p><a href=\"\"></a></p>",
312 "should support an empty link"
313 );
314
315 // See: <https://github.com/commonmark/commonmark.js/issues/192>
316 assert_eq!(
317 to_html("[](<> \"\")"),
318 "<p><a href=\"\"></a></p>",
319 "should ignore an empty title"
320 );
321
322 assert_eq!(
323 to_html_with_options("[a](<b>\"c\")", &danger)?,
324 "<p>[a](<b>"c")</p>",
325 "should require whitespace between enclosed destination and title"
326 );
327
328 assert_eq!(
329 to_html("[](<"),
330 "<p>[](<</p>",
331 "should not support an unclosed enclosed destination"
332 );
333
334 assert_eq!(
335 to_html("[]("),
336 "<p>[](</p>",
337 "should not support an unclosed destination"
338 );
339
340 assert_eq!(
341 to_html("[](\\<)"),
342 "<p><a href=\"%3C\"></a></p>",
343 "should support unenclosed link destination starting w/ escapes"
344 );
345
346 assert_eq!(
347 to_html("[](<\\<>)"),
348 "<p><a href=\"%3C\"></a></p>",
349 "should support enclosed link destination starting w/ escapes"
350 );
351
352 assert_eq!(
353 to_html("[](\\"),
354 "<p>[](\\</p>",
355 "should not support unenclosed link destination starting w/ an incorrect escape"
356 );
357
358 assert_eq!(
359 to_html("[](<\\"),
360 "<p>[](<\\</p>",
361 "should not support enclosed link destination starting w/ an incorrect escape"
362 );
363
364 assert_eq!(
365 to_html("[](a \""),
366 "<p>[](a "</p>",
367 "should not support an eof in a link title (1)"
368 );
369
370 assert_eq!(
371 to_html("[](a '"),
372 "<p>[](a '</p>",
373 "should not support an eof in a link title (2)"
374 );
375
376 assert_eq!(
377 to_html("[](a ("),
378 "<p>[](a (</p>",
379 "should not support an eof in a link title (3)"
380 );
381
382 assert_eq!(
383 to_html("[](a \"\\"),
384 "<p>[](a "\\</p>",
385 "should not support an eof in a link title escape (1)"
386 );
387
388 assert_eq!(
389 to_html("[](a '\\"),
390 "<p>[](a '\\</p>",
391 "should not support an eof in a link title escape (2)"
392 );
393
394 assert_eq!(
395 to_html("[](a (\\"),
396 "<p>[](a (\\</p>",
397 "should not support an eof in a link title escape (3)"
398 );
399
400 assert_eq!(
401 to_html("[](a \"\\\"\")"),
402 "<p><a href=\"a\" title=\""\"></a></p>",
403 "should support a character escape to start a link title (1)"
404 );
405
406 assert_eq!(
407 to_html("[](a '\\'')"),
408 "<p><a href=\"a\" title=\"\'\"></a></p>",
409 "should support a character escape to start a link title (2)"
410 );
411
412 assert_eq!(
413 to_html("[](a (\\)))"),
414 "<p><a href=\"a\" title=\")\"></a></p>",
415 "should support a character escape to start a link title (3)"
416 );
417
418 assert_eq!(
419 to_html("[&©&](example.com/&©& \"&©&\")"),
420 "<p><a href=\"example.com/&%C2%A9&\" title=\"&©&\">&©&</a></p>",
421 "should support character references in links"
422 );
423
424 assert_eq!(
425 to_html("[a](1())"),
426 "<p><a href=\"1()\">a</a></p>",
427 "should support 1 set of parens"
428 );
429
430 assert_eq!(
431 to_html("[a](1(2()))"),
432 "<p><a href=\"1(2())\">a</a></p>",
433 "should support 2 sets of parens"
434 );
435
436 assert_eq!(
437 to_html(
438 "[a](1(2(3(4(5(6(7(8(9(10(11(12(13(14(15(16(17(18(19(20(21(22(23(24(25(26(27(28(29(30(31(32()))))))))))))))))))))))))))))))))"),
439 "<p><a href=\"1(2(3(4(5(6(7(8(9(10(11(12(13(14(15(16(17(18(19(20(21(22(23(24(25(26(27(28(29(30(31(32())))))))))))))))))))))))))))))))\">a</a></p>",
440 "should support 32 sets of parens"
441 );
442
443 assert_eq!(
444 to_html(
445 "[a](1(2(3(4(5(6(7(8(9(10(11(12(13(14(15(16(17(18(19(20(21(22(23(24(25(26(27(28(29(30(31(32(33())))))))))))))))))))))))))))))))))"),
446 "<p>[a](1(2(3(4(5(6(7(8(9(10(11(12(13(14(15(16(17(18(19(20(21(22(23(24(25(26(27(28(29(30(31(32(33())))))))))))))))))))))))))))))))))</p>",
447 "should not support 33 or more sets of parens"
448 );
449
450 assert_eq!(
451 to_html("[a](b \"\n c\")"),
452 "<p><a href=\"b\" title=\"\nc\">a</a></p>",
453 "should support an eol at the start of a title"
454 );
455
456 assert_eq!(
457 to_html("[a](b( \"c\")"),
458 "<p>[a](b( "c")</p>",
459 "should not support whitespace when unbalanced in a raw destination"
460 );
461
462 assert_eq!(
463 to_html("[a](\0)"),
464 "<p><a href=\"%EF%BF%BD\">a</a></p>",
465 "should support a single NUL character as a link resource"
466 );
467
468 assert_eq!(
469 to_mdast(
470 "a [alpha]() b [bravo](charlie 'delta') c.",
471 &Default::default()
472 )?,
473 Node::Root(Root {
474 children: vec![Node::Paragraph(Paragraph {
475 children: vec![
476 Node::Text(Text {
477 value: "a ".into(),
478 position: Some(Position::new(1, 1, 0, 1, 3, 2))
479 }),
480 Node::Link(Link {
481 url: String::new(),
482 title: None,
483 children: vec![Node::Text(Text {
484 value: "alpha".into(),
485 position: Some(Position::new(1, 4, 3, 1, 9, 8))
486 }),],
487 position: Some(Position::new(1, 3, 2, 1, 12, 11))
488 }),
489 Node::Text(Text {
490 value: " b ".into(),
491 position: Some(Position::new(1, 12, 11, 1, 15, 14))
492 }),
493 Node::Link(Link {
494 url: "charlie".into(),
495 title: Some("delta".into()),
496 children: vec![Node::Text(Text {
497 value: "bravo".into(),
498 position: Some(Position::new(1, 16, 15, 1, 21, 20))
499 }),],
500 position: Some(Position::new(1, 15, 14, 1, 39, 38))
501 }),
502 Node::Text(Text {
503 value: " c.".into(),
504 position: Some(Position::new(1, 39, 38, 1, 42, 41))
505 })
506 ],
507 position: Some(Position::new(1, 1, 0, 1, 42, 41))
508 })],
509 position: Some(Position::new(1, 1, 0, 1, 42, 41))
510 }),
511 "should support link (resource) as `Link`s in mdast"
512 );
513
514 assert_eq!(
515 to_mdast("[](url)", &Default::default())?,
516 Node::Root(Root {
517 children: vec![Node::Paragraph(Paragraph {
518 children: vec![Node::Link(Link {
519 children: vec![Node::Image(Image {
520 alt: "name".into(),
521 url: "image".into(),
522 title: None,
523 position: Some(Position::new(1, 2, 1, 1, 16, 15)),
524 }),],
525 url: "url".into(),
526 title: None,
527 position: Some(Position::new(1, 1, 0, 1, 22, 21)),
528 }),],
529 position: Some(Position::new(1, 1, 0, 1, 22, 21)),
530 }),],
531 position: Some(Position::new(1, 1, 0, 1, 22, 21))
532 }),
533 "should support nested links in mdast"
534 );
535
536 Ok(())
537}