mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
b6133657e0
Treat type and code as highlighting options in both transform.Highlight and transform.HighlightCodeBlock. The type option overrides the language and code overrides the code, so the two functions now share the same options handling. transform.Highlight's LANG argument is now optional: transform.Highlight CODE [LANG] [OPTIONS] Fixes #11872 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
643 lines
18 KiB
Go
643 lines
18 KiB
Go
// 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 transform_test
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
qt "github.com/frankban/quicktest"
|
|
"github.com/gohugoio/hugo/hugolib"
|
|
)
|
|
|
|
// Issue #11698
|
|
func TestMarkdownifyIssue11698(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['home','section','rss','sitemap','taxonomy','term']
|
|
[markup.goldmark.parser.attribute]
|
|
title = true
|
|
block = true
|
|
-- layouts/single.html --
|
|
_{{ markdownify .RawContent }}_
|
|
-- content/p1.md --
|
|
---
|
|
title: p1
|
|
---
|
|
foo bar
|
|
-- content/p2.md --
|
|
---
|
|
title: p2
|
|
---
|
|
foo
|
|
|
|
**bar**
|
|
-- content/p3.md --
|
|
---
|
|
title: p3
|
|
---
|
|
## foo
|
|
|
|
bar
|
|
-- content/p4.md --
|
|
---
|
|
title: p4
|
|
---
|
|
foo
|
|
{#bar}
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/p1/index.html", "_foo bar_")
|
|
b.AssertFileContent("public/p2/index.html", "_<p>foo</p>\n<p><strong>bar</strong></p>\n_")
|
|
b.AssertFileContent("public/p3/index.html", "_<h2 id=\"foo\">foo</h2>\n<p>bar</p>\n_")
|
|
b.AssertFileContent("public/p4/index.html", "_<p id=\"bar\">foo</p>\n_")
|
|
}
|
|
|
|
func TestXMLEscape(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['section','sitemap','taxonomy','term']
|
|
-- content/p1.md --
|
|
---
|
|
title: p1
|
|
---
|
|
a **b** ` + "\v" + ` c
|
|
<!--more-->
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/index.xml", `
|
|
<description><p>a <strong>b</strong> c</p></description>
|
|
`)
|
|
}
|
|
|
|
// Issue #9642
|
|
func TestHighlightError(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ highlight "a" "b" 0 }}
|
|
`
|
|
b, err := hugolib.TestE(t, files)
|
|
b.Assert(err.Error(), qt.Contains, "error calling highlight: invalid Highlight option: 0")
|
|
}
|
|
|
|
// transform.Highlight: LANG is optional and may be set via the type option, and
|
|
// the code option overrides CODE — consistent with transform.HighlightCodeBlock.
|
|
// See issue 11872.
|
|
func TestHighlightTypeAndCodeOptions(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
[markup.highlight]
|
|
noClasses = false
|
|
-- layouts/home.html --
|
|
lang:{{ transform.Highlight "i = 42" "go" }}
|
|
type:{{ transform.Highlight "i = 42" (dict "type" "go") }}
|
|
code:{{ transform.Highlight "" (dict "type" "go" "code" "i = 42") }}
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
want := `<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">i</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="mi">42</span></span></span></code></pre></div>`
|
|
|
|
b.AssertFileContent("public/index.html",
|
|
"lang:"+want,
|
|
"type:"+want,
|
|
"code:"+want,
|
|
)
|
|
}
|
|
|
|
// Issue #11884
|
|
func TestUnmarshalCSVLazyDecoding(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- assets/pets.csv --
|
|
name,description,age
|
|
Spot,a nice dog,3
|
|
Rover,"a big dog",5
|
|
Felix,a "malicious" cat,7
|
|
Bella,"an "evil" cat",9
|
|
Scar,"a "dead cat",11
|
|
-- layouts/home.html --
|
|
{{ $opts := dict "lazyQuotes" true }}
|
|
{{ $data := resources.Get "pets.csv" | transform.Unmarshal $opts }}
|
|
{{ printf "%v" $data | safeHTML }}
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/index.html", `
|
|
[[name description age] [Spot a nice dog 3] [Rover a big dog 5] [Felix a "malicious" cat 7] [Bella an "evil" cat 9] [Scar a "dead cat 11]]
|
|
`)
|
|
}
|
|
|
|
func TestToMath(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ transform.ToMath "c = \\pm\\sqrt{a^2 + b^2}" }}
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/index.html", `
|
|
<span class="katex"><math
|
|
`)
|
|
}
|
|
|
|
func TestToMathError(t *testing.T) {
|
|
t.Run("Default", func(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ transform.ToMath "c = \\foo{a^2 + b^2}" }}
|
|
`
|
|
b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
|
|
|
|
b.Assert(err, qt.IsNotNil)
|
|
b.Assert(err.Error(), qt.Contains, "KaTeX parse error: Undefined control sequence: \\foo")
|
|
})
|
|
|
|
t.Run("Disable ThrowOnError", func(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ $opts := dict "throwOnError" false }}
|
|
{{ transform.ToMath "c = \\foo{a^2 + b^2}" $opts }}
|
|
`
|
|
b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
|
|
|
|
b.Assert(err, qt.IsNil)
|
|
b.AssertFileContent("public/index.html", `#cc0000`) // Error color
|
|
})
|
|
|
|
t.Run("Handle in template", func(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ with try (transform.ToMath "c = \\foo{a^2 + b^2}") }}
|
|
{{ with .Err }}
|
|
{{ warnf "error: %s" . }}
|
|
{{ else }}
|
|
{{ .Value }}
|
|
{{ end }}
|
|
{{ end }}
|
|
`
|
|
b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
|
|
|
|
b.Assert(err, qt.IsNil)
|
|
b.AssertLogContains("WARN error: template: home.html:1:22: executing \"home.html\" at <transform.ToMath>: error calling ToMath: KaTeX parse error: Undefined control sequence: \\foo at position 5: c = \\̲f̲o̲o̲{a^2 + b^2}")
|
|
})
|
|
|
|
// See issue 13239.
|
|
t.Run("Handle in template, old Err construct", func(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ with transform.ToMath "c = \\pm\\sqrt{a^2 + b^2}" }}
|
|
{{ with .Err }}
|
|
{{ warnf "error: %s" . }}
|
|
{{ else }}
|
|
{{ . }}
|
|
{{ end }}
|
|
{{ end }}
|
|
`
|
|
b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
|
|
|
|
b.Assert(err, qt.IsNotNil)
|
|
b.Assert(err.Error(), qt.Contains, "the return type of transform.ToMath was changed in Hugo v0.141.0 and the error handling replaced with a new try keyword, see https://gohugo.io/functions/go-template/try/")
|
|
})
|
|
}
|
|
|
|
func TestToMathBigAndManyExpressions(t *testing.T) {
|
|
filesTemplate := `
|
|
-- hugo.toml --
|
|
disableKinds = ['rss','section','sitemap','taxonomy','term']
|
|
[markup.goldmark.extensions.passthrough]
|
|
enable = true
|
|
[markup.goldmark.extensions.passthrough.delimiters]
|
|
block = [['\[', '\]'], ['$$', '$$']]
|
|
inline = [['\(', '\)'], ['$', '$']]
|
|
-- content/p1.md --
|
|
P1_CONTENT
|
|
-- layouts/home.html --
|
|
Home.
|
|
-- layouts/single.html --
|
|
Content: {{ .Content }}|
|
|
-- layouts/_markup/render-passthrough.html --
|
|
{{ $opts := dict "throwOnError" false "displayMode" true }}
|
|
{{ transform.ToMath .Inner $opts }}
|
|
`
|
|
|
|
t.Run("Very large file with many complex KaTeX expressions", func(t *testing.T) {
|
|
files := strings.ReplaceAll(filesTemplate, "P1_CONTENT", "sourcefilename: testdata/large-katex.md")
|
|
b := hugolib.Test(t, files)
|
|
b.AssertFileContent("public/p1/index.html", `
|
|
<span class="katex"><math
|
|
`)
|
|
})
|
|
|
|
t.Run("Large and complex expression", func(t *testing.T) {
|
|
// This is pulled from the file above, which times out for some reason.
|
|
largeAndComplexeExpressions := `\begin{align*} \frac{\pi^2}{6}&=\frac{4}{3}\frac{(\arcsin 1)^2}{2}\\ &=\frac{4}{3}\int_0^1\frac{\arcsin x}{\sqrt{1-x^2}}\,dx\\ &=\frac{4}{3}\int_0^1\frac{x+\sum_{n=1}^{\infty}\frac{(2n-1)!!}{(2n)!!}\frac{x^{2n+1}}{2n+1}}{\sqrt{1-x^2}}\,dx\\ &=\frac{4}{3}\int_0^1\frac{x}{\sqrt{1-x^2}}\,dx +\frac{4}{3}\sum_{n=1}^{\infty}\frac{(2n-1)!!}{(2n)!!(2n+1)}\int_0^1x^{2n}\frac{x}{\sqrt{1-x^2}}\,dx\\ &=\frac{4}{3}+\frac{4}{3}\sum_{n=1}^{\infty}\frac{(2n-1)!!}{(2n)!!(2n+1)}\left[\frac{(2n)!!}{(2n+1)!!}\right]\\ &=\frac{4}{3}\sum_{n=0}^{\infty}\frac{1}{(2n+1)^2}\\ &=\frac{4}{3}\left(\sum_{n=1}^{\infty}\frac{1}{n^2}-\frac{1}{4}\sum_{n=1}^{\infty}\frac{1}{n^2}\right)\\ &=\sum_{n=1}^{\infty}\frac{1}{n^2} \end{align*}`
|
|
files := strings.ReplaceAll(filesTemplate, "P1_CONTENT", fmt.Sprintf(`---
|
|
title: p1
|
|
---
|
|
|
|
$$%s$$
|
|
`, largeAndComplexeExpressions))
|
|
|
|
b := hugolib.Test(t, files)
|
|
b.AssertFileContent("public/p1/index.html", `
|
|
<span class="katex"><math
|
|
`)
|
|
})
|
|
}
|
|
|
|
// Issue #13406.
|
|
func TestToMathRenderHookPosition(t *testing.T) {
|
|
filesTemplate := `
|
|
-- hugo.toml --
|
|
disableKinds = ['rss','section','sitemap','taxonomy','term']
|
|
[markup.goldmark.extensions.passthrough]
|
|
enable = true
|
|
[markup.goldmark.extensions.passthrough.delimiters]
|
|
block = [['\[', '\]'], ['$$', '$$']]
|
|
inline = [['\(', '\)'], ['$', '$']]
|
|
-- content/p1.md --
|
|
---
|
|
title: p1
|
|
---
|
|
|
|
Block:
|
|
|
|
$$1+2$$
|
|
|
|
Some inline $1+3$ math.
|
|
|
|
-- layouts/home.html --
|
|
Home.
|
|
-- layouts/single.html --
|
|
Content: {{ .Content }}|
|
|
-- layouts/_markup/render-passthrough.html --
|
|
{{ $opts := dict "throwOnError" true "displayMode" true }}
|
|
{{- with try (transform.ToMath .Inner $opts ) }}
|
|
{{- with .Err }}
|
|
{{ errorf "KaTeX: %s: see %s." . $.Position }}
|
|
{{- else }}
|
|
{{- .Value }}
|
|
{{- end }}
|
|
{{- end -}}
|
|
|
|
`
|
|
|
|
// Block math.
|
|
files := strings.Replace(filesTemplate, "$$1+2$$", "$$\\foo1+2$$", 1)
|
|
b, err := hugolib.TestE(t, files)
|
|
b.Assert(err, qt.IsNotNil)
|
|
b.AssertLogContains("p1.md:7:1")
|
|
|
|
// Inline math.
|
|
files = strings.Replace(filesTemplate, "$1+3$", "$\\foo1+3$", 1)
|
|
b, err = hugolib.TestE(t, files)
|
|
b.Assert(err, qt.IsNotNil)
|
|
b.AssertLogContains("p1.md:9:13")
|
|
}
|
|
|
|
func TestToMathMacros(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ $macros := dict
|
|
"\\addBar" "\\bar{#1}"
|
|
"\\bold" "\\mathbf{#1}"
|
|
}}
|
|
{{ $opts := dict "macros" $macros }}
|
|
{{ transform.ToMath "\\addBar{y} + \\bold{H}" $opts }}
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/index.html", `
|
|
<mi>y</mi>
|
|
`)
|
|
}
|
|
|
|
// Issue #12977
|
|
func TestUnmarshalWithIndentedYAML(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/home.html --
|
|
{{ $yaml := "\n a:\n b: 1\n c:\n d: 2\n" }}
|
|
{{ $yaml | transform.Unmarshal | encoding.Jsonify }}
|
|
`
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileExists("public/index.html", true)
|
|
b.AssertFileContent("public/index.html", `{"a":{"b":1},"c":{"d":2}}`)
|
|
}
|
|
|
|
func TestPortableText(t *testing.T) {
|
|
files := `
|
|
-- hugo.toml --
|
|
-- assets/sample.json --
|
|
[
|
|
{
|
|
"_key": "a",
|
|
"_type": "block",
|
|
"children": [
|
|
{
|
|
"_key": "b",
|
|
"_type": "span",
|
|
"marks": [],
|
|
"text": "Heading 2"
|
|
}
|
|
],
|
|
"markDefs": [],
|
|
"style": "h2"
|
|
}
|
|
]
|
|
-- layouts/home.html --
|
|
{{ $markdown := resources.Get "sample.json" | transform.Unmarshal | transform.PortableText }}
|
|
Markdown: {{ $markdown }}|
|
|
|
|
`
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/index.html", "Markdown: ## Heading 2\n|")
|
|
}
|
|
|
|
func TestUnmarshalCSV(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/all.html --
|
|
{{ $opts := OPTS }}
|
|
{{ with resources.Get "pets.csv" | transform.Unmarshal $opts }}
|
|
{{ jsonify . }}
|
|
{{ end }}
|
|
-- assets/pets.csv --
|
|
DATA
|
|
`
|
|
|
|
// targetType = map
|
|
f := strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
|
|
f = strings.ReplaceAll(f, "DATA",
|
|
"name,type,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
|
|
)
|
|
b := hugolib.Test(t, f)
|
|
b.AssertFileContent("public/index.html",
|
|
`[{"age":"3","breed":"Collie","name":"Spot","type":"dog"},{"age":"7","breed":"Malicious","name":"Felix","type":"cat"}]`,
|
|
)
|
|
|
|
// targetType = map (no data)
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
|
|
f = strings.ReplaceAll(f, "DATA", "")
|
|
b = hugolib.Test(t, f)
|
|
b.AssertFileContent("public/index.html", "")
|
|
|
|
// targetType = slice
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "slice"`)
|
|
f = strings.ReplaceAll(f, "DATA",
|
|
"name,type,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
|
|
)
|
|
b = hugolib.Test(t, f)
|
|
b.AssertFileContent("public/index.html",
|
|
`[["name","type","breed","age"],["Spot","dog","Collie","3"],["Felix","cat","Malicious","7"]]`,
|
|
)
|
|
|
|
// targetType = slice (no data)
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "slice"`)
|
|
f = strings.ReplaceAll(f, "DATA", "")
|
|
b = hugolib.Test(t, f)
|
|
b.AssertFileContent("public/index.html", "")
|
|
|
|
// targetType not specified
|
|
f = strings.ReplaceAll(files, "OPTS", "dict")
|
|
f = strings.ReplaceAll(f, "DATA",
|
|
"name,type,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
|
|
)
|
|
b = hugolib.Test(t, f)
|
|
b.AssertFileContent("public/index.html",
|
|
`[["name","type","breed","age"],["Spot","dog","Collie","3"],["Felix","cat","Malicious","7"]]`,
|
|
)
|
|
|
|
// targetType not specified (no data)
|
|
f = strings.ReplaceAll(files, "OPTS", "dict")
|
|
f = strings.ReplaceAll(f, "DATA", "")
|
|
b = hugolib.Test(t, f)
|
|
b.AssertFileContent("public/index.html", "")
|
|
|
|
// targetType = foo
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "foo"`)
|
|
_, err := hugolib.TestE(t, f)
|
|
if err == nil {
|
|
t.Errorf("expected error")
|
|
} else {
|
|
if !strings.Contains(err.Error(), `invalid targetType: expected either slice or map, received foo`) {
|
|
t.Log(err.Error())
|
|
t.Errorf("error message does not match expected error message")
|
|
}
|
|
}
|
|
|
|
// targetType = foo (no data)
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "foo"`)
|
|
f = strings.ReplaceAll(f, "DATA", "")
|
|
_, err = hugolib.TestE(t, f)
|
|
if err == nil {
|
|
t.Errorf("expected error")
|
|
} else {
|
|
if !strings.Contains(err.Error(), `invalid targetType: expected either slice or map, received foo`) {
|
|
t.Log(err.Error())
|
|
t.Errorf("error message does not match expected error message")
|
|
}
|
|
}
|
|
|
|
// targetType = map (error: expected at least a header row and one data row)
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
|
|
_, err = hugolib.TestE(t, f)
|
|
if err == nil {
|
|
t.Errorf("expected error")
|
|
} else {
|
|
if !strings.Contains(err.Error(), `expected at least a header row and one data row`) {
|
|
t.Log(err.Error())
|
|
t.Errorf("error message does not match expected error message")
|
|
}
|
|
}
|
|
|
|
// targetType = map (error: header row contains duplicate field names)
|
|
f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
|
|
f = strings.ReplaceAll(f, "DATA",
|
|
"name,name,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
|
|
)
|
|
_, err = hugolib.TestE(t, f)
|
|
if err == nil {
|
|
t.Errorf("expected error")
|
|
} else {
|
|
if !strings.Contains(err.Error(), `header row contains duplicate field names`) {
|
|
t.Log(err.Error())
|
|
t.Errorf("error message does not match expected error message")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Issue 13729
|
|
func TestToMathStrictMode(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- layouts/all.html --
|
|
{{ transform.ToMath "a %" dict }}
|
|
-- foo --
|
|
`
|
|
|
|
// strict mode: default
|
|
f := strings.ReplaceAll(files, "dict", "")
|
|
b, err := hugolib.TestE(t, f)
|
|
b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
|
|
|
|
// strict mode: error
|
|
f = strings.ReplaceAll(files, "dict", `(dict "strict" "error")`)
|
|
b, err = hugolib.TestE(t, f)
|
|
b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
|
|
|
|
// strict mode: ignore
|
|
f = strings.ReplaceAll(files, "dict", `(dict "strict" "ignore")`)
|
|
b = hugolib.Test(t, f, hugolib.TestOptWarn())
|
|
b.AssertLogMatches("")
|
|
b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
|
|
|
|
// strict: warn
|
|
f = strings.ReplaceAll(files, "dict", `(dict "strict" "warn")`)
|
|
b = hugolib.Test(t, f, hugolib.TestOptWarn())
|
|
b.AssertLogMatches("[commentAtEnd]")
|
|
b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
|
|
|
|
// strict mode: invalid value
|
|
f = strings.ReplaceAll(files, "dict", `(dict "strict" "foo")`)
|
|
b, err = hugolib.TestE(t, f)
|
|
b.Assert(err.Error(), qt.Contains, "invalid strict mode")
|
|
}
|
|
|
|
func TestHTMLToMarkdown(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
markdown := `
|
|
# Heading
|
|
|
|
Some **bold** text.
|
|
|
|
A [link](https://example.com).
|
|
|
|
An image:
|
|
|
|

|
|
|
|
A list:
|
|
|
|
- Item 1
|
|
- Item 2
|
|
- Item 2a
|
|
- Item 2b
|
|
|
|
A table:
|
|
|
|
| Header 1 | Header 2 |
|
|
|----------|----------|
|
|
| Cell 1 | Cell 2 |
|
|
| Cell 3 | Cell 4 |
|
|
|
|
A blockquote:
|
|
|
|
> This is a quote.
|
|
`
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['rss','section','sitemap','taxonomy','term']
|
|
-- layouts/all.html --
|
|
All html.
|
|
-- layouts/all.markdown --
|
|
{{ .Content | transform.HTMLToMarkdown | safeHTML }}
|
|
-- content/p1.md --
|
|
---
|
|
title: p1
|
|
outputs: ["html", "markdown"]
|
|
---
|
|
`
|
|
|
|
files += markdown
|
|
|
|
b := hugolib.Test(t, files)
|
|
|
|
b.AssertFileContent("public/p1/index.html", `All html.`)
|
|
|
|
// There are some white space differences, so we cannot do an exact match.
|
|
b.AssertFileContent("public/p1/index.md", markdown)
|
|
}
|
|
|
|
// See https://github.com/goccy/go-yaml/issues/461
|
|
func TestUnmarshalExcessiveYAMLStructureShouldFail(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := `
|
|
-- hugo.toml --
|
|
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
|
-- assets/ddos.yaml --
|
|
a: &a [_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]
|
|
b: &b [*a, *a, *a, *a, *a, *a, *a, *a, *a, *a]
|
|
c: &c [*b, *b, *b, *b, *b, *b, *b, *b, *b, *b]
|
|
d: &d [*c, *c, *c, *c, *c, *c, *c, *c, *c, *c]
|
|
e: &e [*d, *d, *d, *d, *d, *d, *d, *d, *d, *d]
|
|
f: &f [*e, *e, *e, *e, *e, *e, *e, *e, *e, *e]
|
|
g: &g [*f, *f, *f, *f, *f, *f, *f, *f, *f, *f]
|
|
h: &h [*g, *g, *g, *g, *g, *g, *g, *g, *g, *g]
|
|
i: &i [*h, *h, *h, *h, *h, *h, *h, *h, *h, *h]
|
|
-- layouts/home.html --
|
|
{{ $m := resources.Get "ddos.yaml" | transform.Unmarshal }}
|
|
{{ printf "Length: %d" (len $m) }}
|
|
`
|
|
|
|
b, err := hugolib.TestE(t, files)
|
|
|
|
b.Assert(err, qt.IsNotNil)
|
|
b.Assert(err.Error(), qt.Contains, "too many YAML aliases for non-scalar nodes")
|
|
}
|