1// 2// Blackfriday Markdown Processor 3// Available at http://github.com/russross/blackfriday 4// 5// Copyright © 2011 Russ Ross <russ@russross.com>. 6// Distributed under the Simplified BSD License. 7// See README.md for details. 8// 9 10// 11// Unit tests for inline parsing 12// 13 14package blackfriday 15 16import ( 17 "regexp" 18 "testing" 19 20 "strings" 21) 22 23func TestEmphasis(t *testing.T) { 24 t.Parallel() 25 var tests = []string{ 26 "nothing inline\n", 27 "<p>nothing inline</p>\n", 28 29 "simple *inline* test\n", 30 "<p>simple <em>inline</em> test</p>\n", 31 32 "*at the* beginning\n", 33 "<p><em>at the</em> beginning</p>\n", 34 35 "at the *end*\n", 36 "<p>at the <em>end</em></p>\n", 37 38 "*try two* in *one line*\n", 39 "<p><em>try two</em> in <em>one line</em></p>\n", 40 41 "over *two\nlines* test\n", 42 "<p>over <em>two\nlines</em> test</p>\n", 43 44 "odd *number of* markers* here\n", 45 "<p>odd <em>number of</em> markers* here</p>\n", 46 47 "odd *number\nof* markers* here\n", 48 "<p>odd <em>number\nof</em> markers* here</p>\n", 49 50 "simple _inline_ test\n", 51 "<p>simple <em>inline</em> test</p>\n", 52 53 "_at the_ beginning\n", 54 "<p><em>at the</em> beginning</p>\n", 55 56 "at the _end_\n", 57 "<p>at the <em>end</em></p>\n", 58 59 "_try two_ in _one line_\n", 60 "<p><em>try two</em> in <em>one line</em></p>\n", 61 62 "over _two\nlines_ test\n", 63 "<p>over <em>two\nlines</em> test</p>\n", 64 65 "odd _number of_ markers_ here\n", 66 "<p>odd <em>number of</em> markers_ here</p>\n", 67 68 "odd _number\nof_ markers_ here\n", 69 "<p>odd <em>number\nof</em> markers_ here</p>\n", 70 71 "mix of *markers_\n", 72 "<p>mix of *markers_</p>\n", 73 74 "*What is A\\* algorithm?*\n", 75 "<p><em>What is A* algorithm?</em></p>\n", 76 } 77 doTestsInline(t, tests) 78} 79 80func TestReferenceOverride(t *testing.T) { 81 t.Parallel() 82 var tests = []string{ 83 "test [ref1][]\n", 84 "<p>test <a href=\"http://www.ref1.com/\" title=\"Reference 1\">ref1</a></p>\n", 85 86 "test [my ref][ref1]\n", 87 "<p>test <a href=\"http://www.ref1.com/\" title=\"Reference 1\">my ref</a></p>\n", 88 89 "test [ref2][]\n\n[ref2]: http://www.leftalone.com/ (Ref left alone)\n", 90 "<p>test <a href=\"http://www.overridden.com/\" title=\"Reference Overridden\">ref2</a></p>\n", 91 92 "test [ref3][]\n\n[ref3]: http://www.leftalone.com/ (Ref left alone)\n", 93 "<p>test <a href=\"http://www.leftalone.com/\" title=\"Ref left alone\">ref3</a></p>\n", 94 95 "test [ref4][]\n\n[ref4]: http://zombo.com/ (You can do anything)\n", 96 "<p>test [ref4][]</p>\n", 97 98 "test [!(*http.ServeMux).ServeHTTP][] complicated ref\n", 99 "<p>test <a href=\"http://localhost:6060/pkg/net/http/#ServeMux.ServeHTTP\" title=\"ServeHTTP docs\">!(*http.ServeMux).ServeHTTP</a> complicated ref</p>\n", 100 101 "test [ref5][]\n", 102 "<p>test <a href=\"http://www.ref5.com/\" title=\"Reference 5\">Moo</a></p>\n", 103 } 104 doTestsInlineParam(t, tests, TestParams{ 105 referenceOverride: func(reference string) (rv *Reference, overridden bool) { 106 switch reference { 107 case "ref1": 108 // just an overridden reference exists without definition 109 return &Reference{ 110 Link: "http://www.ref1.com/", 111 Title: "Reference 1"}, true 112 case "ref2": 113 // overridden exists and reference defined 114 return &Reference{ 115 Link: "http://www.overridden.com/", 116 Title: "Reference Overridden"}, true 117 case "ref3": 118 // not overridden and reference defined 119 return nil, false 120 case "ref4": 121 // overridden missing and defined 122 return nil, true 123 case "!(*http.ServeMux).ServeHTTP": 124 return &Reference{ 125 Link: "http://localhost:6060/pkg/net/http/#ServeMux.ServeHTTP", 126 Title: "ServeHTTP docs"}, true 127 case "ref5": 128 return &Reference{ 129 Link: "http://www.ref5.com/", 130 Title: "Reference 5", 131 Text: "Moo", 132 }, true 133 } 134 return nil, false 135 }, 136 }) 137} 138 139func TestStrong(t *testing.T) { 140 t.Parallel() 141 var tests = []string{ 142 "nothing inline\n", 143 "<p>nothing inline</p>\n", 144 145 "simple **inline** test\n", 146 "<p>simple <strong>inline</strong> test</p>\n", 147 148 "**at the** beginning\n", 149 "<p><strong>at the</strong> beginning</p>\n", 150 151 "at the **end**\n", 152 "<p>at the <strong>end</strong></p>\n", 153 154 "**try two** in **one line**\n", 155 "<p><strong>try two</strong> in <strong>one line</strong></p>\n", 156 157 "over **two\nlines** test\n", 158 "<p>over <strong>two\nlines</strong> test</p>\n", 159 160 "odd **number of** markers** here\n", 161 "<p>odd <strong>number of</strong> markers** here</p>\n", 162 163 "odd **number\nof** markers** here\n", 164 "<p>odd <strong>number\nof</strong> markers** here</p>\n", 165 166 "simple __inline__ test\n", 167 "<p>simple <strong>inline</strong> test</p>\n", 168 169 "__at the__ beginning\n", 170 "<p><strong>at the</strong> beginning</p>\n", 171 172 "at the __end__\n", 173 "<p>at the <strong>end</strong></p>\n", 174 175 "__try two__ in __one line__\n", 176 "<p><strong>try two</strong> in <strong>one line</strong></p>\n", 177 178 "over __two\nlines__ test\n", 179 "<p>over <strong>two\nlines</strong> test</p>\n", 180 181 "odd __number of__ markers__ here\n", 182 "<p>odd <strong>number of</strong> markers__ here</p>\n", 183 184 "odd __number\nof__ markers__ here\n", 185 "<p>odd <strong>number\nof</strong> markers__ here</p>\n", 186 187 "mix of **markers__\n", 188 "<p>mix of **markers__</p>\n", 189 190 "**`/usr`** : this folder is named `usr`\n", 191 "<p><strong><code>/usr</code></strong> : this folder is named <code>usr</code></p>\n", 192 193 "**`/usr`** :\n\n this folder is named `usr`\n", 194 "<p><strong><code>/usr</code></strong> :</p>\n\n<p>this folder is named <code>usr</code></p>\n", 195 } 196 doTestsInline(t, tests) 197} 198 199func TestEmphasisMix(t *testing.T) { 200 t.Parallel() 201 var tests = []string{ 202 "***triple emphasis***\n", 203 "<p><strong><em>triple emphasis</em></strong></p>\n", 204 205 "***triple\nemphasis***\n", 206 "<p><strong><em>triple\nemphasis</em></strong></p>\n", 207 208 "___triple emphasis___\n", 209 "<p><strong><em>triple emphasis</em></strong></p>\n", 210 211 "***triple emphasis___\n", 212 "<p>***triple emphasis___</p>\n", 213 214 "*__triple emphasis__*\n", 215 "<p><em><strong>triple emphasis</strong></em></p>\n", 216 217 "__*triple emphasis*__\n", 218 "<p><strong><em>triple emphasis</em></strong></p>\n", 219 220 "**improper *nesting** is* bad\n", 221 "<p><strong>improper *nesting</strong> is* bad</p>\n", 222 223 "*improper **nesting* is** bad\n", 224 "<p>*improper <strong>nesting* is</strong> bad</p>\n", 225 } 226 doTestsInline(t, tests) 227} 228 229func TestEmphasisLink(t *testing.T) { 230 t.Parallel() 231 var tests = []string{ 232 "[first](before) *text[second] (inside)text* [third](after)\n", 233 "<p><a href=\"before\">first</a> <em>text<a href=\"inside\">second</a>text</em> <a href=\"after\">third</a></p>\n", 234 235 "*incomplete [link] definition*\n", 236 "<p><em>incomplete [link] definition</em></p>\n", 237 238 "*it's [emphasis*] (not link)\n", 239 "<p><em>it's [emphasis</em>] (not link)</p>\n", 240 241 "*it's [emphasis*] and *[asterisk]\n", 242 "<p><em>it's [emphasis</em>] and *[asterisk]</p>\n", 243 } 244 doTestsInline(t, tests) 245} 246 247func TestStrikeThrough(t *testing.T) { 248 t.Parallel() 249 var tests = []string{ 250 "nothing inline\n", 251 "<p>nothing inline</p>\n", 252 253 "simple ~~inline~~ test\n", 254 "<p>simple <del>inline</del> test</p>\n", 255 256 "~~at the~~ beginning\n", 257 "<p><del>at the</del> beginning</p>\n", 258 259 "at the ~~end~~\n", 260 "<p>at the <del>end</del></p>\n", 261 262 "~~try two~~ in ~~one line~~\n", 263 "<p><del>try two</del> in <del>one line</del></p>\n", 264 265 "over ~~two\nlines~~ test\n", 266 "<p>over <del>two\nlines</del> test</p>\n", 267 268 "odd ~~number of~~ markers~~ here\n", 269 "<p>odd <del>number of</del> markers~~ here</p>\n", 270 271 "odd ~~number\nof~~ markers~~ here\n", 272 "<p>odd <del>number\nof</del> markers~~ here</p>\n", 273 } 274 doTestsInline(t, tests) 275} 276 277func TestCodeSpan(t *testing.T) { 278 t.Parallel() 279 var tests = []string{ 280 "`source code`\n", 281 "<p><code>source code</code></p>\n", 282 283 "` source code with spaces `\n", 284 "<p><code>source code with spaces</code></p>\n", 285 286 "` source code with spaces `not here\n", 287 "<p><code>source code with spaces</code>not here</p>\n", 288 289 "a `single marker\n", 290 "<p>a `single marker</p>\n", 291 292 "a single multi-tick marker with ``` no text\n", 293 "<p>a single multi-tick marker with ``` no text</p>\n", 294 295 "markers with ` ` a space\n", 296 "<p>markers with a space</p>\n", 297 298 "`source code` and a `stray\n", 299 "<p><code>source code</code> and a `stray</p>\n", 300 301 "`source *with* _awkward characters_ in it`\n", 302 "<p><code>source *with* _awkward characters_ in it</code></p>\n", 303 304 "`split over\ntwo lines`\n", 305 "<p><code>split over\ntwo lines</code></p>\n", 306 307 "```multiple ticks``` for the marker\n", 308 "<p><code>multiple ticks</code> for the marker</p>\n", 309 310 "```multiple ticks `with` ticks inside```\n", 311 "<p><code>multiple ticks `with` ticks inside</code></p>\n", 312 } 313 doTestsInline(t, tests) 314} 315 316func TestLineBreak(t *testing.T) { 317 t.Parallel() 318 var tests = []string{ 319 "this line \nhas a break\n", 320 "<p>this line<br />\nhas a break</p>\n", 321 322 "this line \ndoes not\n", 323 "<p>this line\ndoes not</p>\n", 324 325 "this line\\\ndoes not\n", 326 "<p>this line\\\ndoes not</p>\n", 327 328 "this line\\ \ndoes not\n", 329 "<p>this line\\\ndoes not</p>\n", 330 331 "this has an \nextra space\n", 332 "<p>this has an<br />\nextra space</p>\n", 333 } 334 doTestsInline(t, tests) 335 336 tests = []string{ 337 "this line \nhas a break\n", 338 "<p>this line<br />\nhas a break</p>\n", 339 340 "this line \ndoes not\n", 341 "<p>this line\ndoes not</p>\n", 342 343 "this line\\\nhas a break\n", 344 "<p>this line<br />\nhas a break</p>\n", 345 346 "this line\\ \ndoes not\n", 347 "<p>this line\\\ndoes not</p>\n", 348 349 "this has an \nextra space\n", 350 "<p>this has an<br />\nextra space</p>\n", 351 } 352 doTestsInlineParam(t, tests, TestParams{ 353 extensions: BackslashLineBreak}) 354} 355 356func TestInlineLink(t *testing.T) { 357 t.Parallel() 358 var tests = []string{ 359 "[foo](/bar/)\n", 360 "<p><a href=\"/bar/\">foo</a></p>\n", 361 362 "[foo with a title](/bar/ \"title\")\n", 363 "<p><a href=\"/bar/\" title=\"title\">foo with a title</a></p>\n", 364 365 "[foo with a title](/bar/\t\"title\")\n", 366 "<p><a href=\"/bar/\" title=\"title\">foo with a title</a></p>\n", 367 368 "[foo with a title](/bar/ \"title\" )\n", 369 "<p><a href=\"/bar/\" title=\"title\">foo with a title</a></p>\n", 370 371 "[foo with a title](/bar/ title with no quotes)\n", 372 "<p><a href=\"/bar/ title with no quotes\">foo with a title</a></p>\n", 373 374 "[foo]()\n", 375 "<p>[foo]()</p>\n", 376 377 "![foo](/bar/)\n", 378 "<p><img src=\"/bar/\" alt=\"foo\" /></p>\n", 379 380 "![foo with a title](/bar/ \"title\")\n", 381 "<p><img src=\"/bar/\" alt=\"foo with a title\" title=\"title\" /></p>\n", 382 383 "![foo with a title](/bar/\t\"title\")\n", 384 "<p><img src=\"/bar/\" alt=\"foo with a title\" title=\"title\" /></p>\n", 385 386 "![foo with a title](/bar/ \"title\" )\n", 387 "<p><img src=\"/bar/\" alt=\"foo with a title\" title=\"title\" /></p>\n", 388 389 "![foo with a title](/bar/ title with no quotes)\n", 390 "<p><img src=\"/bar/ title with no quotes\" alt=\"foo with a title\" /></p>\n", 391 392 "![](img.jpg)\n", 393 "<p><img src=\"img.jpg\" alt=\"\" /></p>\n", 394 395 "[link](url)\n", 396 "<p><a href=\"url\">link</a></p>\n", 397 398 "![foo]()\n", 399 "<p>![foo]()</p>\n", 400 401 "[a link]\t(/with_a_tab/)\n", 402 "<p><a href=\"/with_a_tab/\">a link</a></p>\n", 403 404 "[a link] (/with_spaces/)\n", 405 "<p><a href=\"/with_spaces/\">a link</a></p>\n", 406 407 "[text (with) [[nested] (brackets)]](/url/)\n", 408 "<p><a href=\"/url/\">text (with) [[nested] (brackets)]</a></p>\n", 409 410 "[text (with) [broken nested] (brackets)]](/url/)\n", 411 "<p>[text (with) <a href=\"brackets\">broken nested</a>]](/url/)</p>\n", 412 413 "[text\nwith a newline](/link/)\n", 414 "<p><a href=\"/link/\">text\nwith a newline</a></p>\n", 415 416 "[text in brackets] [followed](/by a link/)\n", 417 "<p>[text in brackets] <a href=\"/by a link/\">followed</a></p>\n", 418 419 "[link with\\] a closing bracket](/url/)\n", 420 "<p><a href=\"/url/\">link with] a closing bracket</a></p>\n", 421 422 "[link with\\[ an opening bracket](/url/)\n", 423 "<p><a href=\"/url/\">link with[ an opening bracket</a></p>\n", 424 425 "[link with\\) a closing paren](/url/)\n", 426 "<p><a href=\"/url/\">link with) a closing paren</a></p>\n", 427 428 "[link with\\( an opening paren](/url/)\n", 429 "<p><a href=\"/url/\">link with( an opening paren</a></p>\n", 430 431 "[link]( with whitespace)\n", 432 "<p><a href=\"with whitespace\">link</a></p>\n", 433 434 "[link]( with whitespace )\n", 435 "<p><a href=\"with whitespace\">link</a></p>\n", 436 437 "[![image](someimage)](with image)\n", 438 "<p><a href=\"with image\"><img src=\"someimage\" alt=\"image\" /></a></p>\n", 439 440 "[link](url \"one quote)\n", 441 "<p><a href=\"url "one quote\">link</a></p>\n", 442 443 "[link](url 'one quote)\n", 444 "<p><a href=\"url 'one quote\">link</a></p>\n", 445 446 "[link](<url>)\n", 447 "<p><a href=\"url\">link</a></p>\n", 448 449 "[link & ampersand](/url/)\n", 450 "<p><a href=\"/url/\">link & ampersand</a></p>\n", 451 452 "[link & ampersand](/url/)\n", 453 "<p><a href=\"/url/\">link & ampersand</a></p>\n", 454 455 "[link](/url/&query)\n", 456 "<p><a href=\"/url/&query\">link</a></p>\n", 457 458 "[[t]](/t)\n", 459 "<p><a href=\"/t\">[t]</a></p>\n", 460 461 "[link](</>)\n", 462 "<p><a href=\"/\">link</a></p>\n", 463 464 "[link](<./>)\n", 465 "<p><a href=\"./\">link</a></p>\n", 466 467 "[link](<../>)\n", 468 "<p><a href=\"../\">link</a></p>\n", 469 } 470 doLinkTestsInline(t, tests) 471 472} 473 474func TestRelAttrLink(t *testing.T) { 475 t.Parallel() 476 var nofollowTests = []string{ 477 "[foo](http://bar.com/foo/)\n", 478 "<p><a href=\"http://bar.com/foo/\" rel=\"nofollow\">foo</a></p>\n", 479 480 "[foo](/bar/)\n", 481 "<p><a href=\"/bar/\">foo</a></p>\n", 482 483 "[foo](/)\n", 484 "<p><a href=\"/\">foo</a></p>\n", 485 486 "[foo](./)\n", 487 "<p><a href=\"./\">foo</a></p>\n", 488 489 "[foo](../)\n", 490 "<p><a href=\"../\">foo</a></p>\n", 491 492 "[foo](../bar)\n", 493 "<p><a href=\"../bar\">foo</a></p>\n", 494 } 495 doTestsInlineParam(t, nofollowTests, TestParams{ 496 HTMLFlags: Safelink | NofollowLinks, 497 }) 498 499 var noreferrerTests = []string{ 500 "[foo](http://bar.com/foo/)\n", 501 "<p><a href=\"http://bar.com/foo/\" rel=\"noreferrer\">foo</a></p>\n", 502 503 "[foo](/bar/)\n", 504 "<p><a href=\"/bar/\">foo</a></p>\n", 505 } 506 doTestsInlineParam(t, noreferrerTests, TestParams{ 507 HTMLFlags: Safelink | NoreferrerLinks, 508 }) 509 510 var noopenerTests = []string{ 511 "[foo](http://bar.com/foo/)\n", 512 "<p><a href=\"http://bar.com/foo/\" rel=\"noopener\">foo</a></p>\n", 513 514 "[foo](/bar/)\n", 515 "<p><a href=\"/bar/\">foo</a></p>\n", 516 } 517 doTestsInlineParam(t, noopenerTests, TestParams{ 518 HTMLFlags: Safelink | NoopenerLinks, 519 }) 520 521 var nofollownoreferrernoopenerTests = []string{ 522 "[foo](http://bar.com/foo/)\n", 523 "<p><a href=\"http://bar.com/foo/\" rel=\"nofollow noreferrer noopener\">foo</a></p>\n", 524 525 "[foo](/bar/)\n", 526 "<p><a href=\"/bar/\">foo</a></p>\n", 527 } 528 doTestsInlineParam(t, nofollownoreferrernoopenerTests, TestParams{ 529 HTMLFlags: Safelink | NofollowLinks | NoreferrerLinks | NoopenerLinks, 530 }) 531} 532 533func TestHrefTargetBlank(t *testing.T) { 534 t.Parallel() 535 var tests = []string{ 536 // internal link 537 "[foo](/bar/)\n", 538 "<p><a href=\"/bar/\">foo</a></p>\n", 539 540 "[foo](/)\n", 541 "<p><a href=\"/\">foo</a></p>\n", 542 543 "[foo](./)\n", 544 "<p><a href=\"./\">foo</a></p>\n", 545 546 "[foo](./bar)\n", 547 "<p><a href=\"./bar\">foo</a></p>\n", 548 549 "[foo](../)\n", 550 "<p><a href=\"../\">foo</a></p>\n", 551 552 "[foo](../bar)\n", 553 "<p><a href=\"../bar\">foo</a></p>\n", 554 555 "[foo](http://example.com)\n", 556 "<p><a href=\"http://example.com\" target=\"_blank\">foo</a></p>\n", 557 } 558 doTestsInlineParam(t, tests, TestParams{ 559 HTMLFlags: Safelink | HrefTargetBlank, 560 }) 561} 562 563func TestSafeInlineLink(t *testing.T) { 564 t.Parallel() 565 var tests = []string{ 566 "[foo](/bar/)\n", 567 "<p><a href=\"/bar/\">foo</a></p>\n", 568 569 "[foo](/)\n", 570 "<p><a href=\"/\">foo</a></p>\n", 571 572 "[foo](./)\n", 573 "<p><a href=\"./\">foo</a></p>\n", 574 575 "[foo](../)\n", 576 "<p><a href=\"../\">foo</a></p>\n", 577 578 "[foo](http://bar/)\n", 579 "<p><a href=\"http://bar/\">foo</a></p>\n", 580 581 "[foo](https://bar/)\n", 582 "<p><a href=\"https://bar/\">foo</a></p>\n", 583 584 "[foo](ftp://bar/)\n", 585 "<p><a href=\"ftp://bar/\">foo</a></p>\n", 586 587 "[foo](mailto://bar/)\n", 588 "<p><a href=\"mailto://bar/\">foo</a></p>\n", 589 590 // Not considered safe 591 "[foo](baz://bar/)\n", 592 "<p><tt>foo</tt></p>\n", 593 } 594 doSafeTestsInline(t, tests) 595} 596 597func TestReferenceLink(t *testing.T) { 598 t.Parallel() 599 var tests = []string{ 600 "[link][ref]\n", 601 "<p>[link][ref]</p>\n", 602 603 "[link][ref]\n [ref]: /url/ \"title\"\n", 604 "<p><a href=\"/url/\" title=\"title\">link</a></p>\n", 605 606 "[link][ref]\n [ref]: /url/\n", 607 "<p><a href=\"/url/\">link</a></p>\n", 608 609 " [ref]: /url/\n", 610 "", 611 612 " [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n", 613 "", 614 615 " [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n [4spaces]: /url/\n", 616 "<pre><code>[4spaces]: /url/\n</code></pre>\n", 617 618 "[hmm](ref2)\n [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n", 619 "<p><a href=\"ref2\">hmm</a></p>\n", 620 621 "[ref]\n", 622 "<p>[ref]</p>\n", 623 624 "[ref]\n [ref]: /url/ \"title\"\n", 625 "<p><a href=\"/url/\" title=\"title\">ref</a></p>\n", 626 627 "[ref]\n [ref]: ../url/ \"title\"\n", 628 "<p><a href=\"../url/\" title=\"title\">ref</a></p>\n", 629 630 "[link][ref]\n [ref]: /url/", 631 "<p><a href=\"/url/\">link</a></p>\n", 632 } 633 doLinkTestsInline(t, tests) 634} 635 636func TestTags(t *testing.T) { 637 t.Parallel() 638 var tests = []string{ 639 "a <span>tag</span>\n", 640 "<p>a <span>tag</span></p>\n", 641 642 "<span>tag</span>\n", 643 "<p><span>tag</span></p>\n", 644 645 "<span>mismatch</spandex>\n", 646 "<p><span>mismatch</spandex></p>\n", 647 648 "a <singleton /> tag\n", 649 "<p>a <singleton /> tag</p>\n", 650 } 651 doTestsInline(t, tests) 652} 653 654func TestAutoLink(t *testing.T) { 655 t.Parallel() 656 var tests = []string{ 657 "http://foo.com/\n", 658 "<p><a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 659 660 "1 http://foo.com/\n", 661 "<p>1 <a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 662 663 "1http://foo.com/\n", 664 "<p>1<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 665 666 "1.http://foo.com/\n", 667 "<p>1.<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 668 669 "1. http://foo.com/\n", 670 "<ol>\n<li><a href=\"http://foo.com/\">http://foo.com/</a></li>\n</ol>\n", 671 672 "-http://foo.com/\n", 673 "<p>-<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 674 675 "- http://foo.com/\n", 676 "<ul>\n<li><a href=\"http://foo.com/\">http://foo.com/</a></li>\n</ul>\n", 677 678 "_http://foo.com/\n", 679 "<p>_<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 680 681 "令狐http://foo.com/\n", 682 "<p>令狐<a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 683 684 "令狐 http://foo.com/\n", 685 "<p>令狐 <a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 686 687 "ahttp://foo.com/\n", 688 "<p>ahttp://foo.com/</p>\n", 689 690 ">http://foo.com/\n", 691 "<blockquote>\n<p><a href=\"http://foo.com/\">http://foo.com/</a></p>\n</blockquote>\n", 692 693 "> http://foo.com/\n", 694 "<blockquote>\n<p><a href=\"http://foo.com/\">http://foo.com/</a></p>\n</blockquote>\n", 695 696 "go to <http://foo.com/>\n", 697 "<p>go to <a href=\"http://foo.com/\">http://foo.com/</a></p>\n", 698 699 "a secure <https://link.org>\n", 700 "<p>a secure <a href=\"https://link.org\">https://link.org</a></p>\n", 701 702 "an email <mailto:some@one.com>\n", 703 "<p>an email <a href=\"mailto:some@one.com\">some@one.com</a></p>\n", 704 705 "an email <mailto://some@one.com>\n", 706 "<p>an email <a href=\"mailto://some@one.com\">some@one.com</a></p>\n", 707 708 "an email <some@one.com>\n", 709 "<p>an email <a href=\"mailto:some@one.com\">some@one.com</a></p>\n", 710 711 "an ftp <ftp://old.com>\n", 712 "<p>an ftp <a href=\"ftp://old.com\">ftp://old.com</a></p>\n", 713 714 "an ftp <ftp:old.com>\n", 715 "<p>an ftp <a href=\"ftp:old.com\">ftp:old.com</a></p>\n", 716 717 "a link with <http://new.com?query=foo&bar>\n", 718 "<p>a link with <a href=\"http://new.com?query=foo&bar\">" + 719 "http://new.com?query=foo&bar</a></p>\n", 720 721 "quotes mean a tag <http://new.com?query=\"foo\"&bar>\n", 722 "<p>quotes mean a tag <http://new.com?query=\"foo\"&bar></p>\n", 723 724 "quotes mean a tag <http://new.com?query='foo'&bar>\n", 725 "<p>quotes mean a tag <http://new.com?query='foo'&bar></p>\n", 726 727 "unless escaped <http://new.com?query=\\\"foo\\\"&bar>\n", 728 "<p>unless escaped <a href=\"http://new.com?query="foo"&bar\">" + 729 "http://new.com?query="foo"&bar</a></p>\n", 730 731 "even a > can be escaped <http://new.com?q=\\>&etc>\n", 732 "<p>even a > can be escaped <a href=\"http://new.com?q=>&etc\">" + 733 "http://new.com?q=>&etc</a></p>\n", 734 735 "<a href=\"http://fancy.com\">http://fancy.com</a>\n", 736 "<p><a href=\"http://fancy.com\">http://fancy.com</a></p>\n", 737 738 "<a href=\"http://fancy.com\">This is a link</a>\n", 739 "<p><a href=\"http://fancy.com\">This is a link</a></p>\n", 740 741 "<a href=\"http://www.fancy.com/A_B.pdf\">http://www.fancy.com/A_B.pdf</a>\n", 742 "<p><a href=\"http://www.fancy.com/A_B.pdf\">http://www.fancy.com/A_B.pdf</a></p>\n", 743 744 "(<a href=\"http://www.fancy.com/A_B\">http://www.fancy.com/A_B</a> (\n", 745 "<p>(<a href=\"http://www.fancy.com/A_B\">http://www.fancy.com/A_B</a> (</p>\n", 746 747 "(<a href=\"http://www.fancy.com/A_B\">http://www.fancy.com/A_B</a> (part two: <a href=\"http://www.fancy.com/A_B\">http://www.fancy.com/A_B</a>)).\n", 748 "<p>(<a href=\"http://www.fancy.com/A_B\">http://www.fancy.com/A_B</a> (part two: <a href=\"http://www.fancy.com/A_B\">http://www.fancy.com/A_B</a>)).</p>\n", 749 750 "http://www.foo.com<br />\n", 751 "<p><a href=\"http://www.foo.com\">http://www.foo.com</a><br /></p>\n", 752 753 "http://foo.com/viewtopic.php?f=18&t=297", 754 "<p><a href=\"http://foo.com/viewtopic.php?f=18&t=297\">http://foo.com/viewtopic.php?f=18&t=297</a></p>\n", 755 756 "http://foo.com/viewtopic.php?param="18"zz", 757 "<p><a href=\"http://foo.com/viewtopic.php?param="18"zz\">http://foo.com/viewtopic.php?param="18"zz</a></p>\n", 758 759 "http://foo.com/viewtopic.php?param="18"", 760 "<p><a href=\"http://foo.com/viewtopic.php?param="18"\">http://foo.com/viewtopic.php?param="18"</a></p>\n", 761 762 "<a href=\"https://fancy.com\">https://fancy.com</a>\n", 763 "<p><a href=\"https://fancy.com\">https://fancy.com</a></p>\n", 764 } 765 doLinkTestsInline(t, tests) 766} 767 768var footnoteTests = []string{ 769 "testing footnotes.[^a]\n\n[^a]: This is the note\n", 770 `<p>testing footnotes.<sup class="footnote-ref" id="fnref:a"><a href="#fn:a">1</a></sup></p> 771 772<div class="footnotes"> 773 774<hr /> 775 776<ol> 777<li id="fn:a">This is the note</li> 778</ol> 779 780</div> 781`, 782 783 `testing long[^b] notes. 784 785[^b]: Paragraph 1 786 787 Paragraph 2 788 789 ` + "```\n\tsome code\n\t```" + ` 790 791 Paragraph 3 792 793No longer in the footnote 794`, 795 `<p>testing long<sup class="footnote-ref" id="fnref:b"><a href="#fn:b">1</a></sup> notes.</p> 796 797<p>No longer in the footnote</p> 798 799<div class="footnotes"> 800 801<hr /> 802 803<ol> 804<li id="fn:b"><p>Paragraph 1</p> 805 806<p>Paragraph 2</p> 807 808<p><code> 809some code 810</code></p> 811 812<p>Paragraph 3</p></li> 813</ol> 814 815</div> 816`, 817 818 `testing[^c] multiple[^d] notes. 819 820[^c]: this is [note] c 821 822 823omg 824 825[^d]: this is note d 826 827what happens here 828 829[note]: /link/c 830 831`, 832 `<p>testing<sup class="footnote-ref" id="fnref:c"><a href="#fn:c">1</a></sup> multiple<sup class="footnote-ref" id="fnref:d"><a href="#fn:d">2</a></sup> notes.</p> 833 834<p>omg</p> 835 836<p>what happens here</p> 837 838<div class="footnotes"> 839 840<hr /> 841 842<ol> 843<li id="fn:c">this is <a href="/link/c">note</a> c</li> 844 845<li id="fn:d">this is note d</li> 846</ol> 847 848</div> 849`, 850 851 "testing inline^[this is the note] notes.\n", 852 `<p>testing inline<sup class="footnote-ref" id="fnref:this-is-the-note"><a href="#fn:this-is-the-note">1</a></sup> notes.</p> 853 854<div class="footnotes"> 855 856<hr /> 857 858<ol> 859<li id="fn:this-is-the-note">this is the note</li> 860</ol> 861 862</div> 863`, 864 865 "testing multiple[^1] types^[inline note] of notes[^2]\n\n[^2]: the second deferred note\n[^1]: the first deferred note\n\n\twhich happens to be a block\n", 866 `<p>testing multiple<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup> types<sup class="footnote-ref" id="fnref:inline-note"><a href="#fn:inline-note">2</a></sup> of notes<sup class="footnote-ref" id="fnref:2"><a href="#fn:2">3</a></sup></p> 867 868<div class="footnotes"> 869 870<hr /> 871 872<ol> 873<li id="fn:1"><p>the first deferred note</p> 874 875<p>which happens to be a block</p></li> 876 877<li id="fn:inline-note">inline note</li> 878 879<li id="fn:2">the second deferred note</li> 880</ol> 881 882</div> 883`, 884 885 `This is a footnote[^1]^[and this is an inline footnote] 886 887[^1]: the footnote text. 888 889 may be multiple paragraphs. 890`, 891 `<p>This is a footnote<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup><sup class="footnote-ref" id="fnref:and-this-is-an-i"><a href="#fn:and-this-is-an-i">2</a></sup></p> 892 893<div class="footnotes"> 894 895<hr /> 896 897<ol> 898<li id="fn:1"><p>the footnote text.</p> 899 900<p>may be multiple paragraphs.</p></li> 901 902<li id="fn:and-this-is-an-i">and this is an inline footnote</li> 903</ol> 904 905</div> 906`, 907 908 "empty footnote[^]\n\n[^]: fn text", 909 "<p>empty footnote<sup class=\"footnote-ref\" id=\"fnref:\"><a href=\"#fn:\">1</a></sup></p>\n\n<div class=\"footnotes\">\n\n<hr />\n\n<ol>\n<li id=\"fn:\">fn text</li>\n</ol>\n\n</div>\n", 910 911 "Some text.[^note1]\n\n[^note1]: fn1", 912 "<p>Some text.<sup class=\"footnote-ref\" id=\"fnref:note1\"><a href=\"#fn:note1\">1</a></sup></p>\n\n<div class=\"footnotes\">\n\n<hr />\n\n<ol>\n<li id=\"fn:note1\">fn1</li>\n</ol>\n\n</div>\n", 913 914 "Some text.[^note1][^note2]\n\n[^note1]: fn1\n[^note2]: fn2\n", 915 "<p>Some text.<sup class=\"footnote-ref\" id=\"fnref:note1\"><a href=\"#fn:note1\">1</a></sup><sup class=\"footnote-ref\" id=\"fnref:note2\"><a href=\"#fn:note2\">2</a></sup></p>\n\n<div class=\"footnotes\">\n\n<hr />\n\n<ol>\n<li id=\"fn:note1\">fn1</li>\n\n<li id=\"fn:note2\">fn2</li>\n</ol>\n\n</div>\n", 916 917 `Bla bla [^1] [WWW][w3] 918 919[^1]: This is a footnote 920 921[w3]: http://www.w3.org/ 922`, 923 `<p>Bla bla <sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup> <a href="http://www.w3.org/">WWW</a></p> 924 925<div class="footnotes"> 926 927<hr /> 928 929<ol> 930<li id="fn:1">This is a footnote</li> 931</ol> 932 933</div> 934`, 935 936 `This is exciting![^fn1] 937 938[^fn1]: Fine print 939`, 940 `<p>This is exciting!<sup class="footnote-ref" id="fnref:fn1"><a href="#fn:fn1">1</a></sup></p> 941 942<div class="footnotes"> 943 944<hr /> 945 946<ol> 947<li id="fn:fn1">Fine print</li> 948</ol> 949 950</div> 951`, 952 953 `This text does not reference a footnote. 954 955[^footnote]: But it has a footnote! And it gets omitted. 956`, 957 "<p>This text does not reference a footnote.</p>\n", 958} 959 960func TestFootnotes(t *testing.T) { 961 t.Parallel() 962 doTestsInlineParam(t, footnoteTests, TestParams{ 963 extensions: Footnotes, 964 }) 965} 966 967func TestFootnotesWithParameters(t *testing.T) { 968 t.Parallel() 969 tests := make([]string, len(footnoteTests)) 970 971 prefix := "testPrefix" 972 returnText := "ret" 973 re := regexp.MustCompile(`(?ms)<li id="fn:(\S+?)">(.*?)</li>`) 974 975 // Transform the test expectations to match the parameters we're using. 976 for i, test := range footnoteTests { 977 if i%2 == 1 { 978 test = strings.Replace(test, "fn:", "fn:"+prefix, -1) 979 test = strings.Replace(test, "fnref:", "fnref:"+prefix, -1) 980 test = re.ReplaceAllString(test, `<li id="fn:$1">$2 <a class="footnote-return" href="#fnref:$1">ret</a></li>`) 981 } 982 tests[i] = test 983 } 984 985 params := HTMLRendererParameters{ 986 FootnoteAnchorPrefix: prefix, 987 FootnoteReturnLinkContents: returnText, 988 } 989 990 doTestsInlineParam(t, tests, TestParams{ 991 extensions: Footnotes, 992 HTMLFlags: FootnoteReturnLinks, 993 HTMLRendererParameters: params, 994 }) 995} 996 997func TestNestedFootnotes(t *testing.T) { 998 t.Parallel() 999 var tests = []string{ 1000 `Paragraph.[^fn1] 1001 1002[^fn1]: 1003 Asterisk[^fn2] 1004 1005[^fn2]: 1006 Obelisk`, 1007 `<p>Paragraph.<sup class="footnote-ref" id="fnref:fn1"><a href="#fn:fn1">1</a></sup></p> 1008 1009<div class="footnotes"> 1010 1011<hr /> 1012 1013<ol> 1014<li id="fn:fn1">Asterisk<sup class="footnote-ref" id="fnref:fn2"><a href="#fn:fn2">2</a></sup></li> 1015 1016<li id="fn:fn2">Obelisk</li> 1017</ol> 1018 1019</div> 1020`, 1021 } 1022 doTestsInlineParam(t, tests, TestParams{extensions: Footnotes}) 1023} 1024 1025func TestInlineComments(t *testing.T) { 1026 t.Parallel() 1027 var tests = []string{ 1028 "Hello <!-- there ->\n", 1029 "<p>Hello <!— there –></p>\n", 1030 1031 "Hello <!-- there -->\n", 1032 "<p>Hello <!-- there --></p>\n", 1033 1034 "Hello <!-- there -->", 1035 "<p>Hello <!-- there --></p>\n", 1036 1037 "Hello <!---->\n", 1038 "<p>Hello <!----></p>\n", 1039 1040 "Hello <!-- there -->\na", 1041 "<p>Hello <!-- there -->\na</p>\n", 1042 1043 "* list <!-- item -->\n", 1044 "<ul>\n<li>list <!-- item --></li>\n</ul>\n", 1045 1046 "<!-- Front --> comment\n", 1047 "<p><!-- Front --> comment</p>\n", 1048 1049 "blahblah\n<!--- foo -->\nrhubarb\n", 1050 "<p>blahblah\n<!--- foo -->\nrhubarb</p>\n", 1051 } 1052 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsDashes}) 1053} 1054 1055func TestSmartDoubleQuotes(t *testing.T) { 1056 t.Parallel() 1057 var tests = []string{ 1058 "this should be normal \"quoted\" text.\n", 1059 "<p>this should be normal “quoted” text.</p>\n", 1060 "this \" single double\n", 1061 "<p>this “ single double</p>\n", 1062 "two pair of \"some\" quoted \"text\".\n", 1063 "<p>two pair of “some” quoted “text”.</p>\n"} 1064 1065 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants}) 1066} 1067 1068func TestSmartDoubleQuotesNBSP(t *testing.T) { 1069 t.Parallel() 1070 var tests = []string{ 1071 "this should be normal \"quoted\" text.\n", 1072 "<p>this should be normal “ quoted ” text.</p>\n", 1073 "this \" single double\n", 1074 "<p>this “ single double</p>\n", 1075 "two pair of \"some\" quoted \"text\".\n", 1076 "<p>two pair of “ some ” quoted “ text ”.</p>\n"} 1077 1078 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsQuotesNBSP}) 1079} 1080 1081func TestSmartAngledDoubleQuotes(t *testing.T) { 1082 t.Parallel() 1083 var tests = []string{ 1084 "this should be angled \"quoted\" text.\n", 1085 "<p>this should be angled «quoted» text.</p>\n", 1086 "this \" single double\n", 1087 "<p>this « single double</p>\n", 1088 "two pair of \"some\" quoted \"text\".\n", 1089 "<p>two pair of «some» quoted «text».</p>\n"} 1090 1091 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsAngledQuotes}) 1092} 1093 1094func TestSmartAngledDoubleQuotesNBSP(t *testing.T) { 1095 t.Parallel() 1096 var tests = []string{ 1097 "this should be angled \"quoted\" text.\n", 1098 "<p>this should be angled « quoted » text.</p>\n", 1099 "this \" single double\n", 1100 "<p>this « single double</p>\n", 1101 "two pair of \"some\" quoted \"text\".\n", 1102 "<p>two pair of « some » quoted « text ».</p>\n"} 1103 1104 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsAngledQuotes | SmartypantsQuotesNBSP}) 1105} 1106 1107func TestSmartFractions(t *testing.T) { 1108 t.Parallel() 1109 var tests = []string{ 1110 "1/2, 1/4 and 3/4; 1/4th and 3/4ths\n", 1111 "<p>½, ¼ and ¾; ¼th and ¾ths</p>\n", 1112 "1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n", 1113 "<p>1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.</p>\n"} 1114 1115 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants}) 1116 1117 tests = []string{ 1118 "1/2, 2/3, 81/100 and 1000000/1048576.\n", 1119 "<p><sup>1</sup>⁄<sub>2</sub>, <sup>2</sup>⁄<sub>3</sub>, <sup>81</sup>⁄<sub>100</sub> and <sup>1000000</sup>⁄<sub>1048576</sub>.</p>\n", 1120 "1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.\n", 1121 "<p>1/2/2015, 1/4/2015, 3/4/2015; 2015/1/2, 2015/1/4, 2015/3/4.</p>\n"} 1122 1123 doTestsInlineParam(t, tests, TestParams{HTMLFlags: Smartypants | SmartypantsFractions}) 1124} 1125 1126func TestDisableSmartDashes(t *testing.T) { 1127 t.Parallel() 1128 doTestsInlineParam(t, []string{ 1129 "foo - bar\n", 1130 "<p>foo - bar</p>\n", 1131 "foo -- bar\n", 1132 "<p>foo -- bar</p>\n", 1133 "foo --- bar\n", 1134 "<p>foo --- bar</p>\n", 1135 }, TestParams{}) 1136 doTestsInlineParam(t, []string{ 1137 "foo - bar\n", 1138 "<p>foo – bar</p>\n", 1139 "foo -- bar\n", 1140 "<p>foo — bar</p>\n", 1141 "foo --- bar\n", 1142 "<p>foo —– bar</p>\n", 1143 }, TestParams{HTMLFlags: Smartypants | SmartypantsDashes}) 1144 doTestsInlineParam(t, []string{ 1145 "foo - bar\n", 1146 "<p>foo - bar</p>\n", 1147 "foo -- bar\n", 1148 "<p>foo – bar</p>\n", 1149 "foo --- bar\n", 1150 "<p>foo — bar</p>\n", 1151 }, TestParams{HTMLFlags: Smartypants | SmartypantsLatexDashes | SmartypantsDashes}) 1152 doTestsInlineParam(t, []string{ 1153 "foo - bar\n", 1154 "<p>foo - bar</p>\n", 1155 "foo -- bar\n", 1156 "<p>foo -- bar</p>\n", 1157 "foo --- bar\n", 1158 "<p>foo --- bar</p>\n", 1159 }, TestParams{HTMLFlags: Smartypants | SmartypantsLatexDashes}) 1160} 1161 1162func TestSkipLinks(t *testing.T) { 1163 t.Parallel() 1164 doTestsInlineParam(t, []string{ 1165 "[foo](gopher://foo.bar)", 1166 "<p><tt>foo</tt></p>\n", 1167 1168 "[foo](mailto://bar/)\n", 1169 "<p><tt>foo</tt></p>\n", 1170 }, TestParams{ 1171 HTMLFlags: SkipLinks, 1172 }) 1173} 1174 1175func TestSkipImages(t *testing.T) { 1176 t.Parallel() 1177 doTestsInlineParam(t, []string{ 1178 "![foo](/bar/)\n", 1179 "<p></p>\n", 1180 }, TestParams{ 1181 HTMLFlags: SkipImages, 1182 }) 1183} 1184 1185func TestUseXHTML(t *testing.T) { 1186 t.Parallel() 1187 doTestsParam(t, []string{ 1188 "---", 1189 "<hr>\n", 1190 }, TestParams{}) 1191 doTestsParam(t, []string{ 1192 "---", 1193 "<hr />\n", 1194 }, TestParams{HTMLFlags: UseXHTML}) 1195} 1196 1197func TestSkipHTML(t *testing.T) { 1198 t.Parallel() 1199 doTestsParam(t, []string{ 1200 "<div class=\"foo\"></div>\n\ntext\n\n<form>the form</form>", 1201 "<p>text</p>\n\n<p>the form</p>\n", 1202 1203 "text <em>inline html</em> more text", 1204 "<p>text inline html more text</p>\n", 1205 }, TestParams{HTMLFlags: SkipHTML}) 1206} 1207 1208func BenchmarkSmartDoubleQuotes(b *testing.B) { 1209 params := TestParams{HTMLFlags: Smartypants} 1210 params.extensions |= Autolink | Strikethrough 1211 params.HTMLFlags |= UseXHTML 1212 1213 for i := 0; i < b.N; i++ { 1214 runMarkdown("this should be normal \"quoted\" text.\n", params) 1215 } 1216} 1217