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