hugo_sites_build_test.go (39770B)
1 package hugolib 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "strings" 7 "testing" 8 "time" 9 10 qt "github.com/frankban/quicktest" 11 "github.com/gohugoio/hugo/htesting" 12 "github.com/gohugoio/hugo/resources/page" 13 14 "github.com/fortytw2/leaktest" 15 "github.com/fsnotify/fsnotify" 16 "github.com/gohugoio/hugo/helpers" 17 "github.com/gohugoio/hugo/hugofs" 18 "github.com/spf13/afero" 19 ) 20 21 func TestMultiSitesMainLangInRoot(t *testing.T) { 22 t.Parallel() 23 for _, b := range []bool{false} { 24 doTestMultiSitesMainLangInRoot(t, b) 25 } 26 } 27 28 func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) { 29 c := qt.New(t) 30 31 siteConfig := map[string]any{ 32 "DefaultContentLanguage": "fr", 33 "DefaultContentLanguageInSubdir": defaultInSubDir, 34 } 35 36 b := newMultiSiteTestBuilder(t, "toml", multiSiteTOMLConfigTemplate, siteConfig) 37 38 pathMod := func(s string) string { 39 return s 40 } 41 42 if !defaultInSubDir { 43 pathMod = func(s string) string { 44 return strings.Replace(s, "/fr/", "/", -1) 45 } 46 } 47 48 b.CreateSites() 49 b.Build(BuildCfg{}) 50 51 sites := b.H.Sites 52 c.Assert(len(sites), qt.Equals, 4) 53 54 enSite := sites[0] 55 frSite := sites[1] 56 57 c.Assert(enSite.Info.LanguagePrefix, qt.Equals, "/en") 58 59 if defaultInSubDir { 60 c.Assert(frSite.Info.LanguagePrefix, qt.Equals, "/fr") 61 } else { 62 c.Assert(frSite.Info.LanguagePrefix, qt.Equals, "") 63 } 64 65 c.Assert(enSite.PathSpec.RelURL("foo", true), qt.Equals, "/blog/en/foo") 66 67 doc1en := enSite.RegularPages()[0] 68 doc1fr := frSite.RegularPages()[0] 69 70 enPerm := doc1en.Permalink() 71 enRelPerm := doc1en.RelPermalink() 72 c.Assert(enPerm, qt.Equals, "http://example.com/blog/en/sect/doc1-slug/") 73 c.Assert(enRelPerm, qt.Equals, "/blog/en/sect/doc1-slug/") 74 75 frPerm := doc1fr.Permalink() 76 frRelPerm := doc1fr.RelPermalink() 77 78 b.AssertFileContent(pathMod("public/fr/sect/doc1/index.html"), "Single", "Bonjour") 79 b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Hello") 80 81 if defaultInSubDir { 82 c.Assert(frPerm, qt.Equals, "http://example.com/blog/fr/sect/doc1/") 83 c.Assert(frRelPerm, qt.Equals, "/blog/fr/sect/doc1/") 84 85 // should have a redirect on top level. 86 b.AssertFileContent("public/index.html", `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr">`) 87 } else { 88 // Main language in root 89 c.Assert(frPerm, qt.Equals, "http://example.com/blog/sect/doc1/") 90 c.Assert(frRelPerm, qt.Equals, "/blog/sect/doc1/") 91 92 // should have redirect back to root 93 b.AssertFileContent("public/fr/index.html", `<meta http-equiv="refresh" content="0; url=http://example.com/blog">`) 94 } 95 b.AssertFileContent(pathMod("public/fr/index.html"), "Home", "Bonjour") 96 b.AssertFileContent("public/en/index.html", "Home", "Hello") 97 98 // Check list pages 99 b.AssertFileContent(pathMod("public/fr/sect/index.html"), "List", "Bonjour") 100 b.AssertFileContent("public/en/sect/index.html", "List", "Hello") 101 b.AssertFileContent(pathMod("public/fr/plaques/FRtag1/index.html"), "Taxonomy List", "Bonjour") 102 b.AssertFileContent("public/en/tags/tag1/index.html", "Taxonomy List", "Hello") 103 104 // Check sitemaps 105 // Sitemaps behaves different: In a multilanguage setup there will always be a index file and 106 // one sitemap in each lang folder. 107 b.AssertFileContent("public/sitemap.xml", 108 "<loc>http://example.com/blog/en/sitemap.xml</loc>", 109 "<loc>http://example.com/blog/fr/sitemap.xml</loc>") 110 111 if defaultInSubDir { 112 b.AssertFileContent("public/fr/sitemap.xml", "<loc>http://example.com/blog/fr/</loc>") 113 } else { 114 b.AssertFileContent("public/fr/sitemap.xml", "<loc>http://example.com/blog/</loc>") 115 } 116 b.AssertFileContent("public/en/sitemap.xml", "<loc>http://example.com/blog/en/</loc>") 117 118 // Check rss 119 b.AssertFileContent(pathMod("public/fr/index.xml"), pathMod(`<atom:link href="http://example.com/blog/fr/index.xml"`), 120 `rel="self" type="application/rss+xml"`) 121 b.AssertFileContent("public/en/index.xml", `<atom:link href="http://example.com/blog/en/index.xml"`) 122 b.AssertFileContent( 123 pathMod("public/fr/sect/index.xml"), 124 pathMod(`<atom:link href="http://example.com/blog/fr/sect/index.xml"`)) 125 b.AssertFileContent("public/en/sect/index.xml", `<atom:link href="http://example.com/blog/en/sect/index.xml"`) 126 b.AssertFileContent( 127 pathMod("public/fr/plaques/FRtag1/index.xml"), 128 pathMod(`<atom:link href="http://example.com/blog/fr/plaques/FRtag1/index.xml"`)) 129 b.AssertFileContent("public/en/tags/tag1/index.xml", `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`) 130 131 // Check paginators 132 b.AssertFileContent(pathMod("public/fr/page/1/index.html"), pathMod(`refresh" content="0; url=http://example.com/blog/fr/"`)) 133 b.AssertFileContent("public/en/page/1/index.html", `refresh" content="0; url=http://example.com/blog/en/"`) 134 b.AssertFileContent(pathMod("public/fr/page/2/index.html"), "Home Page 2", "Bonjour", pathMod("http://example.com/blog/fr/")) 135 b.AssertFileContent("public/en/page/2/index.html", "Home Page 2", "Hello", "http://example.com/blog/en/") 136 b.AssertFileContent(pathMod("public/fr/sect/page/1/index.html"), pathMod(`refresh" content="0; url=http://example.com/blog/fr/sect/"`)) 137 b.AssertFileContent("public/en/sect/page/1/index.html", `refresh" content="0; url=http://example.com/blog/en/sect/"`) 138 b.AssertFileContent(pathMod("public/fr/sect/page/2/index.html"), "List Page 2", "Bonjour", pathMod("http://example.com/blog/fr/sect/")) 139 b.AssertFileContent("public/en/sect/page/2/index.html", "List Page 2", "Hello", "http://example.com/blog/en/sect/") 140 b.AssertFileContent( 141 pathMod("public/fr/plaques/FRtag1/page/1/index.html"), 142 pathMod(`refresh" content="0; url=http://example.com/blog/fr/plaques/FRtag1/"`)) 143 b.AssertFileContent("public/en/tags/tag1/page/1/index.html", `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`) 144 b.AssertFileContent( 145 pathMod("public/fr/plaques/FRtag1/page/2/index.html"), "List Page 2", "Bonjour", 146 pathMod("http://example.com/blog/fr/plaques/FRtag1/")) 147 b.AssertFileContent("public/en/tags/tag1/page/2/index.html", "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/") 148 // nn (Nynorsk) and nb (Bokmål) have custom pagePath: side ("page" in Norwegian) 149 b.AssertFileContent("public/nn/side/1/index.html", `refresh" content="0; url=http://example.com/blog/nn/"`) 150 b.AssertFileContent("public/nb/side/1/index.html", `refresh" content="0; url=http://example.com/blog/nb/"`) 151 } 152 153 func TestMultiSitesWithTwoLanguages(t *testing.T) { 154 t.Parallel() 155 156 c := qt.New(t) 157 b := newTestSitesBuilder(t).WithConfigFile("toml", ` 158 159 defaultContentLanguage = "nn" 160 161 [languages] 162 [languages.nn] 163 languageName = "Nynorsk" 164 weight = 1 165 title = "Tittel på Nynorsk" 166 [languages.nn.params] 167 p1 = "p1nn" 168 169 [languages.en] 170 title = "Title in English" 171 languageName = "English" 172 weight = 2 173 [languages.en.params] 174 p1 = "p1en" 175 `) 176 177 b.CreateSites() 178 b.Build(BuildCfg{SkipRender: true}) 179 sites := b.H.Sites 180 181 c.Assert(len(sites), qt.Equals, 2) 182 183 nnSite := sites[0] 184 nnHome := nnSite.getPage(page.KindHome) 185 c.Assert(len(nnHome.AllTranslations()), qt.Equals, 2) 186 c.Assert(len(nnHome.Translations()), qt.Equals, 1) 187 c.Assert(nnHome.IsTranslated(), qt.Equals, true) 188 189 enHome := sites[1].getPage(page.KindHome) 190 191 p1, err := enHome.Param("p1") 192 c.Assert(err, qt.IsNil) 193 c.Assert(p1, qt.Equals, "p1en") 194 195 p1, err = nnHome.Param("p1") 196 c.Assert(err, qt.IsNil) 197 c.Assert(p1, qt.Equals, "p1nn") 198 } 199 200 func TestMultiSitesBuild(t *testing.T) { 201 for _, config := range []struct { 202 content string 203 suffix string 204 }{ 205 {multiSiteTOMLConfigTemplate, "toml"}, 206 {multiSiteYAMLConfigTemplate, "yml"}, 207 {multiSiteJSONConfigTemplate, "json"}, 208 } { 209 t.Run(config.suffix, func(t *testing.T) { 210 t.Parallel() 211 doTestMultiSitesBuild(t, config.content, config.suffix) 212 }) 213 } 214 } 215 216 func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) { 217 c := qt.New(t) 218 219 b := newMultiSiteTestBuilder(t, configSuffix, configTemplate, nil) 220 b.CreateSites() 221 222 sites := b.H.Sites 223 c.Assert(len(sites), qt.Equals, 4) 224 225 b.Build(BuildCfg{}) 226 227 // Check site config 228 for _, s := range sites { 229 c.Assert(s.Info.defaultContentLanguageInSubdir, qt.Equals, true) 230 c.Assert(s.disabledKinds, qt.Not(qt.IsNil)) 231 } 232 233 gp1 := b.H.GetContentPage(filepath.FromSlash("content/sect/doc1.en.md")) 234 c.Assert(gp1, qt.Not(qt.IsNil)) 235 c.Assert(gp1.Title(), qt.Equals, "doc1") 236 gp2 := b.H.GetContentPage(filepath.FromSlash("content/dummysect/notfound.md")) 237 c.Assert(gp2, qt.IsNil) 238 239 enSite := sites[0] 240 enSiteHome := enSite.getPage(page.KindHome) 241 c.Assert(enSiteHome.IsTranslated(), qt.Equals, true) 242 243 c.Assert(enSite.language.Lang, qt.Equals, "en") 244 245 // dumpPages(enSite.RegularPages()...) 246 247 c.Assert(len(enSite.RegularPages()), qt.Equals, 5) 248 c.Assert(len(enSite.AllPages()), qt.Equals, 32) 249 250 // Check 404s 251 b.AssertFileContent("public/en/404.html", "404|en|404 Page not found") 252 b.AssertFileContent("public/fr/404.html", "404|fr|404 Page not found") 253 254 // Check robots.txt 255 // the domain root is the public directory, so the robots.txt has to be created there and not in the language directories 256 b.AssertFileContent("public/robots.txt", "robots") 257 b.AssertFileDoesNotExist("public/en/robots.txt") 258 b.AssertFileDoesNotExist("public/nn/robots.txt") 259 260 b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Permalink: http://example.com/blog/en/sect/doc1-slug/") 261 b.AssertFileContent("public/en/sect/doc2/index.html", "Permalink: http://example.com/blog/en/sect/doc2/") 262 b.AssertFileContent("public/superbob/index.html", "Permalink: http://example.com/blog/superbob/") 263 264 doc2 := enSite.RegularPages()[1] 265 doc3 := enSite.RegularPages()[2] 266 c.Assert(doc3, qt.Equals, doc2.Prev()) 267 doc1en := enSite.RegularPages()[0] 268 doc1fr := doc1en.Translations()[0] 269 b.AssertFileContent("public/fr/sect/doc1/index.html", "Permalink: http://example.com/blog/fr/sect/doc1/") 270 271 c.Assert(doc1fr, qt.Equals, doc1en.Translations()[0]) 272 c.Assert(doc1en, qt.Equals, doc1fr.Translations()[0]) 273 c.Assert(doc1fr.Language().Lang, qt.Equals, "fr") 274 275 doc4 := enSite.AllPages()[4] 276 c.Assert(len(doc4.Translations()), qt.Equals, 0) 277 278 // Taxonomies and their URLs 279 c.Assert(len(enSite.Taxonomies()), qt.Equals, 1) 280 tags := enSite.Taxonomies()["tags"] 281 c.Assert(len(tags), qt.Equals, 2) 282 c.Assert(doc1en, qt.Equals, tags["tag1"][0].Page) 283 284 frSite := sites[1] 285 286 c.Assert(frSite.language.Lang, qt.Equals, "fr") 287 c.Assert(len(frSite.RegularPages()), qt.Equals, 4) 288 c.Assert(len(frSite.AllPages()), qt.Equals, 32) 289 290 for _, frenchPage := range frSite.RegularPages() { 291 p := frenchPage 292 c.Assert(p.Language().Lang, qt.Equals, "fr") 293 } 294 295 // See https://github.com/gohugoio/hugo/issues/4285 296 // Before Hugo 0.33 you had to be explicit with the content path to get the correct Page, which 297 // isn't ideal in a multilingual setup. You want a way to get the current language version if available. 298 // Now you can do lookups with translation base name to get that behaviour. 299 // Let us test all the regular page variants: 300 getPageDoc1En := enSite.getPage(page.KindPage, filepath.ToSlash(doc1en.File().Path())) 301 getPageDoc1EnBase := enSite.getPage(page.KindPage, "sect/doc1") 302 getPageDoc1Fr := frSite.getPage(page.KindPage, filepath.ToSlash(doc1fr.File().Path())) 303 getPageDoc1FrBase := frSite.getPage(page.KindPage, "sect/doc1") 304 c.Assert(getPageDoc1En, qt.Equals, doc1en) 305 c.Assert(getPageDoc1Fr, qt.Equals, doc1fr) 306 c.Assert(getPageDoc1EnBase, qt.Equals, doc1en) 307 c.Assert(getPageDoc1FrBase, qt.Equals, doc1fr) 308 309 // Check redirect to main language, French 310 b.AssertFileContent("public/index.html", "0; url=http://example.com/blog/fr") 311 312 // check home page content (including data files rendering) 313 b.AssertFileContent("public/en/index.html", "Default Home Page 1", "Hello", "Hugo Rocks!") 314 b.AssertFileContent("public/fr/index.html", "French Home Page 1", "Bonjour", "Hugo Rocks!") 315 316 // check single page content 317 b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Shortcode: Bonjour", "LingoFrench") 318 b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Shortcode: Hello", "LingoDefault") 319 320 // Check node translations 321 homeEn := enSite.getPage(page.KindHome) 322 c.Assert(homeEn, qt.Not(qt.IsNil)) 323 c.Assert(len(homeEn.Translations()), qt.Equals, 3) 324 c.Assert(homeEn.Translations()[0].Language().Lang, qt.Equals, "fr") 325 c.Assert(homeEn.Translations()[1].Language().Lang, qt.Equals, "nn") 326 c.Assert(homeEn.Translations()[1].Title(), qt.Equals, "På nynorsk") 327 c.Assert(homeEn.Translations()[2].Language().Lang, qt.Equals, "nb") 328 c.Assert(homeEn.Translations()[2].Title(), qt.Equals, "På bokmål") 329 c.Assert(homeEn.Translations()[2].Language().LanguageName, qt.Equals, "Bokmål") 330 331 sectFr := frSite.getPage(page.KindSection, "sect") 332 c.Assert(sectFr, qt.Not(qt.IsNil)) 333 334 c.Assert(sectFr.Language().Lang, qt.Equals, "fr") 335 c.Assert(len(sectFr.Translations()), qt.Equals, 1) 336 c.Assert(sectFr.Translations()[0].Language().Lang, qt.Equals, "en") 337 c.Assert(sectFr.Translations()[0].Title(), qt.Equals, "Sects") 338 339 nnSite := sites[2] 340 c.Assert(nnSite.language.Lang, qt.Equals, "nn") 341 taxNn := nnSite.getPage(page.KindTaxonomy, "lag") 342 c.Assert(taxNn, qt.Not(qt.IsNil)) 343 c.Assert(len(taxNn.Translations()), qt.Equals, 1) 344 c.Assert(taxNn.Translations()[0].Language().Lang, qt.Equals, "nb") 345 346 taxTermNn := nnSite.getPage(page.KindTerm, "lag", "sogndal") 347 c.Assert(taxTermNn, qt.Not(qt.IsNil)) 348 c.Assert(nnSite.getPage(page.KindTerm, "LAG", "SOGNDAL"), qt.Equals, taxTermNn) 349 c.Assert(len(taxTermNn.Translations()), qt.Equals, 1) 350 c.Assert(taxTermNn.Translations()[0].Language().Lang, qt.Equals, "nb") 351 352 // Check sitemap(s) 353 b.AssertFileContent("public/sitemap.xml", 354 "<loc>http://example.com/blog/en/sitemap.xml</loc>", 355 "<loc>http://example.com/blog/fr/sitemap.xml</loc>") 356 b.AssertFileContent("public/en/sitemap.xml", "http://example.com/blog/en/sect/doc2/") 357 b.AssertFileContent("public/fr/sitemap.xml", "http://example.com/blog/fr/sect/doc1/") 358 359 // Check taxonomies 360 enTags := enSite.Taxonomies()["tags"] 361 frTags := frSite.Taxonomies()["plaques"] 362 c.Assert(len(enTags), qt.Equals, 2, qt.Commentf("Tags in en: %v", enTags)) 363 c.Assert(len(frTags), qt.Equals, 2, qt.Commentf("Tags in fr: %v", frTags)) 364 c.Assert(enTags["tag1"], qt.Not(qt.IsNil)) 365 c.Assert(frTags["FRtag1"], qt.Not(qt.IsNil)) 366 b.AssertFileContent("public/fr/plaques/FRtag1/index.html", "FRtag1|Bonjour|http://example.com/blog/fr/plaques/FRtag1/") 367 368 // en and nn have custom site menus 369 c.Assert(len(frSite.Menus()), qt.Equals, 0) 370 c.Assert(len(enSite.Menus()), qt.Equals, 1) 371 c.Assert(len(nnSite.Menus()), qt.Equals, 1) 372 373 c.Assert(enSite.Menus()["main"].ByName()[0].Name, qt.Equals, "Home") 374 c.Assert(nnSite.Menus()["main"].ByName()[0].Name, qt.Equals, "Heim") 375 376 // Issue #3108 377 prevPage := enSite.RegularPages()[0].Prev() 378 c.Assert(prevPage, qt.Not(qt.IsNil)) 379 c.Assert(prevPage.Kind(), qt.Equals, page.KindPage) 380 381 for { 382 if prevPage == nil { 383 break 384 } 385 c.Assert(prevPage.Kind(), qt.Equals, page.KindPage) 386 prevPage = prevPage.Prev() 387 } 388 389 // Check bundles 390 b.AssertFileContent("public/fr/bundles/b1/index.html", "RelPermalink: /blog/fr/bundles/b1/|") 391 bundleFr := frSite.getPage(page.KindPage, "bundles/b1/index.md") 392 c.Assert(bundleFr, qt.Not(qt.IsNil)) 393 c.Assert(len(bundleFr.Resources()), qt.Equals, 1) 394 logoFr := bundleFr.Resources().GetMatch("logo*") 395 logoFrGet := bundleFr.Resources().Get("logo.png") 396 c.Assert(logoFrGet, qt.Equals, logoFr) 397 c.Assert(logoFr, qt.Not(qt.IsNil)) 398 b.AssertFileContent("public/fr/bundles/b1/index.html", "Resources: image/png: /blog/fr/bundles/b1/logo.png") 399 b.AssertFileContent("public/fr/bundles/b1/logo.png", "PNG Data") 400 401 bundleEn := enSite.getPage(page.KindPage, "bundles/b1/index.en.md") 402 c.Assert(bundleEn, qt.Not(qt.IsNil)) 403 b.AssertFileContent("public/en/bundles/b1/index.html", "RelPermalink: /blog/en/bundles/b1/|") 404 c.Assert(len(bundleEn.Resources()), qt.Equals, 1) 405 logoEn := bundleEn.Resources().GetMatch("logo*") 406 c.Assert(logoEn, qt.Not(qt.IsNil)) 407 b.AssertFileContent("public/en/bundles/b1/index.html", "Resources: image/png: /blog/en/bundles/b1/logo.png") 408 b.AssertFileContent("public/en/bundles/b1/logo.png", "PNG Data") 409 } 410 411 func TestMultiSitesRebuild(t *testing.T) { 412 // t.Parallel() not supported, see https://github.com/fortytw2/leaktest/issues/4 413 // This leaktest seems to be a little bit shaky on Travis. 414 if !htesting.IsCI() { 415 defer leaktest.CheckTimeout(t, 10*time.Second)() 416 } 417 418 c := qt.New(t) 419 420 b := newMultiSiteTestDefaultBuilder(t).Running().CreateSites().Build(BuildCfg{}) 421 422 sites := b.H.Sites 423 fs := b.Fs 424 425 b.AssertFileContent("public/en/sect/doc2/index.html", "Single: doc2|Hello|en|", "\n\n<h1 id=\"doc2\">doc2</h1>\n\n<p><em>some content</em>") 426 427 enSite := sites[0] 428 frSite := sites[1] 429 430 c.Assert(len(enSite.RegularPages()), qt.Equals, 5) 431 c.Assert(len(frSite.RegularPages()), qt.Equals, 4) 432 433 // Verify translations 434 b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Hello") 435 b.AssertFileContent("public/fr/sect/doc1/index.html", "Bonjour") 436 437 // check single page content 438 b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Shortcode: Bonjour") 439 b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Shortcode: Hello") 440 441 homeEn := enSite.getPage(page.KindHome) 442 c.Assert(homeEn, qt.Not(qt.IsNil)) 443 c.Assert(len(homeEn.Translations()), qt.Equals, 3) 444 445 contentFs := b.H.Fs.Source 446 447 for i, this := range []struct { 448 preFunc func(t *testing.T) 449 events []fsnotify.Event 450 assertFunc func(t *testing.T) 451 }{ 452 // * Remove doc 453 // * Add docs existing languages 454 // (Add doc new language: TODO(bep) we should load config.toml as part of these so we can add languages). 455 // * Rename file 456 // * Change doc 457 // * Change a template 458 // * Change language file 459 { 460 func(t *testing.T) { 461 fs.Source.Remove("content/sect/doc2.en.md") 462 }, 463 []fsnotify.Event{{Name: filepath.FromSlash("content/sect/doc2.en.md"), Op: fsnotify.Remove}}, 464 func(t *testing.T) { 465 c.Assert(len(enSite.RegularPages()), qt.Equals, 4, qt.Commentf("1 en removed")) 466 }, 467 }, 468 { 469 func(t *testing.T) { 470 writeNewContentFile(t, contentFs, "new_en_1", "2016-07-31", "content/new1.en.md", -5) 471 writeNewContentFile(t, contentFs, "new_en_2", "1989-07-30", "content/new2.en.md", -10) 472 writeNewContentFile(t, contentFs, "new_fr_1", "2016-07-30", "content/new1.fr.md", 10) 473 }, 474 []fsnotify.Event{ 475 {Name: filepath.FromSlash("content/new1.en.md"), Op: fsnotify.Create}, 476 {Name: filepath.FromSlash("content/new2.en.md"), Op: fsnotify.Create}, 477 {Name: filepath.FromSlash("content/new1.fr.md"), Op: fsnotify.Create}, 478 }, 479 func(t *testing.T) { 480 c.Assert(len(enSite.RegularPages()), qt.Equals, 6) 481 c.Assert(len(enSite.AllPages()), qt.Equals, 34) 482 c.Assert(len(frSite.RegularPages()), qt.Equals, 5) 483 c.Assert(frSite.RegularPages()[3].Title(), qt.Equals, "new_fr_1") 484 c.Assert(enSite.RegularPages()[0].Title(), qt.Equals, "new_en_2") 485 c.Assert(enSite.RegularPages()[1].Title(), qt.Equals, "new_en_1") 486 487 rendered := readWorkingDir(t, fs, "public/en/new1/index.html") 488 c.Assert(strings.Contains(rendered, "new_en_1"), qt.Equals, true) 489 }, 490 }, 491 { 492 func(t *testing.T) { 493 p := "content/sect/doc1.en.md" 494 doc1 := readFileFromFs(t, contentFs, p) 495 doc1 += "CHANGED" 496 writeToFs(t, contentFs, p, doc1) 497 }, 498 []fsnotify.Event{{Name: filepath.FromSlash("content/sect/doc1.en.md"), Op: fsnotify.Write}}, 499 func(t *testing.T) { 500 c.Assert(len(enSite.RegularPages()), qt.Equals, 6) 501 doc1 := readWorkingDir(t, fs, "public/en/sect/doc1-slug/index.html") 502 c.Assert(strings.Contains(doc1, "CHANGED"), qt.Equals, true) 503 }, 504 }, 505 // Rename a file 506 { 507 func(t *testing.T) { 508 if err := contentFs.Rename("content/new1.en.md", "content/new1renamed.en.md"); err != nil { 509 t.Fatalf("Rename failed: %s", err) 510 } 511 }, 512 []fsnotify.Event{ 513 {Name: filepath.FromSlash("content/new1renamed.en.md"), Op: fsnotify.Rename}, 514 {Name: filepath.FromSlash("content/new1.en.md"), Op: fsnotify.Rename}, 515 }, 516 func(t *testing.T) { 517 c.Assert(len(enSite.RegularPages()), qt.Equals, 6, qt.Commentf("Rename")) 518 c.Assert(enSite.RegularPages()[1].Title(), qt.Equals, "new_en_1") 519 rendered := readWorkingDir(t, fs, "public/en/new1renamed/index.html") 520 c.Assert(rendered, qt.Contains, "new_en_1") 521 }, 522 }, 523 { 524 // Change a template 525 func(t *testing.T) { 526 template := "layouts/_default/single.html" 527 templateContent := readSource(t, fs, template) 528 templateContent += "{{ print \"Template Changed\"}}" 529 writeSource(t, fs, template, templateContent) 530 }, 531 []fsnotify.Event{{Name: filepath.FromSlash("layouts/_default/single.html"), Op: fsnotify.Write}}, 532 func(t *testing.T) { 533 c.Assert(len(enSite.RegularPages()), qt.Equals, 6) 534 c.Assert(len(enSite.AllPages()), qt.Equals, 34) 535 c.Assert(len(frSite.RegularPages()), qt.Equals, 5) 536 doc1 := readWorkingDir(t, fs, "public/en/sect/doc1-slug/index.html") 537 c.Assert(strings.Contains(doc1, "Template Changed"), qt.Equals, true) 538 }, 539 }, 540 { 541 // Change a language file 542 func(t *testing.T) { 543 languageFile := "i18n/fr.yaml" 544 langContent := readSource(t, fs, languageFile) 545 langContent = strings.Replace(langContent, "Bonjour", "Salut", 1) 546 writeSource(t, fs, languageFile, langContent) 547 }, 548 []fsnotify.Event{{Name: filepath.FromSlash("i18n/fr.yaml"), Op: fsnotify.Write}}, 549 func(t *testing.T) { 550 c.Assert(len(enSite.RegularPages()), qt.Equals, 6) 551 c.Assert(len(enSite.AllPages()), qt.Equals, 34) 552 c.Assert(len(frSite.RegularPages()), qt.Equals, 5) 553 docEn := readWorkingDir(t, fs, "public/en/sect/doc1-slug/index.html") 554 c.Assert(strings.Contains(docEn, "Hello"), qt.Equals, true) 555 docFr := readWorkingDir(t, fs, "public/fr/sect/doc1/index.html") 556 c.Assert(strings.Contains(docFr, "Salut"), qt.Equals, true) 557 558 homeEn := enSite.getPage(page.KindHome) 559 c.Assert(homeEn, qt.Not(qt.IsNil)) 560 c.Assert(len(homeEn.Translations()), qt.Equals, 3) 561 c.Assert(homeEn.Translations()[0].Language().Lang, qt.Equals, "fr") 562 }, 563 }, 564 // Change a shortcode 565 { 566 func(t *testing.T) { 567 writeSource(t, fs, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}") 568 }, 569 []fsnotify.Event{ 570 {Name: filepath.FromSlash("layouts/shortcodes/shortcode.html"), Op: fsnotify.Write}, 571 }, 572 func(t *testing.T) { 573 c.Assert(len(enSite.RegularPages()), qt.Equals, 6) 574 c.Assert(len(enSite.AllPages()), qt.Equals, 34) 575 c.Assert(len(frSite.RegularPages()), qt.Equals, 5) 576 b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Modified Shortcode: Salut") 577 b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Modified Shortcode: Hello") 578 }, 579 }, 580 } { 581 582 if this.preFunc != nil { 583 this.preFunc(t) 584 } 585 586 err := b.H.Build(BuildCfg{}, this.events...) 587 if err != nil { 588 t.Fatalf("[%d] Failed to rebuild sites: %s", i, err) 589 } 590 591 this.assertFunc(t) 592 } 593 } 594 595 // https://github.com/gohugoio/hugo/issues/4706 596 func TestContentStressTest(t *testing.T) { 597 b := newTestSitesBuilder(t) 598 599 numPages := 500 600 601 contentTempl := ` 602 --- 603 %s 604 title: %q 605 weight: %d 606 multioutput: %t 607 --- 608 609 # Header 610 611 CONTENT 612 613 The End. 614 ` 615 616 contentTempl = strings.Replace(contentTempl, "CONTENT", strings.Repeat(` 617 618 ## Another header 619 620 Some text. Some more text. 621 622 `, 100), -1) 623 624 var content []string 625 defaultOutputs := `outputs: ["html", "json", "rss" ]` 626 627 for i := 1; i <= numPages; i++ { 628 outputs := defaultOutputs 629 multioutput := true 630 if i%3 == 0 { 631 outputs = `outputs: ["json"]` 632 multioutput = false 633 } 634 section := "s1" 635 if i%10 == 0 { 636 section = "s2" 637 } 638 content = append(content, []string{fmt.Sprintf("%s/page%d.md", section, i), fmt.Sprintf(contentTempl, outputs, fmt.Sprintf("Title %d", i), i, multioutput)}...) 639 } 640 641 content = append(content, []string{"_index.md", fmt.Sprintf(contentTempl, defaultOutputs, fmt.Sprintf("Home %d", 0), 0, true)}...) 642 content = append(content, []string{"s1/_index.md", fmt.Sprintf(contentTempl, defaultOutputs, fmt.Sprintf("S %d", 1), 1, true)}...) 643 content = append(content, []string{"s2/_index.md", fmt.Sprintf(contentTempl, defaultOutputs, fmt.Sprintf("S %d", 2), 2, true)}...) 644 645 b.WithSimpleConfigFile() 646 b.WithTemplates("layouts/_default/single.html", `Single: {{ .Content }}|RelPermalink: {{ .RelPermalink }}|Permalink: {{ .Permalink }}`) 647 b.WithTemplates("layouts/_default/myview.html", `View: {{ len .Content }}`) 648 b.WithTemplates("layouts/_default/single.json", `Single JSON: {{ .Content }}|RelPermalink: {{ .RelPermalink }}|Permalink: {{ .Permalink }}`) 649 b.WithTemplates("layouts/_default/list.html", ` 650 Page: {{ .Paginator.PageNumber }} 651 P: {{ with .File }}{{ path.Join .Path }}{{ end }} 652 List: {{ len .Paginator.Pages }}|List Content: {{ len .Content }} 653 {{ $shuffled := where .Site.RegularPages "Params.multioutput" true | shuffle }} 654 {{ $first5 := $shuffled | first 5 }} 655 L1: {{ len .Site.RegularPages }} L2: {{ len $first5 }} 656 {{ range $i, $e := $first5 }} 657 Render {{ $i }}: {{ .Render "myview" }} 658 {{ end }} 659 END 660 `) 661 662 b.WithContent(content...) 663 664 b.CreateSites().Build(BuildCfg{}) 665 666 contentMatchers := []string{"<h2 id=\"another-header\">Another header</h2>", "<h2 id=\"another-header-99\">Another header</h2>", "<p>The End.</p>"} 667 668 for i := 1; i <= numPages; i++ { 669 if i%3 != 0 { 670 section := "s1" 671 if i%10 == 0 { 672 section = "s2" 673 } 674 checkContent(b, fmt.Sprintf("public/%s/page%d/index.html", section, i), contentMatchers...) 675 } 676 } 677 678 for i := 1; i <= numPages; i++ { 679 section := "s1" 680 if i%10 == 0 { 681 section = "s2" 682 } 683 checkContent(b, fmt.Sprintf("public/%s/page%d/index.json", section, i), contentMatchers...) 684 } 685 686 checkContent(b, "public/s1/index.html", "P: s1/_index.md\nList: 10|List Content: 8132\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8132\n\nRender 1: View: 8132\n\nRender 2: View: 8132\n\nRender 3: View: 8132\n\nRender 4: View: 8132\n\nEND\n") 687 checkContent(b, "public/s2/index.html", "P: s2/_index.md\nList: 10|List Content: 8132", "Render 4: View: 8132\n\nEND") 688 checkContent(b, "public/index.html", "P: _index.md\nList: 10|List Content: 8132", "4: View: 8132\n\nEND") 689 690 // Check paginated pages 691 for i := 2; i <= 9; i++ { 692 checkContent(b, fmt.Sprintf("public/page/%d/index.html", i), fmt.Sprintf("Page: %d", i), "Content: 8132\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8132", "Render 4: View: 8132\n\nEND") 693 } 694 } 695 696 func checkContent(s *sitesBuilder, filename string, matches ...string) { 697 s.T.Helper() 698 content := readWorkingDir(s.T, s.Fs, filename) 699 for _, match := range matches { 700 if !strings.Contains(content, match) { 701 s.Fatalf("No match for\n%q\nin content for %s\n%q\nDiff:\n%s", match, filename, content, htesting.DiffStrings(content, match)) 702 } 703 } 704 } 705 706 func TestTranslationsFromContentToNonContent(t *testing.T) { 707 b := newTestSitesBuilder(t) 708 b.WithConfigFile("toml", ` 709 710 baseURL = "http://example.com/" 711 712 defaultContentLanguage = "en" 713 714 [languages] 715 [languages.en] 716 weight = 10 717 contentDir = "content/en" 718 [languages.nn] 719 weight = 20 720 contentDir = "content/nn" 721 722 723 `) 724 725 b.WithContent("en/mysection/_index.md", ` 726 --- 727 Title: My Section 728 --- 729 730 `) 731 732 b.WithContent("en/_index.md", ` 733 --- 734 Title: My Home 735 --- 736 737 `) 738 739 b.WithContent("en/categories/mycat/_index.md", ` 740 --- 741 Title: My MyCat 742 --- 743 744 `) 745 746 b.WithContent("en/categories/_index.md", ` 747 --- 748 Title: My categories 749 --- 750 751 `) 752 753 for _, lang := range []string{"en", "nn"} { 754 b.WithContent(lang+"/mysection/page.md", ` 755 --- 756 Title: My Page 757 categories: ["mycat"] 758 --- 759 760 `) 761 } 762 763 b.Build(BuildCfg{}) 764 765 for _, path := range []string{ 766 "/", 767 "/mysection", 768 "/categories", 769 "/categories/mycat", 770 } { 771 t.Run(path, func(t *testing.T) { 772 c := qt.New(t) 773 774 s1, _ := b.H.Sites[0].getPageNew(nil, path) 775 s2, _ := b.H.Sites[1].getPageNew(nil, path) 776 777 c.Assert(s1, qt.Not(qt.IsNil)) 778 c.Assert(s2, qt.Not(qt.IsNil)) 779 780 c.Assert(len(s1.Translations()), qt.Equals, 1) 781 c.Assert(len(s2.Translations()), qt.Equals, 1) 782 c.Assert(s1.Translations()[0], qt.Equals, s2) 783 c.Assert(s2.Translations()[0], qt.Equals, s1) 784 785 m1 := s1.Translations().MergeByLanguage(s2.Translations()) 786 m2 := s2.Translations().MergeByLanguage(s1.Translations()) 787 788 c.Assert(len(m1), qt.Equals, 1) 789 c.Assert(len(m2), qt.Equals, 1) 790 }) 791 } 792 } 793 794 var tocShortcode = ` 795 TOC1: {{ .Page.TableOfContents }} 796 797 TOC2: {{ .Page.TableOfContents }} 798 ` 799 800 func TestSelfReferencedContentInShortcode(t *testing.T) { 801 t.Parallel() 802 803 b := newMultiSiteTestDefaultBuilder(t) 804 805 var ( 806 shortcode = `{{- .Page.Content -}}{{- .Page.Summary -}}{{- .Page.Plain -}}{{- .Page.PlainWords -}}{{- .Page.WordCount -}}{{- .Page.ReadingTime -}}` 807 808 page = `--- 809 title: sctest 810 --- 811 Empty:{{< mycontent >}}: 812 ` 813 ) 814 815 b.WithTemplatesAdded("layouts/shortcodes/mycontent.html", shortcode) 816 b.WithContent("post/simple.en.md", page) 817 818 b.CreateSites().Build(BuildCfg{}) 819 820 b.AssertFileContent("public/en/post/simple/index.html", "Empty:[]00:") 821 } 822 823 var tocPageSimple = `--- 824 title: tocTest 825 publishdate: "2000-01-01" 826 --- 827 {{< toc >}} 828 # Heading 1 {#1} 829 Some text. 830 ## Subheading 1.1 {#1-1} 831 Some more text. 832 # Heading 2 {#2} 833 Even more text. 834 ## Subheading 2.1 {#2-1} 835 Lorem ipsum... 836 ` 837 838 var tocPageVariants1 = `--- 839 title: tocTest 840 publishdate: "2000-01-01" 841 --- 842 Variant 1: 843 {{% wrapper %}} 844 {{< toc >}} 845 {{% /wrapper %}} 846 # Heading 1 847 848 Variant 3: 849 {{% toc %}} 850 851 ` 852 853 var tocPageVariants2 = `--- 854 title: tocTest 855 publishdate: "2000-01-01" 856 --- 857 Variant 1: 858 {{% wrapper %}} 859 {{< toc >}} 860 {{% /wrapper %}} 861 # Heading 1 862 863 Variant 2: 864 {{< wrapper >}} 865 {{< toc >}} 866 {{< /wrapper >}} 867 868 Variant 3: 869 {{% toc %}} 870 871 ` 872 873 var tocPageSimpleExpected = `<nav id="TableOfContents"> 874 <ul> 875 <li><a href="#1">Heading 1</a> 876 <ul> 877 <li><a href="#1-1">Subheading 1.1</a></li> 878 </ul></li> 879 <li><a href="#2">Heading 2</a> 880 <ul> 881 <li><a href="#2-1">Subheading 2.1</a></li> 882 </ul></li> 883 </ul> 884 </nav>` 885 886 var tocPageWithShortcodesInHeadings = `--- 887 title: tocTest 888 publishdate: "2000-01-01" 889 --- 890 891 {{< toc >}} 892 893 # Heading 1 {#1} 894 895 Some text. 896 897 ## Subheading 1.1 {{< shortcode >}} {#1-1} 898 899 Some more text. 900 901 # Heading 2 {{% shortcode %}} {#2} 902 903 Even more text. 904 905 ## Subheading 2.1 {#2-1} 906 907 Lorem ipsum... 908 ` 909 910 var tocPageWithShortcodesInHeadingsExpected = `<nav id="TableOfContents"> 911 <ul> 912 <li><a href="#1">Heading 1</a> 913 <ul> 914 <li><a href="#1-1">Subheading 1.1 Shortcode: Hello</a></li> 915 </ul></li> 916 <li><a href="#2">Heading 2 Shortcode: Hello</a> 917 <ul> 918 <li><a href="#2-1">Subheading 2.1</a></li> 919 </ul></li> 920 </ul> 921 </nav>` 922 923 var multiSiteTOMLConfigTemplate = ` 924 baseURL = "http://example.com/blog" 925 926 paginate = 1 927 disablePathToLower = true 928 defaultContentLanguage = "{{ .DefaultContentLanguage }}" 929 defaultContentLanguageInSubdir = {{ .DefaultContentLanguageInSubdir }} 930 enableRobotsTXT = true 931 932 [permalinks] 933 other = "/somewhere/else/:filename" 934 935 [Taxonomies] 936 tag = "tags" 937 938 [Languages] 939 [Languages.en] 940 weight = 10 941 title = "In English" 942 languageName = "English" 943 [[Languages.en.menu.main]] 944 url = "/" 945 name = "Home" 946 weight = 0 947 948 [Languages.fr] 949 weight = 20 950 title = "Le Français" 951 languageName = "Français" 952 [Languages.fr.Taxonomies] 953 plaque = "plaques" 954 955 [Languages.nn] 956 weight = 30 957 title = "På nynorsk" 958 languageName = "Nynorsk" 959 paginatePath = "side" 960 [Languages.nn.Taxonomies] 961 lag = "lag" 962 [[Languages.nn.menu.main]] 963 url = "/" 964 name = "Heim" 965 weight = 1 966 967 [Languages.nb] 968 weight = 40 969 title = "På bokmål" 970 languageName = "Bokmål" 971 paginatePath = "side" 972 [Languages.nb.Taxonomies] 973 lag = "lag" 974 ` 975 976 var multiSiteYAMLConfigTemplate = ` 977 baseURL: "http://example.com/blog" 978 979 disablePathToLower: true 980 paginate: 1 981 defaultContentLanguage: "{{ .DefaultContentLanguage }}" 982 defaultContentLanguageInSubdir: {{ .DefaultContentLanguageInSubdir }} 983 enableRobotsTXT: true 984 985 permalinks: 986 other: "/somewhere/else/:filename" 987 988 Taxonomies: 989 tag: "tags" 990 991 Languages: 992 en: 993 weight: 10 994 title: "In English" 995 languageName: "English" 996 menu: 997 main: 998 - url: "/" 999 name: "Home" 1000 weight: 0 1001 fr: 1002 weight: 20 1003 title: "Le Français" 1004 languageName: "Français" 1005 Taxonomies: 1006 plaque: "plaques" 1007 nn: 1008 weight: 30 1009 title: "På nynorsk" 1010 languageName: "Nynorsk" 1011 paginatePath: "side" 1012 Taxonomies: 1013 lag: "lag" 1014 menu: 1015 main: 1016 - url: "/" 1017 name: "Heim" 1018 weight: 1 1019 nb: 1020 weight: 40 1021 title: "På bokmål" 1022 languageName: "Bokmål" 1023 paginatePath: "side" 1024 Taxonomies: 1025 lag: "lag" 1026 1027 ` 1028 1029 // TODO(bep) clean move 1030 var multiSiteJSONConfigTemplate = ` 1031 { 1032 "baseURL": "http://example.com/blog", 1033 "paginate": 1, 1034 "disablePathToLower": true, 1035 "defaultContentLanguage": "{{ .DefaultContentLanguage }}", 1036 "defaultContentLanguageInSubdir": true, 1037 "enableRobotsTXT": true, 1038 "permalinks": { 1039 "other": "/somewhere/else/:filename" 1040 }, 1041 "Taxonomies": { 1042 "tag": "tags" 1043 }, 1044 "Languages": { 1045 "en": { 1046 "weight": 10, 1047 "title": "In English", 1048 "languageName": "English", 1049 "menu": { 1050 "main": [ 1051 { 1052 "url": "/", 1053 "name": "Home", 1054 "weight": 0 1055 } 1056 ] 1057 } 1058 }, 1059 "fr": { 1060 "weight": 20, 1061 "title": "Le Français", 1062 "languageName": "Français", 1063 "Taxonomies": { 1064 "plaque": "plaques" 1065 } 1066 }, 1067 "nn": { 1068 "weight": 30, 1069 "title": "På nynorsk", 1070 "paginatePath": "side", 1071 "languageName": "Nynorsk", 1072 "Taxonomies": { 1073 "lag": "lag" 1074 }, 1075 "menu": { 1076 "main": [ 1077 { 1078 "url": "/", 1079 "name": "Heim", 1080 "weight": 1 1081 } 1082 ] 1083 } 1084 }, 1085 "nb": { 1086 "weight": 40, 1087 "title": "På bokmål", 1088 "paginatePath": "side", 1089 "languageName": "Bokmål", 1090 "Taxonomies": { 1091 "lag": "lag" 1092 } 1093 } 1094 } 1095 } 1096 ` 1097 1098 func writeSource(t testing.TB, fs *hugofs.Fs, filename, content string) { 1099 t.Helper() 1100 writeToFs(t, fs.Source, filename, content) 1101 } 1102 1103 func writeToFs(t testing.TB, fs afero.Fs, filename, content string) { 1104 t.Helper() 1105 if err := afero.WriteFile(fs, filepath.FromSlash(filename), []byte(content), 0755); err != nil { 1106 t.Fatalf("Failed to write file: %s", err) 1107 } 1108 } 1109 1110 func readWorkingDir(t testing.TB, fs *hugofs.Fs, filename string) string { 1111 t.Helper() 1112 return readFileFromFs(t, fs.WorkingDirReadOnly, filename) 1113 } 1114 1115 func workingDirExists(fs *hugofs.Fs, filename string) bool { 1116 b, err := helpers.Exists(filename, fs.WorkingDirReadOnly) 1117 if err != nil { 1118 panic(err) 1119 } 1120 return b 1121 } 1122 1123 func readSource(t *testing.T, fs *hugofs.Fs, filename string) string { 1124 return readFileFromFs(t, fs.Source, filename) 1125 } 1126 1127 func readFileFromFs(t testing.TB, fs afero.Fs, filename string) string { 1128 t.Helper() 1129 filename = filepath.Clean(filename) 1130 b, err := afero.ReadFile(fs, filename) 1131 if err != nil { 1132 // Print some debug info 1133 hadSlash := strings.HasPrefix(filename, helpers.FilePathSeparator) 1134 start := 0 1135 if hadSlash { 1136 start = 1 1137 } 1138 end := start + 1 1139 1140 parts := strings.Split(filename, helpers.FilePathSeparator) 1141 if parts[start] == "work" { 1142 end++ 1143 } 1144 1145 /* 1146 root := filepath.Join(parts[start:end]...) 1147 if hadSlash { 1148 root = helpers.FilePathSeparator + root 1149 } 1150 1151 helpers.PrintFs(fs, root, os.Stdout) 1152 */ 1153 1154 t.Fatalf("Failed to read file: %s", err) 1155 } 1156 return string(b) 1157 } 1158 1159 const testPageTemplate = `--- 1160 title: "%s" 1161 publishdate: "%s" 1162 weight: %d 1163 --- 1164 # Doc %s 1165 ` 1166 1167 func newTestPage(title, date string, weight int) string { 1168 return fmt.Sprintf(testPageTemplate, title, date, weight, title) 1169 } 1170 1171 func writeNewContentFile(t *testing.T, fs afero.Fs, title, date, filename string, weight int) { 1172 content := newTestPage(title, date, weight) 1173 writeToFs(t, fs, filename, content) 1174 } 1175 1176 type multiSiteTestBuilder struct { 1177 configData any 1178 config string 1179 configFormat string 1180 1181 *sitesBuilder 1182 } 1183 1184 func newMultiSiteTestDefaultBuilder(t testing.TB) *multiSiteTestBuilder { 1185 return newMultiSiteTestBuilder(t, "", "", nil) 1186 } 1187 1188 func (b *multiSiteTestBuilder) WithNewConfig(config string) *multiSiteTestBuilder { 1189 b.WithConfigTemplate(b.configData, b.configFormat, config) 1190 return b 1191 } 1192 1193 func (b *multiSiteTestBuilder) WithNewConfigData(data any) *multiSiteTestBuilder { 1194 b.WithConfigTemplate(data, b.configFormat, b.config) 1195 return b 1196 } 1197 1198 func newMultiSiteTestBuilder(t testing.TB, configFormat, config string, configData any) *multiSiteTestBuilder { 1199 if configData == nil { 1200 configData = map[string]any{ 1201 "DefaultContentLanguage": "fr", 1202 "DefaultContentLanguageInSubdir": true, 1203 } 1204 } 1205 1206 if config == "" { 1207 config = multiSiteTOMLConfigTemplate 1208 } 1209 1210 if configFormat == "" { 1211 configFormat = "toml" 1212 } 1213 1214 b := newTestSitesBuilder(t).WithConfigTemplate(configData, configFormat, config) 1215 b.WithContent("root.en.md", `--- 1216 title: root 1217 weight: 10000 1218 slug: root 1219 publishdate: "2000-01-01" 1220 --- 1221 # root 1222 `, 1223 "sect/doc1.en.md", `--- 1224 title: doc1 1225 weight: 1 1226 slug: doc1-slug 1227 tags: 1228 - tag1 1229 publishdate: "2000-01-01" 1230 --- 1231 # doc1 1232 *some "content"* 1233 1234 {{< shortcode >}} 1235 1236 {{< lingo >}} 1237 1238 NOTE: slug should be used as URL 1239 `, 1240 "sect/doc1.fr.md", `--- 1241 title: doc1 1242 weight: 1 1243 plaques: 1244 - FRtag1 1245 - FRtag2 1246 publishdate: "2000-01-04" 1247 --- 1248 # doc1 1249 *quelque "contenu"* 1250 1251 {{< shortcode >}} 1252 1253 {{< lingo >}} 1254 1255 NOTE: should be in the 'en' Page's 'Translations' field. 1256 NOTE: date is after "doc3" 1257 `, 1258 "sect/doc2.en.md", `--- 1259 title: doc2 1260 weight: 2 1261 publishdate: "2000-01-02" 1262 --- 1263 # doc2 1264 *some content* 1265 NOTE: without slug, "doc2" should be used, without ".en" as URL 1266 `, 1267 "sect/doc3.en.md", `--- 1268 title: doc3 1269 weight: 3 1270 publishdate: "2000-01-03" 1271 aliases: [/en/al/alias1,/al/alias2/] 1272 tags: 1273 - tag2 1274 - tag1 1275 url: /superbob/ 1276 --- 1277 # doc3 1278 *some content* 1279 NOTE: third 'en' doc, should trigger pagination on home page. 1280 `, 1281 "sect/doc4.md", `--- 1282 title: doc4 1283 weight: 4 1284 plaques: 1285 - FRtag1 1286 publishdate: "2000-01-05" 1287 --- 1288 # doc4 1289 *du contenu francophone* 1290 NOTE: should use the defaultContentLanguage and mark this doc as 'fr'. 1291 NOTE: doesn't have any corresponding translation in 'en' 1292 `, 1293 "other/doc5.fr.md", `--- 1294 title: doc5 1295 weight: 5 1296 publishdate: "2000-01-06" 1297 --- 1298 # doc5 1299 *autre contenu francophone* 1300 NOTE: should use the "permalinks" configuration with :filename 1301 `, 1302 // Add some for the stats 1303 "stats/expired.fr.md", `--- 1304 title: expired 1305 publishdate: "2000-01-06" 1306 expiryDate: "2001-01-06" 1307 --- 1308 # Expired 1309 `, 1310 "stats/future.fr.md", `--- 1311 title: future 1312 weight: 6 1313 publishdate: "2100-01-06" 1314 --- 1315 # Future 1316 `, 1317 "stats/expired.en.md", `--- 1318 title: expired 1319 weight: 7 1320 publishdate: "2000-01-06" 1321 expiryDate: "2001-01-06" 1322 --- 1323 # Expired 1324 `, 1325 "stats/future.en.md", `--- 1326 title: future 1327 weight: 6 1328 publishdate: "2100-01-06" 1329 --- 1330 # Future 1331 `, 1332 "stats/draft.en.md", `--- 1333 title: expired 1334 publishdate: "2000-01-06" 1335 draft: true 1336 --- 1337 # Draft 1338 `, 1339 "stats/tax.nn.md", `--- 1340 title: Tax NN 1341 weight: 8 1342 publishdate: "2000-01-06" 1343 weight: 1001 1344 lag: 1345 - Sogndal 1346 --- 1347 # Tax NN 1348 `, 1349 "stats/tax.nb.md", `--- 1350 title: Tax NB 1351 weight: 8 1352 publishdate: "2000-01-06" 1353 weight: 1002 1354 lag: 1355 - Sogndal 1356 --- 1357 # Tax NB 1358 `, 1359 // Bundle 1360 "bundles/b1/index.en.md", `--- 1361 title: Bundle EN 1362 publishdate: "2000-01-06" 1363 weight: 2001 1364 --- 1365 # Bundle Content EN 1366 `, 1367 "bundles/b1/index.md", `--- 1368 title: Bundle Default 1369 publishdate: "2000-01-06" 1370 weight: 2002 1371 --- 1372 # Bundle Content Default 1373 `, 1374 "bundles/b1/logo.png", ` 1375 PNG Data 1376 `) 1377 1378 i18nContent := func(id, value string) string { 1379 return fmt.Sprintf(` 1380 [%s] 1381 other = %q 1382 `, id, value) 1383 } 1384 1385 b.WithSourceFile("i18n/en.toml", i18nContent("hello", "Hello")) 1386 b.WithSourceFile("i18n/fr.toml", i18nContent("hello", "Bonjour")) 1387 b.WithSourceFile("i18n/nb.toml", i18nContent("hello", "Hallo")) 1388 b.WithSourceFile("i18n/nn.toml", i18nContent("hello", "Hallo")) 1389 1390 return &multiSiteTestBuilder{sitesBuilder: b, configFormat: configFormat, config: config, configData: configData} 1391 } 1392 1393 func TestRebuildOnAssetChange(t *testing.T) { 1394 b := newTestSitesBuilder(t).Running() 1395 b.WithTemplatesAdded("index.html", ` 1396 {{ (resources.Get "data.json").Content }} 1397 `) 1398 b.WithSourceFile("assets/data.json", "orig data") 1399 1400 b.Build(BuildCfg{}) 1401 b.AssertFileContent("public/index.html", `orig data`) 1402 1403 b.EditFiles("assets/data.json", "changed data") 1404 1405 b.Build(BuildCfg{}) 1406 b.AssertFileContent("public/index.html", `changed data`) 1407 }