From 2a0329423ce4b3359842affad76c40140d426943 Mon Sep 17 00:00:00 2001 From: Bjørn Erik Pedersen Date: Sun, 28 Jan 2024 11:41:59 +0100 Subject: testing: Rename integration_test.go to PACKAGE_integration_test.go Primary motivation making them easier to find in the code editor. --- .../codeblocks/codeblocks_integration_test.go | 422 +++++++++++ markup/goldmark/codeblocks/integration_test.go | 422 ----------- markup/goldmark/goldmark_integration_test.go | 813 +++++++++++++++++++++ markup/goldmark/images/images_integration_test.go | 112 +++ markup/goldmark/images/integration_test.go | 112 --- markup/goldmark/integration_test.go | 813 --------------------- markup/highlight/highlight_integration_test.go | 117 +++ markup/highlight/integration_test.go | 117 --- markup/tableofcontents/integration_test.go | 45 -- .../tableofcontents_integration_test.go | 45 ++ 10 files changed, 1509 insertions(+), 1509 deletions(-) create mode 100644 markup/goldmark/codeblocks/codeblocks_integration_test.go delete mode 100644 markup/goldmark/codeblocks/integration_test.go create mode 100644 markup/goldmark/goldmark_integration_test.go create mode 100644 markup/goldmark/images/images_integration_test.go delete mode 100644 markup/goldmark/images/integration_test.go delete mode 100644 markup/goldmark/integration_test.go create mode 100644 markup/highlight/highlight_integration_test.go delete mode 100644 markup/highlight/integration_test.go delete mode 100644 markup/tableofcontents/integration_test.go create mode 100644 markup/tableofcontents/tableofcontents_integration_test.go (limited to 'markup') diff --git a/markup/goldmark/codeblocks/codeblocks_integration_test.go b/markup/goldmark/codeblocks/codeblocks_integration_test.go new file mode 100644 index 000000000..5597fc507 --- /dev/null +++ b/markup/goldmark/codeblocks/codeblocks_integration_test.go @@ -0,0 +1,422 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package codeblocks_test + +import ( + "path/filepath" + "strings" + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestCodeblocks(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup] + [markup.highlight] + anchorLineNos = false + codeFences = true + guessSyntax = false + hl_Lines = '' + lineAnchors = '' + lineNoStart = 1 + lineNos = false + lineNumbersInTable = true + noClasses = false + style = 'monokai' + tabWidth = 4 +-- layouts/_default/_markup/render-codeblock-goat.html -- +{{ $diagram := diagrams.Goat .Inner }} +Goat SVG:{{ substr $diagram.Wrapped 0 100 | safeHTML }} }}| +Goat Attribute: {{ .Attributes.width}}| +-- layouts/_default/_markup/render-codeblock-go.html -- +Go Code: {{ .Inner | safeHTML }}| +Go Language: {{ .Type }}| +-- layouts/_default/single.html -- +{{ .Content }} +-- content/p1.md -- +--- +title: "p1" +--- + +## Ascii Diagram + +§§§goat { width="600" } +---> +§§§ + +## Go Code + +§§§go +fmt.Println("Hello, World!"); +§§§ + +## Golang Code + +§§§golang +fmt.Println("Hello, Golang!"); +§§§ + +## Bash Code + +§§§bash { linenos=inline,hl_lines=[2,"5-6"],linenostart=32 class=blue } +echo "l1"; +echo "l2"; +echo "l3"; +echo "l4"; +echo "l5"; +echo "l6"; +echo "l7"; +echo "l8"; +§§§ +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` +Goat SVG:Go Code\nGo Code: fmt.Println(\"Hello, World!\");\n|\nGo Language: go|", + "

Golang Code

