//
// Blackfriday Markdown Processor
// Available at http://github.com/russross/blackfriday
//
// Copyright © 2011 Russ Ross .
// Distributed under the Simplified BSD License.
// See README.md for details.
//
//
// Unit tests for inline parsing
//
package blackfriday
import (
"regexp"
"testing"
"strings"
)
func runMarkdownInline(input string, extensions, htmlFlags int, params HtmlRendererParameters) string {
extensions |= EXTENSION_AUTOLINK
extensions |= EXTENSION_STRIKETHROUGH
htmlFlags |= HTML_USE_XHTML
renderer := HtmlRendererWithParameters(htmlFlags, "", "", params)
return string(Markdown([]byte(input), renderer, extensions))
}
func doTestsInline(t *testing.T, tests []string) {
doTestsInlineParam(t, tests, 0, 0, HtmlRendererParameters{})
}
func doLinkTestsInline(t *testing.T, tests []string) {
doTestsInline(t, tests)
prefix := "http://localhost"
params := HtmlRendererParameters{AbsolutePrefix: prefix}
transformTests := transformLinks(tests, prefix)
doTestsInlineParam(t, transformTests, 0, 0, params)
}
func doSafeTestsInline(t *testing.T, tests []string) {
doTestsInlineParam(t, tests, 0, HTML_SAFELINK, HtmlRendererParameters{})
// All the links in this test should not have the prefix appended, so
// just rerun it with different parameters and the same expectations.
prefix := "http://localhost"
params := HtmlRendererParameters{AbsolutePrefix: prefix}
transformTests := transformLinks(tests, prefix)
doTestsInlineParam(t, transformTests, 0, HTML_SAFELINK, params)
}
func doTestsInlineParam(t *testing.T, tests []string, extensions, htmlFlags int,
params HtmlRendererParameters) {
// catch and report panics
var candidate string
/*
defer func() {
if err := recover(); err != nil {
t.Errorf("\npanic while processing [%#v] (%v)\n", candidate, err)
}
}()
*/
for i := 0; i+1 < len(tests); i += 2 {
input := tests[i]
candidate = input
expected := tests[i+1]
actual := runMarkdownInline(candidate, extensions, htmlFlags, params)
if actual != expected {
t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]",
candidate, expected, actual)
}
// now test every substring to stress test bounds checking
if !testing.Short() {
for start := 0; start < len(input); start++ {
for end := start + 1; end <= len(input); end++ {
candidate = input[start:end]
_ = runMarkdownInline(candidate, extensions, htmlFlags, params)
}
}
}
}
}
func transformLinks(tests []string, prefix string) []string {
newTests := make([]string, len(tests))
anchorRe := regexp.MustCompile(`nothing inline
\n",
"simple *inline* test\n",
"simple inline test
\n",
"*at the* beginning\n",
"at the beginning
\n",
"at the *end*\n",
"at the end
\n",
"*try two* in *one line*\n",
"try two in one line
\n",
"over *two\nlines* test\n",
"over two\nlines test
\n",
"odd *number of* markers* here\n",
"odd number of markers* here
\n",
"odd *number\nof* markers* here\n",
"odd number\nof markers* here
\n",
"simple _inline_ test\n",
"simple inline test
\n",
"_at the_ beginning\n",
"at the beginning
\n",
"at the _end_\n",
"at the end
\n",
"_try two_ in _one line_\n",
"try two in one line
\n",
"over _two\nlines_ test\n",
"over two\nlines test
\n",
"odd _number of_ markers_ here\n",
"odd number of markers_ here
\n",
"odd _number\nof_ markers_ here\n",
"odd number\nof markers_ here
\n",
"mix of *markers_\n",
"mix of *markers_
\n",
}
doTestsInline(t, tests)
}
func TestStrong(t *testing.T) {
var tests = []string{
"nothing inline\n",
"nothing inline
\n",
"simple **inline** test\n",
"simple inline test
\n",
"**at the** beginning\n",
"at the beginning
\n",
"at the **end**\n",
"at the end
\n",
"**try two** in **one line**\n",
"try two in one line
\n",
"over **two\nlines** test\n",
"over two\nlines test
\n",
"odd **number of** markers** here\n",
"odd number of markers** here
\n",
"odd **number\nof** markers** here\n",
"odd number\nof markers** here
\n",
"simple __inline__ test\n",
"simple inline test
\n",
"__at the__ beginning\n",
"at the beginning
\n",
"at the __end__\n",
"at the end
\n",
"__try two__ in __one line__\n",
"try two in one line
\n",
"over __two\nlines__ test\n",
"over two\nlines test
\n",
"odd __number of__ markers__ here\n",
"odd number of markers__ here
\n",
"odd __number\nof__ markers__ here\n",
"odd number\nof markers__ here
\n",
"mix of **markers__\n",
"mix of **markers__
\n",
}
doTestsInline(t, tests)
}
func TestEmphasisMix(t *testing.T) {
var tests = []string{
"***triple emphasis***\n",
"triple emphasis
\n",
"***triple\nemphasis***\n",
"triple\nemphasis
\n",
"___triple emphasis___\n",
"triple emphasis
\n",
"***triple emphasis___\n",
"***triple emphasis___
\n",
"*__triple emphasis__*\n",
"triple emphasis
\n",
"__*triple emphasis*__\n",
"triple emphasis
\n",
"**improper *nesting** is* bad\n",
"improper *nesting is* bad
\n",
"*improper **nesting* is** bad\n",
"improper **nesting is** bad
\n",
}
doTestsInline(t, tests)
}
func TestStrikeThrough(t *testing.T) {
var tests = []string{
"nothing inline\n",
"nothing inline
\n",
"simple ~~inline~~ test\n",
"simple inline test
\n",
"~~at the~~ beginning\n",
"at the beginning
\n",
"at the ~~end~~\n",
"at the end
\n",
"~~try two~~ in ~~one line~~\n",
"try two in one line
\n",
"over ~~two\nlines~~ test\n",
"over two\nlines test
\n",
"odd ~~number of~~ markers~~ here\n",
"odd number of markers~~ here
\n",
"odd ~~number\nof~~ markers~~ here\n",
"odd number\nof markers~~ here
\n",
}
doTestsInline(t, tests)
}
func TestCodeSpan(t *testing.T) {
var tests = []string{
"`source code`\n",
"source code
\n",
"` source code with spaces `\n",
"source code with spaces
\n",
"` source code with spaces `not here\n",
"source code with spaces
not here
\n",
"a `single marker\n",
"a `single marker
\n",
"a single multi-tick marker with ``` no text\n",
"a single multi-tick marker with ``` no text
\n",
"markers with ` ` a space\n",
"markers with a space
\n",
"`source code` and a `stray\n",
"source code
and a `stray
\n",
"`source *with* _awkward characters_ in it`\n",
"source *with* _awkward characters_ in it
\n",
"`split over\ntwo lines`\n",
"split over\ntwo lines
\n",
"```multiple ticks``` for the marker\n",
"multiple ticks
for the marker
\n",
"```multiple ticks `with` ticks inside```\n",
"multiple ticks `with` ticks inside
\n",
}
doTestsInline(t, tests)
}
func TestLineBreak(t *testing.T) {
var tests = []string{
"this line \nhas a break\n",
"this line
\nhas a break
\n",
"this line \ndoes not\n",
"this line\ndoes not
\n",
"this has an \nextra space\n",
"this has an
\nextra space
\n",
}
doTestsInline(t, tests)
}
func TestInlineLink(t *testing.T) {
var tests = []string{
"[foo](/bar/)\n",
"foo
\n",
"[foo with a title](/bar/ \"title\")\n",
"foo with a title
\n",
"[foo with a title](/bar/\t\"title\")\n",
"foo with a title
\n",
"[foo with a title](/bar/ \"title\" )\n",
"foo with a title
\n",
"[foo with a title](/bar/ title with no quotes)\n",
"foo with a title
\n",
"[foo]()\n",
"[foo]()
\n",
"![foo](/bar/)\n",
"\n
\n",
"![foo with a title](/bar/ \"title\")\n",
"\n
\n",
"![foo with a title](/bar/\t\"title\")\n",
"\n
\n",
"![foo with a title](/bar/ \"title\" )\n",
"\n
\n",
"![foo with a title](/bar/ title with no quotes)\n",
"\n
\n",
"![foo]()\n",
"![foo]()
\n",
"[a link]\t(/with_a_tab/)\n",
"a link
\n",
"[a link] (/with_spaces/)\n",
"a link
\n",
"[text (with) [[nested] (brackets)]](/url/)\n",
"text (with) [[nested] (brackets)]
\n",
"[text (with) [broken nested] (brackets)]](/url/)\n",
"[text (with) broken nested]](/url/)
\n",
"[text\nwith a newline](/link/)\n",
"text\nwith a newline
\n",
"[text in brackets] [followed](/by a link/)\n",
"[text in brackets] followed
\n",
"[link with\\] a closing bracket](/url/)\n",
"link with] a closing bracket
\n",
"[link with\\[ an opening bracket](/url/)\n",
"link with[ an opening bracket
\n",
"[link with\\) a closing paren](/url/)\n",
"link with) a closing paren
\n",
"[link with\\( an opening paren](/url/)\n",
"link with( an opening paren
\n",
"[link]( with whitespace)\n",
"link
\n",
"[link]( with whitespace )\n",
"link
\n",
"[![image](someimage)](with image)\n",
"\n
\n",
"[link](url \"one quote)\n",
"link
\n",
"[link](url 'one quote)\n",
"link
\n",
"[link]()\n",
"link
\n",
"[link & ampersand](/url/)\n",
"link & ampersand
\n",
"[link & ampersand](/url/)\n",
"link & ampersand
\n",
"[link](/url/&query)\n",
"link
\n",
"[[t]](/t)\n",
"[t]
\n",
}
doLinkTestsInline(t, tests)
}
func TestNofollowLink(t *testing.T) {
var tests = []string{
"[foo](http://bar.com/foo/)\n",
"foo
\n",
}
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS|HTML_SANITIZE_OUTPUT,
HtmlRendererParameters{})
// HTML_SANITIZE_OUTPUT won't allow relative links, so test that separately:
tests = []string{
"[foo](/bar/)\n",
"foo
\n",
}
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_NOFOLLOW_LINKS, HtmlRendererParameters{})
}
func TestHrefTargetBlank(t *testing.T) {
var tests = []string{
// internal link
"[foo](/bar/)\n",
"foo
\n",
"[foo](http://example.com)\n",
"foo
\n",
}
doTestsInlineParam(t, tests, 0, HTML_SAFELINK|HTML_HREF_TARGET_BLANK, HtmlRendererParameters{})
}
func TestSafeInlineLink(t *testing.T) {
var tests = []string{
"[foo](/bar/)\n",
"foo
\n",
"[foo](http://bar/)\n",
"foo
\n",
"[foo](https://bar/)\n",
"foo
\n",
"[foo](ftp://bar/)\n",
"foo
\n",
"[foo](mailto://bar/)\n",
"foo
\n",
// Not considered safe
"[foo](baz://bar/)\n",
"foo
\n",
}
doSafeTestsInline(t, tests)
}
func TestReferenceLink(t *testing.T) {
var tests = []string{
"[link][ref]\n",
"[link][ref]
\n",
"[link][ref]\n [ref]: /url/ \"title\"\n",
"link
\n",
"[link][ref]\n [ref]: /url/\n",
"link
\n",
" [ref]: /url/\n",
"",
" [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n",
"",
" [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n [4spaces]: /url/\n",
"[4spaces]: /url/\n
\n",
"[hmm](ref2)\n [ref]: /url/\n[ref2]: /url/\n [ref3]: /url/\n",
"hmm
\n",
"[ref]\n",
"[ref]
\n",
"[ref]\n [ref]: /url/ \"title\"\n",
"ref
\n",
}
doLinkTestsInline(t, tests)
}
func TestTags(t *testing.T) {
var tests = []string{
"a tag\n",
"a tag
\n",
"tag\n",
"tag
\n",
"mismatch\n",
"mismatch
\n",
"a tag\n",
"a tag
\n",
}
doTestsInline(t, tests)
}
func TestAutoLink(t *testing.T) {
var tests = []string{
"http://foo.com/\n",
"http://foo.com/
\n",
"1 http://foo.com/\n",
"1 http://foo.com/
\n",
"1http://foo.com/\n",
"1http://foo.com/
\n",
"1.http://foo.com/\n",
"1.http://foo.com/
\n",
"1. http://foo.com/\n",
"\n- http://foo.com/
\n
\n",
"-http://foo.com/\n",
"-http://foo.com/
\n",
"- http://foo.com/\n",
"\n",
"_http://foo.com/\n",
"_http://foo.com/
\n",
"令狐http://foo.com/\n",
"令狐http://foo.com/
\n",
"令狐 http://foo.com/\n",
"令狐 http://foo.com/
\n",
"ahttp://foo.com/\n",
"ahttp://foo.com/
\n",
">http://foo.com/\n",
"\nhttp://foo.com/
\n
\n",
"> http://foo.com/\n",
"\nhttp://foo.com/
\n
\n",
"go to \n",
"go to http://foo.com/
\n",
"a secure \n",
"a secure https://link.org
\n",
"an email \n",
"an email some@one.com
\n",
"an email \n",
"an email some@one.com
\n",
"an email \n",
"an email some@one.com
\n",
"an ftp \n",
"an ftp ftp://old.com
\n",
"an ftp \n",
"an ftp ftp:old.com
\n",
"a link with \n",
"a link with " +
"http://new.com?query=foo&bar
\n",
"quotes mean a tag \n",
"quotes mean a tag
\n",
"quotes mean a tag \n",
"quotes mean a tag
\n",
"unless escaped \n",
"unless escaped " +
"http://new.com?query="foo"&bar
\n",
"even a > can be escaped &etc>\n",
"even a > can be escaped " +
"http://new.com?q=>&etc
\n",
"http://fancy.com\n",
"http://fancy.com
\n",
"This is a link\n",
"This is a link
\n",
"http://www.fancy.com/A_B.pdf\n",
"http://www.fancy.com/A_B.pdf
\n",
"(http://www.fancy.com/A_B (\n",
"(http://www.fancy.com/A_B (
\n",
"(http://www.fancy.com/A_B (part two: http://www.fancy.com/A_B)).\n",
"(http://www.fancy.com/A_B (part two: http://www.fancy.com/A_B)).
\n",
"http://www.foo.com
\n",
"http://www.foo.com
\n",
"http://foo.com/viewtopic.php?f=18&t=297",
"http://foo.com/viewtopic.php?f=18&t=297
\n",
"http://foo.com/viewtopic.php?param="18"zz",
"http://foo.com/viewtopic.php?param="18"zz
\n",
"http://foo.com/viewtopic.php?param="18"",
"http://foo.com/viewtopic.php?param="18"
\n",
}
doLinkTestsInline(t, tests)
}
var footnoteTests = []string{
"testing footnotes.[^a]\n\n[^a]: This is the note\n",
`testing footnotes.
`,
`testing long[^b] notes.
[^b]: Paragraph 1
Paragraph 2
` + "```\n\tsome code\n\t```" + `
Paragraph 3
No longer in the footnote
`,
`testing long notes.
No longer in the footnote
`,
`testing[^c] multiple[^d] notes.
[^c]: this is [note] c
omg
[^d]: this is note d
what happens here
[note]: /link/c
`,
`testing multiple notes.
omg
what happens here
`,
"testing inline^[this is the note] notes.\n",
`testing inline notes.
`,
"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",
`testing multiple types of notes
`,
`This is a footnote[^1]^[and this is an inline footnote]
[^1]: the footnote text.
may be multiple paragraphs.
`,
`This is a footnote2
`,
"empty footnote[^]\n\n[^]: fn text",
"empty footnote
\n\n",
}
func TestFootnotes(t *testing.T) {
doTestsInlineParam(t, footnoteTests, EXTENSION_FOOTNOTES, 0, HtmlRendererParameters{})
}
func TestFootnotesWithParameters(t *testing.T) {
tests := make([]string, len(footnoteTests))
prefix := "testPrefix"
returnText := "ret"
re := regexp.MustCompile(`(?ms)(.*?)`)
// Transform the test expectations to match the parameters we're using.
for i, test := range footnoteTests {
if i%2 == 1 {
test = strings.Replace(test, "fn:", "fn:"+prefix, -1)
test = strings.Replace(test, "fnref:", "fnref:"+prefix, -1)
test = re.ReplaceAllString(test, `$2 `)
}
tests[i] = test
}
params := HtmlRendererParameters{
FootnoteAnchorPrefix: prefix,
FootnoteReturnLinkContents: returnText,
}
doTestsInlineParam(t, tests, EXTENSION_FOOTNOTES, HTML_FOOTNOTE_RETURN_LINKS, params)
}