github.com/olliephillips/hugo@v0.42.2/hugolib/site_output_test.go (about) 1 // Copyright 2017-present 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 hugolib 15 16 import ( 17 "strings" 18 "testing" 19 20 "github.com/spf13/afero" 21 22 "github.com/stretchr/testify/require" 23 24 "fmt" 25 26 "github.com/gohugoio/hugo/helpers" 27 "github.com/gohugoio/hugo/output" 28 "github.com/spf13/viper" 29 ) 30 31 func TestSiteWithPageOutputs(t *testing.T) { 32 for _, outputs := range [][]string{{"html", "json", "calendar"}, {"json"}} { 33 t.Run(fmt.Sprintf("%v", outputs), func(t *testing.T) { 34 doTestSiteWithPageOutputs(t, outputs) 35 }) 36 } 37 } 38 39 func doTestSiteWithPageOutputs(t *testing.T, outputs []string) { 40 t.Parallel() 41 42 outputsStr := strings.Replace(fmt.Sprintf("%q", outputs), " ", ", ", -1) 43 44 siteConfig := ` 45 baseURL = "http://example.com/blog" 46 47 paginate = 1 48 defaultContentLanguage = "en" 49 50 disableKinds = ["page", "section", "taxonomy", "taxonomyTerm", "RSS", "sitemap", "robotsTXT", "404"] 51 52 [Taxonomies] 53 tag = "tags" 54 category = "categories" 55 56 defaultContentLanguage = "en" 57 58 [languages] 59 60 [languages.en] 61 title = "Title in English" 62 languageName = "English" 63 weight = 1 64 65 [languages.nn] 66 languageName = "Nynorsk" 67 weight = 2 68 title = "Tittel på Nynorsk" 69 70 ` 71 72 pageTemplate := `--- 73 title: "%s" 74 outputs: %s 75 --- 76 # Doc 77 78 {{< myShort >}} 79 80 {{< myOtherShort >}} 81 82 ` 83 84 mf := afero.NewMemMapFs() 85 86 writeToFs(t, mf, "i18n/en.toml", ` 87 [elbow] 88 other = "Elbow" 89 `) 90 writeToFs(t, mf, "i18n/nn.toml", ` 91 [elbow] 92 other = "Olboge" 93 `) 94 95 th, h := newTestSitesFromConfig(t, mf, siteConfig, 96 97 // Case issue partials #3333 98 "layouts/partials/GoHugo.html", `Go Hugo Partial`, 99 "layouts/_default/baseof.json", `START JSON:{{block "main" .}}default content{{ end }}:END JSON`, 100 "layouts/_default/baseof.html", `START HTML:{{block "main" .}}default content{{ end }}:END HTML`, 101 "layouts/shortcodes/myOtherShort.html", `OtherShort: {{ "<h1>Hi!</h1>" | safeHTML }}`, 102 "layouts/shortcodes/myShort.html", `ShortHTML`, 103 "layouts/shortcodes/myShort.json", `ShortJSON`, 104 105 "layouts/_default/list.json", `{{ define "main" }} 106 List JSON|{{ .Title }}|{{ .Content }}|Alt formats: {{ len .AlternativeOutputFormats -}}| 107 {{- range .AlternativeOutputFormats -}} 108 Alt Output: {{ .Name -}}| 109 {{- end -}}| 110 {{- range .OutputFormats -}} 111 Output/Rel: {{ .Name -}}/{{ .Rel }}|{{ .MediaType }} 112 {{- end -}} 113 {{ with .OutputFormats.Get "JSON" }} 114 <atom:link href={{ .Permalink }} rel="self" type="{{ .MediaType }}" /> 115 {{ end }} 116 {{ .Site.Language.Lang }}: {{ T "elbow" -}} 117 {{ end }} 118 `, 119 "layouts/_default/list.html", `{{ define "main" }} 120 List HTML|{{.Title }}| 121 {{- with .OutputFormats.Get "HTML" -}} 122 <atom:link href={{ .Permalink }} rel="self" type="{{ .MediaType }}" /> 123 {{- end -}} 124 {{ .Site.Language.Lang }}: {{ T "elbow" -}} 125 Partial Hugo 1: {{ partial "GoHugo.html" . }} 126 Partial Hugo 2: {{ partial "GoHugo" . -}} 127 Content: {{ .Content }} 128 {{ end }} 129 `, 130 ) 131 require.Len(t, h.Sites, 2) 132 133 fs := th.Fs 134 135 writeSource(t, fs, "content/_index.md", fmt.Sprintf(pageTemplate, "JSON Home", outputsStr)) 136 writeSource(t, fs, "content/_index.nn.md", fmt.Sprintf(pageTemplate, "JSON Nynorsk Heim", outputsStr)) 137 138 err := h.Build(BuildCfg{}) 139 140 require.NoError(t, err) 141 142 s := h.Sites[0] 143 require.Equal(t, "en", s.Language.Lang) 144 145 home := s.getPage(KindHome) 146 147 require.NotNil(t, home) 148 149 lenOut := len(outputs) 150 151 require.Len(t, home.outputFormats, lenOut) 152 153 // There is currently always a JSON output to make it simpler ... 154 altFormats := lenOut - 1 155 hasHTML := helpers.InStringArray(outputs, "html") 156 th.assertFileContent("public/index.json", 157 "List JSON", 158 fmt.Sprintf("Alt formats: %d", altFormats), 159 ) 160 161 if hasHTML { 162 th.assertFileContent("public/index.json", 163 "Alt Output: HTML", 164 "Output/Rel: JSON/alternate|", 165 "Output/Rel: HTML/canonical|", 166 "en: Elbow", 167 "ShortJSON", 168 "OtherShort: <h1>Hi!</h1>", 169 ) 170 171 th.assertFileContent("public/index.html", 172 // The HTML entity is a deliberate part of this test: The HTML templates are 173 // parsed with html/template. 174 `List HTML|JSON Home|<atom:link href=http://example.com/blog/ rel="self" type="text/html+html" />`, 175 "en: Elbow", 176 "ShortHTML", 177 "OtherShort: <h1>Hi!</h1>", 178 ) 179 th.assertFileContent("public/nn/index.html", 180 "List HTML|JSON Nynorsk Heim|", 181 "nn: Olboge") 182 } else { 183 th.assertFileContent("public/index.json", 184 "Output/Rel: JSON/canonical|", 185 // JSON is plain text, so no need to safeHTML this and that 186 `<atom:link href=http://example.com/blog/index.json rel="self" type="application/json+json" />`, 187 "ShortJSON", 188 "OtherShort: <h1>Hi!</h1>", 189 ) 190 th.assertFileContent("public/nn/index.json", 191 "List JSON|JSON Nynorsk Heim|", 192 "nn: Olboge", 193 "ShortJSON", 194 ) 195 } 196 197 of := home.OutputFormats() 198 require.Len(t, of, lenOut) 199 require.Nil(t, of.Get("Hugo")) 200 require.NotNil(t, of.Get("json")) 201 json := of.Get("JSON") 202 _, err = home.AlternativeOutputFormats() 203 require.Error(t, err) 204 require.NotNil(t, json) 205 require.Equal(t, "/blog/index.json", json.RelPermalink()) 206 require.Equal(t, "http://example.com/blog/index.json", json.Permalink()) 207 208 if helpers.InStringArray(outputs, "cal") { 209 cal := of.Get("calendar") 210 require.NotNil(t, cal) 211 require.Equal(t, "/blog/index.ics", cal.RelPermalink()) 212 require.Equal(t, "webcal://example.com/blog/index.ics", cal.Permalink()) 213 } 214 215 require.True(t, home.HasShortcode("myShort")) 216 require.False(t, home.HasShortcode("doesNotExist")) 217 218 } 219 220 // Issue #3447 221 func TestRedefineRSSOutputFormat(t *testing.T) { 222 siteConfig := ` 223 baseURL = "http://example.com/blog" 224 225 paginate = 1 226 defaultContentLanguage = "en" 227 228 disableKinds = ["page", "section", "taxonomy", "taxonomyTerm", "sitemap", "robotsTXT", "404"] 229 230 [outputFormats] 231 [outputFormats.RSS] 232 mediatype = "application/rss" 233 baseName = "feed" 234 235 ` 236 237 mf := afero.NewMemMapFs() 238 writeToFs(t, mf, "content/foo.html", `foo`) 239 240 th, h := newTestSitesFromConfig(t, mf, siteConfig) 241 242 err := h.Build(BuildCfg{}) 243 244 require.NoError(t, err) 245 246 th.assertFileContent("public/feed.xml", "Recent content on") 247 248 s := h.Sites[0] 249 250 //Issue #3450 251 require.Equal(t, "http://example.com/blog/feed.xml", s.Info.RSSLink) 252 253 } 254 255 // Issue #3614 256 func TestDotLessOutputFormat(t *testing.T) { 257 siteConfig := ` 258 baseURL = "http://example.com/blog" 259 260 paginate = 1 261 defaultContentLanguage = "en" 262 263 disableKinds = ["page", "section", "taxonomy", "taxonomyTerm", "sitemap", "robotsTXT", "404"] 264 265 [mediaTypes] 266 [mediaTypes."text/nodot"] 267 suffix = "" 268 delimiter = "" 269 [mediaTypes."text/defaultdelim"] 270 suffix = "defd" 271 [mediaTypes."text/nosuffix"] 272 suffix = "" 273 [mediaTypes."text/customdelim"] 274 suffix = "del" 275 delimiter = "_" 276 277 [outputs] 278 home = [ "DOTLESS", "DEF", "NOS", "CUS" ] 279 280 [outputFormats] 281 [outputFormats.DOTLESS] 282 mediatype = "text/nodot" 283 baseName = "_redirects" # This is how Netlify names their redirect files. 284 [outputFormats.DEF] 285 mediatype = "text/defaultdelim" 286 baseName = "defaultdelimbase" 287 [outputFormats.NOS] 288 mediatype = "text/nosuffix" 289 baseName = "nosuffixbase" 290 [outputFormats.CUS] 291 mediatype = "text/customdelim" 292 baseName = "customdelimbase" 293 294 ` 295 296 mf := afero.NewMemMapFs() 297 writeToFs(t, mf, "content/foo.html", `foo`) 298 writeToFs(t, mf, "layouts/_default/list.dotless", `a dotless`) 299 writeToFs(t, mf, "layouts/_default/list.def.defd", `default delimim`) 300 writeToFs(t, mf, "layouts/_default/list.nos", `no suffix`) 301 writeToFs(t, mf, "layouts/_default/list.cus.del", `custom delim`) 302 303 th, h := newTestSitesFromConfig(t, mf, siteConfig) 304 305 err := h.Build(BuildCfg{}) 306 307 require.NoError(t, err) 308 309 th.assertFileContent("public/_redirects", "a dotless") 310 th.assertFileContent("public/defaultdelimbase.defd", "default delimim") 311 // This looks weird, but the user has chosen this definition. 312 th.assertFileContent("public/nosuffixbase.", "no suffix") 313 th.assertFileContent("public/customdelimbase_del", "custom delim") 314 315 s := h.Sites[0] 316 home := s.getPage(KindHome) 317 require.NotNil(t, home) 318 319 outputs := home.OutputFormats() 320 321 require.Equal(t, "/blog/_redirects", outputs.Get("DOTLESS").RelPermalink()) 322 require.Equal(t, "/blog/defaultdelimbase.defd", outputs.Get("DEF").RelPermalink()) 323 require.Equal(t, "/blog/nosuffixbase.", outputs.Get("NOS").RelPermalink()) 324 require.Equal(t, "/blog/customdelimbase_del", outputs.Get("CUS").RelPermalink()) 325 326 } 327 328 func TestCreateSiteOutputFormats(t *testing.T) { 329 assert := require.New(t) 330 331 outputsConfig := map[string]interface{}{ 332 KindHome: []string{"HTML", "JSON"}, 333 KindSection: []string{"JSON"}, 334 } 335 336 cfg := viper.New() 337 cfg.Set("outputs", outputsConfig) 338 339 outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg) 340 assert.NoError(err) 341 assert.Equal(output.Formats{output.JSONFormat}, outputs[KindSection]) 342 assert.Equal(output.Formats{output.HTMLFormat, output.JSONFormat}, outputs[KindHome]) 343 344 // Defaults 345 assert.Equal(output.Formats{output.HTMLFormat, output.RSSFormat}, outputs[KindTaxonomy]) 346 assert.Equal(output.Formats{output.HTMLFormat, output.RSSFormat}, outputs[KindTaxonomyTerm]) 347 assert.Equal(output.Formats{output.HTMLFormat}, outputs[KindPage]) 348 349 // These aren't (currently) in use when rendering in Hugo, 350 // but the pages needs to be assigned an output format, 351 // so these should also be correct/sensible. 352 assert.Equal(output.Formats{output.RSSFormat}, outputs[kindRSS]) 353 assert.Equal(output.Formats{output.SitemapFormat}, outputs[kindSitemap]) 354 assert.Equal(output.Formats{output.RobotsTxtFormat}, outputs[kindRobotsTXT]) 355 assert.Equal(output.Formats{output.HTMLFormat}, outputs[kind404]) 356 357 } 358 359 func TestCreateSiteOutputFormatsInvalidConfig(t *testing.T) { 360 assert := require.New(t) 361 362 outputsConfig := map[string]interface{}{ 363 KindHome: []string{"FOO", "JSON"}, 364 } 365 366 cfg := viper.New() 367 cfg.Set("outputs", outputsConfig) 368 369 _, err := createSiteOutputFormats(output.DefaultFormats, cfg) 370 assert.Error(err) 371 } 372 373 func TestCreateSiteOutputFormatsEmptyConfig(t *testing.T) { 374 assert := require.New(t) 375 376 outputsConfig := map[string]interface{}{ 377 KindHome: []string{}, 378 } 379 380 cfg := viper.New() 381 cfg.Set("outputs", outputsConfig) 382 383 outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg) 384 assert.NoError(err) 385 assert.Equal(output.Formats{output.HTMLFormat, output.RSSFormat}, outputs[KindHome]) 386 } 387 388 func TestCreateSiteOutputFormatsCustomFormats(t *testing.T) { 389 assert := require.New(t) 390 391 outputsConfig := map[string]interface{}{ 392 KindHome: []string{}, 393 } 394 395 cfg := viper.New() 396 cfg.Set("outputs", outputsConfig) 397 398 var ( 399 customRSS = output.Format{Name: "RSS", BaseName: "customRSS"} 400 customHTML = output.Format{Name: "HTML", BaseName: "customHTML"} 401 ) 402 403 outputs, err := createSiteOutputFormats(output.Formats{customRSS, customHTML}, cfg) 404 assert.NoError(err) 405 assert.Equal(output.Formats{customHTML, customRSS}, outputs[KindHome]) 406 }