\nGo Code: fmt.Println(\"Hello, Golang!\");\n|\nGo Language: golang|", + "

Bash Code

\n
32echo "l1";\n33",
+	)
+}
+
+func TestHighlightCodeblock(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- config.toml --
+[markup]
+[markup.highlight]
+anchorLineNos = false
+codeFences = true
+guessSyntax = false
+hl_Lines = ''
+lineAnchors = ''
+lineNoStart = 1
+lineNos = false
+lineNumbersInTable = true
+noClasses = false
+style = 'monokai'
+tabWidth = 4
+-- layouts/_default/_markup/render-codeblock.html --
+{{ $result := transform.HighlightCodeBlock . }}
+Inner: |{{ $result.Inner | safeHTML }}|
+Wrapped: |{{ $result.Wrapped | safeHTML }}|
+-- layouts/_default/single.html --
+{{ .Content }}
+-- content/p1.md --
+---
+title: "p1"
+---
+
+## Go Code
+
+§§§go
+fmt.Println("Hello, World!");
+§§§
+
+`
+
+	b := hugolib.NewIntegrationTestBuilder(
+		hugolib.IntegrationTestConfig{
+			T:           t,
+			TxtarString: files,
+			NeedsOsFS:   false,
+		},
+	).Build()
+
+	b.AssertFileContent("public/p1/index.html",
+		"Inner: |fmt.Println("Hello, World!");|",
+		"Wrapped: |
fmt.Println("Hello, World!");
|", + ) +} + +func TestCodeblocksBugs(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- layouts/_default/_markup/render-codeblock.html -- +{{ .Position | safeHTML }} +-- layouts/_default/single.html -- +{{ .Content }} +-- content/p1.md -- +--- +title: "p1" +--- + +## Issue 9627 + +§§§text +{{}} +§§§ + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` +# Issue 9627: For the Position in code blocks we try to match the .Inner with the original source. This isn't always possible. +p1.md:0:0 + `, + ) +} + +func TestCodeChomp(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +§§§bash +echo "p1"; +§§§ +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +|{{ .Inner | safeHTML }}| + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "|echo \"p1\";|") +} + +func TestCodePosition(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +## Code + +§§§ +echo "p1"; +§§§ +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +Position: {{ .Position | safeHTML }} + + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", filepath.FromSlash("Position: \"/content/p1.md:7:1\"")) +} + +// Issue 10118 +func TestAttributes(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +## Issue 10118 + +§§§ {foo="bar"} +Hello, World! +§§§ + +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +Attributes: {{ .Attributes }}|Type: {{ .Type }}| +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "

Issue 10118

\nAttributes: map[foo:bar]|Type: |") +} + +// Issue 9571 +func TestAttributesChroma(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- + +## Code + +§§§LANGUAGE {style=monokai} +echo "p1"; +§§§ +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +Attributes: {{ .Attributes }}|Options: {{ .Options }}| + + +` + testLanguage := func(language, expect string) { + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: strings.ReplaceAll(files, "LANGUAGE", language), + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", expect) + } + + testLanguage("bash", "Attributes: map[]|Options: map[style:monokai]|") + testLanguage("hugo", "Attributes: map[style:monokai]|Options: map[]|") +} + +func TestPanics(t *testing.T) { + files := ` +-- config.toml -- +[markup] +[markup.goldmark] +[markup.goldmark.parser] +autoHeadingID = true +autoHeadingIDType = "github" +[markup.goldmark.parser.attribute] +block = true +title = true +-- content/p1.md -- +--- +title: "p1" +--- + +BLOCK + +Common + +-- layouts/_default/single.html -- +{{ .Content }} + + +` + + for _, test := range []struct { + name string + markdown string + }{ + {"issue-9819", "asdf\n: {#myid}"}, + } { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: strings.ReplaceAll(files, "BLOCK", test.markdown), + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "Common") + }) + } +} + +// Issue 10835 +func TestAttributesValidation(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ["taxonomy", "term"] +-- content/p1.md -- +--- +title: "p1" +--- + +## Issue 10835 + +§§§bash { color=red dimensions=300x200 } +Hello, World! +§§§ + +-- layouts/index.html -- +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-codeblock.html -- +Attributes: {{ .Attributes }}|Type: {{ .Type }}| +` + + b, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.Not(qt.IsNil)) + b.Assert(err.Error(), qt.Contains, "p1.md:7:9\": failed to parse Markdown attributes; you may need to quote the values") +} diff --git a/markup/goldmark/codeblocks/integration_test.go b/markup/goldmark/codeblocks/integration_test.go deleted file mode 100644 index 5597fc507..000000000 --- a/markup/goldmark/codeblocks/integration_test.go +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright 2024 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package codeblocks_test - -import ( - "path/filepath" - "strings" - "testing" - - qt "github.com/frankban/quicktest" - - "github.com/gohugoio/hugo/hugolib" -) - -func TestCodeblocks(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup] - [markup.highlight] - anchorLineNos = false - codeFences = true - guessSyntax = false - hl_Lines = '' - lineAnchors = '' - lineNoStart = 1 - lineNos = false - lineNumbersInTable = true - noClasses = false - style = 'monokai' - tabWidth = 4 --- layouts/_default/_markup/render-codeblock-goat.html -- -{{ $diagram := diagrams.Goat .Inner }} -Goat SVG:{{ substr $diagram.Wrapped 0 100 | safeHTML }} }}| -Goat Attribute: {{ .Attributes.width}}| --- layouts/_default/_markup/render-codeblock-go.html -- -Go Code: {{ .Inner | safeHTML }}| -Go Language: {{ .Type }}| --- layouts/_default/single.html -- -{{ .Content }} --- content/p1.md -- ---- -title: "p1" ---- - -## Ascii Diagram - -§§§goat { width="600" } ----> -§§§ - -## Go Code - -§§§go -fmt.Println("Hello, World!"); -§§§ - -## Golang Code - -§§§golang -fmt.Println("Hello, Golang!"); -§§§ - -## Bash Code - -§§§bash { linenos=inline,hl_lines=[2,"5-6"],linenostart=32 class=blue } -echo "l1"; -echo "l2"; -echo "l3"; -echo "l4"; -echo "l5"; -echo "l6"; -echo "l7"; -echo "l8"; -§§§ -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` -Goat SVG:Go Code\nGo Code: fmt.Println(\"Hello, World!\");\n|\nGo Language: go|", - "

Golang Code

