tplimpl_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
---
tplimpl_integration_test.go (15412B)
---
1 // Copyright 2025 The Hugo Authors. All rights reserved.
2 //
3 // Portions Copyright The Go Authors.
4
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 package tplimpl_test
17
18 import (
19 "strings"
20 "testing"
21
22 qt "github.com/frankban/quicktest"
23 "github.com/gohugoio/hugo/hugolib"
24 )
25
26 // Verify that the new keywords in Go 1.18 is available.
27 func TestGo18Constructs(t *testing.T) {
28 t.Parallel()
29
30 files := `
31 -- config.toml --
32 baseURL = 'http://example.com/'
33 disableKinds = ["section", "home", "rss", "taxonomy", "term", "rss"]
34 -- content/p1.md --
35 ---
36 title: "P1"
37 ---
38 -- layouts/partials/counter.html --
39 {{ if .Scratch.Get "counter" }}{{ .Scratch.Add "counter" 1 }}{{ else }}{{ .Scratch.Set "counter" 1 }}{{ end }}{{ return true }}
40 -- layouts/_default/single.html --
41 continue:{{ range seq 5 }}{{ if eq . 2 }}{{continue}}{{ end }}{{ . }}{{ end }}:END:
42 break:{{ range seq 5 }}{{ if eq . 2 }}{{break}}{{ end }}{{ . }}{{ end }}:END:
43 continue2:{{ range seq 5 }}{{ if eq . 2 }}{{ continue }}{{ end }}{{ . }}{{ end }}:END:
44 break2:{{ range seq 5 }}{{ if eq . 2 }}{{ break }}{{ end }}{{ . }}{{ end }}:END:
45
46 counter1: {{ partial "counter.html" . }}/{{ .Scratch.Get "counter" }}
47 and1: {{ if (and false (partial "counter.html" .)) }}true{{ else }}false{{ end }}
48 or1: {{ if (or true (partial "counter.html" .)) }}true{{ else }}false{{ end }}
49 and2: {{ if (and true (partial "counter.html" .)) }}true{{ else }}false{{ end }}
50 or2: {{ if (or false (partial "counter.html" .)) }}true{{ else }}false{{ end }}
51
52
53 counter2: {{ .Scratch.Get "counter" }}
54
55
56 `
57
58 b := hugolib.NewIntegrationTestBuilder(
59 hugolib.IntegrationTestConfig{
60 T: t,
61 TxtarString: files,
62 NeedsOsFS: true,
63 },
64 )
65 b.Build()
66
67 b.AssertFileContent("public/p1/index.html", `
68 continue:1345:END:
69 break:1:END:
70 continue2:1345:END:
71 break2:1:END:
72 counter1: true/1
73 and1: false
74 or1: true
75 and2: true
76 or2: true
77 counter2: 3
78 `)
79 }
80
81 func TestGo23ElseWith(t *testing.T) {
82 t.Parallel()
83
84 files := `
85 -- hugo.toml --
86 title = "Hugo"
87 -- layouts/index.html --
88 {{ with false }}{{ else with .Site }}{{ .Title }}{{ end }}|
89 `
90 b := hugolib.Test(t, files)
91
92 b.AssertFileContent("public/index.html", "Hugo|")
93 }
94
95 // Issue 10495
96 func TestCommentsBeforeBlockDefinition(t *testing.T) {
97 t.Parallel()
98
99 files := `
100 -- config.toml --
101 baseURL = 'http://example.com/'
102 -- content/s1/p1.md --
103 ---
104 title: "S1P1"
105 ---
106 -- content/s2/p1.md --
107 ---
108 title: "S2P1"
109 ---
110 -- content/s3/p1.md --
111 ---
112 title: "S3P1"
113 ---
114 -- layouts/_default/baseof.html --
115 {{ block "main" . }}{{ end }}
116 -- layouts/s1/single.html --
117 {{/* foo */}}
118 {{ define "main" }}{{ .Title }}{{ end }}
119 -- layouts/s2/single.html --
120 {{- /* foo */}}
121 {{ define "main" }}{{ .Title }}{{ end }}
122 -- layouts/s3/single.html --
123 {{- /* foo */ -}}
124 {{ define "main" }}{{ .Title }}{{ end }}
125 `
126
127 b := hugolib.NewIntegrationTestBuilder(
128 hugolib.IntegrationTestConfig{
129 T: t,
130 TxtarString: files,
131 },
132 )
133 b.Build()
134
135 b.AssertFileContent("public/s1/p1/index.html", `S1P1`)
136 b.AssertFileContent("public/s2/p1/index.html", `S2P1`)
137 b.AssertFileContent("public/s3/p1/index.html", `S3P1`)
138 }
139
140 func TestGoTemplateBugs(t *testing.T) {
141 t.Run("Issue 11112", func(t *testing.T) {
142 t.Parallel()
143
144 files := `
145 -- config.toml --
146 -- layouts/index.html --
147 {{ $m := dict "key" "value" }}
148 {{ $k := "" }}
149 {{ $v := "" }}
150 {{ range $k, $v = $m }}
151 {{ $k }} = {{ $v }}
152 {{ end }}
153 `
154
155 b := hugolib.NewIntegrationTestBuilder(
156 hugolib.IntegrationTestConfig{
157 T: t,
158 TxtarString: files,
159 },
160 )
161 b.Build()
162
163 b.AssertFileContent("public/index.html", `key = value`)
164 })
165 }
166
167 func TestSecurityAllowActionJSTmpl(t *testing.T) {
168 filesTemplate := `
169 -- config.toml --
170 SECURITYCONFIG
171 -- layouts/index.html --
172 <script>
173 var a = §§{{.Title }}§§;
174 </script>
175 `
176
177 files := strings.ReplaceAll(filesTemplate, "SECURITYCONFIG", "")
178
179 b, err := hugolib.NewIntegrationTestBuilder(
180 hugolib.IntegrationTestConfig{
181 T: t,
182 TxtarString: files,
183 },
184 ).BuildE()
185
186 // This used to fail, but not in >= Hugo 0.121.0.
187 b.Assert(err, qt.IsNil)
188 }
189
190 func TestGoogleAnalyticsTemplate(t *testing.T) {
191 t.Parallel()
192
193 files := `
194 -- hugo.toml --
195 disableKinds = ['page','section','rss','sitemap','taxonomy','term']
196 [privacy.googleAnalytics]
197 disable = false
198 respectDoNotTrack = true
199 [services.googleAnalytics]
200 id = 'G-0123456789'
201 -- layouts/index.html --
202 {{ template "_internal/google_analytics.html" . }}
203 `
204
205 b := hugolib.Test(t, files)
206
207 b.AssertFileContent("public/index.html",
208 `<script async src="https://www.googletagmanager.com/gtag/js?id=G-0123456789"></script>`,
209 `var dnt = (navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack);`,
210 )
211 }
212
213 func TestDisqusTemplate(t *testing.T) {
214 t.Parallel()
215
216 files := `
217 -- hugo.toml --
218 disableKinds = ['page','section','rss','sitemap','taxonomy','term']
219 [services.disqus]
220 shortname = 'foo'
221 [privacy.disqus]
222 disable = false
223 -- layouts/index.html --
224 {{ template "_internal/disqus.html" . }}
225 `
226
227 b := hugolib.Test(t, files)
228
229 b.AssertFileContent("public/index.html",
230 `s.src = '//' + "foo" + '.disqus.com/embed.js';`,
231 )
232 }
233
234 func TestSitemap(t *testing.T) {
235 t.Parallel()
236
237 files := `
238 -- hugo.toml --
239 disableKinds = ['home','rss','section','taxonomy','term']
240 [sitemap]
241 disable = true
242 -- content/p1.md --
243 ---
244 title: p1
245 sitemap:
246 p1_disable: foo
247 ---
248 -- content/p2.md --
249 ---
250 title: p2
251
252 ---
253 -- layouts/_default/single.html --
254 {{ .Title }}
255 `
256
257 // Test A: Exclude all pages via site config.
258 b := hugolib.Test(t, files)
259 b.AssertFileContentExact("public/sitemap.xml",
260 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\n \n</urlset>\n",
261 )
262
263 // Test B: Include all pages via site config.
264 files_b := strings.ReplaceAll(files, "disable = true", "disable = false")
265 b = hugolib.Test(t, files_b)
266 b.AssertFileContentExact("public/sitemap.xml",
267 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\n <url>\n <loc>/p1/</loc>\n </url><url>\n <loc>/p2/</loc>\n </url>\n</urlset>\n",
268 )
269
270 // Test C: Exclude all pages via site config, but include p1 via front matter.
271 files_c := strings.ReplaceAll(files, "p1_disable: foo", "disable: false")
272 b = hugolib.Test(t, files_c)
273 b.AssertFileContentExact("public/sitemap.xml",
274 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\n <url>\n <loc>/p1/</loc>\n </url>\n</urlset>\n",
275 )
276
277 // Test D: Include all pages via site config, but exclude p1 via front matter.
278 files_d := strings.ReplaceAll(files_b, "p1_disable: foo", "disable: true")
279 b = hugolib.Test(t, files_d)
280 b.AssertFileContentExact("public/sitemap.xml",
281 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">\n <url>\n <loc>/p2/</loc>\n </url>\n</urlset>\n",
282 )
283 }
284
285 // Issue 12418
286 func TestOpengraph(t *testing.T) {
287 t.Parallel()
288
289 files := `
290 -- hugo.toml --
291 capitalizeListTitles = false
292 disableKinds = ['rss','sitemap']
293 languageCode = 'en-US'
294 [markup.goldmark.renderer]
295 unsafe = true
296 [params]
297 description = "m <em>n</em> and **o** can't."
298 [params.social]
299 facebook_admin = 'foo'
300 [taxonomies]
301 series = 'series'
302 tag = 'tags'
303 -- layouts/_default/list.html --
304 {{ template "_internal/opengraph.html" . }}
305 -- layouts/_default/single.html --
306 {{ template "_internal/opengraph.html" . }}
307 -- content/s1/p1.md --
308 ---
309 title: p1
310 date: 2024-04-24T08:00:00-07:00
311 lastmod: 2024-04-24T11:00:00-07:00
312 images: [a.jpg,b.jpg]
313 audio: [c.mp3,d.mp3]
314 videos: [e.mp4,f.mp4]
315 series: [series-1]
316 tags: [t1,t2]
317 ---
318 a <em>b</em> and **c** can't.
319 -- content/s1/p2.md --
320 ---
321 title: p2
322 series: [series-1]
323 ---
324 d <em>e</em> and **f** can't.
325 <!--more-->
326 -- content/s1/p3.md --
327 ---
328 title: p3
329 series: [series-1]
330 summary: g <em>h</em> and **i** can't.
331 ---
332 -- content/s1/p4.md --
333 ---
334 title: p4
335 series: [series-1]
336 description: j <em>k</em> and **l** can't.
337 ---
338 -- content/s1/p5.md --
339 ---
340 title: p5
341 series: [series-1]
342 ---
343 `
344
345 b := hugolib.Test(t, files)
346
347 b.AssertFileContent("public/s1/p1/index.html", `
348 <meta property="og:url" content="/s1/p1/">
349 <meta property="og:title" content="p1">
350 <meta property="og:description" content="a b and c can’t.">
351 <meta property="og:locale" content="en_US">
352 <meta property="og:type" content="article">
353 <meta property="article:section" content="s1">
354 <meta property="article:published_time" content="2024-04-24T08:00:00-07:00">
355 <meta property="article:modified_time" content="2024-04-24T11:00:00-07:00">
356 <meta property="article:tag" content="t1">
357 <meta property="article:tag" content="t2">
358 <meta property="og:image" content="/a.jpg">
359 <meta property="og:image" content="/b.jpg">
360 <meta property="og:audio" content="/c.mp3">
361 <meta property="og:audio" content="/d.mp3">
362 <meta property="og:video" content="/e.mp4">
363 <meta property="og:video" content="/f.mp4">
364 <meta property="og:see_also" content="/s1/p2/">
365 <meta property="og:see_also" content="/s1/p3/">
366 <meta property="og:see_also" content="/s1/p4/">
367 <meta property="og:see_also" content="/s1/p5/">
368 <meta property="fb:admins" content="foo">
369 `,
370 )
371
372 b.AssertFileContent("public/s1/p2/index.html",
373 `<meta property="og:description" content="d e and f can’t.">`,
374 )
375
376 b.AssertFileContent("public/s1/p3/index.html",
377 `<meta property="og:description" content="g h and i can’t.">`,
378 )
379
380 // The markdown is intentionally not rendered to HTML.
381 b.AssertFileContent("public/s1/p4/index.html",
382 `<meta property="og:description" content="j k and **l** can't.">`,
383 )
384
385 // The markdown is intentionally not rendered to HTML.
386 b.AssertFileContent("public/s1/p5/index.html",
387 `<meta property="og:description" content="m n and **o** can't.">`,
388 )
389 }
390
391 // Issue 12432
392 func TestSchema(t *testing.T) {
393 t.Parallel()
394
395 files := `
396 -- hugo.toml --
397 capitalizeListTitles = false
398 disableKinds = ['rss','sitemap']
399 [markup.goldmark.renderer]
400 unsafe = true
401 [params]
402 description = "m <em>n</em> and **o** can't."
403 [taxonomies]
404 tag = 'tags'
405 -- layouts/_default/list.html --
406 {{ template "_internal/schema.html" . }}
407 -- layouts/_default/single.html --
408 {{ template "_internal/schema.html" . }}
409 -- content/s1/p1.md --
410 ---
411 title: p1
412 date: 2024-04-24T08:00:00-07:00
413 lastmod: 2024-04-24T11:00:00-07:00
414 images: [a.jpg,b.jpg]
415 tags: [t1,t2]
416 ---
417 a <em>b</em> and **c** can't.
418 -- content/s1/p2.md --
419 ---
420 title: p2
421 ---
422 d <em>e</em> and **f** can't.
423 <!--more-->
424 -- content/s1/p3.md --
425 ---
426 title: p3
427 summary: g <em>h</em> and **i** can't.
428 ---
429 -- content/s1/p4.md --
430 ---
431 title: p4
432 description: j <em>k</em> and **l** can't.
433 ---
434 -- content/s1/p5.md --
435 ---
436 title: p5
437 ---
438 `
439
440 b := hugolib.Test(t, files)
441
442 b.AssertFileContent("public/s1/p1/index.html", `
443 <meta itemprop="name" content="p1">
444 <meta itemprop="description" content="a b and c can’t.">
445 <meta itemprop="datePublished" content="2024-04-24T08:00:00-07:00">
446 <meta itemprop="dateModified" content="2024-04-24T11:00:00-07:00">
447 <meta itemprop="wordCount" content="5">
448 <meta itemprop="image" content="/a.jpg">
449 <meta itemprop="image" content="/b.jpg">
450 <meta itemprop="keywords" content="t1,t2">
451 `,
452 )
453
454 b.AssertFileContent("public/s1/p2/index.html",
455 `<meta itemprop="description" content="d e and f can’t.">`,
456 )
457
458 b.AssertFileContent("public/s1/p3/index.html",
459 `<meta itemprop="description" content="g h and i can’t.">`,
460 )
461
462 // The markdown is intentionally not rendered to HTML.
463 b.AssertFileContent("public/s1/p4/index.html",
464 `<meta itemprop="description" content="j k and **l** can't.">`,
465 )
466
467 // The markdown is intentionally not rendered to HTML.
468 b.AssertFileContent("public/s1/p5/index.html",
469 `<meta itemprop="description" content="m n and **o** can't.">`,
470 )
471 }
472
473 // Issue 12433
474 func TestTwitterCards(t *testing.T) {
475 t.Parallel()
476
477 files := `
478 -- hugo.toml --
479 capitalizeListTitles = false
480 disableKinds = ['rss','sitemap','taxonomy','term']
481 [markup.goldmark.renderer]
482 unsafe = true
483 [params]
484 description = "m <em>n</em> and **o** can't."
485 [params.social]
486 twitter = 'foo'
487 -- layouts/_default/list.html --
488 {{ template "_internal/twitter_cards.html" . }}
489 -- layouts/_default/single.html --
490 {{ template "_internal/twitter_cards.html" . }}
491 -- content/s1/p1.md --
492 ---
493 title: p1
494 images: [a.jpg,b.jpg]
495 ---
496 a <em>b</em> and **c** can't.
497 -- content/s1/p2.md --
498 ---
499 title: p2
500 ---
501 d <em>e</em> and **f** can't.
502 <!--more-->
503 -- content/s1/p3.md --
504 ---
505 title: p3
506 summary: g <em>h</em> and **i** can't.
507 ---
508 -- content/s1/p4.md --
509 ---
510 title: p4
511 description: j <em>k</em> and **l** can't.
512 ---
513 -- content/s1/p5.md --
514 ---
515 title: p5
516 ---
517 `
518
519 b := hugolib.Test(t, files)
520
521 b.AssertFileContent("public/s1/p1/index.html", `
522 <meta name="twitter:card" content="summary_large_image">
523 <meta name="twitter:image" content="/a.jpg">
524 <meta name="twitter:title" content="p1">
525 <meta name="twitter:description" content="a b and c can’t.">
526 <meta name="twitter:site" content="@foo">
527 `,
528 )
529
530 b.AssertFileContent("public/s1/p2/index.html",
531 `<meta name="twitter:card" content="summary">`,
532 `<meta name="twitter:description" content="d e and f can’t.">`,
533 )
534
535 b.AssertFileContent("public/s1/p3/index.html",
536 `<meta name="twitter:description" content="g h and i can’t.">`,
537 )
538
539 // The markdown is intentionally not rendered to HTML.
540 b.AssertFileContent("public/s1/p4/index.html",
541 `<meta name="twitter:description" content="j k and **l** can't.">`,
542 )
543
544 // The markdown is intentionally not rendered to HTML.
545 b.AssertFileContent("public/s1/p5/index.html",
546 `<meta name="twitter:description" content="m n and **o** can't.">`,
547 )
548 }
549
550 // Issue 12963
551 func TestEditBaseofParseAfterExecute(t *testing.T) {
552 files := `
553 -- hugo.toml --
554 baseURL = "https://example.com"
555 disableLiveReload = true
556 disableKinds = ["taxonomy", "term", "rss", "404", "sitemap"]
557 [internal]
558 fastRenderMode = true
559 -- layouts/_default/baseof.html --
560 Baseof!
561 {{ block "main" . }}default{{ end }}
562 {{ with (templates.Defer (dict "key" "global")) }}
563 Now. {{ now }}
564 {{ end }}
565 -- layouts/_default/single.html --
566 {{ define "main" }}
567 Single.
568 {{ end }}
569 -- layouts/_default/list.html --
570 {{ define "main" }}
571 List.
572 {{ .Content }}
573 {{ range .Pages }}{{ .Title }}{{ end }}|
574 {{ end }}
575 -- content/mybundle1/index.md --
576 ---
577 title: "My Bundle 1"
578 ---
579 -- content/mybundle2/index.md --
580 ---
581 title: "My Bundle 2"
582 ---
583 -- content/_index.md --
584 ---
585 title: "Home"
586 ---
587 Home!
588 `
589
590 b := hugolib.TestRunning(t, files)
591 b.AssertFileContent("public/index.html", "Home!")
592 b.EditFileReplaceAll("layouts/_default/baseof.html", "baseof", "Baseof!").Build()
593 b.BuildPartial("/")
594 b.AssertFileContent("public/index.html", "Baseof!")
595 b.BuildPartial("/mybundle1/")
596 b.AssertFileContent("public/mybundle1/index.html", "Baseof!")
597 }