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