hugo/hugolib/hugo_smoke_test.go
Bjørn Erik Pedersen e2d66e3218
Create pages from _content.gotmpl
Closes #12427
Closes #12485
Closes #6310
Closes #5074
2024-05-14 13:12:08 +02:00

596 lines
15 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 hugolib
import (
"fmt"
"math/rand"
"testing"
"github.com/bep/logg"
qt "github.com/frankban/quicktest"
)
// The most basic build test.
func TestHello(t *testing.T) {
files := `
-- hugo.toml --
title = "Hello"
baseURL="https://example.org"
disableKinds = ["term", "taxonomy", "section", "page"]
-- content/p1.md --
---
title: Page
---
-- layouts/index.html --
Home: {{ .Title }}
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
// LogLevel: logg.LevelTrace,
},
).Build()
b.Assert(b.H.Log.LoggCount(logg.LevelWarn), qt.Equals, 0)
b.AssertFileContent("public/index.html", `Hello`)
}
func TestSmokeOutputFormats(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com/"
defaultContentLanguage = "en"
disableKinds = ["term", "taxonomy", "robotsTXT", "sitemap"]
[outputs]
home = ["html", "rss"]
section = ["html", "rss"]
page = ["html"]
-- content/p1.md --
---
title: Page
---
Page.
-- layouts/_default/list.html --
List: {{ .Title }}|{{ .RelPermalink}}|{{ range .OutputFormats }}{{ .Name }}: {{ .RelPermalink }}|{{ end }}$
-- layouts/_default/list.xml --
List xml: {{ .Title }}|{{ .RelPermalink}}|{{ range .OutputFormats }}{{ .Name }}: {{ .RelPermalink }}|{{ end }}$
-- layouts/_default/single.html --
Single: {{ .Title }}|{{ .RelPermalink}}|{{ range .OutputFormats }}{{ .Name }}: {{ .RelPermalink }}|{{ end }}$
`
b := Test(t, files)
b.AssertFileContent("public/index.html", `List: |/|html: /|rss: /index.xml|$`)
b.AssertFileContent("public/index.xml", `List xml: |/|html: /|rss: /index.xml|$`)
b.AssertFileContent("public/p1/index.html", `Single: Page|/p1/|html: /p1/|$`)
b.AssertFileExists("public/p1/index.xml", false)
}
func TestSmoke(t *testing.T) {
t.Parallel()
// Basic test cases.
// OK translations
// OK page collections
// OK next, prev in section
// OK GetPage
// OK Pagination
// OK RenderString with shortcode
// OK cascade
// OK site last mod, section last mod.
// OK main sections
// OK taxonomies
// OK GetTerms
// OK Resource page
// OK Resource txt
const files = `
-- hugo.toml --
baseURL = "https://example.com"
title = "Smoke Site"
rssLimit = 3
paginate = 1
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
enableRobotsTXT = true
[taxonomies]
category = 'categories'
tag = 'tags'
[languages]
[languages.en]
weight = 1
title = "In English"
[languages.no]
weight = 2
title = "På norsk"
[params]
hugo = "Rules!"
[outputs]
home = ["html", "json", "rss"]
-- layouts/index.html --
Home: {{ .Lang}}|{{ .Kind }}|{{ .RelPermalink }}|{{ .Title }}|{{ .Content }}|Len Resources: {{ len .Resources }}|HTML
Resources: {{ range .Resources }}{{ .ResourceType }}|{{ .RelPermalink }}|{{ .MediaType }} - {{ end }}|
Site last mod: {{ site.Lastmod.Format "2006-02-01" }}|
Home last mod: {{ .Lastmod.Format "2006-02-01" }}|
Len Translations: {{ len .Translations }}|
Len home.RegularPagesRecursive: {{ len .RegularPagesRecursive }}|
RegularPagesRecursive: {{ range .RegularPagesRecursive }}{{ .RelPermalink }}|{{ end }}@
Len site.RegularPages: {{ len site.RegularPages }}|
Len site.Pages: {{ len site.Pages }}|
Len site.AllPages: {{ len site.AllPages }}|
GetPage: {{ with .Site.GetPage "posts/p1" }}{{ .RelPermalink }}|{{ .Title }}{{ end }}|
RenderString with shortcode: {{ .RenderString "{{% hello %}}" }}|
Paginate: {{ .Paginator.PageNumber }}/{{ .Paginator.TotalPages }}|
-- layouts/index.json --
Home:{{ .Lang}}|{{ .Kind }}|{{ .RelPermalink }}|{{ .Title }}|{{ .Content }}|Len Resources: {{ len .Resources }}|JSON
-- layouts/_default/list.html --
List: {{ .Lang}}|{{ .Kind }}|{{ .RelPermalink }}|{{ .Title }}|{{ .Content }}|Len Resources: {{ len .Resources }}|
Resources: {{ range .Resources }}{{ .ResourceType }}|{{ .RelPermalink }}|{{ .MediaType }} - {{ end }}
Pages Length: {{ len .Pages }}
RegularPages Length: {{ len .RegularPages }}
RegularPagesRecursive Length: {{ len .RegularPagesRecursive }}
List last mod: {{ .Lastmod.Format "2006-02-01" }}
Background: {{ .Params.background }}|
Kind: {{ .Kind }}
Type: {{ .Type }}
Paginate: {{ .Paginator.PageNumber }}/{{ .Paginator.TotalPages }}|
-- layouts/_default/single.html --
Single: {{ .Lang}}|{{ .Kind }}|{{ .RelPermalink }}|{{ .Title }}|{{ .Content }}|Len Resources: {{ len .Resources }}|Background: {{ .Params.background }}|
Resources: {{ range .Resources }}{{ .ResourceType }}|{{ .RelPermalink }}|{{ .MediaType }}|{{ .Params }} - {{ end }}
{{ $textResource := .Resources.GetMatch "**.txt" }}
{{ with $textResource }}
Icon: {{ .Params.icon }}|
{{ $textResourceFingerprinted := . | fingerprint }}
Icon fingerprinted: {{ with $textResourceFingerprinted }}{{ .Params.icon }}|{{ .RelPermalink }}{{ end }}|
{{ end }}
NextInSection: {{ with .NextInSection }}{{ .RelPermalink }}|{{ .Title }}{{ end }}|
PrevInSection: {{ with .PrevInSection }}{{ .RelPermalink }}|{{ .Title }}{{ end }}|
GetTerms: {{ range .GetTerms "tags" }}name: {{ .Name }}, title: {{ .Title }}|{{ end }}
-- layouts/shortcodes/hello.html --
Hello.
-- content/_index.md --
---
title: Home in English
---
Home Content.
-- content/_index.no.md --
---
title: Hjem
cascade:
- _target:
kind: page
path: /posts/**
background: post.jpg
- _target:
kind: term
background: term.jpg
---
Hjem Innhold.
-- content/posts/f1.txt --
posts f1 text.
-- content/posts/sub/f1.txt --
posts sub f1 text.
-- content/posts/p1/index.md --
+++
title = "Post 1"
lastMod = "2001-01-01"
tags = ["tag1"]
[[resources]]
src = '**'
[resources.params]
icon = 'enicon'
+++
Content 1.
-- content/posts/p1/index.no.md --
+++
title = "Post 1 no"
lastMod = "2002-02-02"
tags = ["tag1", "tag2"]
[[resources]]
src = '**'
[resources.params]
icon = 'noicon'
+++
Content 1 no.
-- content/posts/_index.md --
---
title: Posts
---
-- content/posts/p1/f1.txt --
posts p1 f1 text.
-- content/posts/p1/sub/ps1.md --
---
title: Post Sub 1
---
Content Sub 1.
-- content/posts/p2.md --
---
title: Post 2
tags: ["tag1", "tag3"]
---
Content 2.
-- content/posts/p2.no.md --
---
title: Post 2 No
---
Content 2 No.
-- content/tags/_index.md --
---
title: Tags
---
Content Tags.
-- content/tags/tag1/_index.md --
---
title: Tag 1
---
Content Tag 1.
`
b := NewIntegrationTestBuilder(IntegrationTestConfig{
T: t,
TxtarString: files,
NeedsOsFS: true,
// Verbose: true,
// LogLevel: logg.LevelTrace,
}).Build()
b.AssertFileContent("public/en/index.html",
"Home: en|home|/en/|Home in English|<p>Home Content.</p>\n|HTML",
"Site last mod: 2001-01-01",
"Home last mod: 2001-01-01",
"Translations: 1|",
"Len home.RegularPagesRecursive: 2|",
"Len site.RegularPages: 2|",
"Len site.Pages: 8|",
"Len site.AllPages: 16|",
"GetPage: /en/posts/p1/|Post 1|",
"RenderString with shortcode: Hello.|",
"Paginate: 1/2|",
)
b.AssertFileContent("public/en/page/2/index.html", "Paginate: 2/2|")
b.AssertFileContent("public/no/index.html",
"Home: no|home|/no/|Hjem|<p>Hjem Innhold.</p>\n|HTML",
"Site last mod: 2002-02-02",
"Home last mod: 2002-02-02",
"Translations: 1",
"GetPage: /no/posts/p1/|Post 1 no|",
)
b.AssertFileContent("public/en/index.json", "Home:en|home|/en/|Home in English|<p>Home Content.</p>\n|JSON")
b.AssertFileContent("public/no/index.json", "Home:no|home|/no/|Hjem|<p>Hjem Innhold.</p>\n|JSON")
b.AssertFileContent("public/en/posts/p1/index.html",
"Single: en|page|/en/posts/p1/|Post 1|<p>Content 1.</p>\n|Len Resources: 2|",
"Resources: text|/en/posts/p1/f1.txt|text/plain|map[icon:enicon] - page||application/octet-stream|map[draft:false iscjklanguage:false title:Post Sub 1] -",
"Icon: enicon",
"Icon fingerprinted: enicon|/en/posts/p1/f1.e5746577af5cbfc4f34c558051b7955a9a5a795a84f1c6ab0609cb3473a924cb.txt|",
"NextInSection: |\nPrevInSection: /en/posts/p2/|Post 2|",
"GetTerms: name: tag1, title: Tag 1|",
)
b.AssertFileContent("public/no/posts/p1/index.html",
"Resources: 1",
"Resources: text|/en/posts/p1/f1.txt|text/plain|map[icon:noicon] -",
"Icon: noicon",
"Icon fingerprinted: noicon|/en/posts/p1/f1.e5746577af5cbfc4f34c558051b7955a9a5a795a84f1c6ab0609cb3473a924cb.txt|",
"Background: post.jpg",
"NextInSection: |\nPrevInSection: /no/posts/p2/|Post 2 No|",
)
b.AssertFileContent("public/en/posts/index.html",
"List: en|section|/en/posts/|Posts||Len Resources: 2|",
"Resources: text|/en/posts/f1.txt|text/plain - text|/en/posts/sub/f1.txt|text/plain -",
"List last mod: 2001-01-01",
)
b.AssertFileContent("public/no/posts/index.html",
"List last mod: 2002-02-02",
)
b.AssertFileContent("public/en/posts/p2/index.html", "Single: en|page|/en/posts/p2/|Post 2|<p>Content 2.</p>\n|",
"|Len Resources: 0",
"GetTerms: name: tag1, title: Tag 1|name: tag3, title: Tag3|",
)
b.AssertFileContent("public/no/posts/p2/index.html", "Single: no|page|/no/posts/p2/|Post 2 No|<p>Content 2 No.</p>\n|")
b.AssertFileContent("public/no/categories/index.html",
"Kind: taxonomy",
"Type: categories",
)
b.AssertFileContent("public/no/tags/index.html",
"Kind: taxonomy",
"Type: tags",
)
b.AssertFileContent("public/no/tags/tag1/index.html",
"Background: term.jpg",
"Kind: term",
"Type: tags",
"Paginate: 1/1|",
)
b.AssertFileContent("public/en/tags/tag1/index.html",
"Kind: term",
"Type: tags",
"Paginate: 1/2|",
)
}
// Basic tests that verifies that the different file systems work as expected.
func TestSmokeFilesystems(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
defaultContentLanguage = "en"
defaultContentLanguageInSubdir = true
[languages]
[languages.en]
title = "In English"
[languages.nn]
title = "På nynorsk"
[module]
[[module.mounts]]
source = "i18n"
target = "i18n"
[[module.mounts]]
source = "data"
target = "data"
[[module.mounts]]
source = "content/en"
target = "content"
lang = "en"
[[module.mounts]]
source = "content/nn"
target = "content"
lang = "nn"
[[module.imports]]
path = "mytheme"
-- layouts/index.html --
i18n s1: {{ i18n "s1" }}|
i18n s2: {{ i18n "s2" }}|
data s1: {{ site.Data.d1.s1 }}|
data s2: {{ site.Data.d1.s2 }}|
title: {{ .Title }}|
-- themes/mytheme/hugo.toml --
[[module.mounts]]
source = "i18n"
target = "i18n"
[[module.mounts]]
source = "data"
target = "data"
# i18n files both project and theme.
-- i18n/en.toml --
[s1]
other = 's1project'
-- i18n/nn.toml --
[s1]
other = 's1prosjekt'
-- themes/mytheme/i18n/en.toml --
[s1]
other = 's1theme'
[s2]
other = 's2theme'
# data files both project and theme.
-- data/d1.yaml --
s1: s1project
-- themes/mytheme/data/d1.yaml --
s1: s1theme
s2: s2theme
# Content
-- content/en/_index.md --
---
title: "Home"
---
-- content/nn/_index.md --
---
title: "Heim"
---
`
b := Test(t, files)
b.AssertFileContent("public/en/index.html",
"i18n s1: s1project", "i18n s2: s2theme",
"data s1: s1project", "data s2: s2theme",
"title: Home",
)
b.AssertFileContent("public/nn/index.html",
"i18n s1: s1prosjekt", "i18n s2: s2theme",
"data s1: s1project", "data s2: s2theme",
"title: Heim",
)
}
// https://github.com/golang/go/issues/30286
func TestDataRace(t *testing.T) {
const page = `
---
title: "The Page"
outputs: ["HTML", "JSON"]
---
The content.
`
b := newTestSitesBuilder(t).WithSimpleConfigFile()
for i := 1; i <= 50; i++ {
b.WithContent(fmt.Sprintf("blog/page%d.md", i), page)
}
b.WithContent("_index.md", `
---
title: "The Home"
outputs: ["HTML", "JSON", "CSV", "RSS"]
---
The content.
`)
commonTemplate := `{{ .Data.Pages }}`
b.WithTemplatesAdded("_default/single.html", "HTML Single: "+commonTemplate)
b.WithTemplatesAdded("_default/list.html", "HTML List: "+commonTemplate)
b.CreateSites().Build(BuildCfg{})
}
// This is just a test to verify that BenchmarkBaseline is working as intended.
func TestBenchmarkBaseline(t *testing.T) {
cfg := IntegrationTestConfig{
T: t,
TxtarString: benchmarkBaselineFiles(true),
}
b := NewIntegrationTestBuilder(cfg).Build()
b.Assert(len(b.H.Sites), qt.Equals, 4)
b.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 161)
b.Assert(len(b.H.Sites[0].Pages()), qt.Equals, 197)
b.Assert(len(b.H.Sites[2].RegularPages()), qt.Equals, 158)
b.Assert(len(b.H.Sites[2].Pages()), qt.Equals, 194)
}
func BenchmarkBaseline(b *testing.B) {
cfg := IntegrationTestConfig{
T: b,
TxtarString: benchmarkBaselineFiles(false),
}
builders := make([]*IntegrationTestBuilder, b.N)
for i := range builders {
builders[i] = NewIntegrationTestBuilder(cfg)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
builders[i].Build()
}
}
func benchmarkBaselineFiles(leafBundles bool) string {
rnd := rand.New(rand.NewSource(32))
files := `
-- config.toml --
baseURL = "https://example.com"
defaultContentLanguage = 'en'
[module]
[[module.mounts]]
source = 'content/en'
target = 'content/en'
lang = 'en'
[[module.mounts]]
source = 'content/nn'
target = 'content/nn'
lang = 'nn'
[[module.mounts]]
source = 'content/no'
target = 'content/no'
lang = 'no'
[[module.mounts]]
source = 'content/sv'
target = 'content/sv'
lang = 'sv'
[[module.mounts]]
source = 'layouts'
target = 'layouts'
[languages]
[languages.en]
title = "English"
weight = 1
[languages.nn]
title = "Nynorsk"
weight = 2
[languages.no]
title = "Norsk"
weight = 3
[languages.sv]
title = "Svenska"
weight = 4
-- layouts/_default/list.html --
{{ .Title }}
{{ .Content }}
-- layouts/_default/single.html --
{{ .Title }}
{{ .Content }}
-- layouts/shortcodes/myshort.html --
{{ .Inner }}
`
contentTemplate := `
---
title: "Page %d"
date: "2018-01-01"
weight: %d
---
## Heading 1
Duis nisi reprehenderit nisi cupidatat cillum aliquip ea id eu esse commodo et.
## Heading 2
Aliqua labore enim et sint anim amet excepteur ea dolore.
{{< myshort >}}
Hello, World!
{{< /myshort >}}
Aliqua labore enim et sint anim amet excepteur ea dolore.
`
for _, lang := range []string{"en", "nn", "no", "sv"} {
files += fmt.Sprintf("\n-- content/%s/_index.md --\n"+contentTemplate, lang, 1, 1, 1)
for i, root := range []string{"", "foo", "bar", "baz"} {
for j, section := range []string{"posts", "posts/funny", "posts/science", "posts/politics", "posts/world", "posts/technology", "posts/world/news", "posts/world/news/europe"} {
n := i + j + 1
files += fmt.Sprintf("\n-- content/%s/%s/%s/_index.md --\n"+contentTemplate, lang, root, section, n, n, n)
for k := 1; k < rnd.Intn(30)+1; k++ {
n := n + k
ns := fmt.Sprintf("%d", n)
if leafBundles {
ns = fmt.Sprintf("%d/index", n)
}
file := fmt.Sprintf("\n-- content/%s/%s/%s/p%s.md --\n"+contentTemplate, lang, root, section, ns, n, n)
files += file
}
}
}
}
return files
}