transform_integration_test.go - hugo - [fork] hugo port for 9front
 (HTM) git clone https://git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       transform_integration_test.go (15369B)
       ---
            1 // Copyright 2024 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 package transform_test
           15 
           16 import (
           17         "fmt"
           18         "strings"
           19         "testing"
           20 
           21         qt "github.com/frankban/quicktest"
           22         "github.com/gohugoio/hugo/hugolib"
           23 )
           24 
           25 // Issue #11698
           26 func TestMarkdownifyIssue11698(t *testing.T) {
           27         t.Parallel()
           28 
           29         files := `
           30 -- config.toml --
           31 disableKinds = ['home','section','rss','sitemap','taxonomy','term']
           32 [markup.goldmark.parser.attribute]
           33 title = true
           34 block = true
           35 -- layouts/_default/single.html --
           36 _{{ markdownify .RawContent }}_
           37 -- content/p1.md --
           38 ---
           39 title: p1
           40 ---
           41 foo bar
           42 -- content/p2.md --
           43 ---
           44 title: p2
           45 ---
           46 foo
           47 
           48 **bar**
           49 -- content/p3.md --
           50 ---
           51 title: p3
           52 ---
           53 ## foo
           54 
           55 bar
           56 -- content/p4.md --
           57 ---
           58 title: p4
           59 ---
           60 foo
           61 {#bar}
           62   `
           63 
           64         b := hugolib.Test(t, files)
           65 
           66         b.AssertFileContent("public/p1/index.html", "_foo bar_")
           67         b.AssertFileContent("public/p2/index.html", "_<p>foo</p>\n<p><strong>bar</strong></p>\n_")
           68         b.AssertFileContent("public/p3/index.html", "_<h2 id=\"foo\">foo</h2>\n<p>bar</p>\n_")
           69         b.AssertFileContent("public/p4/index.html", "_<p id=\"bar\">foo</p>\n_")
           70 }
           71 
           72 func TestXMLEscape(t *testing.T) {
           73         t.Parallel()
           74 
           75         files := `
           76 -- config.toml --
           77 disableKinds = ['section','sitemap','taxonomy','term']
           78 -- content/p1.md --
           79 ---
           80 title: p1
           81 ---
           82 a **b** ` + "\v" + ` c
           83 <!--more-->
           84   `
           85         b := hugolib.Test(t, files)
           86 
           87         b.AssertFileContent("public/index.xml", `
           88         <description>&lt;p&gt;a &lt;strong&gt;b&lt;/strong&gt;  c&lt;/p&gt;</description>
           89         `)
           90 }
           91 
           92 // Issue #9642
           93 func TestHighlightError(t *testing.T) {
           94         t.Parallel()
           95 
           96         files := `
           97 -- hugo.toml --
           98 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
           99 -- layouts/index.html --
          100 {{ highlight "a" "b" 0 }}
          101   `
          102         b := hugolib.NewIntegrationTestBuilder(
          103                 hugolib.IntegrationTestConfig{
          104                         T:           t,
          105                         TxtarString: files,
          106                 },
          107         )
          108 
          109         _, err := b.BuildE()
          110         b.Assert(err.Error(), qt.Contains, "error calling highlight: invalid Highlight option: 0")
          111 }
          112 
          113 // Issue #11884
          114 func TestUnmarshalCSVLazyDecoding(t *testing.T) {
          115         t.Parallel()
          116 
          117         files := `
          118 -- hugo.toml --
          119 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          120 -- assets/pets.csv --
          121 name,description,age
          122 Spot,a nice dog,3
          123 Rover,"a big dog",5
          124 Felix,a "malicious" cat,7
          125 Bella,"an "evil" cat",9
          126 Scar,"a "dead cat",11
          127 -- layouts/index.html --
          128 {{ $opts := dict "lazyQuotes" true }}
          129 {{ $data := resources.Get "pets.csv" | transform.Unmarshal $opts }}
          130 {{ printf "%v" $data | safeHTML }}
          131   `
          132         b := hugolib.Test(t, files)
          133 
          134         b.AssertFileContent("public/index.html", `
          135 [[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]]
          136         `)
          137 }
          138 
          139 func TestToMath(t *testing.T) {
          140         files := `
          141 -- hugo.toml --
          142 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          143 -- layouts/index.html --
          144 {{ transform.ToMath "c = \\pm\\sqrt{a^2 + b^2}" }}
          145   `
          146         b := hugolib.Test(t, files)
          147 
          148         b.AssertFileContent("public/index.html", `
          149 <span class="katex"><math
          150         `)
          151 }
          152 
          153 func TestToMathError(t *testing.T) {
          154         t.Run("Default", func(t *testing.T) {
          155                 files := `
          156 -- hugo.toml --
          157 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          158 -- layouts/index.html --
          159 {{  transform.ToMath "c = \\foo{a^2 + b^2}" }}
          160   `
          161                 b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
          162 
          163                 b.Assert(err, qt.IsNotNil)
          164                 b.Assert(err.Error(), qt.Contains, "KaTeX parse error: Undefined control sequence: \\foo")
          165         })
          166 
          167         t.Run("Disable ThrowOnError", func(t *testing.T) {
          168                 files := `
          169 -- hugo.toml --
          170 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          171 -- layouts/index.html --
          172 {{ $opts := dict "throwOnError" false }}
          173 {{  transform.ToMath "c = \\foo{a^2 + b^2}" $opts }}
          174   `
          175                 b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
          176 
          177                 b.Assert(err, qt.IsNil)
          178                 b.AssertFileContent("public/index.html", `#cc0000`) // Error color
          179         })
          180 
          181         t.Run("Handle in template", func(t *testing.T) {
          182                 files := `
          183 -- hugo.toml --
          184 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          185 -- layouts/index.html --
          186 {{ with try (transform.ToMath "c = \\foo{a^2 + b^2}") }}
          187         {{ with .Err }}
          188                  {{ warnf "error: %s" . }}
          189         {{ else }}
          190                 {{ .Value }}
          191         {{ end }}
          192 {{ end }}
          193   `
          194                 b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
          195 
          196                 b.Assert(err, qt.IsNil)
          197                 b.AssertLogContains("WARN  error: template: index.html:1:22: executing \"index.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}")
          198         })
          199 
          200         // See issue 13239.
          201         t.Run("Handle in template, old Err construct", func(t *testing.T) {
          202                 files := `
          203 -- hugo.toml --
          204 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          205 -- layouts/index.html --
          206 {{ with transform.ToMath "c = \\pm\\sqrt{a^2 + b^2}" }}
          207         {{ with .Err }}
          208                  {{ warnf "error: %s" . }}
          209         {{ else }}
          210                 {{ . }}
          211         {{ end }}
          212 {{ end }}
          213   `
          214                 b, err := hugolib.TestE(t, files, hugolib.TestOptWarn())
          215 
          216                 b.Assert(err, qt.IsNotNil)
          217                 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/")
          218         })
          219 }
          220 
          221 func TestToMathBigAndManyExpressions(t *testing.T) {
          222         filesTemplate := `
          223 -- hugo.toml --
          224 disableKinds = ['rss','section','sitemap','taxonomy','term']
          225 [markup.goldmark.extensions.passthrough]
          226 enable = true
          227 [markup.goldmark.extensions.passthrough.delimiters]
          228 block  = [['\[', '\]'], ['$$', '$$']]
          229 inline = [['\(', '\)'], ['$', '$']]
          230 -- content/p1.md --
          231 P1_CONTENT
          232 -- layouts/index.html --
          233 Home.
          234 -- layouts/_default/single.html --
          235 Content: {{ .Content }}|
          236 -- layouts/_default/_markup/render-passthrough.html --
          237 {{ $opts := dict "throwOnError" false "displayMode" true }}
          238 {{ transform.ToMath .Inner $opts }}
          239   `
          240 
          241         t.Run("Very large file with many complex KaTeX expressions", func(t *testing.T) {
          242                 files := strings.ReplaceAll(filesTemplate, "P1_CONTENT", "sourcefilename: testdata/large-katex.md")
          243                 b := hugolib.Test(t, files)
          244                 b.AssertFileContent("public/p1/index.html", `
          245                 <span class="katex"><math
          246                         `)
          247         })
          248 
          249         t.Run("Large and complex expression", func(t *testing.T) {
          250                 // This is pulled from the file above, which times out for some reason.
          251                 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*}`
          252                 files := strings.ReplaceAll(filesTemplate, "P1_CONTENT", fmt.Sprintf(`---
          253 title: p1
          254 ---
          255 
          256 $$%s$$
          257         `, largeAndComplexeExpressions))
          258 
          259                 b := hugolib.Test(t, files)
          260                 b.AssertFileContent("public/p1/index.html", `
          261                 <span class="katex"><math
          262                         `)
          263         })
          264 }
          265 
          266 // Issue #13406.
          267 func TestToMathRenderHookPosition(t *testing.T) {
          268         filesTemplate := `
          269 -- hugo.toml --
          270 disableKinds = ['rss','section','sitemap','taxonomy','term']
          271 [markup.goldmark.extensions.passthrough]
          272 enable = true
          273 [markup.goldmark.extensions.passthrough.delimiters]
          274 block  = [['\[', '\]'], ['$$', '$$']]
          275 inline = [['\(', '\)'], ['$', '$']]
          276 -- content/p1.md --
          277 ---
          278 title: p1
          279 ---
          280 
          281 Block:
          282 
          283 $$1+2$$
          284 
          285 Some inline $1+3$ math.
          286 
          287 -- layouts/index.html --
          288 Home.
          289 -- layouts/_default/single.html --
          290 Content: {{ .Content }}|
          291 -- layouts/_default/_markup/render-passthrough.html --
          292 {{ $opts := dict "throwOnError" true "displayMode" true }}
          293 {{- with try (transform.ToMath .Inner $opts ) }}
          294   {{- with .Err }}
          295     {{ errorf "KaTeX: %s: see %s." . $.Position }}
          296   {{- else }}
          297     {{- .Value }}
          298   {{- end }}
          299 {{- end -}}
          300 
          301 `
          302 
          303         // Block math.
          304         files := strings.Replace(filesTemplate, "$$1+2$$", "$$\\foo1+2$$", 1)
          305         b, err := hugolib.TestE(t, files)
          306         b.Assert(err, qt.IsNotNil)
          307         b.AssertLogContains("p1.md:6:1")
          308 
          309         // Inline math.
          310         files = strings.Replace(filesTemplate, "$1+3$", "$\\foo1+3$", 1)
          311         b, err = hugolib.TestE(t, files)
          312         b.Assert(err, qt.IsNotNil)
          313         b.AssertLogContains("p1.md:8:13")
          314 }
          315 
          316 func TestToMathMacros(t *testing.T) {
          317         files := `
          318 -- hugo.toml --
          319 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          320 -- layouts/index.html --
          321 {{ $macros := dict
          322     "\\addBar" "\\bar{#1}"
          323         "\\bold" "\\mathbf{#1}"
          324 }}
          325 {{ $opts := dict "macros" $macros }}
          326 {{ transform.ToMath "\\addBar{y} + \\bold{H}" $opts }}
          327   `
          328         b := hugolib.Test(t, files)
          329 
          330         b.AssertFileContent("public/index.html", `
          331 <mi>y</mi>
          332         `)
          333 }
          334 
          335 // Issue #12977
          336 func TestUnmarshalWithIndentedYAML(t *testing.T) {
          337         t.Parallel()
          338 
          339         files := `
          340 -- hugo.toml --
          341 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          342 -- layouts/index.html --
          343 {{ $yaml := "\n  a:\n    b: 1\n  c:\n    d: 2\n" }}
          344 {{ $yaml | transform.Unmarshal | encoding.Jsonify }}
          345 `
          346 
          347         b := hugolib.Test(t, files)
          348 
          349         b.AssertFileExists("public/index.html", true)
          350         b.AssertFileContent("public/index.html", `{"a":{"b":1},"c":{"d":2}}`)
          351 }
          352 
          353 func TestPortableText(t *testing.T) {
          354         files := `
          355 -- hugo.toml --
          356 -- assets/sample.json --
          357 [
          358   {
          359     "_key": "a",
          360     "_type": "block",
          361     "children": [
          362       {
          363         "_key": "b",
          364         "_type": "span",
          365         "marks": [],
          366         "text": "Heading 2"
          367       }
          368     ],
          369     "markDefs": [],
          370     "style": "h2"
          371   }
          372 ]
          373 -- layouts/index.html --
          374 {{ $markdown := resources.Get "sample.json" | transform.Unmarshal | transform.PortableText }}
          375 Markdown: {{ $markdown }}|
          376 
          377 `
          378         b := hugolib.Test(t, files)
          379 
          380         b.AssertFileContent("public/index.html", "Markdown: ## Heading 2\n|")
          381 }
          382 
          383 func TestUnmarshalCSV(t *testing.T) {
          384         t.Parallel()
          385 
          386         files := `
          387 -- hugo.toml --
          388 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          389 -- layouts/all.html --
          390 {{ $opts := OPTS }}
          391 {{ with resources.Get "pets.csv" | transform.Unmarshal $opts }}
          392   {{ jsonify . }}
          393 {{ end }}
          394 -- assets/pets.csv --
          395 DATA
          396 `
          397 
          398         // targetType = map
          399         f := strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
          400         f = strings.ReplaceAll(f, "DATA",
          401                 "name,type,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
          402         )
          403         b := hugolib.Test(t, f)
          404         b.AssertFileContent("public/index.html",
          405                 `[{"age":"3","breed":"Collie","name":"Spot","type":"dog"},{"age":"7","breed":"Malicious","name":"Felix","type":"cat"}]`,
          406         )
          407 
          408         // targetType = map (no data)
          409         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
          410         f = strings.ReplaceAll(f, "DATA", "")
          411         b = hugolib.Test(t, f)
          412         b.AssertFileContent("public/index.html", "")
          413 
          414         // targetType = slice
          415         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "slice"`)
          416         f = strings.ReplaceAll(f, "DATA",
          417                 "name,type,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
          418         )
          419         b = hugolib.Test(t, f)
          420         b.AssertFileContent("public/index.html",
          421                 `[["name","type","breed","age"],["Spot","dog","Collie","3"],["Felix","cat","Malicious","7"]]`,
          422         )
          423 
          424         // targetType = slice (no data)
          425         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "slice"`)
          426         f = strings.ReplaceAll(f, "DATA", "")
          427         b = hugolib.Test(t, f)
          428         b.AssertFileContent("public/index.html", "")
          429 
          430         // targetType not specified
          431         f = strings.ReplaceAll(files, "OPTS", "dict")
          432         f = strings.ReplaceAll(f, "DATA",
          433                 "name,type,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
          434         )
          435         b = hugolib.Test(t, f)
          436         b.AssertFileContent("public/index.html",
          437                 `[["name","type","breed","age"],["Spot","dog","Collie","3"],["Felix","cat","Malicious","7"]]`,
          438         )
          439 
          440         // targetType not specified (no data)
          441         f = strings.ReplaceAll(files, "OPTS", "dict")
          442         f = strings.ReplaceAll(f, "DATA", "")
          443         b = hugolib.Test(t, f)
          444         b.AssertFileContent("public/index.html", "")
          445 
          446         // targetType = foo
          447         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "foo"`)
          448         _, err := hugolib.TestE(t, f)
          449         if err == nil {
          450                 t.Errorf("expected error")
          451         } else {
          452                 if !strings.Contains(err.Error(), `invalid targetType: expected either slice or map, received foo`) {
          453                         t.Log(err.Error())
          454                         t.Errorf("error message does not match expected error message")
          455                 }
          456         }
          457 
          458         // targetType = foo (no data)
          459         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "foo"`)
          460         f = strings.ReplaceAll(f, "DATA", "")
          461         _, err = hugolib.TestE(t, f)
          462         if err == nil {
          463                 t.Errorf("expected error")
          464         } else {
          465                 if !strings.Contains(err.Error(), `invalid targetType: expected either slice or map, received foo`) {
          466                         t.Log(err.Error())
          467                         t.Errorf("error message does not match expected error message")
          468                 }
          469         }
          470 
          471         // targetType = map (error: expected at least a header row and one data row)
          472         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
          473         _, err = hugolib.TestE(t, f)
          474         if err == nil {
          475                 t.Errorf("expected error")
          476         } else {
          477                 if !strings.Contains(err.Error(), `expected at least a header row and one data row`) {
          478                         t.Log(err.Error())
          479                         t.Errorf("error message does not match expected error message")
          480                 }
          481         }
          482 
          483         // targetType = map (error: header row contains duplicate field names)
          484         f = strings.ReplaceAll(files, "OPTS", `dict "targetType" "map"`)
          485         f = strings.ReplaceAll(f, "DATA",
          486                 "name,name,breed,age\nSpot,dog,Collie,3\nFelix,cat,Malicious,7",
          487         )
          488         _, err = hugolib.TestE(t, f)
          489         if err == nil {
          490                 t.Errorf("expected error")
          491         } else {
          492                 if !strings.Contains(err.Error(), `header row contains duplicate field names`) {
          493                         t.Log(err.Error())
          494                         t.Errorf("error message does not match expected error message")
          495                 }
          496         }
          497 }
          498 
          499 // Issue 13729
          500 func TestToMathStrictMode(t *testing.T) {
          501         t.Parallel()
          502 
          503         files := `
          504 -- hugo.toml --
          505 disableKinds = ['page','rss','section','sitemap','taxonomy','term']
          506 -- layouts/all.html --
          507 {{ transform.ToMath "a %" dict }}
          508 -- foo --
          509 `
          510 
          511         // strict mode: default
          512         f := strings.ReplaceAll(files, "dict", "")
          513         b, err := hugolib.TestE(t, f)
          514         b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
          515 
          516         // strict mode: error
          517         f = strings.ReplaceAll(files, "dict", `(dict "strict" "error")`)
          518         b, err = hugolib.TestE(t, f)
          519         b.Assert(err.Error(), qt.Contains, "[commentAtEnd]")
          520 
          521         // strict mode: ignore
          522         f = strings.ReplaceAll(files, "dict", `(dict "strict" "ignore")`)
          523         b = hugolib.Test(t, f, hugolib.TestOptWarn())
          524         b.AssertLogMatches("")
          525         b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
          526 
          527         // strict: warn
          528         f = strings.ReplaceAll(files, "dict", `(dict "strict" "warn")`)
          529         b = hugolib.Test(t, f, hugolib.TestOptWarn())
          530         b.AssertLogMatches("[commentAtEnd]")
          531         b.AssertFileContent("public/index.html", `<annotation encoding="application/x-tex">a %</annotation>`)
          532 
          533         // strict mode: invalid value
          534         f = strings.ReplaceAll(files, "dict", `(dict "strict" "foo")`)
          535         b, err = hugolib.TestE(t, f)
          536         b.Assert(err.Error(), qt.Contains, "invalid strict mode")
          537 }