github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/hugolib/pagebundler_test.go (about) 1 // Copyright 2019 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 "fmt" 18 "io" 19 "os" 20 "path" 21 "path/filepath" 22 "regexp" 23 "strings" 24 "testing" 25 26 "github.com/gohugoio/hugo/config" 27 28 "github.com/gohugoio/hugo/hugofs/files" 29 30 "github.com/gohugoio/hugo/helpers" 31 32 "github.com/gohugoio/hugo/hugofs" 33 34 "github.com/gohugoio/hugo/common/loggers" 35 "github.com/gohugoio/hugo/resources/page" 36 37 "github.com/gohugoio/hugo/htesting" 38 39 "github.com/gohugoio/hugo/deps" 40 41 qt "github.com/frankban/quicktest" 42 ) 43 44 func TestPageBundlerSiteRegular(t *testing.T) { 45 c := qt.New(t) 46 baseBaseURL := "https://example.com" 47 48 for _, baseURLPath := range []string{"", "/hugo"} { 49 for _, canonify := range []bool{false, true} { 50 for _, ugly := range []bool{false, true} { 51 baseURLPathId := baseURLPath 52 if baseURLPathId == "" { 53 baseURLPathId = "NONE" 54 } 55 ugly := ugly 56 canonify := canonify 57 c.Run(fmt.Sprintf("ugly=%t,canonify=%t,path=%s", ugly, canonify, baseURLPathId), 58 func(c *qt.C) { 59 c.Parallel() 60 baseURL := baseBaseURL + baseURLPath 61 relURLBase := baseURLPath 62 if canonify { 63 relURLBase = "" 64 } 65 fs, cfg := newTestBundleSources(c) 66 cfg.Set("baseURL", baseURL) 67 cfg.Set("canonifyURLs", canonify) 68 69 cfg.Set("permalinks", map[string]string{ 70 "a": ":sections/:filename", 71 "b": ":year/:slug/", 72 "c": ":sections/:slug", 73 "/": ":filename/", 74 }) 75 76 cfg.Set("outputFormats", map[string]interface{}{ 77 "CUSTOMO": map[string]interface{}{ 78 "mediaType": "text/html", 79 "baseName": "cindex", 80 "path": "cpath", 81 "permalinkable": true, 82 }, 83 }) 84 85 cfg.Set("outputs", map[string]interface{}{ 86 "home": []string{"HTML", "CUSTOMO"}, 87 "page": []string{"HTML", "CUSTOMO"}, 88 "section": []string{"HTML", "CUSTOMO"}, 89 }) 90 91 cfg.Set("uglyURLs", ugly) 92 93 b := newTestSitesBuilderFromDepsCfg(c, deps.DepsCfg{Logger: loggers.NewErrorLogger(), Fs: fs, Cfg: cfg}).WithNothingAdded() 94 95 b.Build(BuildCfg{}) 96 97 s := b.H.Sites[0] 98 99 c.Assert(len(s.RegularPages()), qt.Equals, 8) 100 101 singlePage := s.getPage(page.KindPage, "a/1.md") 102 c.Assert(singlePage.BundleType(), qt.Equals, files.ContentClass("")) 103 104 c.Assert(singlePage, qt.Not(qt.IsNil)) 105 c.Assert(s.getPage("page", "a/1"), qt.Equals, singlePage) 106 c.Assert(s.getPage("page", "1"), qt.Equals, singlePage) 107 108 c.Assert(content(singlePage), qt.Contains, "TheContent") 109 110 relFilename := func(basePath, outBase string) (string, string) { 111 rel := basePath 112 if ugly { 113 rel = strings.TrimSuffix(basePath, "/") + ".html" 114 } 115 116 var filename string 117 if !ugly { 118 filename = path.Join(basePath, outBase) 119 } else { 120 filename = rel 121 } 122 123 rel = fmt.Sprintf("%s%s", relURLBase, rel) 124 125 return rel, filename 126 } 127 128 // Check both output formats 129 rel, filename := relFilename("/a/1/", "index.html") 130 b.AssertFileContent(filepath.Join("/work/public", filename), 131 "TheContent", 132 "Single RelPermalink: "+rel, 133 ) 134 135 rel, filename = relFilename("/cpath/a/1/", "cindex.html") 136 137 b.AssertFileContent(filepath.Join("/work/public", filename), 138 "TheContent", 139 "Single RelPermalink: "+rel, 140 ) 141 142 b.AssertFileContent(filepath.FromSlash("/work/public/images/hugo-logo.png"), "content") 143 144 // This should be just copied to destination. 145 b.AssertFileContent(filepath.FromSlash("/work/public/assets/pic1.png"), "content") 146 147 leafBundle1 := s.getPage(page.KindPage, "b/my-bundle/index.md") 148 c.Assert(leafBundle1, qt.Not(qt.IsNil)) 149 c.Assert(leafBundle1.BundleType(), qt.Equals, files.ContentClassLeaf) 150 c.Assert(leafBundle1.Section(), qt.Equals, "b") 151 sectionB := s.getPage(page.KindSection, "b") 152 c.Assert(sectionB, qt.Not(qt.IsNil)) 153 home, _ := s.Info.Home() 154 c.Assert(home.BundleType(), qt.Equals, files.ContentClassBranch) 155 156 // This is a root bundle and should live in the "home section" 157 // See https://github.com/gohugoio/hugo/issues/4332 158 rootBundle := s.getPage(page.KindPage, "root") 159 c.Assert(rootBundle, qt.Not(qt.IsNil)) 160 c.Assert(rootBundle.Parent().IsHome(), qt.Equals, true) 161 if !ugly { 162 b.AssertFileContent(filepath.FromSlash("/work/public/root/index.html"), "Single RelPermalink: "+relURLBase+"/root/") 163 b.AssertFileContent(filepath.FromSlash("/work/public/cpath/root/cindex.html"), "Single RelPermalink: "+relURLBase+"/cpath/root/") 164 } 165 166 leafBundle2 := s.getPage(page.KindPage, "a/b/index.md") 167 c.Assert(leafBundle2, qt.Not(qt.IsNil)) 168 unicodeBundle := s.getPage(page.KindPage, "c/bundle/index.md") 169 c.Assert(unicodeBundle, qt.Not(qt.IsNil)) 170 171 pageResources := leafBundle1.Resources().ByType(pageResourceType) 172 c.Assert(len(pageResources), qt.Equals, 2) 173 firstPage := pageResources[0].(page.Page) 174 secondPage := pageResources[1].(page.Page) 175 176 c.Assert(firstPage.File().Filename(), qt.Equals, filepath.FromSlash("/work/base/b/my-bundle/1.md")) 177 c.Assert(content(firstPage), qt.Contains, "TheContent") 178 c.Assert(len(leafBundle1.Resources()), qt.Equals, 6) 179 180 // Verify shortcode in bundled page 181 c.Assert(content(secondPage), qt.Contains, filepath.FromSlash("MyShort in b/my-bundle/2.md")) 182 183 // https://github.com/gohugoio/hugo/issues/4582 184 c.Assert(firstPage.Parent(), qt.Equals, leafBundle1) 185 c.Assert(secondPage.Parent(), qt.Equals, leafBundle1) 186 187 c.Assert(pageResources.GetMatch("1*"), qt.Equals, firstPage) 188 c.Assert(pageResources.GetMatch("2*"), qt.Equals, secondPage) 189 c.Assert(pageResources.GetMatch("doesnotexist*"), qt.IsNil) 190 191 imageResources := leafBundle1.Resources().ByType("image") 192 c.Assert(len(imageResources), qt.Equals, 3) 193 194 c.Assert(leafBundle1.OutputFormats().Get("CUSTOMO"), qt.Not(qt.IsNil)) 195 196 relPermalinker := func(s string) string { 197 return fmt.Sprintf(s, relURLBase) 198 } 199 200 permalinker := func(s string) string { 201 return fmt.Sprintf(s, baseURL) 202 } 203 204 if ugly { 205 b.AssertFileContent("/work/public/2017/pageslug.html", 206 relPermalinker("Single RelPermalink: %s/2017/pageslug.html"), 207 permalinker("Single Permalink: %s/2017/pageslug.html"), 208 relPermalinker("Sunset RelPermalink: %s/2017/pageslug/sunset1.jpg"), 209 permalinker("Sunset Permalink: %s/2017/pageslug/sunset1.jpg")) 210 } else { 211 b.AssertFileContent("/work/public/2017/pageslug/index.html", 212 relPermalinker("Sunset RelPermalink: %s/2017/pageslug/sunset1.jpg"), 213 permalinker("Sunset Permalink: %s/2017/pageslug/sunset1.jpg")) 214 215 b.AssertFileContent("/work/public/cpath/2017/pageslug/cindex.html", 216 relPermalinker("Single RelPermalink: %s/cpath/2017/pageslug/"), 217 relPermalinker("Short Sunset RelPermalink: %s/cpath/2017/pageslug/sunset2.jpg"), 218 relPermalinker("Sunset RelPermalink: %s/cpath/2017/pageslug/sunset1.jpg"), 219 permalinker("Sunset Permalink: %s/cpath/2017/pageslug/sunset1.jpg"), 220 ) 221 } 222 223 b.AssertFileContent(filepath.FromSlash("/work/public/2017/pageslug/c/logo.png"), "content") 224 b.AssertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/c/logo.png"), "content") 225 c.Assert(b.CheckExists("/work/public/cpath/cpath/2017/pageslug/c/logo.png"), qt.Equals, false) 226 227 // Custom media type defined in site config. 228 c.Assert(len(leafBundle1.Resources().ByType("bepsays")), qt.Equals, 1) 229 230 if ugly { 231 b.AssertFileContent(filepath.FromSlash("/work/public/2017/pageslug.html"), 232 "TheContent", 233 relPermalinker("Sunset RelPermalink: %s/2017/pageslug/sunset1.jpg"), 234 permalinker("Sunset Permalink: %s/2017/pageslug/sunset1.jpg"), 235 "Thumb Width: 123", 236 "Thumb Name: my-sunset-1", 237 relPermalinker("Short Sunset RelPermalink: %s/2017/pageslug/sunset2.jpg"), 238 "Short Thumb Width: 56", 239 "1: Image Title: Sunset Galore 1", 240 "1: Image Params: map[myparam:My Sunny Param]", 241 relPermalinker("1: Image RelPermalink: %s/2017/pageslug/sunset1.jpg"), 242 "2: Image Title: Sunset Galore 2", 243 "2: Image Params: map[myparam:My Sunny Param]", 244 "1: Image myParam: Lower: My Sunny Param Caps: My Sunny Param", 245 "0: Page Title: Bundle Galore", 246 ) 247 248 // https://github.com/gohugoio/hugo/issues/5882 249 b.AssertFileContent( 250 filepath.FromSlash("/work/public/2017/pageslug.html"), "0: Page RelPermalink: |") 251 252 b.AssertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug.html"), "TheContent") 253 254 // 은행 255 b.AssertFileContent(filepath.FromSlash("/work/public/c/은행/logo-은행.png"), "은행 PNG") 256 257 } else { 258 b.AssertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "TheContent") 259 b.AssertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/cindex.html"), "TheContent") 260 b.AssertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "Single Title") 261 b.AssertFileContent(filepath.FromSlash("/work/public/root/index.html"), "Single Title") 262 263 } 264 }) 265 } 266 } 267 } 268 } 269 270 func TestPageBundlerSiteMultilingual(t *testing.T) { 271 t.Parallel() 272 273 for _, ugly := range []bool{false, true} { 274 ugly := ugly 275 t.Run(fmt.Sprintf("ugly=%t", ugly), 276 func(t *testing.T) { 277 t.Parallel() 278 c := qt.New(t) 279 fs, cfg := newTestBundleSourcesMultilingual(t) 280 cfg.Set("uglyURLs", ugly) 281 282 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded() 283 b.Build(BuildCfg{}) 284 285 sites := b.H 286 287 c.Assert(len(sites.Sites), qt.Equals, 2) 288 289 s := sites.Sites[0] 290 291 c.Assert(len(s.RegularPages()), qt.Equals, 8) 292 c.Assert(len(s.Pages()), qt.Equals, 16) 293 //dumpPages(s.AllPages()...) 294 295 c.Assert(len(s.AllPages()), qt.Equals, 31) 296 297 bundleWithSubPath := s.getPage(page.KindPage, "lb/index") 298 c.Assert(bundleWithSubPath, qt.Not(qt.IsNil)) 299 300 // See https://github.com/gohugoio/hugo/issues/4312 301 // Before that issue: 302 // A bundle in a/b/index.en.md 303 // a/b/index.en.md => OK 304 // a/b/index => OK 305 // index.en.md => ambiguous, but OK. 306 // With bundles, the file name has little meaning, the folder it lives in does. So this should also work: 307 // a/b 308 // and probably also just b (aka "my-bundle") 309 // These may also be translated, so we also need to test that. 310 // "bf", "my-bf-bundle", "index.md + nn 311 bfBundle := s.getPage(page.KindPage, "bf/my-bf-bundle/index") 312 c.Assert(bfBundle, qt.Not(qt.IsNil)) 313 c.Assert(bfBundle.Language().Lang, qt.Equals, "en") 314 c.Assert(s.getPage(page.KindPage, "bf/my-bf-bundle/index.md"), qt.Equals, bfBundle) 315 c.Assert(s.getPage(page.KindPage, "bf/my-bf-bundle"), qt.Equals, bfBundle) 316 c.Assert(s.getPage(page.KindPage, "my-bf-bundle"), qt.Equals, bfBundle) 317 318 nnSite := sites.Sites[1] 319 c.Assert(len(nnSite.RegularPages()), qt.Equals, 7) 320 321 bfBundleNN := nnSite.getPage(page.KindPage, "bf/my-bf-bundle/index") 322 c.Assert(bfBundleNN, qt.Not(qt.IsNil)) 323 c.Assert(bfBundleNN.Language().Lang, qt.Equals, "nn") 324 c.Assert(nnSite.getPage(page.KindPage, "bf/my-bf-bundle/index.nn.md"), qt.Equals, bfBundleNN) 325 c.Assert(nnSite.getPage(page.KindPage, "bf/my-bf-bundle"), qt.Equals, bfBundleNN) 326 c.Assert(nnSite.getPage(page.KindPage, "my-bf-bundle"), qt.Equals, bfBundleNN) 327 328 // See https://github.com/gohugoio/hugo/issues/4295 329 // Every resource should have its Name prefixed with its base folder. 330 cBundleResources := bundleWithSubPath.Resources().Match("c/**") 331 c.Assert(len(cBundleResources), qt.Equals, 4) 332 bundlePage := bundleWithSubPath.Resources().GetMatch("c/page*") 333 c.Assert(bundlePage, qt.Not(qt.IsNil)) 334 335 bcBundleNN, _ := nnSite.getPageNew(nil, "bc") 336 c.Assert(bcBundleNN, qt.Not(qt.IsNil)) 337 bcBundleEN, _ := s.getPageNew(nil, "bc") 338 c.Assert(bcBundleNN.Language().Lang, qt.Equals, "nn") 339 c.Assert(bcBundleEN.Language().Lang, qt.Equals, "en") 340 c.Assert(len(bcBundleNN.Resources()), qt.Equals, 3) 341 c.Assert(len(bcBundleEN.Resources()), qt.Equals, 3) 342 b.AssertFileContent("public/en/bc/data1.json", "data1") 343 b.AssertFileContent("public/en/bc/data2.json", "data2") 344 b.AssertFileContent("public/en/bc/logo-bc.png", "logo") 345 b.AssertFileContent("public/nn/bc/data1.nn.json", "data1.nn") 346 b.AssertFileContent("public/nn/bc/data2.json", "data2") 347 b.AssertFileContent("public/nn/bc/logo-bc.png", "logo") 348 }) 349 } 350 } 351 352 func TestMultilingualDisableDefaultLanguage(t *testing.T) { 353 t.Parallel() 354 355 c := qt.New(t) 356 _, cfg := newTestBundleSourcesMultilingual(t) 357 cfg.Set("disableLanguages", []string{"en"}) 358 l := configLoader{cfg: cfg} 359 err := l.applyConfigDefaults() 360 c.Assert(err, qt.IsNil) 361 err = l.loadLanguageSettings(nil) 362 c.Assert(err, qt.Not(qt.IsNil)) 363 c.Assert(err.Error(), qt.Contains, "cannot disable default language") 364 } 365 366 func TestMultilingualDisableLanguage(t *testing.T) { 367 t.Parallel() 368 369 c := qt.New(t) 370 fs, cfg := newTestBundleSourcesMultilingual(t) 371 cfg.Set("disableLanguages", []string{"nn"}) 372 373 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded() 374 b.Build(BuildCfg{}) 375 sites := b.H 376 377 c.Assert(len(sites.Sites), qt.Equals, 1) 378 379 s := sites.Sites[0] 380 381 c.Assert(len(s.RegularPages()), qt.Equals, 8) 382 c.Assert(len(s.Pages()), qt.Equals, 16) 383 // No nn pages 384 c.Assert(len(s.AllPages()), qt.Equals, 16) 385 s.pageMap.withEveryBundlePage(func(p *pageState) bool { 386 c.Assert(p.Language().Lang != "nn", qt.Equals, true) 387 return false 388 }) 389 } 390 391 func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) { 392 skipSymlink(t) 393 394 wd, _ := os.Getwd() 395 defer func() { 396 os.Chdir(wd) 397 }() 398 399 c := qt.New(t) 400 // We need to use the OS fs for this. 401 cfg := config.New() 402 fs := hugofs.NewFrom(hugofs.Os, cfg) 403 404 workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugosym") 405 c.Assert(err, qt.IsNil) 406 407 contentDirName := "content" 408 409 contentDir := filepath.Join(workDir, contentDirName) 410 c.Assert(os.MkdirAll(filepath.Join(contentDir, "a"), 0777), qt.IsNil) 411 412 for i := 1; i <= 3; i++ { 413 c.Assert(os.MkdirAll(filepath.Join(workDir, fmt.Sprintf("symcontent%d", i)), 0777), qt.IsNil) 414 } 415 416 c.Assert(os.MkdirAll(filepath.Join(workDir, "symcontent2", "a1"), 0777), qt.IsNil) 417 418 // Symlinked sections inside content. 419 os.Chdir(contentDir) 420 for i := 1; i <= 3; i++ { 421 c.Assert(os.Symlink(filepath.FromSlash(fmt.Sprintf(("../symcontent%d"), i)), fmt.Sprintf("symbolic%d", i)), qt.IsNil) 422 } 423 424 c.Assert(os.Chdir(filepath.Join(contentDir, "a")), qt.IsNil) 425 426 // Create a symlink to one single content file 427 c.Assert(os.Symlink(filepath.FromSlash("../../symcontent2/a1/page.md"), "page_s.md"), qt.IsNil) 428 429 c.Assert(os.Chdir(filepath.FromSlash("../../symcontent3")), qt.IsNil) 430 431 // Create a circular symlink. Will print some warnings. 432 c.Assert(os.Symlink(filepath.Join("..", contentDirName), filepath.FromSlash("circus")), qt.IsNil) 433 434 c.Assert(os.Chdir(workDir), qt.IsNil) 435 436 defer clean() 437 438 cfg.Set("workingDir", workDir) 439 cfg.Set("contentDir", contentDirName) 440 cfg.Set("baseURL", "https://example.com") 441 442 layout := `{{ .Title }}|{{ .Content }}` 443 pageContent := `--- 444 slug: %s 445 date: 2017-10-09 446 --- 447 448 TheContent. 449 ` 450 451 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{ 452 Fs: fs, 453 Cfg: cfg, 454 }) 455 456 b.WithTemplates( 457 "_default/single.html", layout, 458 "_default/list.html", layout, 459 ) 460 461 b.WithContent( 462 "a/regular.md", fmt.Sprintf(pageContent, "a1"), 463 ) 464 465 b.WithSourceFile( 466 "symcontent1/s1.md", fmt.Sprintf(pageContent, "s1"), 467 "symcontent1/s2.md", fmt.Sprintf(pageContent, "s2"), 468 // Regular files inside symlinked folder. 469 "symcontent1/s1.md", fmt.Sprintf(pageContent, "s1"), 470 "symcontent1/s2.md", fmt.Sprintf(pageContent, "s2"), 471 472 // A bundle 473 "symcontent2/a1/index.md", fmt.Sprintf(pageContent, ""), 474 "symcontent2/a1/page.md", fmt.Sprintf(pageContent, "page"), 475 "symcontent2/a1/logo.png", "image", 476 477 // Assets 478 "symcontent3/s1.png", "image", 479 "symcontent3/s2.png", "image", 480 ) 481 482 b.Build(BuildCfg{}) 483 s := b.H.Sites[0] 484 485 c.Assert(len(s.RegularPages()), qt.Equals, 7) 486 a1Bundle := s.getPage(page.KindPage, "symbolic2/a1/index.md") 487 c.Assert(a1Bundle, qt.Not(qt.IsNil)) 488 c.Assert(len(a1Bundle.Resources()), qt.Equals, 2) 489 c.Assert(len(a1Bundle.Resources().ByType(pageResourceType)), qt.Equals, 1) 490 491 b.AssertFileContent(filepath.FromSlash(workDir+"/public/a/page/index.html"), "TheContent") 492 b.AssertFileContent(filepath.FromSlash(workDir+"/public/symbolic1/s1/index.html"), "TheContent") 493 b.AssertFileContent(filepath.FromSlash(workDir+"/public/symbolic2/a1/index.html"), "TheContent") 494 } 495 496 func TestPageBundlerHeadless(t *testing.T) { 497 t.Parallel() 498 499 cfg, fs := newTestCfg() 500 c := qt.New(t) 501 502 workDir := "/work" 503 cfg.Set("workingDir", workDir) 504 cfg.Set("contentDir", "base") 505 cfg.Set("baseURL", "https://example.com") 506 507 pageContent := `--- 508 title: "Bundle Galore" 509 slug: s1 510 date: 2017-01-23 511 --- 512 513 TheContent. 514 515 {{< myShort >}} 516 ` 517 518 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), "single {{ .Content }}") 519 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), "list") 520 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), "SHORTCODE") 521 522 writeSource(t, fs, filepath.Join(workDir, "base", "a", "index.md"), pageContent) 523 writeSource(t, fs, filepath.Join(workDir, "base", "a", "l1.png"), "PNG image") 524 writeSource(t, fs, filepath.Join(workDir, "base", "a", "l2.png"), "PNG image") 525 526 writeSource(t, fs, filepath.Join(workDir, "base", "b", "index.md"), `--- 527 title: "Headless Bundle in Topless Bar" 528 slug: s2 529 headless: true 530 date: 2017-01-23 531 --- 532 533 TheContent. 534 HEADLESS {{< myShort >}} 535 `) 536 writeSource(t, fs, filepath.Join(workDir, "base", "b", "l1.png"), "PNG image") 537 writeSource(t, fs, filepath.Join(workDir, "base", "b", "l2.png"), "PNG image") 538 writeSource(t, fs, filepath.Join(workDir, "base", "b", "p1.md"), pageContent) 539 540 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 541 542 c.Assert(len(s.RegularPages()), qt.Equals, 1) 543 544 regular := s.getPage(page.KindPage, "a/index") 545 c.Assert(regular.RelPermalink(), qt.Equals, "/s1/") 546 547 headless := s.getPage(page.KindPage, "b/index") 548 c.Assert(headless, qt.Not(qt.IsNil)) 549 c.Assert(headless.Title(), qt.Equals, "Headless Bundle in Topless Bar") 550 c.Assert(headless.RelPermalink(), qt.Equals, "") 551 c.Assert(headless.Permalink(), qt.Equals, "") 552 c.Assert(content(headless), qt.Contains, "HEADLESS SHORTCODE") 553 554 headlessResources := headless.Resources() 555 c.Assert(len(headlessResources), qt.Equals, 3) 556 c.Assert(len(headlessResources.Match("l*")), qt.Equals, 2) 557 pageResource := headlessResources.GetMatch("p*") 558 c.Assert(pageResource, qt.Not(qt.IsNil)) 559 p := pageResource.(page.Page) 560 c.Assert(content(p), qt.Contains, "SHORTCODE") 561 c.Assert(p.Name(), qt.Equals, "p1.md") 562 563 th := newTestHelper(s.Cfg, s.Fs, t) 564 565 th.assertFileContent(filepath.FromSlash(workDir+"/public/s1/index.html"), "TheContent") 566 th.assertFileContent(filepath.FromSlash(workDir+"/public/s1/l1.png"), "PNG") 567 568 th.assertFileNotExist(workDir + "/public/s2/index.html") 569 // But the bundled resources needs to be published 570 th.assertFileContent(filepath.FromSlash(workDir+"/public/s2/l1.png"), "PNG") 571 572 // No headless bundles here, please. 573 // https://github.com/gohugoio/hugo/issues/6492 574 c.Assert(s.RegularPages(), qt.HasLen, 1) 575 c.Assert(s.home.RegularPages(), qt.HasLen, 1) 576 c.Assert(s.home.Pages(), qt.HasLen, 1) 577 } 578 579 func TestPageBundlerHeadlessIssue6552(t *testing.T) { 580 t.Parallel() 581 582 b := newTestSitesBuilder(t) 583 b.WithContent("headless/h1/index.md", ` 584 --- 585 title: My Headless Bundle1 586 headless: true 587 --- 588 `, "headless/h1/p1.md", ` 589 --- 590 title: P1 591 --- 592 `, "headless/h2/index.md", ` 593 --- 594 title: My Headless Bundle2 595 headless: true 596 --- 597 `) 598 599 b.WithTemplatesAdded("index.html", ` 600 {{ $headless1 := .Site.GetPage "headless/h1" }} 601 {{ $headless2 := .Site.GetPage "headless/h2" }} 602 603 HEADLESS1: {{ $headless1.Title }}|{{ $headless1.RelPermalink }}|{{ len $headless1.Resources }}| 604 HEADLESS2: {{ $headless2.Title }}{{ $headless2.RelPermalink }}|{{ len $headless2.Resources }}| 605 606 `) 607 608 b.Build(BuildCfg{}) 609 610 b.AssertFileContent("public/index.html", ` 611 HEADLESS1: My Headless Bundle1||1| 612 HEADLESS2: My Headless Bundle2|0| 613 `) 614 } 615 616 func TestMultiSiteBundles(t *testing.T) { 617 c := qt.New(t) 618 b := newTestSitesBuilder(t) 619 b.WithConfigFile("toml", ` 620 621 baseURL = "http://example.com/" 622 623 defaultContentLanguage = "en" 624 625 [languages] 626 [languages.en] 627 weight = 10 628 contentDir = "content/en" 629 [languages.nn] 630 weight = 20 631 contentDir = "content/nn" 632 633 634 `) 635 636 b.WithContent("en/mybundle/index.md", ` 637 --- 638 headless: true 639 --- 640 641 `) 642 643 b.WithContent("nn/mybundle/index.md", ` 644 --- 645 headless: true 646 --- 647 648 `) 649 650 b.WithContent("en/mybundle/data.yaml", `data en`) 651 b.WithContent("en/mybundle/forms.yaml", `forms en`) 652 b.WithContent("nn/mybundle/data.yaml", `data nn`) 653 654 b.WithContent("en/_index.md", ` 655 --- 656 Title: Home 657 --- 658 659 Home content. 660 661 `) 662 663 b.WithContent("en/section-not-bundle/_index.md", ` 664 --- 665 Title: Section Page 666 --- 667 668 Section content. 669 670 `) 671 672 b.WithContent("en/section-not-bundle/single.md", ` 673 --- 674 Title: Section Single 675 Date: 2018-02-01 676 --- 677 678 Single content. 679 680 `) 681 682 b.Build(BuildCfg{}) 683 684 b.AssertFileContent("public/nn/mybundle/data.yaml", "data nn") 685 b.AssertFileContent("public/nn/mybundle/forms.yaml", "forms en") 686 b.AssertFileContent("public/mybundle/data.yaml", "data en") 687 b.AssertFileContent("public/mybundle/forms.yaml", "forms en") 688 689 c.Assert(b.CheckExists("public/nn/nn/mybundle/data.yaml"), qt.Equals, false) 690 c.Assert(b.CheckExists("public/en/mybundle/data.yaml"), qt.Equals, false) 691 692 homeEn := b.H.Sites[0].home 693 c.Assert(homeEn, qt.Not(qt.IsNil)) 694 c.Assert(homeEn.Date().Year(), qt.Equals, 2018) 695 696 b.AssertFileContent("public/section-not-bundle/index.html", "Section Page", "Content: <p>Section content.</p>") 697 b.AssertFileContent("public/section-not-bundle/single/index.html", "Section Single", "|<p>Single content.</p>") 698 } 699 700 func newTestBundleSources(t testing.TB) (*hugofs.Fs, config.Provider) { 701 cfg, fs := newTestCfgBasic() 702 c := qt.New(t) 703 704 workDir := "/work" 705 cfg.Set("workingDir", workDir) 706 cfg.Set("contentDir", "base") 707 cfg.Set("baseURL", "https://example.com") 708 cfg.Set("mediaTypes", map[string]interface{}{ 709 "bepsays/bep": map[string]interface{}{ 710 "suffixes": []string{"bep"}, 711 }, 712 }) 713 714 pageContent := `--- 715 title: "Bundle Galore" 716 slug: pageslug 717 date: 2017-10-09 718 --- 719 720 TheContent. 721 ` 722 723 pageContentShortcode := `--- 724 title: "Bundle Galore" 725 slug: pageslug 726 date: 2017-10-09 727 --- 728 729 TheContent. 730 731 {{< myShort >}} 732 ` 733 734 pageWithImageShortcodeAndResourceMetadataContent := `--- 735 title: "Bundle Galore" 736 slug: pageslug 737 date: 2017-10-09 738 resources: 739 - src: "*.jpg" 740 name: "my-sunset-:counter" 741 title: "Sunset Galore :counter" 742 params: 743 myParam: "My Sunny Param" 744 --- 745 746 TheContent. 747 748 {{< myShort >}} 749 ` 750 751 pageContentNoSlug := `--- 752 title: "Bundle Galore #2" 753 date: 2017-10-09 754 --- 755 756 TheContent. 757 ` 758 759 singleLayout := ` 760 Single Title: {{ .Title }} 761 Single RelPermalink: {{ .RelPermalink }} 762 Single Permalink: {{ .Permalink }} 763 Content: {{ .Content }} 764 {{ $sunset := .Resources.GetMatch "my-sunset-1*" }} 765 {{ with $sunset }} 766 Sunset RelPermalink: {{ .RelPermalink }} 767 Sunset Permalink: {{ .Permalink }} 768 {{ $thumb := .Fill "123x123" }} 769 Thumb Width: {{ $thumb.Width }} 770 Thumb Name: {{ $thumb.Name }} 771 Thumb Title: {{ $thumb.Title }} 772 Thumb RelPermalink: {{ $thumb.RelPermalink }} 773 {{ end }} 774 {{ $types := slice "image" "page" }} 775 {{ range $types }} 776 {{ $typeTitle := . | title }} 777 {{ range $i, $e := $.Resources.ByType . }} 778 {{ $i }}: {{ $typeTitle }} Title: {{ .Title }} 779 {{ $i }}: {{ $typeTitle }} Name: {{ .Name }} 780 {{ $i }}: {{ $typeTitle }} RelPermalink: {{ .RelPermalink }}| 781 {{ $i }}: {{ $typeTitle }} Params: {{ printf "%v" .Params }} 782 {{ $i }}: {{ $typeTitle }} myParam: Lower: {{ .Params.myparam }} Caps: {{ .Params.MYPARAM }} 783 {{ end }} 784 {{ end }} 785 ` 786 787 myShort := ` 788 MyShort in {{ .Page.File.Path }}: 789 {{ $sunset := .Page.Resources.GetMatch "my-sunset-2*" }} 790 {{ with $sunset }} 791 Short Sunset RelPermalink: {{ .RelPermalink }} 792 {{ $thumb := .Fill "56x56" }} 793 Short Thumb Width: {{ $thumb.Width }} 794 {{ end }} 795 ` 796 797 listLayout := `{{ .Title }}|{{ .Content }}` 798 799 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), singleLayout) 800 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), listLayout) 801 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), myShort) 802 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.customo"), myShort) 803 804 writeSource(t, fs, filepath.Join(workDir, "base", "_index.md"), pageContent) 805 writeSource(t, fs, filepath.Join(workDir, "base", "_1.md"), pageContent) 806 writeSource(t, fs, filepath.Join(workDir, "base", "_1.png"), pageContent) 807 808 writeSource(t, fs, filepath.Join(workDir, "base", "images", "hugo-logo.png"), "content") 809 writeSource(t, fs, filepath.Join(workDir, "base", "a", "2.md"), pageContent) 810 writeSource(t, fs, filepath.Join(workDir, "base", "a", "1.md"), pageContent) 811 812 writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "index.md"), pageContentNoSlug) 813 writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "ab1.md"), pageContentNoSlug) 814 815 // Mostly plain static assets in a folder with a page in a sub folder thrown in. 816 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic1.png"), "content") 817 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic2.png"), "content") 818 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pages", "mypage.md"), pageContent) 819 820 // Bundle 821 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "index.md"), pageWithImageShortcodeAndResourceMetadataContent) 822 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "1.md"), pageContent) 823 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "2.md"), pageContentShortcode) 824 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "custom-mime.bep"), "bepsays") 825 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "c", "logo.png"), "content") 826 827 // Bundle with 은행 slug 828 // See https://github.com/gohugoio/hugo/issues/4241 829 writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "index.md"), `--- 830 title: "은행 은행" 831 slug: 은행 832 date: 2017-10-09 833 --- 834 835 Content for 은행. 836 `) 837 838 // Bundle in root 839 writeSource(t, fs, filepath.Join(workDir, "base", "root", "index.md"), pageWithImageShortcodeAndResourceMetadataContent) 840 writeSource(t, fs, filepath.Join(workDir, "base", "root", "1.md"), pageContent) 841 writeSource(t, fs, filepath.Join(workDir, "base", "root", "c", "logo.png"), "content") 842 843 writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "logo-은행.png"), "은행 PNG") 844 845 // Write a real image into one of the bundle above. 846 src, err := os.Open("testdata/sunset.jpg") 847 c.Assert(err, qt.IsNil) 848 849 // We need 2 to test https://github.com/gohugoio/hugo/issues/4202 850 out, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset1.jpg")) 851 c.Assert(err, qt.IsNil) 852 out2, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset2.jpg")) 853 c.Assert(err, qt.IsNil) 854 855 _, err = io.Copy(out, src) 856 c.Assert(err, qt.IsNil) 857 out.Close() 858 src.Seek(0, 0) 859 _, err = io.Copy(out2, src) 860 out2.Close() 861 src.Close() 862 c.Assert(err, qt.IsNil) 863 864 return fs, cfg 865 } 866 867 func newTestBundleSourcesMultilingual(t *testing.T) (*hugofs.Fs, config.Provider) { 868 cfg, fs := newTestCfgBasic() 869 870 workDir := "/work" 871 cfg.Set("workingDir", workDir) 872 cfg.Set("contentDir", "base") 873 cfg.Set("baseURL", "https://example.com") 874 cfg.Set("defaultContentLanguage", "en") 875 876 langConfig := map[string]interface{}{ 877 "en": map[string]interface{}{ 878 "weight": 1, 879 "languageName": "English", 880 }, 881 "nn": map[string]interface{}{ 882 "weight": 2, 883 "languageName": "Nynorsk", 884 }, 885 } 886 887 cfg.Set("languages", langConfig) 888 889 pageContent := `--- 890 slug: pageslug 891 date: 2017-10-09 892 --- 893 894 TheContent. 895 ` 896 897 layout := `{{ .Title }}|{{ .Content }}|Lang: {{ .Site.Language.Lang }}` 898 899 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), layout) 900 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout) 901 902 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.md"), pageContent) 903 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.nn.md"), pageContent) 904 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mylogo.png"), "content") 905 906 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.md"), pageContent) 907 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.nn.md"), pageContent) 908 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "en.md"), pageContent) 909 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.md"), pageContent) 910 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.nn.md"), pageContent) 911 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "a.png"), "content") 912 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.png"), "content") 913 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.nn.png"), "content") 914 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "c.nn.png"), "content") 915 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b", "d.nn.png"), "content") 916 917 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "_index.md"), pageContent) 918 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "_index.nn.md"), pageContent) 919 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.md"), pageContent) 920 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "logo-bc.png"), "logo") 921 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.nn.md"), pageContent) 922 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "data1.json"), "data1") 923 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "data2.json"), "data2") 924 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "data1.nn.json"), "data1.nn") 925 926 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "index.md"), pageContent) 927 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.md"), pageContent) 928 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.nn.md"), pageContent) 929 930 writeSource(t, fs, filepath.Join(workDir, "base", "be", "_index.md"), pageContent) 931 writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.md"), pageContent) 932 writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.nn.md"), pageContent) 933 934 // Bundle leaf, multilingual 935 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.md"), pageContent) 936 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.nn.md"), pageContent) 937 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "1.md"), pageContent) 938 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.md"), pageContent) 939 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.nn.md"), pageContent) 940 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "page.md"), pageContent) 941 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.png"), "content") 942 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.nn.png"), "content") 943 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "one.png"), "content") 944 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "d", "deep.png"), "content") 945 946 // Translated bundle in some sensible sub path. 947 writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "index.md"), pageContent) 948 writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "index.nn.md"), pageContent) 949 writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "page.md"), pageContent) 950 951 return fs, cfg 952 } 953 954 // https://github.com/gohugoio/hugo/issues/5858 955 func TestBundledResourcesWhenMultipleOutputFormats(t *testing.T) { 956 t.Parallel() 957 958 b := newTestSitesBuilder(t).Running().WithConfigFile("toml", ` 959 baseURL = "https://example.org" 960 [outputs] 961 # This looks odd, but it triggers the behaviour in #5858 962 # The total output formats list gets sorted, so CSS before HTML. 963 home = [ "CSS" ] 964 965 `) 966 b.WithContent("mybundle/index.md", ` 967 --- 968 title: Page 969 date: 2017-01-15 970 --- 971 `, 972 "mybundle/data.json", "MyData", 973 ) 974 975 b.CreateSites().Build(BuildCfg{}) 976 977 b.AssertFileContent("public/mybundle/data.json", "MyData") 978 979 // Change the bundled JSON file and make sure it gets republished. 980 b.EditFiles("content/mybundle/data.json", "My changed data") 981 982 b.Build(BuildCfg{}) 983 984 b.AssertFileContent("public/mybundle/data.json", "My changed data") 985 } 986 987 // https://github.com/gohugoio/hugo/issues/4870 988 func TestBundleSlug(t *testing.T) { 989 t.Parallel() 990 c := qt.New(t) 991 992 const pageTemplate = `--- 993 title: Title 994 slug: %s 995 --- 996 ` 997 998 b := newTestSitesBuilder(t) 999 1000 b.WithTemplatesAdded("index.html", `{{ range .Site.RegularPages }}|{{ .RelPermalink }}{{ end }}|`) 1001 b.WithSimpleConfigFile(). 1002 WithContent("about/services1/misc.md", fmt.Sprintf(pageTemplate, "this-is-the-slug")). 1003 WithContent("about/services2/misc/index.md", fmt.Sprintf(pageTemplate, "this-is-another-slug")) 1004 1005 b.CreateSites().Build(BuildCfg{}) 1006 1007 b.AssertHome( 1008 "|/about/services1/this-is-the-slug/|/", 1009 "|/about/services2/this-is-another-slug/|") 1010 1011 c.Assert(b.CheckExists("public/about/services1/this-is-the-slug/index.html"), qt.Equals, true) 1012 c.Assert(b.CheckExists("public/about/services2/this-is-another-slug/index.html"), qt.Equals, true) 1013 } 1014 1015 func TestBundleMisc(t *testing.T) { 1016 config := ` 1017 baseURL = "https://example.com" 1018 defaultContentLanguage = "en" 1019 defaultContentLanguageInSubdir = true 1020 ignoreFiles = ["README\\.md", "content/en/ignore"] 1021 1022 [Languages] 1023 [Languages.en] 1024 weight = 99999 1025 contentDir = "content/en" 1026 [Languages.nn] 1027 weight = 20 1028 contentDir = "content/nn" 1029 [Languages.sv] 1030 weight = 30 1031 contentDir = "content/sv" 1032 [Languages.nb] 1033 weight = 40 1034 contentDir = "content/nb" 1035 1036 ` 1037 1038 const pageContent = `--- 1039 title: %q 1040 --- 1041 ` 1042 createPage := func(s string) string { 1043 return fmt.Sprintf(pageContent, s) 1044 } 1045 1046 b := newTestSitesBuilder(t).WithConfigFile("toml", config) 1047 b.WithLogger(loggers.NewWarningLogger()) 1048 1049 b.WithTemplates("_default/list.html", `{{ range .Site.Pages }} 1050 {{ .Kind }}|{{ .Path }}|{{ with .CurrentSection }}CurrentSection: {{ .Path }}{{ end }}|{{ .RelPermalink }}{{ end }} 1051 `) 1052 1053 b.WithTemplates("_default/single.html", `Single: {{ .Title }}`) 1054 1055 b.WithContent("en/sect1/sect2/_index.md", createPage("en: Sect 2")) 1056 b.WithContent("en/sect1/sect2/page.md", createPage("en: Page")) 1057 b.WithContent("en/sect1/sect2/data-branch.json", "mydata") 1058 b.WithContent("nn/sect1/sect2/page.md", createPage("nn: Page")) 1059 b.WithContent("nn/sect1/sect2/data-branch.json", "my nn data") 1060 1061 // En only 1062 b.WithContent("en/enonly/myen.md", createPage("en: Page")) 1063 b.WithContent("en/enonly/myendata.json", "mydata") 1064 1065 // Leaf 1066 1067 b.WithContent("nn/b1/index.md", createPage("nn: leaf")) 1068 b.WithContent("en/b1/index.md", createPage("en: leaf")) 1069 b.WithContent("sv/b1/index.md", createPage("sv: leaf")) 1070 b.WithContent("nb/b1/index.md", createPage("nb: leaf")) 1071 1072 // Should be ignored 1073 b.WithContent("en/ignore/page.md", createPage("en: ignore")) 1074 b.WithContent("en/README.md", createPage("en: ignore")) 1075 1076 // Both leaf and branch bundle in same dir 1077 b.WithContent("en/b2/index.md", `--- 1078 slug: leaf 1079 --- 1080 `) 1081 b.WithContent("en/b2/_index.md", createPage("en: branch")) 1082 1083 b.WithContent("en/b1/data1.json", "en: data") 1084 b.WithContent("sv/b1/data1.json", "sv: data") 1085 b.WithContent("sv/b1/data2.json", "sv: data2") 1086 b.WithContent("nb/b1/data2.json", "nb: data2") 1087 1088 b.WithContent("en/b3/_index.md", createPage("en: branch")) 1089 b.WithContent("en/b3/p1.md", createPage("en: page")) 1090 b.WithContent("en/b3/data1.json", "en: data") 1091 1092 b.Build(BuildCfg{}) 1093 1094 b.AssertFileContent("public/en/index.html", 1095 filepath.FromSlash("section|sect1/sect2/_index.md|CurrentSection: sect1/sect2/_index.md"), 1096 "myen.md|CurrentSection: enonly") 1097 1098 b.AssertFileContentFn("public/en/index.html", func(s string) bool { 1099 // Check ignored files 1100 return !regexp.MustCompile("README|ignore").MatchString(s) 1101 }) 1102 1103 b.AssertFileContent("public/nn/index.html", filepath.FromSlash("page|sect1/sect2/page.md|CurrentSection: sect1")) 1104 b.AssertFileContentFn("public/nn/index.html", func(s string) bool { 1105 return !strings.Contains(s, "enonly") 1106 }) 1107 1108 // Check order of inherited data file 1109 b.AssertFileContent("public/nb/b1/data1.json", "en: data") // Default content 1110 b.AssertFileContent("public/nn/b1/data2.json", "sv: data") // First match 1111 1112 b.AssertFileContent("public/en/enonly/myen/index.html", "Single: en: Page") 1113 b.AssertFileContent("public/en/enonly/myendata.json", "mydata") 1114 1115 c := qt.New(t) 1116 c.Assert(b.CheckExists("public/sv/enonly/myen/index.html"), qt.Equals, false) 1117 1118 // Both leaf and branch bundle in same dir 1119 // We log a warning about it, but we keep both. 1120 b.AssertFileContent("public/en/b2/index.html", 1121 "/en/b2/leaf/", 1122 filepath.FromSlash("section|sect1/sect2/_index.md|CurrentSection: sect1/sect2/_index.md")) 1123 } 1124 1125 // Issue 6136 1126 func TestPageBundlerPartialTranslations(t *testing.T) { 1127 config := ` 1128 baseURL = "https://example.org" 1129 defaultContentLanguage = "en" 1130 defaultContentLanguageInSubDir = true 1131 disableKinds = ["taxonomy", "term"] 1132 [languages] 1133 [languages.nn] 1134 languageName = "Nynorsk" 1135 weight = 2 1136 title = "Tittel på Nynorsk" 1137 [languages.en] 1138 title = "Title in English" 1139 languageName = "English" 1140 weight = 1 1141 ` 1142 1143 pageContent := func(id string) string { 1144 return fmt.Sprintf(` 1145 --- 1146 title: %q 1147 --- 1148 `, id) 1149 } 1150 1151 dataContent := func(id string) string { 1152 return id 1153 } 1154 1155 b := newTestSitesBuilder(t).WithConfigFile("toml", config) 1156 1157 b.WithContent("blog/sect1/_index.nn.md", pageContent("s1.nn")) 1158 b.WithContent("blog/sect1/data.json", dataContent("s1.data")) 1159 1160 b.WithContent("blog/sect1/b1/index.nn.md", pageContent("s1.b1.nn")) 1161 b.WithContent("blog/sect1/b1/data.json", dataContent("s1.b1.data")) 1162 1163 b.WithContent("blog/sect2/_index.md", pageContent("s2")) 1164 b.WithContent("blog/sect2/data.json", dataContent("s2.data")) 1165 1166 b.WithContent("blog/sect2/b1/index.md", pageContent("s2.b1")) 1167 b.WithContent("blog/sect2/b1/data.json", dataContent("s2.b1.data")) 1168 1169 b.WithContent("blog/sect2/b2/index.md", pageContent("s2.b2")) 1170 b.WithContent("blog/sect2/b2/bp.md", pageContent("s2.b2.bundlecontent")) 1171 1172 b.WithContent("blog/sect2/b3/index.md", pageContent("s2.b3")) 1173 b.WithContent("blog/sect2/b3/bp.nn.md", pageContent("s2.b3.bundlecontent.nn")) 1174 1175 b.WithContent("blog/sect2/b4/index.nn.md", pageContent("s2.b4")) 1176 b.WithContent("blog/sect2/b4/bp.nn.md", pageContent("s2.b4.bundlecontent.nn")) 1177 1178 b.WithTemplates("index.html", ` 1179 Num Pages: {{ len .Site.Pages }} 1180 {{ range .Site.Pages }} 1181 {{ .Kind }}|{{ .RelPermalink }}|Content: {{ .Title }}|Resources: {{ range .Resources }}R: {{ .Title }}|{{ .Content }}|{{ end -}} 1182 {{ end }} 1183 `) 1184 1185 b.Build(BuildCfg{}) 1186 1187 b.AssertFileContent("public/nn/index.html", 1188 "Num Pages: 6", 1189 "page|/nn/blog/sect1/b1/|Content: s1.b1.nn|Resources: R: data.json|s1.b1.data|", 1190 "page|/nn/blog/sect2/b3/|Content: s2.b3|Resources: R: s2.b3.bundlecontent.nn|", 1191 "page|/nn/blog/sect2/b4/|Content: s2.b4|Resources: R: s2.b4.bundlecontent.nn", 1192 ) 1193 1194 b.AssertFileContent("public/en/index.html", 1195 "Num Pages: 6", 1196 "section|/en/blog/sect2/|Content: s2|Resources: R: data.json|s2.data|", 1197 "page|/en/blog/sect2/b1/|Content: s2.b1|Resources: R: data.json|s2.b1.data|", 1198 "page|/en/blog/sect2/b2/|Content: s2.b2|Resources: R: s2.b2.bundlecontent|", 1199 ) 1200 } 1201 1202 // #6208 1203 func TestBundleIndexInSubFolder(t *testing.T) { 1204 config := ` 1205 baseURL = "https://example.com" 1206 1207 ` 1208 1209 const pageContent = `--- 1210 title: %q 1211 --- 1212 ` 1213 createPage := func(s string) string { 1214 return fmt.Sprintf(pageContent, s) 1215 } 1216 1217 b := newTestSitesBuilder(t).WithConfigFile("toml", config) 1218 b.WithLogger(loggers.NewWarningLogger()) 1219 1220 b.WithTemplates("_default/single.html", `{{ range .Resources }} 1221 {{ .ResourceType }}|{{ .Title }}| 1222 {{ end }} 1223 1224 1225 `) 1226 1227 b.WithContent("bundle/index.md", createPage("bundle index")) 1228 b.WithContent("bundle/p1.md", createPage("bundle p1")) 1229 b.WithContent("bundle/sub/p2.md", createPage("bundle sub p2")) 1230 b.WithContent("bundle/sub/index.md", createPage("bundle sub index")) 1231 b.WithContent("bundle/sub/data.json", "data") 1232 1233 b.Build(BuildCfg{}) 1234 1235 b.AssertFileContent("public/bundle/index.html", ` 1236 application|sub/data.json| 1237 page|bundle p1| 1238 page|bundle sub index| 1239 page|bundle sub p2| 1240 `) 1241 } 1242 1243 func TestBundleTransformMany(t *testing.T) { 1244 b := newTestSitesBuilder(t).WithSimpleConfigFile().Running() 1245 1246 for i := 1; i <= 50; i++ { 1247 b.WithContent(fmt.Sprintf("bundle%d/index.md", i), fmt.Sprintf(` 1248 --- 1249 title: "Page" 1250 weight: %d 1251 --- 1252 1253 `, i)) 1254 b.WithSourceFile(fmt.Sprintf("content/bundle%d/data.yaml", i), fmt.Sprintf(`data: v%d`, i)) 1255 b.WithSourceFile(fmt.Sprintf("content/bundle%d/data.json", i), fmt.Sprintf(`{ "data": "v%d" }`, i)) 1256 b.WithSourceFile(fmt.Sprintf("assets/data%d/data.yaml", i), fmt.Sprintf(`vdata: v%d`, i)) 1257 1258 } 1259 1260 b.WithTemplatesAdded("_default/single.html", ` 1261 {{ $bundleYaml := .Resources.GetMatch "*.yaml" }} 1262 {{ $bundleJSON := .Resources.GetMatch "*.json" }} 1263 {{ $assetsYaml := resources.GetMatch (printf "data%d/*.yaml" .Weight) }} 1264 {{ $data1 := $bundleYaml | transform.Unmarshal }} 1265 {{ $data2 := $assetsYaml | transform.Unmarshal }} 1266 {{ $bundleFingerprinted := $bundleYaml | fingerprint "md5" }} 1267 {{ $assetsFingerprinted := $assetsYaml | fingerprint "md5" }} 1268 {{ $jsonMin := $bundleJSON | minify }} 1269 {{ $jsonMinMin := $jsonMin | minify }} 1270 {{ $jsonMinMinMin := $jsonMinMin | minify }} 1271 1272 data content unmarshaled: {{ $data1.data }} 1273 data assets content unmarshaled: {{ $data2.vdata }} 1274 bundle fingerprinted: {{ $bundleFingerprinted.RelPermalink }} 1275 assets fingerprinted: {{ $assetsFingerprinted.RelPermalink }} 1276 1277 bundle min min min: {{ $jsonMinMinMin.RelPermalink }} 1278 bundle min min key: {{ $jsonMinMin.Key }} 1279 1280 `) 1281 1282 for i := 0; i < 3; i++ { 1283 1284 b.Build(BuildCfg{}) 1285 1286 for i := 1; i <= 50; i++ { 1287 index := fmt.Sprintf("public/bundle%d/index.html", i) 1288 b.AssertFileContent(fmt.Sprintf("public/bundle%d/data.yaml", i), fmt.Sprintf("data: v%d", i)) 1289 b.AssertFileContent(index, fmt.Sprintf("data content unmarshaled: v%d", i)) 1290 b.AssertFileContent(index, fmt.Sprintf("data assets content unmarshaled: v%d", i)) 1291 1292 md5Asset := helpers.MD5String(fmt.Sprintf(`vdata: v%d`, i)) 1293 b.AssertFileContent(index, fmt.Sprintf("assets fingerprinted: /data%d/data.%s.yaml", i, md5Asset)) 1294 1295 // The original is not used, make sure it's not published. 1296 b.Assert(b.CheckExists(fmt.Sprintf("public/data%d/data.yaml", i)), qt.Equals, false) 1297 1298 md5Bundle := helpers.MD5String(fmt.Sprintf(`data: v%d`, i)) 1299 b.AssertFileContent(index, fmt.Sprintf("bundle fingerprinted: /bundle%d/data.%s.yaml", i, md5Bundle)) 1300 1301 b.AssertFileContent(index, 1302 fmt.Sprintf("bundle min min min: /bundle%d/data.min.min.min.json", i), 1303 fmt.Sprintf("bundle min min key: /bundle%d/data.min.min.json", i), 1304 ) 1305 b.Assert(b.CheckExists(fmt.Sprintf("public/bundle%d/data.min.min.min.json", i)), qt.Equals, true) 1306 b.Assert(b.CheckExists(fmt.Sprintf("public/bundle%d/data.min.json", i)), qt.Equals, false) 1307 b.Assert(b.CheckExists(fmt.Sprintf("public/bundle%d/data.min.min.json", i)), qt.Equals, false) 1308 1309 } 1310 1311 b.EditFiles("assets/data/foo.yaml", "FOO") 1312 1313 } 1314 } 1315 1316 func TestPageBundlerHome(t *testing.T) { 1317 t.Parallel() 1318 c := qt.New(t) 1319 1320 workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-bundler-home") 1321 c.Assert(err, qt.IsNil) 1322 1323 cfg := config.New() 1324 cfg.Set("workingDir", workDir) 1325 fs := hugofs.NewFrom(hugofs.Os, cfg) 1326 1327 os.MkdirAll(filepath.Join(workDir, "content"), 0777) 1328 1329 defer clean() 1330 1331 b := newTestSitesBuilder(t) 1332 b.Fs = fs 1333 1334 b.WithWorkingDir(workDir).WithViper(cfg) 1335 1336 b.WithContent("_index.md", "---\ntitle: Home\n---\n![Alt text](image.jpg)") 1337 b.WithSourceFile("content/data.json", "DATA") 1338 1339 b.WithTemplates("index.html", `Title: {{ .Title }}|First Resource: {{ index .Resources 0 }}|Content: {{ .Content }}`) 1340 b.WithTemplates("_default/_markup/render-image.html", `Hook Len Page Resources {{ len .Page.Resources }}`) 1341 1342 b.Build(BuildCfg{}) 1343 b.AssertFileContent("public/index.html", ` 1344 Title: Home|First Resource: data.json|Content: <p>Hook Len Page Resources 1</p> 1345 `) 1346 }