1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-25 16:19:46 +02:00

Upgrade to latest Markdown processing engine

This commit is contained in:
Harvey Kandola 2018-02-09 13:34:53 +00:00
parent 1e2c468dc4
commit 0262763c95
22 changed files with 3560 additions and 2394 deletions

View file

@ -14,68 +14,10 @@
package blackfriday
import (
"strings"
"testing"
)
func runMarkdownBlockWithRenderer(input string, extensions int, renderer Renderer) string {
return string(Markdown([]byte(input), renderer, extensions))
}
func runMarkdownBlock(input string, extensions int) string {
htmlFlags := 0
htmlFlags |= HTML_USE_XHTML
renderer := HtmlRenderer(htmlFlags, "", "")
return runMarkdownBlockWithRenderer(input, extensions, renderer)
}
func runnerWithRendererParameters(parameters HtmlRendererParameters) func(string, int) string {
return func(input string, extensions int) string {
htmlFlags := 0
htmlFlags |= HTML_USE_XHTML
renderer := HtmlRendererWithParameters(htmlFlags, "", "", parameters)
return runMarkdownBlockWithRenderer(input, extensions, renderer)
}
}
func doTestsBlock(t *testing.T, tests []string, extensions int) {
doTestsBlockWithRunner(t, tests, extensions, runMarkdownBlock)
}
func doTestsBlockWithRunner(t *testing.T, tests []string, extensions int, runner func(string, int) string) {
// catch and report panics
var candidate string
defer func() {
if err := recover(); err != nil {
t.Errorf("\npanic while processing [%#v]: %s\n", candidate, err)
}
}()
for i := 0; i+1 < len(tests); i += 2 {
input := tests[i]
candidate = input
expected := tests[i+1]
actual := runner(candidate, extensions)
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]
_ = runMarkdownBlock(candidate, extensions)
}
}
}
}
}
func TestPrefixHeaderNoExtensions(t *testing.T) {
var tests = []string{
"# Header 1\n",
@ -202,7 +144,7 @@ func TestPrefixHeaderSpaceExtension(t *testing.T) {
"<ul>\n<li><p>List</p>\n\n<ul>\n<li><p>Nested list</p>\n\n" +
"<h1>Nested header</h1></li>\n</ul></li>\n</ul>\n",
}
doTestsBlock(t, tests, EXTENSION_SPACE_HEADERS)
doTestsBlock(t, tests, SpaceHeadings)
}
func TestPrefixHeaderIdExtension(t *testing.T) {
@ -262,7 +204,7 @@ func TestPrefixHeaderIdExtension(t *testing.T) {
"<ul>\n<li><p>List</p>\n\n<ul>\n<li><p>Nested list</p>\n\n" +
"<h1 id=\"someid\">Nested header</h1></li>\n</ul></li>\n</ul>\n",
}
doTestsBlock(t, tests, EXTENSION_HEADER_IDS)
doTestsBlock(t, tests, HeadingIDs)
}
func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) {
@ -305,12 +247,16 @@ func TestPrefixHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) {
"<h1 id=\"PRE:someid:POST\">Nested header</h1></li>\n</ul></li>\n</ul>\n",
}
parameters := HtmlRendererParameters{
HeaderIDPrefix: "PRE:",
HeaderIDSuffix: ":POST",
parameters := HTMLRendererParameters{
HeadingIDPrefix: "PRE:",
HeadingIDSuffix: ":POST",
}
doTestsBlockWithRunner(t, tests, EXTENSION_HEADER_IDS, runnerWithRendererParameters(parameters))
doTestsParam(t, tests, TestParams{
extensions: HeadingIDs,
HTMLFlags: UseXHTML,
HTMLRendererParameters: parameters,
})
}
func TestPrefixAutoHeaderIdExtension(t *testing.T) {
@ -361,7 +307,7 @@ func TestPrefixAutoHeaderIdExtension(t *testing.T) {
"# Header\n\n# Header 1\n\n# Header\n\n# Header",
"<h1 id=\"header\">Header</h1>\n\n<h1 id=\"header-1\">Header 1</h1>\n\n<h1 id=\"header-1-1\">Header</h1>\n\n<h1 id=\"header-1-2\">Header</h1>\n",
}
doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS)
doTestsBlock(t, tests, AutoHeadingIDs)
}
func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) {
@ -413,12 +359,16 @@ func TestPrefixAutoHeaderIdExtensionWithPrefixAndSuffix(t *testing.T) {
"<h1 id=\"PRE:header:POST\">Header</h1>\n\n<h1 id=\"PRE:header-1:POST\">Header 1</h1>\n\n<h1 id=\"PRE:header-1-1:POST\">Header</h1>\n\n<h1 id=\"PRE:header-1-2:POST\">Header</h1>\n",
}
parameters := HtmlRendererParameters{
HeaderIDPrefix: "PRE:",
HeaderIDSuffix: ":POST",
parameters := HTMLRendererParameters{
HeadingIDPrefix: "PRE:",
HeadingIDSuffix: ":POST",
}
doTestsBlockWithRunner(t, tests, EXTENSION_AUTO_HEADER_IDS, runnerWithRendererParameters(parameters))
doTestsParam(t, tests, TestParams{
extensions: AutoHeadingIDs,
HTMLFlags: UseXHTML,
HTMLRendererParameters: parameters,
})
}
func TestPrefixMultipleHeaderExtensions(t *testing.T) {
@ -426,7 +376,7 @@ func TestPrefixMultipleHeaderExtensions(t *testing.T) {
"# Header\n\n# Header {#header}\n\n# Header 1",
"<h1 id=\"header\">Header</h1>\n\n<h1 id=\"header-1\">Header</h1>\n\n<h1 id=\"header-1-1\">Header 1</h1>\n",
}
doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS|EXTENSION_HEADER_IDS)
doTestsBlock(t, tests, AutoHeadingIDs|HeadingIDs)
}
func TestUnderlineHeaders(t *testing.T) {
@ -526,7 +476,7 @@ func TestUnderlineHeadersAutoIDs(t *testing.T) {
"Header 1\n========\n\nHeader 1\n========\n",
"<h1 id=\"header-1\">Header 1</h1>\n\n<h1 id=\"header-1-1\">Header 1</h1>\n",
}
doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS)
doTestsBlock(t, tests, AutoHeadingIDs)
}
func TestHorizontalRule(t *testing.T) {
@ -900,7 +850,7 @@ func TestDefinitionList(t *testing.T) {
"</dl>\n" +
"\n<p>Text 2</p>\n",
}
doTestsBlock(t, tests, EXTENSION_DEFINITION_LISTS)
doTestsBlock(t, tests, DefinitionLists)
}
func TestPreformattedHtml(t *testing.T) {
@ -976,7 +926,7 @@ func TestPreformattedHtmlLax(t *testing.T) {
"Paragraph\n\n<div>\nHow about here? >&<\n</div>\n\nAnd here?\n",
"<p>Paragraph</p>\n\n<div>\nHow about here? >&<\n</div>\n\n<p>And here?</p>\n",
}
doTestsBlock(t, tests, EXTENSION_LAX_HTML_BLOCKS)
doTestsBlock(t, tests, LaxHTMLBlocks)
}
func TestFencedCodeBlock(t *testing.T) {
@ -1061,8 +1011,126 @@ func TestFencedCodeBlock(t *testing.T) {
"Some text before a fenced code block\n``` oz\ncode blocks breakup paragraphs\n```\nSome text in between\n``` oz\nmultiple code blocks work okay\n```\nAnd some text after a fenced code block",
"<p>Some text before a fenced code block</p>\n\n<pre><code class=\"language-oz\">code blocks breakup paragraphs\n</code></pre>\n\n<p>Some text in between</p>\n\n<pre><code class=\"language-oz\">multiple code blocks work okay\n</code></pre>\n\n<p>And some text after a fenced code block</p>\n",
"```\n[]:()\n```\n",
"<pre><code>[]:()\n</code></pre>\n",
"```\n[]:()\n[]:)\n[]:(\n[]:x\n[]:testing\n[:testing\n\n[]:\nlinebreak\n[]()\n\n[]:\n[]()\n```",
"<pre><code>[]:()\n[]:)\n[]:(\n[]:x\n[]:testing\n[:testing\n\n[]:\nlinebreak\n[]()\n\n[]:\n[]()\n</code></pre>\n",
}
doTestsBlock(t, tests, EXTENSION_FENCED_CODE)
doTestsBlock(t, tests, FencedCode)
}
func TestFencedCodeInsideBlockquotes(t *testing.T) {
cat := func(s ...string) string { return strings.Join(s, "\n") }
var tests = []string{
cat("> ```go",
"package moo",
"",
"```",
""),
`<blockquote>
<pre><code class="language-go">package moo
</code></pre>
</blockquote>
`,
// -------------------------------------------
cat("> foo",
"> ",
"> ```go",
"package moo",
"```",
"> ",
"> goo.",
""),
`<blockquote>
<p>foo</p>
<pre><code class="language-go">package moo
</code></pre>
<p>goo.</p>
</blockquote>
`,
// -------------------------------------------
cat("> foo",
"> ",
"> quote",
"continues",
"```",
""),
`<blockquote>
<p>foo</p>
<p>quote
continues
` + "```" + `</p>
</blockquote>
`,
// -------------------------------------------
cat("> foo",
"> ",
"> ```go",
"package moo",
"```",
"> ",
"> goo.",
"> ",
"> ```go",
"package zoo",
"```",
"> ",
"> woo.",
""),
`<blockquote>
<p>foo</p>
<pre><code class="language-go">package moo
</code></pre>
<p>goo.</p>
<pre><code class="language-go">package zoo
</code></pre>
<p>woo.</p>
</blockquote>
`,
}
// These 2 alternative forms of blockquoted fenced code blocks should produce same output.
forms := [2]string{
cat("> plain quoted text",
"> ```fenced",
"code",
" with leading single space correctly preserved",
"okay",
"```",
"> rest of quoted text"),
cat("> plain quoted text",
"> ```fenced",
"> code",
"> with leading single space correctly preserved",
"> okay",
"> ```",
"> rest of quoted text"),
}
want := `<blockquote>
<p>plain quoted text</p>
<pre><code class="language-fenced">code
with leading single space correctly preserved
okay
</code></pre>
<p>rest of quoted text</p>
</blockquote>
`
tests = append(tests, forms[0], want)
tests = append(tests, forms[1], want)
doTestsBlock(t, tests, FencedCode)
}
func TestTable(t *testing.T) {
@ -1109,7 +1177,7 @@ func TestTable(t *testing.T) {
"a|b\\|c|d\n---|---|---\nf|g\\|h|i\n",
"<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b|c</th>\n<th>d</th>\n</tr>\n</thead>\n\n<tbody>\n<tr>\n<td>f</td>\n<td>g|h</td>\n<td>i</td>\n</tr>\n</tbody>\n</table>\n",
}
doTestsBlock(t, tests, EXTENSION_TABLES)
doTestsBlock(t, tests, Tables)
}
func TestUnorderedListWith_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
@ -1220,7 +1288,7 @@ func TestUnorderedListWith_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
"* List\n\n * sublist\n\n normal text\n\n * another sublist\n",
"<ul>\n<li><p>List</p>\n\n<ul>\n<li>sublist</li>\n</ul>\n\n<p>normal text</p>\n\n<ul>\n<li>another sublist</li>\n</ul></li>\n</ul>\n",
}
doTestsBlock(t, tests, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK)
doTestsBlock(t, tests, NoEmptyLineBeforeBlock)
}
func TestOrderedList_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
@ -1316,7 +1384,7 @@ func TestOrderedList_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
"1. numbers\n1. are ignored\n",
"<ol>\n<li>numbers</li>\n<li>are ignored</li>\n</ol>\n",
}
doTestsBlock(t, tests, EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK)
doTestsBlock(t, tests, NoEmptyLineBeforeBlock)
}
func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
@ -1387,7 +1455,7 @@ func TestFencedCodeBlock_EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK(t *testing.T) {
" ``` oz\nleading spaces\n ```\n",
"<pre><code>``` oz\n</code></pre>\n\n<p>leading spaces</p>\n\n<pre><code>```\n</code></pre>\n",
}
doTestsBlock(t, tests, EXTENSION_FENCED_CODE|EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK)
doTestsBlock(t, tests, FencedCode|NoEmptyLineBeforeBlock)
}
func TestTitleBlock_EXTENSION_TITLEBLOCK(t *testing.T) {
@ -1398,10 +1466,226 @@ func TestTitleBlock_EXTENSION_TITLEBLOCK(t *testing.T) {
"<h1 class=\"title\">" +
"Some title\n" +
"Another title line\n" +
"Yep, more here too\n" +
"</h1>",
"Yep, more here too" +
"</h1>\n",
}
doTestsBlock(t, tests, Titleblock)
}
func TestBlockComments(t *testing.T) {
var tests = []string{
"Some text\n\n<!-- comment -->\n",
"<p>Some text</p>\n\n<!-- comment -->\n",
"Some text\n\n<!--\n\nmultiline\ncomment\n-->\n",
"<p>Some text</p>\n\n<!--\n\nmultiline\ncomment\n-->\n",
"Some text\n\n<!--\n\n<div><p>Commented</p>\n<span>html</span></div>\n-->\n",
"<p>Some text</p>\n\n<!--\n\n<div><p>Commented</p>\n<span>html</span></div>\n-->\n",
}
doTestsBlock(t, tests, 0)
}
func TestTOC(t *testing.T) {
var tests = []string{
"# Title\n\n##Subtitle1\n\n##Subtitle2",
//"<nav>\n<ul>\n<li><a href=\"#toc_0\">Title</a>\n<ul>\n<li><a href=\"#toc_1\">Subtitle1</a></li>\n<li><a href=\"#toc_2\">Subtitle2</a></li>\n</ul></li>\n</ul>\n</nav>\n\n<h1 id=\"toc_0\">Title</h1>\n\n<h2 id=\"toc_1\">Subtitle1</h2>\n\n<h2 id=\"toc_2\">Subtitle2</h2>\n",
`<nav>
<ul>
<li><a href="#toc_0">Title</a>
<ul>
<li><a href="#toc_1">Subtitle1</a></li>
<li><a href="#toc_2">Subtitle2</a></li>
</ul></li>
</ul>
</nav>
<h1 id="toc_0">Title</h1>
<h2 id="toc_1">Subtitle1</h2>
<h2 id="toc_2">Subtitle2</h2>
`,
"# Title\n\n##Subtitle\n\n#Title2",
//"<nav>\n<ul>\n<li><a href=\"#toc_0\">Title</a>\n<ul>\n<li><a href=\"#toc_1\">Subtitle</a></li>\n</ul></li>\n<li><a href=\"#toc_2\">Title2</a></li>\n</ul>\n</nav>\n\n<h1 id=\"toc_0\">Title</h1>\n\n<h2 id=\"toc_1\">Subtitle</h2>\n\n<h1 id=\"toc_2\">Title2</h1>\n",
`<nav>
<ul>
<li><a href="#toc_0">Title</a>
<ul>
<li><a href="#toc_1">Subtitle</a></li>
</ul></li>
<li><a href="#toc_2">Title2</a></li>
</ul>
</nav>
<h1 id="toc_0">Title</h1>
<h2 id="toc_1">Subtitle</h2>
<h1 id="toc_2">Title2</h1>
`,
"## Subtitle\n\n# Title",
`<nav>
<ul>
<li>
<ul>
<li><a href="#toc_0">Subtitle</a></li>
</ul></li>
<li><a href="#toc_1">Title</a></li>
</ul>
</nav>
<h2 id="toc_0">Subtitle</h2>
<h1 id="toc_1">Title</h1>
`,
"# Title 1\n\n## Subtitle 1\n\n### Subsubtitle 1\n\n# Title 2\n\n### Subsubtitle 2",
`<nav>
<ul>
<li><a href="#toc_0">Title 1</a>
<ul>
<li><a href="#toc_1">Subtitle 1</a>
<ul>
<li><a href="#toc_2">Subsubtitle 1</a></li>
</ul></li>
</ul></li>
<li><a href="#toc_3">Title 2</a>
<ul>
<li>
<ul>
<li><a href="#toc_4">Subsubtitle 2</a></li>
</ul></li>
</ul></li>
</ul>
</nav>
<h1 id="toc_0">Title 1</h1>
<h2 id="toc_1">Subtitle 1</h2>
<h3 id="toc_2">Subsubtitle 1</h3>
<h1 id="toc_3">Title 2</h1>
<h3 id="toc_4">Subsubtitle 2</h3>
`,
"# Title with `code`",
`<nav>
<ul>
<li><a href="#toc_0">Title with <code>code</code></a></li>
</ul>
</nav>
<h1 id="toc_0">Title with <code>code</code></h1>
`,
// Trigger empty TOC
"#",
"",
}
doTestsParam(t, tests, TestParams{
HTMLFlags: UseXHTML | TOC,
})
}
func TestCompletePage(t *testing.T) {
var tests = []string{
"*foo*",
`<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="GENERATOR" content="Blackfriday Markdown Processor v2.0" />
<meta charset="utf-8" />
</head>
<body>
<p><em>foo</em></p>
</body>
</html>
`,
}
doTestsParam(t, tests, TestParams{HTMLFlags: UseXHTML | CompletePage})
}
func TestIsFenceLine(t *testing.T) {
tests := []struct {
data []byte
syntaxRequested bool
wantEnd int
wantMarker string
wantSyntax string
}{
{
data: []byte("```"),
wantEnd: 3,
wantMarker: "```",
},
{
data: []byte("```\nstuff here\n"),
wantEnd: 4,
wantMarker: "```",
},
{
data: []byte("```\nstuff here\n"),
syntaxRequested: true,
wantEnd: 4,
wantMarker: "```",
},
{
data: []byte("stuff here\n```\n"),
wantEnd: 0,
},
{
data: []byte("```"),
syntaxRequested: true,
wantEnd: 3,
wantMarker: "```",
},
{
data: []byte("``` go"),
syntaxRequested: true,
wantEnd: 6,
wantMarker: "```",
wantSyntax: "go",
},
}
doTestsBlock(t, tests, EXTENSION_TITLEBLOCK)
for _, test := range tests {
var syntax *string
if test.syntaxRequested {
syntax = new(string)
}
end, marker := isFenceLine(test.data, syntax, "```")
if got, want := end, test.wantEnd; got != want {
t.Errorf("got end %v, want %v", got, want)
}
if got, want := marker, test.wantMarker; got != want {
t.Errorf("got marker %q, want %q", got, want)
}
if test.syntaxRequested {
if got, want := *syntax, test.wantSyntax; got != want {
t.Errorf("got syntax %q, want %q", got, want)
}
}
}
}