pagebundler_test.go (44549B)
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]any{
77 "CUSTOMO": map[string]any{
78 "mediaType": "text/html",
79 "baseName": "cindex",
80 "path": "cpath",
81 "permalinkable": true,
82 },
83 })
84
85 cfg.Set("outputs", map[string]any{
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("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("public", filename),
138 "TheContent",
139 "Single RelPermalink: "+rel,
140 )
141
142 b.AssertFileContent(filepath.FromSlash("public/images/hugo-logo.png"), "content")
143
144 // This should be just copied to destination.
145 b.AssertFileContent(filepath.FromSlash("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("public/root/index.html"), "Single RelPermalink: "+relURLBase+"/root/")
163 b.AssertFileContent(filepath.FromSlash("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("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("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("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("public/2017/pageslug/c/logo.png"), "content")
224 b.AssertFileContent(filepath.FromSlash("public/cpath/2017/pageslug/c/logo.png"), "content")
225 c.Assert(b.CheckExists("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("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("public/2017/pageslug.html"), "0: Page RelPermalink: |")
251
252 b.AssertFileContent(filepath.FromSlash("public/cpath/2017/pageslug.html"), "TheContent")
253
254 // 은행
255 b.AssertFileContent(filepath.FromSlash("public/c/은행/logo-은행.png"), "은행 PNG")
256
257 } else {
258 b.AssertFileContent(filepath.FromSlash("public/2017/pageslug/index.html"), "TheContent")
259 b.AssertFileContent(filepath.FromSlash("public/cpath/2017/pageslug/cindex.html"), "TheContent")
260 b.AssertFileContent(filepath.FromSlash("public/2017/pageslug/index.html"), "Single Title")
261 b.AssertFileContent(filepath.FromSlash("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
401 // We need to use the OS fs for this.
402 workingDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugosym")
403 c.Assert(err, qt.IsNil)
404 cfg := config.NewWithTestDefaults()
405 cfg.Set("workingDir", workingDir)
406 fs := hugofs.NewFrom(hugofs.Os, cfg)
407
408 contentDirName := "content"
409
410 contentDir := filepath.Join(workingDir, contentDirName)
411 c.Assert(os.MkdirAll(filepath.Join(contentDir, "a"), 0777), qt.IsNil)
412
413 for i := 1; i <= 3; i++ {
414 c.Assert(os.MkdirAll(filepath.Join(workingDir, fmt.Sprintf("symcontent%d", i)), 0777), qt.IsNil)
415 }
416
417 c.Assert(os.MkdirAll(filepath.Join(workingDir, "symcontent2", "a1"), 0777), qt.IsNil)
418
419 // Symlinked sections inside content.
420 os.Chdir(contentDir)
421 for i := 1; i <= 3; i++ {
422 c.Assert(os.Symlink(filepath.FromSlash(fmt.Sprintf(("../symcontent%d"), i)), fmt.Sprintf("symbolic%d", i)), qt.IsNil)
423 }
424
425 c.Assert(os.Chdir(filepath.Join(contentDir, "a")), qt.IsNil)
426
427 // Create a symlink to one single content file
428 c.Assert(os.Symlink(filepath.FromSlash("../../symcontent2/a1/page.md"), "page_s.md"), qt.IsNil)
429
430 c.Assert(os.Chdir(filepath.FromSlash("../../symcontent3")), qt.IsNil)
431
432 // Create a circular symlink. Will print some warnings.
433 c.Assert(os.Symlink(filepath.Join("..", contentDirName), filepath.FromSlash("circus")), qt.IsNil)
434
435 c.Assert(os.Chdir(workingDir), qt.IsNil)
436
437 defer clean()
438
439 cfg.Set("workingDir", workingDir)
440 cfg.Set("contentDir", contentDirName)
441 cfg.Set("baseURL", "https://example.com")
442
443 layout := `{{ .Title }}|{{ .Content }}`
444 pageContent := `---
445 slug: %s
446 date: 2017-10-09
447 ---
448
449 TheContent.
450 `
451
452 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{
453 Fs: fs,
454 Cfg: cfg,
455 })
456
457 b.WithTemplates(
458 "_default/single.html", layout,
459 "_default/list.html", layout,
460 )
461
462 b.WithContent(
463 "a/regular.md", fmt.Sprintf(pageContent, "a1"),
464 )
465
466 b.WithSourceFile(
467 "symcontent1/s1.md", fmt.Sprintf(pageContent, "s1"),
468 "symcontent1/s2.md", fmt.Sprintf(pageContent, "s2"),
469 // Regular files inside symlinked folder.
470 "symcontent1/s1.md", fmt.Sprintf(pageContent, "s1"),
471 "symcontent1/s2.md", fmt.Sprintf(pageContent, "s2"),
472
473 // A bundle
474 "symcontent2/a1/index.md", fmt.Sprintf(pageContent, ""),
475 "symcontent2/a1/page.md", fmt.Sprintf(pageContent, "page"),
476 "symcontent2/a1/logo.png", "image",
477
478 // Assets
479 "symcontent3/s1.png", "image",
480 "symcontent3/s2.png", "image",
481 )
482
483 b.Build(BuildCfg{})
484 s := b.H.Sites[0]
485
486 c.Assert(len(s.RegularPages()), qt.Equals, 7)
487 a1Bundle := s.getPage(page.KindPage, "symbolic2/a1/index.md")
488 c.Assert(a1Bundle, qt.Not(qt.IsNil))
489 c.Assert(len(a1Bundle.Resources()), qt.Equals, 2)
490 c.Assert(len(a1Bundle.Resources().ByType(pageResourceType)), qt.Equals, 1)
491
492 b.AssertFileContent(filepath.FromSlash("public/a/page/index.html"), "TheContent")
493 b.AssertFileContent(filepath.FromSlash("public/symbolic1/s1/index.html"), "TheContent")
494 b.AssertFileContent(filepath.FromSlash("public/symbolic2/a1/index.html"), "TheContent")
495 }
496
497 func TestPageBundlerHeadless(t *testing.T) {
498 t.Parallel()
499
500 cfg, fs := newTestCfg()
501 c := qt.New(t)
502
503 workDir := "/work"
504 cfg.Set("workingDir", workDir)
505 cfg.Set("contentDir", "base")
506 cfg.Set("baseURL", "https://example.com")
507
508 pageContent := `---
509 title: "Bundle Galore"
510 slug: s1
511 date: 2017-01-23
512 ---
513
514 TheContent.
515
516 {{< myShort >}}
517 `
518
519 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), "single {{ .Content }}")
520 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), "list")
521 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), "SHORTCODE")
522
523 writeSource(t, fs, filepath.Join(workDir, "base", "a", "index.md"), pageContent)
524 writeSource(t, fs, filepath.Join(workDir, "base", "a", "l1.png"), "PNG image")
525 writeSource(t, fs, filepath.Join(workDir, "base", "a", "l2.png"), "PNG image")
526
527 writeSource(t, fs, filepath.Join(workDir, "base", "b", "index.md"), `---
528 title: "Headless Bundle in Topless Bar"
529 slug: s2
530 headless: true
531 date: 2017-01-23
532 ---
533
534 TheContent.
535 HEADLESS {{< myShort >}}
536 `)
537 writeSource(t, fs, filepath.Join(workDir, "base", "b", "l1.png"), "PNG image")
538 writeSource(t, fs, filepath.Join(workDir, "base", "b", "l2.png"), "PNG image")
539 writeSource(t, fs, filepath.Join(workDir, "base", "b", "p1.md"), pageContent)
540
541 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
542
543 c.Assert(len(s.RegularPages()), qt.Equals, 1)
544
545 regular := s.getPage(page.KindPage, "a/index")
546 c.Assert(regular.RelPermalink(), qt.Equals, "/s1/")
547
548 headless := s.getPage(page.KindPage, "b/index")
549 c.Assert(headless, qt.Not(qt.IsNil))
550 c.Assert(headless.Title(), qt.Equals, "Headless Bundle in Topless Bar")
551 c.Assert(headless.RelPermalink(), qt.Equals, "")
552 c.Assert(headless.Permalink(), qt.Equals, "")
553 c.Assert(content(headless), qt.Contains, "HEADLESS SHORTCODE")
554
555 headlessResources := headless.Resources()
556 c.Assert(len(headlessResources), qt.Equals, 3)
557 res := headlessResources.Match("l*")
558 c.Assert(len(res), qt.Equals, 2)
559 pageResource := headlessResources.GetMatch("p*")
560 c.Assert(pageResource, qt.Not(qt.IsNil))
561 p := pageResource.(page.Page)
562 c.Assert(content(p), qt.Contains, "SHORTCODE")
563 c.Assert(p.Name(), qt.Equals, "p1.md")
564
565 th := newTestHelper(s.Cfg, s.Fs, t)
566
567 th.assertFileContent(filepath.FromSlash("public/s1/index.html"), "TheContent")
568 th.assertFileContent(filepath.FromSlash("public/s1/l1.png"), "PNG")
569
570 th.assertFileNotExist("public/s2/index.html")
571 // But the bundled resources needs to be published
572 th.assertFileContent(filepath.FromSlash("public/s2/l1.png"), "PNG")
573
574 // No headless bundles here, please.
575 // https://github.com/gohugoio/hugo/issues/6492
576 c.Assert(s.RegularPages(), qt.HasLen, 1)
577 c.Assert(s.home.RegularPages(), qt.HasLen, 1)
578 c.Assert(s.home.Pages(), qt.HasLen, 1)
579 }
580
581 func TestPageBundlerHeadlessIssue6552(t *testing.T) {
582 t.Parallel()
583
584 b := newTestSitesBuilder(t)
585 b.WithContent("headless/h1/index.md", `
586 ---
587 title: My Headless Bundle1
588 headless: true
589 ---
590 `, "headless/h1/p1.md", `
591 ---
592 title: P1
593 ---
594 `, "headless/h2/index.md", `
595 ---
596 title: My Headless Bundle2
597 headless: true
598 ---
599 `)
600
601 b.WithTemplatesAdded("index.html", `
602 {{ $headless1 := .Site.GetPage "headless/h1" }}
603 {{ $headless2 := .Site.GetPage "headless/h2" }}
604
605 HEADLESS1: {{ $headless1.Title }}|{{ $headless1.RelPermalink }}|{{ len $headless1.Resources }}|
606 HEADLESS2: {{ $headless2.Title }}{{ $headless2.RelPermalink }}|{{ len $headless2.Resources }}|
607
608 `)
609
610 b.Build(BuildCfg{})
611
612 b.AssertFileContent("public/index.html", `
613 HEADLESS1: My Headless Bundle1||1|
614 HEADLESS2: My Headless Bundle2|0|
615 `)
616 }
617
618 func TestMultiSiteBundles(t *testing.T) {
619 c := qt.New(t)
620 b := newTestSitesBuilder(t)
621 b.WithConfigFile("toml", `
622
623 baseURL = "http://example.com/"
624
625 defaultContentLanguage = "en"
626
627 [languages]
628 [languages.en]
629 weight = 10
630 contentDir = "content/en"
631 [languages.nn]
632 weight = 20
633 contentDir = "content/nn"
634
635
636 `)
637
638 b.WithContent("en/mybundle/index.md", `
639 ---
640 headless: true
641 ---
642
643 `)
644
645 b.WithContent("nn/mybundle/index.md", `
646 ---
647 headless: true
648 ---
649
650 `)
651
652 b.WithContent("en/mybundle/data.yaml", `data en`)
653 b.WithContent("en/mybundle/forms.yaml", `forms en`)
654 b.WithContent("nn/mybundle/data.yaml", `data nn`)
655
656 b.WithContent("en/_index.md", `
657 ---
658 Title: Home
659 ---
660
661 Home content.
662
663 `)
664
665 b.WithContent("en/section-not-bundle/_index.md", `
666 ---
667 Title: Section Page
668 ---
669
670 Section content.
671
672 `)
673
674 b.WithContent("en/section-not-bundle/single.md", `
675 ---
676 Title: Section Single
677 Date: 2018-02-01
678 ---
679
680 Single content.
681
682 `)
683
684 b.Build(BuildCfg{})
685
686 b.AssertFileContent("public/nn/mybundle/data.yaml", "data nn")
687 b.AssertFileContent("public/nn/mybundle/forms.yaml", "forms en")
688 b.AssertFileContent("public/mybundle/data.yaml", "data en")
689 b.AssertFileContent("public/mybundle/forms.yaml", "forms en")
690
691 c.Assert(b.CheckExists("public/nn/nn/mybundle/data.yaml"), qt.Equals, false)
692 c.Assert(b.CheckExists("public/en/mybundle/data.yaml"), qt.Equals, false)
693
694 homeEn := b.H.Sites[0].home
695 c.Assert(homeEn, qt.Not(qt.IsNil))
696 c.Assert(homeEn.Date().Year(), qt.Equals, 2018)
697
698 b.AssertFileContent("public/section-not-bundle/index.html", "Section Page", "Content: <p>Section content.</p>")
699 b.AssertFileContent("public/section-not-bundle/single/index.html", "Section Single", "|<p>Single content.</p>")
700 }
701
702 func newTestBundleSources(t testing.TB) (*hugofs.Fs, config.Provider) {
703 cfg, fs := newTestCfgBasic()
704 c := qt.New(t)
705
706 workDir := "/work"
707 cfg.Set("workingDir", workDir)
708 cfg.Set("contentDir", "base")
709 cfg.Set("baseURL", "https://example.com")
710 cfg.Set("mediaTypes", map[string]any{
711 "bepsays/bep": map[string]any{
712 "suffixes": []string{"bep"},
713 },
714 })
715
716 pageContent := `---
717 title: "Bundle Galore"
718 slug: pageslug
719 date: 2017-10-09
720 ---
721
722 TheContent.
723 `
724
725 pageContentShortcode := `---
726 title: "Bundle Galore"
727 slug: pageslug
728 date: 2017-10-09
729 ---
730
731 TheContent.
732
733 {{< myShort >}}
734 `
735
736 pageWithImageShortcodeAndResourceMetadataContent := `---
737 title: "Bundle Galore"
738 slug: pageslug
739 date: 2017-10-09
740 resources:
741 - src: "*.jpg"
742 name: "my-sunset-:counter"
743 title: "Sunset Galore :counter"
744 params:
745 myParam: "My Sunny Param"
746 ---
747
748 TheContent.
749
750 {{< myShort >}}
751 `
752
753 pageContentNoSlug := `---
754 title: "Bundle Galore #2"
755 date: 2017-10-09
756 ---
757
758 TheContent.
759 `
760
761 singleLayout := `
762 Single Title: {{ .Title }}
763 Single RelPermalink: {{ .RelPermalink }}
764 Single Permalink: {{ .Permalink }}
765 Content: {{ .Content }}
766 {{ $sunset := .Resources.GetMatch "my-sunset-1*" }}
767 {{ with $sunset }}
768 Sunset RelPermalink: {{ .RelPermalink }}
769 Sunset Permalink: {{ .Permalink }}
770 {{ $thumb := .Fill "123x123" }}
771 Thumb Width: {{ $thumb.Width }}
772 Thumb Name: {{ $thumb.Name }}
773 Thumb Title: {{ $thumb.Title }}
774 Thumb RelPermalink: {{ $thumb.RelPermalink }}
775 {{ end }}
776 {{ $types := slice "image" "page" }}
777 {{ range $types }}
778 {{ $typeTitle := . | title }}
779 {{ range $i, $e := $.Resources.ByType . }}
780 {{ $i }}: {{ $typeTitle }} Title: {{ .Title }}
781 {{ $i }}: {{ $typeTitle }} Name: {{ .Name }}
782 {{ $i }}: {{ $typeTitle }} RelPermalink: {{ .RelPermalink }}|
783 {{ $i }}: {{ $typeTitle }} Params: {{ printf "%v" .Params }}
784 {{ $i }}: {{ $typeTitle }} myParam: Lower: {{ .Params.myparam }} Caps: {{ .Params.MYPARAM }}
785 {{ end }}
786 {{ end }}
787 `
788
789 myShort := `
790 MyShort in {{ .Page.File.Path }}:
791 {{ $sunset := .Page.Resources.GetMatch "my-sunset-2*" }}
792 {{ with $sunset }}
793 Short Sunset RelPermalink: {{ .RelPermalink }}
794 {{ $thumb := .Fill "56x56" }}
795 Short Thumb Width: {{ $thumb.Width }}
796 {{ end }}
797 `
798
799 listLayout := `{{ .Title }}|{{ .Content }}`
800
801 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), singleLayout)
802 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), listLayout)
803 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), myShort)
804 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.customo"), myShort)
805
806 writeSource(t, fs, filepath.Join(workDir, "base", "_index.md"), pageContent)
807 writeSource(t, fs, filepath.Join(workDir, "base", "_1.md"), pageContent)
808 writeSource(t, fs, filepath.Join(workDir, "base", "_1.png"), pageContent)
809
810 writeSource(t, fs, filepath.Join(workDir, "base", "images", "hugo-logo.png"), "content")
811 writeSource(t, fs, filepath.Join(workDir, "base", "a", "2.md"), pageContent)
812 writeSource(t, fs, filepath.Join(workDir, "base", "a", "1.md"), pageContent)
813
814 writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "index.md"), pageContentNoSlug)
815 writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "ab1.md"), pageContentNoSlug)
816
817 // Mostly plain static assets in a folder with a page in a sub folder thrown in.
818 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic1.png"), "content")
819 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic2.png"), "content")
820 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pages", "mypage.md"), pageContent)
821
822 // Bundle
823 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
824 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "1.md"), pageContent)
825 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "2.md"), pageContentShortcode)
826 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "custom-mime.bep"), "bepsays")
827 writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "c", "logo.png"), "content")
828
829 // Bundle with 은행 slug
830 // See https://github.com/gohugoio/hugo/issues/4241
831 writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "index.md"), `---
832 title: "은행 은행"
833 slug: 은행
834 date: 2017-10-09
835 ---
836
837 Content for 은행.
838 `)
839
840 // Bundle in root
841 writeSource(t, fs, filepath.Join(workDir, "base", "root", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
842 writeSource(t, fs, filepath.Join(workDir, "base", "root", "1.md"), pageContent)
843 writeSource(t, fs, filepath.Join(workDir, "base", "root", "c", "logo.png"), "content")
844
845 writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "logo-은행.png"), "은행 PNG")
846
847 // Write a real image into one of the bundle above.
848 src, err := os.Open("testdata/sunset.jpg")
849 c.Assert(err, qt.IsNil)
850
851 // We need 2 to test https://github.com/gohugoio/hugo/issues/4202
852 out, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset1.jpg"))
853 c.Assert(err, qt.IsNil)
854 out2, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset2.jpg"))
855 c.Assert(err, qt.IsNil)
856
857 _, err = io.Copy(out, src)
858 c.Assert(err, qt.IsNil)
859 out.Close()
860 src.Seek(0, 0)
861 _, err = io.Copy(out2, src)
862 out2.Close()
863 src.Close()
864 c.Assert(err, qt.IsNil)
865
866 return fs, cfg
867 }
868
869 func newTestBundleSourcesMultilingual(t *testing.T) (*hugofs.Fs, config.Provider) {
870 cfg, fs := newTestCfgBasic()
871
872 workDir := "/work"
873 cfg.Set("workingDir", workDir)
874 cfg.Set("contentDir", "base")
875 cfg.Set("baseURL", "https://example.com")
876 cfg.Set("defaultContentLanguage", "en")
877
878 langConfig := map[string]any{
879 "en": map[string]any{
880 "weight": 1,
881 "languageName": "English",
882 },
883 "nn": map[string]any{
884 "weight": 2,
885 "languageName": "Nynorsk",
886 },
887 }
888
889 cfg.Set("languages", langConfig)
890
891 pageContent := `---
892 slug: pageslug
893 date: 2017-10-09
894 ---
895
896 TheContent.
897 `
898
899 layout := `{{ .Title }}|{{ .Content }}|Lang: {{ .Site.Language.Lang }}`
900
901 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), layout)
902 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout)
903
904 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.md"), pageContent)
905 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.nn.md"), pageContent)
906 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mylogo.png"), "content")
907
908 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.md"), pageContent)
909 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.nn.md"), pageContent)
910 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "en.md"), pageContent)
911 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.md"), pageContent)
912 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.nn.md"), pageContent)
913 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "a.png"), "content")
914 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.png"), "content")
915 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.nn.png"), "content")
916 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "c.nn.png"), "content")
917 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b", "d.nn.png"), "content")
918
919 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "_index.md"), pageContent)
920 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "_index.nn.md"), pageContent)
921 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.md"), pageContent)
922 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "logo-bc.png"), "logo")
923 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.nn.md"), pageContent)
924 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "data1.json"), "data1")
925 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "data2.json"), "data2")
926 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "data1.nn.json"), "data1.nn")
927
928 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "index.md"), pageContent)
929 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.md"), pageContent)
930 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.nn.md"), pageContent)
931
932 writeSource(t, fs, filepath.Join(workDir, "base", "be", "_index.md"), pageContent)
933 writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.md"), pageContent)
934 writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.nn.md"), pageContent)
935
936 // Bundle leaf, multilingual
937 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.md"), pageContent)
938 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.nn.md"), pageContent)
939 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "1.md"), pageContent)
940 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.md"), pageContent)
941 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.nn.md"), pageContent)
942 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "page.md"), pageContent)
943 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.png"), "content")
944 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.nn.png"), "content")
945 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "one.png"), "content")
946 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "d", "deep.png"), "content")
947
948 // Translated bundle in some sensible sub path.
949 writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "index.md"), pageContent)
950 writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "index.nn.md"), pageContent)
951 writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "page.md"), pageContent)
952
953 return fs, cfg
954 }
955
956 // https://github.com/gohugoio/hugo/issues/5858
957 func TestBundledResourcesWhenMultipleOutputFormats(t *testing.T) {
958 t.Parallel()
959
960 b := newTestSitesBuilder(t).Running().WithConfigFile("toml", `
961 baseURL = "https://example.org"
962 [outputs]
963 # This looks odd, but it triggers the behaviour in #5858
964 # The total output formats list gets sorted, so CSS before HTML.
965 home = [ "CSS" ]
966
967 `)
968 b.WithContent("mybundle/index.md", `
969 ---
970 title: Page
971 date: 2017-01-15
972 ---
973 `,
974 "mybundle/data.json", "MyData",
975 )
976
977 b.CreateSites().Build(BuildCfg{})
978
979 b.AssertFileContent("public/mybundle/data.json", "MyData")
980
981 // Change the bundled JSON file and make sure it gets republished.
982 b.EditFiles("content/mybundle/data.json", "My changed data")
983
984 b.Build(BuildCfg{})
985
986 b.AssertFileContent("public/mybundle/data.json", "My changed data")
987 }
988
989 // https://github.com/gohugoio/hugo/issues/4870
990 func TestBundleSlug(t *testing.T) {
991 t.Parallel()
992 c := qt.New(t)
993
994 const pageTemplate = `---
995 title: Title
996 slug: %s
997 ---
998 `
999
1000 b := newTestSitesBuilder(t)
1001
1002 b.WithTemplatesAdded("index.html", `{{ range .Site.RegularPages }}|{{ .RelPermalink }}{{ end }}|`)
1003 b.WithSimpleConfigFile().
1004 WithContent("about/services1/misc.md", fmt.Sprintf(pageTemplate, "this-is-the-slug")).
1005 WithContent("about/services2/misc/index.md", fmt.Sprintf(pageTemplate, "this-is-another-slug"))
1006
1007 b.CreateSites().Build(BuildCfg{})
1008
1009 b.AssertHome(
1010 "|/about/services1/this-is-the-slug/|/",
1011 "|/about/services2/this-is-another-slug/|")
1012
1013 c.Assert(b.CheckExists("public/about/services1/this-is-the-slug/index.html"), qt.Equals, true)
1014 c.Assert(b.CheckExists("public/about/services2/this-is-another-slug/index.html"), qt.Equals, true)
1015 }
1016
1017 func TestBundleMisc(t *testing.T) {
1018 config := `
1019 baseURL = "https://example.com"
1020 defaultContentLanguage = "en"
1021 defaultContentLanguageInSubdir = true
1022 ignoreFiles = ["README\\.md", "content/en/ignore"]
1023
1024 [Languages]
1025 [Languages.en]
1026 weight = 99999
1027 contentDir = "content/en"
1028 [Languages.nn]
1029 weight = 20
1030 contentDir = "content/nn"
1031 [Languages.sv]
1032 weight = 30
1033 contentDir = "content/sv"
1034 [Languages.nb]
1035 weight = 40
1036 contentDir = "content/nb"
1037
1038 `
1039
1040 const pageContent = `---
1041 title: %q
1042 ---
1043 `
1044 createPage := func(s string) string {
1045 return fmt.Sprintf(pageContent, s)
1046 }
1047
1048 b := newTestSitesBuilder(t).WithConfigFile("toml", config)
1049 b.WithLogger(loggers.NewWarningLogger())
1050
1051 b.WithTemplates("_default/list.html", `{{ range .Site.Pages }}
1052 {{ .Kind }}|{{ .Path }}|{{ with .CurrentSection }}CurrentSection: {{ .Path }}{{ end }}|{{ .RelPermalink }}{{ end }}
1053 `)
1054
1055 b.WithTemplates("_default/single.html", `Single: {{ .Title }}`)
1056
1057 b.WithContent("en/sect1/sect2/_index.md", createPage("en: Sect 2"))
1058 b.WithContent("en/sect1/sect2/page.md", createPage("en: Page"))
1059 b.WithContent("en/sect1/sect2/data-branch.json", "mydata")
1060 b.WithContent("nn/sect1/sect2/page.md", createPage("nn: Page"))
1061 b.WithContent("nn/sect1/sect2/data-branch.json", "my nn data")
1062
1063 // En only
1064 b.WithContent("en/enonly/myen.md", createPage("en: Page"))
1065 b.WithContent("en/enonly/myendata.json", "mydata")
1066
1067 // Leaf
1068
1069 b.WithContent("nn/b1/index.md", createPage("nn: leaf"))
1070 b.WithContent("en/b1/index.md", createPage("en: leaf"))
1071 b.WithContent("sv/b1/index.md", createPage("sv: leaf"))
1072 b.WithContent("nb/b1/index.md", createPage("nb: leaf"))
1073
1074 // Should be ignored
1075 b.WithContent("en/ignore/page.md", createPage("en: ignore"))
1076 b.WithContent("en/README.md", createPage("en: ignore"))
1077
1078 // Both leaf and branch bundle in same dir
1079 b.WithContent("en/b2/index.md", `---
1080 slug: leaf
1081 ---
1082 `)
1083 b.WithContent("en/b2/_index.md", createPage("en: branch"))
1084
1085 b.WithContent("en/b1/data1.json", "en: data")
1086 b.WithContent("sv/b1/data1.json", "sv: data")
1087 b.WithContent("sv/b1/data2.json", "sv: data2")
1088 b.WithContent("nb/b1/data2.json", "nb: data2")
1089
1090 b.WithContent("en/b3/_index.md", createPage("en: branch"))
1091 b.WithContent("en/b3/p1.md", createPage("en: page"))
1092 b.WithContent("en/b3/data1.json", "en: data")
1093
1094 b.Build(BuildCfg{})
1095
1096 b.AssertFileContent("public/en/index.html",
1097 filepath.FromSlash("section|sect1/sect2/_index.md|CurrentSection: sect1/sect2/_index.md"),
1098 "myen.md|CurrentSection: enonly")
1099
1100 b.AssertFileContentFn("public/en/index.html", func(s string) bool {
1101 // Check ignored files
1102 return !regexp.MustCompile("README|ignore").MatchString(s)
1103 })
1104
1105 b.AssertFileContent("public/nn/index.html", filepath.FromSlash("page|sect1/sect2/page.md|CurrentSection: sect1"))
1106 b.AssertFileContentFn("public/nn/index.html", func(s string) bool {
1107 return !strings.Contains(s, "enonly")
1108 })
1109
1110 // Check order of inherited data file
1111 b.AssertFileContent("public/nb/b1/data1.json", "en: data") // Default content
1112 b.AssertFileContent("public/nn/b1/data2.json", "sv: data") // First match
1113
1114 b.AssertFileContent("public/en/enonly/myen/index.html", "Single: en: Page")
1115 b.AssertFileContent("public/en/enonly/myendata.json", "mydata")
1116
1117 c := qt.New(t)
1118 c.Assert(b.CheckExists("public/sv/enonly/myen/index.html"), qt.Equals, false)
1119
1120 // Both leaf and branch bundle in same dir
1121 // We log a warning about it, but we keep both.
1122 b.AssertFileContent("public/en/b2/index.html",
1123 "/en/b2/leaf/",
1124 filepath.FromSlash("section|sect1/sect2/_index.md|CurrentSection: sect1/sect2/_index.md"))
1125 }
1126
1127 // Issue 6136
1128 func TestPageBundlerPartialTranslations(t *testing.T) {
1129 config := `
1130 baseURL = "https://example.org"
1131 defaultContentLanguage = "en"
1132 defaultContentLanguageInSubDir = true
1133 disableKinds = ["taxonomy", "term"]
1134 [languages]
1135 [languages.nn]
1136 languageName = "Nynorsk"
1137 weight = 2
1138 title = "Tittel på Nynorsk"
1139 [languages.en]
1140 title = "Title in English"
1141 languageName = "English"
1142 weight = 1
1143 `
1144
1145 pageContent := func(id string) string {
1146 return fmt.Sprintf(`
1147 ---
1148 title: %q
1149 ---
1150 `, id)
1151 }
1152
1153 dataContent := func(id string) string {
1154 return id
1155 }
1156
1157 b := newTestSitesBuilder(t).WithConfigFile("toml", config)
1158
1159 b.WithContent("blog/sect1/_index.nn.md", pageContent("s1.nn"))
1160 b.WithContent("blog/sect1/data.json", dataContent("s1.data"))
1161
1162 b.WithContent("blog/sect1/b1/index.nn.md", pageContent("s1.b1.nn"))
1163 b.WithContent("blog/sect1/b1/data.json", dataContent("s1.b1.data"))
1164
1165 b.WithContent("blog/sect2/_index.md", pageContent("s2"))
1166 b.WithContent("blog/sect2/data.json", dataContent("s2.data"))
1167
1168 b.WithContent("blog/sect2/b1/index.md", pageContent("s2.b1"))
1169 b.WithContent("blog/sect2/b1/data.json", dataContent("s2.b1.data"))
1170
1171 b.WithContent("blog/sect2/b2/index.md", pageContent("s2.b2"))
1172 b.WithContent("blog/sect2/b2/bp.md", pageContent("s2.b2.bundlecontent"))
1173
1174 b.WithContent("blog/sect2/b3/index.md", pageContent("s2.b3"))
1175 b.WithContent("blog/sect2/b3/bp.nn.md", pageContent("s2.b3.bundlecontent.nn"))
1176
1177 b.WithContent("blog/sect2/b4/index.nn.md", pageContent("s2.b4"))
1178 b.WithContent("blog/sect2/b4/bp.nn.md", pageContent("s2.b4.bundlecontent.nn"))
1179
1180 b.WithTemplates("index.html", `
1181 Num Pages: {{ len .Site.Pages }}
1182 {{ range .Site.Pages }}
1183 {{ .Kind }}|{{ .RelPermalink }}|Content: {{ .Title }}|Resources: {{ range .Resources }}R: {{ .Title }}|{{ .Content }}|{{ end -}}
1184 {{ end }}
1185 `)
1186
1187 b.Build(BuildCfg{})
1188
1189 b.AssertFileContent("public/nn/index.html",
1190 "Num Pages: 6",
1191 "page|/nn/blog/sect1/b1/|Content: s1.b1.nn|Resources: R: data.json|s1.b1.data|",
1192 "page|/nn/blog/sect2/b3/|Content: s2.b3|Resources: R: s2.b3.bundlecontent.nn|",
1193 "page|/nn/blog/sect2/b4/|Content: s2.b4|Resources: R: s2.b4.bundlecontent.nn",
1194 )
1195
1196 b.AssertFileContent("public/en/index.html",
1197 "Num Pages: 6",
1198 "section|/en/blog/sect2/|Content: s2|Resources: R: data.json|s2.data|",
1199 "page|/en/blog/sect2/b1/|Content: s2.b1|Resources: R: data.json|s2.b1.data|",
1200 "page|/en/blog/sect2/b2/|Content: s2.b2|Resources: R: s2.b2.bundlecontent|",
1201 )
1202 }
1203
1204 // #6208
1205 func TestBundleIndexInSubFolder(t *testing.T) {
1206 config := `
1207 baseURL = "https://example.com"
1208
1209 `
1210
1211 const pageContent = `---
1212 title: %q
1213 ---
1214 `
1215 createPage := func(s string) string {
1216 return fmt.Sprintf(pageContent, s)
1217 }
1218
1219 b := newTestSitesBuilder(t).WithConfigFile("toml", config)
1220 b.WithLogger(loggers.NewWarningLogger())
1221
1222 b.WithTemplates("_default/single.html", `{{ range .Resources }}
1223 {{ .ResourceType }}|{{ .Title }}|
1224 {{ end }}
1225
1226
1227 `)
1228
1229 b.WithContent("bundle/index.md", createPage("bundle index"))
1230 b.WithContent("bundle/p1.md", createPage("bundle p1"))
1231 b.WithContent("bundle/sub/p2.md", createPage("bundle sub p2"))
1232 b.WithContent("bundle/sub/index.md", createPage("bundle sub index"))
1233 b.WithContent("bundle/sub/data.json", "data")
1234
1235 b.Build(BuildCfg{})
1236
1237 b.AssertFileContent("public/bundle/index.html", `
1238 application|sub/data.json|
1239 page|bundle p1|
1240 page|bundle sub index|
1241 page|bundle sub p2|
1242 `)
1243 }
1244
1245 func TestBundleTransformMany(t *testing.T) {
1246 b := newTestSitesBuilder(t).WithSimpleConfigFile().Running()
1247
1248 for i := 1; i <= 50; i++ {
1249 b.WithContent(fmt.Sprintf("bundle%d/index.md", i), fmt.Sprintf(`
1250 ---
1251 title: "Page"
1252 weight: %d
1253 ---
1254
1255 `, i))
1256 b.WithSourceFile(fmt.Sprintf("content/bundle%d/data.yaml", i), fmt.Sprintf(`data: v%d`, i))
1257 b.WithSourceFile(fmt.Sprintf("content/bundle%d/data.json", i), fmt.Sprintf(`{ "data": "v%d" }`, i))
1258 b.WithSourceFile(fmt.Sprintf("assets/data%d/data.yaml", i), fmt.Sprintf(`vdata: v%d`, i))
1259
1260 }
1261
1262 b.WithTemplatesAdded("_default/single.html", `
1263 {{ $bundleYaml := .Resources.GetMatch "*.yaml" }}
1264 {{ $bundleJSON := .Resources.GetMatch "*.json" }}
1265 {{ $assetsYaml := resources.GetMatch (printf "data%d/*.yaml" .Weight) }}
1266 {{ $data1 := $bundleYaml | transform.Unmarshal }}
1267 {{ $data2 := $assetsYaml | transform.Unmarshal }}
1268 {{ $bundleFingerprinted := $bundleYaml | fingerprint "md5" }}
1269 {{ $assetsFingerprinted := $assetsYaml | fingerprint "md5" }}
1270 {{ $jsonMin := $bundleJSON | minify }}
1271 {{ $jsonMinMin := $jsonMin | minify }}
1272 {{ $jsonMinMinMin := $jsonMinMin | minify }}
1273
1274 data content unmarshaled: {{ $data1.data }}
1275 data assets content unmarshaled: {{ $data2.vdata }}
1276 bundle fingerprinted: {{ $bundleFingerprinted.RelPermalink }}
1277 assets fingerprinted: {{ $assetsFingerprinted.RelPermalink }}
1278
1279 bundle min min min: {{ $jsonMinMinMin.RelPermalink }}
1280 bundle min min key: {{ $jsonMinMin.Key }}
1281
1282 `)
1283
1284 for i := 0; i < 3; i++ {
1285
1286 b.Build(BuildCfg{})
1287
1288 for i := 1; i <= 50; i++ {
1289 index := fmt.Sprintf("public/bundle%d/index.html", i)
1290 b.AssertFileContent(fmt.Sprintf("public/bundle%d/data.yaml", i), fmt.Sprintf("data: v%d", i))
1291 b.AssertFileContent(index, fmt.Sprintf("data content unmarshaled: v%d", i))
1292 b.AssertFileContent(index, fmt.Sprintf("data assets content unmarshaled: v%d", i))
1293
1294 md5Asset := helpers.MD5String(fmt.Sprintf(`vdata: v%d`, i))
1295 b.AssertFileContent(index, fmt.Sprintf("assets fingerprinted: /data%d/data.%s.yaml", i, md5Asset))
1296
1297 // The original is not used, make sure it's not published.
1298 b.Assert(b.CheckExists(fmt.Sprintf("public/data%d/data.yaml", i)), qt.Equals, false)
1299
1300 md5Bundle := helpers.MD5String(fmt.Sprintf(`data: v%d`, i))
1301 b.AssertFileContent(index, fmt.Sprintf("bundle fingerprinted: /bundle%d/data.%s.yaml", i, md5Bundle))
1302
1303 b.AssertFileContent(index,
1304 fmt.Sprintf("bundle min min min: /bundle%d/data.min.min.min.json", i),
1305 fmt.Sprintf("bundle min min key: /bundle%d/data.min.min.json", i),
1306 )
1307 b.Assert(b.CheckExists(fmt.Sprintf("public/bundle%d/data.min.min.min.json", i)), qt.Equals, true)
1308 b.Assert(b.CheckExists(fmt.Sprintf("public/bundle%d/data.min.json", i)), qt.Equals, false)
1309 b.Assert(b.CheckExists(fmt.Sprintf("public/bundle%d/data.min.min.json", i)), qt.Equals, false)
1310
1311 }
1312
1313 b.EditFiles("assets/data/foo.yaml", "FOO")
1314
1315 }
1316 }
1317
1318 func TestPageBundlerHome(t *testing.T) {
1319 t.Parallel()
1320 c := qt.New(t)
1321
1322 workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-bundler-home")
1323 c.Assert(err, qt.IsNil)
1324
1325 cfg := config.NewWithTestDefaults()
1326 cfg.Set("workingDir", workDir)
1327 fs := hugofs.NewFrom(hugofs.Os, cfg)
1328
1329 os.MkdirAll(filepath.Join(workDir, "content"), 0777)
1330
1331 defer clean()
1332
1333 b := newTestSitesBuilder(t)
1334 b.Fs = fs
1335
1336 b.WithWorkingDir(workDir).WithViper(cfg)
1337
1338 b.WithContent("_index.md", "---\ntitle: Home\n---\n")
1339 b.WithSourceFile("content/data.json", "DATA")
1340
1341 b.WithTemplates("index.html", `Title: {{ .Title }}|First Resource: {{ index .Resources 0 }}|Content: {{ .Content }}`)
1342 b.WithTemplates("_default/_markup/render-image.html", `Hook Len Page Resources {{ len .Page.Resources }}`)
1343
1344 b.Build(BuildCfg{})
1345 b.AssertFileContent("public/index.html", `
1346 Title: Home|First Resource: data.json|Content: <p>Hook Len Page Resources 1</p>
1347 `)
1348 }