\nGo Code: fmt.Println(\"Hello, Golang!\");\n|\nGo Language: golang|", - "

Bash Code

\n
32echo "l1";\n33",
-	)
-}
-
-func TestHighlightCodeblock(t *testing.T) {
-	t.Parallel()
-
-	files := `
--- config.toml --
-[markup]
-[markup.highlight]
-anchorLineNos = false
-codeFences = true
-guessSyntax = false
-hl_Lines = ''
-lineAnchors = ''
-lineNoStart = 1
-lineNos = false
-lineNumbersInTable = true
-noClasses = false
-style = 'monokai'
-tabWidth = 4
--- layouts/_default/_markup/render-codeblock.html --
-{{ $result := transform.HighlightCodeBlock . }}
-Inner: |{{ $result.Inner | safeHTML }}|
-Wrapped: |{{ $result.Wrapped | safeHTML }}|
--- layouts/_default/single.html --
-{{ .Content }}
--- content/p1.md --
----
-title: "p1"
----
-
-## Go Code
-
-§§§go
-fmt.Println("Hello, World!");
-§§§
-
-`
-
-	b := hugolib.NewIntegrationTestBuilder(
-		hugolib.IntegrationTestConfig{
-			T:           t,
-			TxtarString: files,
-			NeedsOsFS:   false,
-		},
-	).Build()
-
-	b.AssertFileContent("public/p1/index.html",
-		"Inner: |fmt.Println("Hello, World!");|",
-		"Wrapped: |
fmt.Println("Hello, World!");
|", - ) -} - -func TestCodeblocksBugs(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- --- layouts/_default/_markup/render-codeblock.html -- -{{ .Position | safeHTML }} --- layouts/_default/single.html -- -{{ .Content }} --- content/p1.md -- ---- -title: "p1" ---- - -## Issue 9627 - -§§§text -{{}} -§§§ - -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` -# Issue 9627: For the Position in code blocks we try to match the .Inner with the original source. This isn't always possible. -p1.md:0:0 - `, - ) -} - -func TestCodeChomp(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- --- content/p1.md -- ---- -title: "p1" ---- - -§§§bash -echo "p1"; -§§§ --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-codeblock.html -- -|{{ .Inner | safeHTML }}| - -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", "|echo \"p1\";|") -} - -func TestCodePosition(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- --- content/p1.md -- ---- -title: "p1" ---- - -## Code - -§§§ -echo "p1"; -§§§ --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-codeblock.html -- -Position: {{ .Position | safeHTML }} - - -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", filepath.FromSlash("Position: \"/content/p1.md:7:1\"")) -} - -// Issue 10118 -func TestAttributes(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- --- content/p1.md -- ---- -title: "p1" ---- - -## Issue 10118 - -§§§ {foo="bar"} -Hello, World! -§§§ - --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-codeblock.html -- -Attributes: {{ .Attributes }}|Type: {{ .Type }}| -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", "

Issue 10118

\nAttributes: map[foo:bar]|Type: |") -} - -// Issue 9571 -func TestAttributesChroma(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- --- content/p1.md -- ---- -title: "p1" ---- - -## Code - -§§§LANGUAGE {style=monokai} -echo "p1"; -§§§ --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-codeblock.html -- -Attributes: {{ .Attributes }}|Options: {{ .Options }}| - - -` - testLanguage := func(language, expect string) { - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: strings.ReplaceAll(files, "LANGUAGE", language), - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", expect) - } - - testLanguage("bash", "Attributes: map[]|Options: map[style:monokai]|") - testLanguage("hugo", "Attributes: map[style:monokai]|Options: map[]|") -} - -func TestPanics(t *testing.T) { - files := ` --- config.toml -- -[markup] -[markup.goldmark] -[markup.goldmark.parser] -autoHeadingID = true -autoHeadingIDType = "github" -[markup.goldmark.parser.attribute] -block = true -title = true --- content/p1.md -- ---- -title: "p1" ---- - -BLOCK - -Common - --- layouts/_default/single.html -- -{{ .Content }} - - -` - - for _, test := range []struct { - name string - markdown string - }{ - {"issue-9819", "asdf\n: {#myid}"}, - } { - test := test - t.Run(test.name, func(t *testing.T) { - t.Parallel() - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: strings.ReplaceAll(files, "BLOCK", test.markdown), - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", "Common") - }) - } -} - -// Issue 10835 -func TestAttributesValidation(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -disableKinds = ["taxonomy", "term"] --- content/p1.md -- ---- -title: "p1" ---- - -## Issue 10835 - -§§§bash { color=red dimensions=300x200 } -Hello, World! -§§§ - --- layouts/index.html -- --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-codeblock.html -- -Attributes: {{ .Attributes }}|Type: {{ .Type }}| -` - - b, err := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).BuildE() - - b.Assert(err, qt.Not(qt.IsNil)) - b.Assert(err.Error(), qt.Contains, "p1.md:7:9\": failed to parse Markdown attributes; you may need to quote the values") -} diff --git a/markup/goldmark/goldmark_integration_test.go b/markup/goldmark/goldmark_integration_test.go new file mode 100644 index 000000000..c9c6ef338 --- /dev/null +++ b/markup/goldmark/goldmark_integration_test.go @@ -0,0 +1,813 @@ +// Copyright 2021 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package goldmark_test + +import ( + "fmt" + "strings" + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/gohugoio/hugo/hugolib" +) + +// Issue 9463 +func TestAttributeExclusion(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.goldmark.renderer] + unsafe = false +[markup.goldmark.parser.attribute] + block = true + title = true +-- content/p1.md -- +--- +title: "p1" +--- +## Heading {class="a" onclick="alert('heading')"} + +> Blockquote +{class="b" ondblclick="alert('blockquote')"} + +~~~bash {id="c" onmouseover="alert('code fence')" LINENOS=true} +foo +~~~ +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` +

+
+
+ `) +} + +// Issue 9511 +func TestAttributeExclusionWithRenderHook(t *testing.T) { + t.Parallel() + + files := ` +-- content/p1.md -- +--- +title: "p1" +--- +## Heading {onclick="alert('renderhook')" data-foo="bar"} +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-heading.html -- +{{ .Text | safeHTML }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` +

Heading

+ `) +} + +func TestAttributesDefaultRenderer(t *testing.T) { + t.Parallel() + + files := ` +-- content/p1.md -- +--- +title: "p1" +--- +## Heading Attribute Which Needs Escaping { class="a < b" } +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` +class="a < b" + `) +} + +// Issue 9558. +func TestAttributesHookNoEscape(t *testing.T) { + t.Parallel() + + files := ` +-- content/p1.md -- +--- +title: "p1" +--- +## Heading Attribute Which Needs Escaping { class="Smith & Wesson" } +-- layouts/_default/_markup/render-heading.html -- +plain: |{{- range $k, $v := .Attributes -}}{{ $k }}: {{ $v }}|{{ end }}| +safeHTML: |{{- range $k, $v := .Attributes -}}{{ $k }}: {{ $v | safeHTML }}|{{ end }}| +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` +plain: |class: Smith & Wesson|id: heading-attribute-which-needs-escaping| +safeHTML: |class: Smith & Wesson|id: heading-attribute-which-needs-escaping| + `) +} + +// Issue 9504 +func TestLinkInTitle(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- +## Hello [Test](https://example.com) +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-heading.html -- + + {{ .Text | safeHTML }} + # + +-- layouts/_default/_markup/render-link.html -- +{{ .Text | safeHTML }} + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "

\n Hello Test\n\n #\n

", + ) +} + +func TestHighlight(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup] +[markup.highlight] +anchorLineNos = false +codeFences = true +guessSyntax = false +hl_Lines = '' +lineAnchors = '' +lineNoStart = 1 +lineNos = false +lineNumbersInTable = true +noClasses = false +style = 'monokai' +tabWidth = 4 +-- layouts/_default/single.html -- +{{ .Content }} +-- content/p1.md -- +--- +title: "p1" +--- + +## Code Fences + +§§§bash +LINE1 +§§§ + +## Code Fences No Lexer + +§§§moo +LINE1 +§§§ + +## Code Fences Simple Attributes + +§§A§bash { .myclass id="myid" } +LINE1 +§§A§ + +## Code Fences Line Numbers + +§§§bash {linenos=table,hl_lines=[8,"15-17"],linenostart=199} +LINE1 +LINE2 +LINE3 +LINE4 +LINE5 +LINE6 +LINE7 +LINE8 +§§§ + + + + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "
LINE1\n
", + "Code Fences No Lexer

\n
LINE1\n
", + "lnt", + ) +} + +func BenchmarkRenderHooks(b *testing.B) { + files := ` +-- config.toml -- +-- layouts/_default/_markup/render-heading.html -- + + {{ .Text | safeHTML }} + # + +-- layouts/_default/_markup/render-link.html -- +{{ .Text | safeHTML }} +-- layouts/_default/single.html -- +{{ .Content }} +` + + content := ` + +## Hello1 [Test](https://example.com) + +A. + +## Hello2 [Test](https://example.com) + +B. + +## Hello3 [Test](https://example.com) + +C. + +## Hello4 [Test](https://example.com) + +D. + +[Test](https://example.com) + +## Hello5 + + +` + + for i := 1; i < 100; i++ { + files += fmt.Sprintf("\n-- content/posts/p%d.md --\n"+content, i+1) + } + + cfg := hugolib.IntegrationTestConfig{ + T: b, + TxtarString: files, + } + builders := make([]*hugolib.IntegrationTestBuilder, b.N) + + for i := range builders { + builders[i] = hugolib.NewIntegrationTestBuilder(cfg) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + builders[i].Build() + } +} + +func BenchmarkCodeblocks(b *testing.B) { + filesTemplate := ` +-- config.toml -- +[markup] + [markup.highlight] + anchorLineNos = false + codeFences = true + guessSyntax = false + hl_Lines = '' + lineAnchors = '' + lineNoStart = 1 + lineNos = false + lineNumbersInTable = true + noClasses = true + style = 'monokai' + tabWidth = 4 +-- layouts/_default/single.html -- +{{ .Content }} +` + + content := ` + +FENCEgo +package main +import "fmt" +func main() { + fmt.Println("hello world") +} +FENCE + +FENCEunknownlexer +hello +FENCE +` + + content = strings.ReplaceAll(content, "FENCE", "```") + + for i := 1; i < 100; i++ { + filesTemplate += fmt.Sprintf("\n-- content/posts/p%d.md --\n"+content, i+1) + } + + runBenchmark := func(files string, b *testing.B) { + cfg := hugolib.IntegrationTestConfig{ + T: b, + TxtarString: files, + } + builders := make([]*hugolib.IntegrationTestBuilder, b.N) + + for i := range builders { + builders[i] = hugolib.NewIntegrationTestBuilder(cfg) + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + builders[i].Build() + } + } + + b.Run("Default", func(b *testing.B) { + runBenchmark(filesTemplate, b) + }) + + b.Run("Hook no higlight", func(b *testing.B) { + files := filesTemplate + ` +-- layouts/_default/_markup/render-codeblock.html -- +{{ .Inner }} +` + + runBenchmark(files, b) + }) + +} + +// Iisse #8959 +func TestHookInfiniteRecursion(t *testing.T) { + t.Parallel() + + for _, renderFunc := range []string{"markdownify", ".Page.RenderString"} { + t.Run(renderFunc, func(t *testing.T) { + + files := ` +-- config.toml -- +-- layouts/_default/_markup/render-link.html -- +{{ .Text | RENDERFUNC }} +-- layouts/_default/single.html -- +{{ .Content }} +-- content/p1.md -- +--- +title: "p1" +--- + +https://example.org + +a@b.com + + + ` + + files = strings.ReplaceAll(files, "RENDERFUNC", renderFunc) + + b, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, "text is already rendered, repeating it may cause infinite recursion") + + }) + + } + +} + +// Issue 9594 +func TestQuotesInImgAltAttr(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.goldmark.extensions] + typographer = false +-- content/p1.md -- +--- +title: "p1" +--- +!["a"](b.jpg) +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", ` + "a" + `) +} + +func TestLinkifyProtocol(t *testing.T) { + t.Parallel() + + runTest := func(protocol string, withHook bool) *hugolib.IntegrationTestBuilder { + + files := ` +-- config.toml -- +[markup.goldmark] +[markup.goldmark.extensions] +linkify = true +linkifyProtocol = "PROTOCOL" +-- content/p1.md -- +--- +title: "p1" +--- +Link no procol: www.example.org +Link http procol: http://www.example.org +Link https procol: https://www.example.org + +-- layouts/_default/single.html -- +{{ .Content }} +` + files = strings.ReplaceAll(files, "PROTOCOL", protocol) + + if withHook { + files += `-- layouts/_default/_markup/render-link.html -- +{{ .Text | safeHTML }}` + } + + return hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + } + + for _, withHook := range []bool{false, true} { + + b := runTest("https", withHook) + + b.AssertFileContent("public/p1/index.html", + "Link no procol: www.example.org", + "Link http procol: http://www.example.org", + "Link https procol: https://www.example.org

", + ) + + b = runTest("http", withHook) + + b.AssertFileContent("public/p1/index.html", + "Link no procol: www.example.org", + "Link http procol: http://www.example.org", + "Link https procol: https://www.example.org

", + ) + + b = runTest("gopher", withHook) + + b.AssertFileContent("public/p1/index.html", + "Link no procol: www.example.org", + "Link http procol: http://www.example.org", + "Link https procol: https://www.example.org

", + ) + + } +} + +func TestGoldmarkBugs(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.goldmark.renderer] +unsafe = true +-- content/p1.md -- +--- +title: "p1" +--- + +## Issue 9650 + +a c + +## Issue 9658 + +- This is a list item + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContentExact("public/p1/index.html", + // Issue 9650 + "

a c

", + // Issue 9658 (crash) + "
  • This is a list item
  • ", + ) +} + +// Issue #7332 +// Issue #11587 +func TestGoldmarkEmojiExtension(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +enableEmoji = true +-- content/p1.md -- +--- +title: "p1" +--- +~~~text +:x: +~~~ + +{{% include "/p2" %}} + +{{< sc1 >}}:smiley:{{< /sc1 >}} + +{{< sc2 >}}:+1:{{< /sc2 >}} + +{{% sc3 %}}:-1:{{% /sc3 %}} + +-- content/p2.md -- +--- +title: "p2" +--- +:heavy_check_mark: +-- layouts/shortcodes/include.html -- +{{ $p := site.GetPage (.Get 0) }} +{{ $p.RenderShortcodes }} +-- layouts/shortcodes/sc1.html -- +sc1_begin|{{ .Inner }}|sc1_end +-- layouts/shortcodes/sc2.html -- +sc2_begin|{{ .Inner | .Page.RenderString }}|sc2_end +-- layouts/shortcodes/sc3.html -- +sc3_begin|{{ .Inner }}|sc3_end +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContentExact("public/p1/index.html", + // Issue #7332 + ":x:\n", + // Issue #11587 + "

    ✔️

    ", + // Should not be converted to emoji + "sc1_begin|:smiley:|sc1_end", + // Should be converted to emoji + "sc2_begin|👍|sc2_end", + // Should be converted to emoji + "sc3_begin|👎|sc3_end", + ) +} + +func TestEmojiDisabled(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +enableEmoji = false +-- content/p1.md -- +--- +title: "p1" +--- +:x: +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContentExact("public/p1/index.html", "

    :x:

    ") +} + +func TestEmojiDefaultConfig(t *testing.T) { + t.Parallel() + + files := ` +-- content/p1.md -- +--- +title: "p1" +--- +:x: +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + }, + ).Build() + + b.AssertFileContentExact("public/p1/index.html", "

    :x:

    ") +} + +// Issue #5748 +func TestGoldmarkTemplateDelims(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[minify] + minifyOutput = true +[minify.tdewolff.html] + templateDelims = [""] +-- layouts/index.html -- +
    +{{ safeHTML "" }} +
    +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/index.html", "
    \n
    ") +} + +// Issue #10894 +func TestPassthroughInlineFences(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.goldmark.extensions.passthrough] +enable = true +[markup.goldmark.extensions.passthrough.delimiters] +inline = [['$', '$'], ['\(', '\)']] +-- content/p1.md -- +--- +title: "p1" +--- +## LaTeX test + +Inline equation that would be mangled by default parser: $a^*=x-b^*$ + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", ` + $a^*=x-b^*$ + `) +} + +func TestPassthroughBlockFences(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.goldmark.extensions.passthrough] +enable = true +[markup.goldmark.extensions.passthrough.delimiters] +block = [['$$', '$$']] +-- content/p1.md -- +--- +title: "p1" +--- +## LaTeX test + +Block equation that would be mangled by default parser: + +$$a^*=x-b^*$$ + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", ` + $$a^*=x-b^*$$ + `) +} + +func TestPassthroughWithAlternativeFences(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.goldmark.extensions.passthrough] +enable = true +[markup.goldmark.extensions.passthrough.delimiters] +inline = [['(((', ')))']] +block = [['%!%', '%!%']] +-- content/p1.md -- +--- +title: "p1" +--- +## LaTeX test + +Inline equation that would be mangled by default parser: (((a^*=x-b^*))) +Inline equation that should be mangled by default parser: $a^*=x-b^*$ + +Block equation that would be mangled by default parser: + +%!% +a^*=x-b^* +%!% + +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/p1/index.html", ` + (((a^*=x-b^*))) + `) + b.AssertFileContent("public/p1/index.html", ` + $a^=x-b^$ + `) + b.AssertFileContent("public/p1/index.html", ` +%!% +a^*=x-b^* +%!% + `) +} diff --git a/markup/goldmark/images/images_integration_test.go b/markup/goldmark/images/images_integration_test.go new file mode 100644 index 000000000..8b0ba99c1 --- /dev/null +++ b/markup/goldmark/images/images_integration_test.go @@ -0,0 +1,112 @@ +package images_test + +import ( + "strings" + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestDisableWrapStandAloneImageWithinParagraph(t *testing.T) { + t.Parallel() + + filesTemplate := ` +-- config.toml -- +[markup.goldmark.renderer] + unsafe = false +[markup.goldmark.parser] +wrapStandAloneImageWithinParagraph = CONFIG_VALUE +[markup.goldmark.parser.attribute] + block = true + title = true +-- content/p1.md -- +--- +title: "p1" +--- + +This is an inline image: ![Inline Image](/inline.jpg). Some more text. + +![Block Image](/block.jpg) +{.b} + + +-- layouts/_default/single.html -- +{{ .Content }} +` + + t.Run("With Hook, no wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "false") + files = files + `-- layouts/_default/_markup/render-image.html -- +{{ if .IsBlock }} +
    + {{ .Text }}|{{ .Ordinal }} +
    +{{ else }} + {{ .Text }}|{{ .Ordinal }} +{{ end }} +` + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "This is an inline image: \n\t\"Inline\n. Some more text.

    ", + "
    \n\t\"Block", + ) + }) + + t.Run("With Hook, wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "true") + files = files + `-- layouts/_default/_markup/render-image.html -- +{{ if .IsBlock }} +
    + {{ .Text }} +
    +{{ else }} + {{ .Text }} +{{ end }} +` + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "This is an inline image: \n\t\"Inline\n. Some more text.

    ", + "

    \n\t\"Block\n

    ", + ) + }) + + t.Run("No Hook, no wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "false") + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "

    This is an inline image: \"Inline. Some more text.

    \n\"Block") + }) + + t.Run("No Hook, wrap", func(t *testing.T) { + files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "true") + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", "

    \"Block

    ") + }) +} diff --git a/markup/goldmark/images/integration_test.go b/markup/goldmark/images/integration_test.go deleted file mode 100644 index 8b0ba99c1..000000000 --- a/markup/goldmark/images/integration_test.go +++ /dev/null @@ -1,112 +0,0 @@ -package images_test - -import ( - "strings" - "testing" - - "github.com/gohugoio/hugo/hugolib" -) - -func TestDisableWrapStandAloneImageWithinParagraph(t *testing.T) { - t.Parallel() - - filesTemplate := ` --- config.toml -- -[markup.goldmark.renderer] - unsafe = false -[markup.goldmark.parser] -wrapStandAloneImageWithinParagraph = CONFIG_VALUE -[markup.goldmark.parser.attribute] - block = true - title = true --- content/p1.md -- ---- -title: "p1" ---- - -This is an inline image: ![Inline Image](/inline.jpg). Some more text. - -![Block Image](/block.jpg) -{.b} - - --- layouts/_default/single.html -- -{{ .Content }} -` - - t.Run("With Hook, no wrap", func(t *testing.T) { - files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "false") - files = files + `-- layouts/_default/_markup/render-image.html -- -{{ if .IsBlock }} -
    - {{ .Text }}|{{ .Ordinal }} -
    -{{ else }} - {{ .Text }}|{{ .Ordinal }} -{{ end }} -` - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", - "This is an inline image: \n\t\"Inline\n. Some more text.

    ", - "
    \n\t\"Block", - ) - }) - - t.Run("With Hook, wrap", func(t *testing.T) { - files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "true") - files = files + `-- layouts/_default/_markup/render-image.html -- -{{ if .IsBlock }} -
    - {{ .Text }} -
    -{{ else }} - {{ .Text }} -{{ end }} -` - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", - "This is an inline image: \n\t\"Inline\n. Some more text.

    ", - "

    \n\t\"Block\n

    ", - ) - }) - - t.Run("No Hook, no wrap", func(t *testing.T) { - files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "false") - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", "

    This is an inline image: \"Inline. Some more text.

    \n\"Block") - }) - - t.Run("No Hook, wrap", func(t *testing.T) { - files := strings.ReplaceAll(filesTemplate, "CONFIG_VALUE", "true") - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", "

    \"Block

    ") - }) -} diff --git a/markup/goldmark/integration_test.go b/markup/goldmark/integration_test.go deleted file mode 100644 index c9c6ef338..000000000 --- a/markup/goldmark/integration_test.go +++ /dev/null @@ -1,813 +0,0 @@ -// Copyright 2021 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package goldmark_test - -import ( - "fmt" - "strings" - "testing" - - qt "github.com/frankban/quicktest" - - "github.com/gohugoio/hugo/hugolib" -) - -// Issue 9463 -func TestAttributeExclusion(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.goldmark.renderer] - unsafe = false -[markup.goldmark.parser.attribute] - block = true - title = true --- content/p1.md -- ---- -title: "p1" ---- -## Heading {class="a" onclick="alert('heading')"} - -> Blockquote -{class="b" ondblclick="alert('blockquote')"} - -~~~bash {id="c" onmouseover="alert('code fence')" LINENOS=true} -foo -~~~ --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` -

    -
    -
    - `) -} - -// Issue 9511 -func TestAttributeExclusionWithRenderHook(t *testing.T) { - t.Parallel() - - files := ` --- content/p1.md -- ---- -title: "p1" ---- -## Heading {onclick="alert('renderhook')" data-foo="bar"} --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-heading.html -- -{{ .Text | safeHTML }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` -

    Heading

    - `) -} - -func TestAttributesDefaultRenderer(t *testing.T) { - t.Parallel() - - files := ` --- content/p1.md -- ---- -title: "p1" ---- -## Heading Attribute Which Needs Escaping { class="a < b" } --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` -class="a < b" - `) -} - -// Issue 9558. -func TestAttributesHookNoEscape(t *testing.T) { - t.Parallel() - - files := ` --- content/p1.md -- ---- -title: "p1" ---- -## Heading Attribute Which Needs Escaping { class="Smith & Wesson" } --- layouts/_default/_markup/render-heading.html -- -plain: |{{- range $k, $v := .Attributes -}}{{ $k }}: {{ $v }}|{{ end }}| -safeHTML: |{{- range $k, $v := .Attributes -}}{{ $k }}: {{ $v | safeHTML }}|{{ end }}| --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` -plain: |class: Smith & Wesson|id: heading-attribute-which-needs-escaping| -safeHTML: |class: Smith & Wesson|id: heading-attribute-which-needs-escaping| - `) -} - -// Issue 9504 -func TestLinkInTitle(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- --- content/p1.md -- ---- -title: "p1" ---- -## Hello [Test](https://example.com) --- layouts/_default/single.html -- -{{ .Content }} --- layouts/_default/_markup/render-heading.html -- - - {{ .Text | safeHTML }} - # - --- layouts/_default/_markup/render-link.html -- -{{ .Text | safeHTML }} - -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", - "

    \n Hello Test\n\n #\n

    ", - ) -} - -func TestHighlight(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup] -[markup.highlight] -anchorLineNos = false -codeFences = true -guessSyntax = false -hl_Lines = '' -lineAnchors = '' -lineNoStart = 1 -lineNos = false -lineNumbersInTable = true -noClasses = false -style = 'monokai' -tabWidth = 4 --- layouts/_default/single.html -- -{{ .Content }} --- content/p1.md -- ---- -title: "p1" ---- - -## Code Fences - -§§§bash -LINE1 -§§§ - -## Code Fences No Lexer - -§§§moo -LINE1 -§§§ - -## Code Fences Simple Attributes - -§§A§bash { .myclass id="myid" } -LINE1 -§§A§ - -## Code Fences Line Numbers - -§§§bash {linenos=table,hl_lines=[8,"15-17"],linenostart=199} -LINE1 -LINE2 -LINE3 -LINE4 -LINE5 -LINE6 -LINE7 -LINE8 -§§§ - - - - -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", - "
    LINE1\n
    ", - "Code Fences No Lexer

    \n
    LINE1\n
    ", - "lnt", - ) -} - -func BenchmarkRenderHooks(b *testing.B) { - files := ` --- config.toml -- --- layouts/_default/_markup/render-heading.html -- - - {{ .Text | safeHTML }} - # - --- layouts/_default/_markup/render-link.html -- -{{ .Text | safeHTML }} --- layouts/_default/single.html -- -{{ .Content }} -` - - content := ` - -## Hello1 [Test](https://example.com) - -A. - -## Hello2 [Test](https://example.com) - -B. - -## Hello3 [Test](https://example.com) - -C. - -## Hello4 [Test](https://example.com) - -D. - -[Test](https://example.com) - -## Hello5 - - -` - - for i := 1; i < 100; i++ { - files += fmt.Sprintf("\n-- content/posts/p%d.md --\n"+content, i+1) - } - - cfg := hugolib.IntegrationTestConfig{ - T: b, - TxtarString: files, - } - builders := make([]*hugolib.IntegrationTestBuilder, b.N) - - for i := range builders { - builders[i] = hugolib.NewIntegrationTestBuilder(cfg) - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - builders[i].Build() - } -} - -func BenchmarkCodeblocks(b *testing.B) { - filesTemplate := ` --- config.toml -- -[markup] - [markup.highlight] - anchorLineNos = false - codeFences = true - guessSyntax = false - hl_Lines = '' - lineAnchors = '' - lineNoStart = 1 - lineNos = false - lineNumbersInTable = true - noClasses = true - style = 'monokai' - tabWidth = 4 --- layouts/_default/single.html -- -{{ .Content }} -` - - content := ` - -FENCEgo -package main -import "fmt" -func main() { - fmt.Println("hello world") -} -FENCE - -FENCEunknownlexer -hello -FENCE -` - - content = strings.ReplaceAll(content, "FENCE", "```") - - for i := 1; i < 100; i++ { - filesTemplate += fmt.Sprintf("\n-- content/posts/p%d.md --\n"+content, i+1) - } - - runBenchmark := func(files string, b *testing.B) { - cfg := hugolib.IntegrationTestConfig{ - T: b, - TxtarString: files, - } - builders := make([]*hugolib.IntegrationTestBuilder, b.N) - - for i := range builders { - builders[i] = hugolib.NewIntegrationTestBuilder(cfg) - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - builders[i].Build() - } - } - - b.Run("Default", func(b *testing.B) { - runBenchmark(filesTemplate, b) - }) - - b.Run("Hook no higlight", func(b *testing.B) { - files := filesTemplate + ` --- layouts/_default/_markup/render-codeblock.html -- -{{ .Inner }} -` - - runBenchmark(files, b) - }) - -} - -// Iisse #8959 -func TestHookInfiniteRecursion(t *testing.T) { - t.Parallel() - - for _, renderFunc := range []string{"markdownify", ".Page.RenderString"} { - t.Run(renderFunc, func(t *testing.T) { - - files := ` --- config.toml -- --- layouts/_default/_markup/render-link.html -- -{{ .Text | RENDERFUNC }} --- layouts/_default/single.html -- -{{ .Content }} --- content/p1.md -- ---- -title: "p1" ---- - -https://example.org - -a@b.com - - - ` - - files = strings.ReplaceAll(files, "RENDERFUNC", renderFunc) - - b, err := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).BuildE() - - b.Assert(err, qt.IsNotNil) - b.Assert(err.Error(), qt.Contains, "text is already rendered, repeating it may cause infinite recursion") - - }) - - } - -} - -// Issue 9594 -func TestQuotesInImgAltAttr(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.goldmark.extensions] - typographer = false --- content/p1.md -- ---- -title: "p1" ---- -!["a"](b.jpg) --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", ` - "a" - `) -} - -func TestLinkifyProtocol(t *testing.T) { - t.Parallel() - - runTest := func(protocol string, withHook bool) *hugolib.IntegrationTestBuilder { - - files := ` --- config.toml -- -[markup.goldmark] -[markup.goldmark.extensions] -linkify = true -linkifyProtocol = "PROTOCOL" --- content/p1.md -- ---- -title: "p1" ---- -Link no procol: www.example.org -Link http procol: http://www.example.org -Link https procol: https://www.example.org - --- layouts/_default/single.html -- -{{ .Content }} -` - files = strings.ReplaceAll(files, "PROTOCOL", protocol) - - if withHook { - files += `-- layouts/_default/_markup/render-link.html -- -{{ .Text | safeHTML }}` - } - - return hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - } - - for _, withHook := range []bool{false, true} { - - b := runTest("https", withHook) - - b.AssertFileContent("public/p1/index.html", - "Link no procol: www.example.org", - "Link http procol: http://www.example.org", - "Link https procol: https://www.example.org

    ", - ) - - b = runTest("http", withHook) - - b.AssertFileContent("public/p1/index.html", - "Link no procol: www.example.org", - "Link http procol: http://www.example.org", - "Link https procol: https://www.example.org

    ", - ) - - b = runTest("gopher", withHook) - - b.AssertFileContent("public/p1/index.html", - "Link no procol: www.example.org", - "Link http procol: http://www.example.org", - "Link https procol: https://www.example.org

    ", - ) - - } -} - -func TestGoldmarkBugs(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.goldmark.renderer] -unsafe = true --- content/p1.md -- ---- -title: "p1" ---- - -## Issue 9650 - -a c - -## Issue 9658 - -- This is a list item - - --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContentExact("public/p1/index.html", - // Issue 9650 - "

    a c

    ", - // Issue 9658 (crash) - "
  • This is a list item
  • ", - ) -} - -// Issue #7332 -// Issue #11587 -func TestGoldmarkEmojiExtension(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -enableEmoji = true --- content/p1.md -- ---- -title: "p1" ---- -~~~text -:x: -~~~ - -{{% include "/p2" %}} - -{{< sc1 >}}:smiley:{{< /sc1 >}} - -{{< sc2 >}}:+1:{{< /sc2 >}} - -{{% sc3 %}}:-1:{{% /sc3 %}} - --- content/p2.md -- ---- -title: "p2" ---- -:heavy_check_mark: --- layouts/shortcodes/include.html -- -{{ $p := site.GetPage (.Get 0) }} -{{ $p.RenderShortcodes }} --- layouts/shortcodes/sc1.html -- -sc1_begin|{{ .Inner }}|sc1_end --- layouts/shortcodes/sc2.html -- -sc2_begin|{{ .Inner | .Page.RenderString }}|sc2_end --- layouts/shortcodes/sc3.html -- -sc3_begin|{{ .Inner }}|sc3_end --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContentExact("public/p1/index.html", - // Issue #7332 - ":x:\n", - // Issue #11587 - "

    ✔️

    ", - // Should not be converted to emoji - "sc1_begin|:smiley:|sc1_end", - // Should be converted to emoji - "sc2_begin|👍|sc2_end", - // Should be converted to emoji - "sc3_begin|👎|sc3_end", - ) -} - -func TestEmojiDisabled(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -enableEmoji = false --- content/p1.md -- ---- -title: "p1" ---- -:x: --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContentExact("public/p1/index.html", "

    :x:

    ") -} - -func TestEmojiDefaultConfig(t *testing.T) { - t.Parallel() - - files := ` --- content/p1.md -- ---- -title: "p1" ---- -:x: --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - }, - ).Build() - - b.AssertFileContentExact("public/p1/index.html", "

    :x:

    ") -} - -// Issue #5748 -func TestGoldmarkTemplateDelims(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[minify] - minifyOutput = true -[minify.tdewolff.html] - templateDelims = [""] --- layouts/index.html -- -
    -{{ safeHTML "" }} -
    -` - - b := hugolib.Test(t, files) - b.AssertFileContent("public/index.html", "
    \n
    ") -} - -// Issue #10894 -func TestPassthroughInlineFences(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.goldmark.extensions.passthrough] -enable = true -[markup.goldmark.extensions.passthrough.delimiters] -inline = [['$', '$'], ['\(', '\)']] --- content/p1.md -- ---- -title: "p1" ---- -## LaTeX test - -Inline equation that would be mangled by default parser: $a^*=x-b^*$ - --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.Test(t, files) - b.AssertFileContent("public/p1/index.html", ` - $a^*=x-b^*$ - `) -} - -func TestPassthroughBlockFences(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.goldmark.extensions.passthrough] -enable = true -[markup.goldmark.extensions.passthrough.delimiters] -block = [['$$', '$$']] --- content/p1.md -- ---- -title: "p1" ---- -## LaTeX test - -Block equation that would be mangled by default parser: - -$$a^*=x-b^*$$ - --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.Test(t, files) - b.AssertFileContent("public/p1/index.html", ` - $$a^*=x-b^*$$ - `) -} - -func TestPassthroughWithAlternativeFences(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.goldmark.extensions.passthrough] -enable = true -[markup.goldmark.extensions.passthrough.delimiters] -inline = [['(((', ')))']] -block = [['%!%', '%!%']] --- content/p1.md -- ---- -title: "p1" ---- -## LaTeX test - -Inline equation that would be mangled by default parser: (((a^*=x-b^*))) -Inline equation that should be mangled by default parser: $a^*=x-b^*$ - -Block equation that would be mangled by default parser: - -%!% -a^*=x-b^* -%!% - --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.Test(t, files) - b.AssertFileContent("public/p1/index.html", ` - (((a^*=x-b^*))) - `) - b.AssertFileContent("public/p1/index.html", ` - $a^=x-b^$ - `) - b.AssertFileContent("public/p1/index.html", ` -%!% -a^*=x-b^* -%!% - `) -} diff --git a/markup/highlight/highlight_integration_test.go b/markup/highlight/highlight_integration_test.go new file mode 100644 index 000000000..b53b585c0 --- /dev/null +++ b/markup/highlight/highlight_integration_test.go @@ -0,0 +1,117 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package highlight_test + +import ( + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestHighlightInline(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup] +[markup.highlight] +codeFences = true +noClasses = false +-- content/p1.md -- +--- +title: "p1" +--- + +## Inline in Shortcode + +Inline:{{< highlight emacs "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End. +Inline Unknown:{{< highlight foo "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End. + +## Inline in code block + +Not sure if this makes sense, but add a test for it: + +§§§bash {hl_inline=true} +(message "highlight me") +§§§ + +## HighlightCodeBlock in hook + +§§§html +(message "highlight me 2") +§§§ + +## Unknown lexer + +§§§foo {hl_inline=true} +(message "highlight me 3") +§§§ + + +-- layouts/_default/_markup/render-codeblock-html.html -- +{{ $opts := dict "hl_inline" true }} +{{ $result := transform.HighlightCodeBlock . $opts }} +HighlightCodeBlock: Wrapped:{{ $result.Wrapped }}|Inner:{{ $result.Inner }} +-- layouts/_default/single.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "Inline:(message "this highlight shortcode"):End.", + "Inline Unknown:(message "this highlight shortcode"):End.", + "Not sure if this makes sense, but add a test for it:

    \n(message "highlight me")\n", + "HighlightCodeBlock: Wrapped:(message "highlight me 2")|Inner:(message "highlight me 2")", + "(message "highlight me 3")\n", + ) +} + +// Issue #11311 +func TestIssue11311(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +[markup.highlight] +noClasses = false +-- content/_index.md -- +--- +title: home +--- +§§§go +xəx := 0 +§§§ +-- layouts/index.html -- +{{ .Content }} +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/index.html", ` + xəx + `) +} diff --git a/markup/highlight/integration_test.go b/markup/highlight/integration_test.go deleted file mode 100644 index b53b585c0..000000000 --- a/markup/highlight/integration_test.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2024 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package highlight_test - -import ( - "testing" - - "github.com/gohugoio/hugo/hugolib" -) - -func TestHighlightInline(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup] -[markup.highlight] -codeFences = true -noClasses = false --- content/p1.md -- ---- -title: "p1" ---- - -## Inline in Shortcode - -Inline:{{< highlight emacs "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End. -Inline Unknown:{{< highlight foo "hl_inline=true" >}}(message "this highlight shortcode"){{< /highlight >}}:End. - -## Inline in code block - -Not sure if this makes sense, but add a test for it: - -§§§bash {hl_inline=true} -(message "highlight me") -§§§ - -## HighlightCodeBlock in hook - -§§§html -(message "highlight me 2") -§§§ - -## Unknown lexer - -§§§foo {hl_inline=true} -(message "highlight me 3") -§§§ - - --- layouts/_default/_markup/render-codeblock-html.html -- -{{ $opts := dict "hl_inline" true }} -{{ $result := transform.HighlightCodeBlock . $opts }} -HighlightCodeBlock: Wrapped:{{ $result.Wrapped }}|Inner:{{ $result.Inner }} --- layouts/_default/single.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/p1/index.html", - "Inline:(message "this highlight shortcode"):End.", - "Inline Unknown:(message "this highlight shortcode"):End.", - "Not sure if this makes sense, but add a test for it:

    \n(message "highlight me")\n", - "HighlightCodeBlock: Wrapped:(message "highlight me 2")|Inner:(message "highlight me 2")", - "(message "highlight me 3")\n", - ) -} - -// Issue #11311 -func TestIssue11311(t *testing.T) { - t.Parallel() - - files := ` --- config.toml -- -[markup.highlight] -noClasses = false --- content/_index.md -- ---- -title: home ---- -§§§go -xəx := 0 -§§§ --- layouts/index.html -- -{{ .Content }} -` - - b := hugolib.NewIntegrationTestBuilder( - hugolib.IntegrationTestConfig{ - T: t, - TxtarString: files, - NeedsOsFS: false, - }, - ).Build() - - b.AssertFileContent("public/index.html", ` - xəx - `) -} diff --git a/markup/tableofcontents/integration_test.go b/markup/tableofcontents/integration_test.go deleted file mode 100644 index 87a7c0108..000000000 --- a/markup/tableofcontents/integration_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2024 The Hugo Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package tableofcontents_test - -import ( - "testing" - - "github.com/gohugoio/hugo/hugolib" -) - -// Issue #10776 -func TestHeadingsLevel(t *testing.T) { - t.Parallel() - - files := ` --- hugo.toml -- -disableKinds = ['page','rss','section','sitemap','taxonomy','term'] --- layouts/index.html -- -{{ range .Fragments.HeadingsMap }} - {{ printf "%s|%d|%s" .ID .Level .Title }} -{{ end }} --- content/_index.md -- -## Heading L2 -### Heading L3 -##### Heading L5 -` - - b := hugolib.Test(t, files) - b.AssertFileContent("public/index.html", - "heading-l2|2|Heading L2", - "heading-l3|3|Heading L3", - "heading-l5|5|Heading L5", - ) -} diff --git a/markup/tableofcontents/tableofcontents_integration_test.go b/markup/tableofcontents/tableofcontents_integration_test.go new file mode 100644 index 000000000..87a7c0108 --- /dev/null +++ b/markup/tableofcontents/tableofcontents_integration_test.go @@ -0,0 +1,45 @@ +// Copyright 2024 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tableofcontents_test + +import ( + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +// Issue #10776 +func TestHeadingsLevel(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- layouts/index.html -- +{{ range .Fragments.HeadingsMap }} + {{ printf "%s|%d|%s" .ID .Level .Title }} +{{ end }} +-- content/_index.md -- +## Heading L2 +### Heading L3 +##### Heading L5 +` + + b := hugolib.Test(t, files) + b.AssertFileContent("public/index.html", + "heading-l2|2|Heading L2", + "heading-l3|3|Heading L3", + "heading-l5|5|Heading L5", + ) +} -- cgit v1.2.3