site_test.go (34199B)
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 "encoding/json" 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "strings" 23 "testing" 24 25 "github.com/gobuffalo/flect" 26 "github.com/gohugoio/hugo/config" 27 "github.com/gohugoio/hugo/publisher" 28 29 qt "github.com/frankban/quicktest" 30 "github.com/gohugoio/hugo/deps" 31 "github.com/gohugoio/hugo/resources/page" 32 ) 33 34 const ( 35 templateMissingFunc = "{{ .Title | funcdoesnotexists }}" 36 templateWithURLAbs = "<a href=\"/foobar.jpg\">Going</a>" 37 ) 38 39 func TestRenderWithInvalidTemplate(t *testing.T) { 40 t.Parallel() 41 cfg, fs := newTestCfg() 42 43 writeSource(t, fs, filepath.Join("content", "foo.md"), "foo") 44 45 withTemplate := createWithTemplateFromNameValues("missing", templateMissingFunc) 46 47 buildSingleSiteExpected(t, true, false, deps.DepsCfg{Fs: fs, Cfg: cfg, WithTemplate: withTemplate}, BuildCfg{}) 48 } 49 50 func TestDraftAndFutureRender(t *testing.T) { 51 t.Parallel() 52 sources := [][2]string{ 53 {filepath.FromSlash("sect/doc1.md"), "---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*"}, 54 {filepath.FromSlash("sect/doc2.md"), "---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*"}, 55 {filepath.FromSlash("sect/doc3.md"), "---\ntitle: doc3\ndraft: false\npublishdate: \"2414-05-29\"\n---\n# doc3\n*some content*"}, 56 {filepath.FromSlash("sect/doc4.md"), "---\ntitle: doc4\ndraft: false\npublishdate: \"2012-05-29\"\n---\n# doc4\n*some content*"}, 57 } 58 59 siteSetup := func(t *testing.T, configKeyValues ...any) *Site { 60 cfg, fs := newTestCfg() 61 62 cfg.Set("baseURL", "http://auth/bub") 63 64 for i := 0; i < len(configKeyValues); i += 2 { 65 cfg.Set(configKeyValues[i].(string), configKeyValues[i+1]) 66 } 67 68 for _, src := range sources { 69 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 70 } 71 72 return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 73 } 74 75 // Testing Defaults.. Only draft:true and publishDate in the past should be rendered 76 s := siteSetup(t) 77 if len(s.RegularPages()) != 1 { 78 t.Fatal("Draft or Future dated content published unexpectedly") 79 } 80 81 // only publishDate in the past should be rendered 82 s = siteSetup(t, "buildDrafts", true) 83 if len(s.RegularPages()) != 2 { 84 t.Fatal("Future Dated Posts published unexpectedly") 85 } 86 87 // drafts should not be rendered, but all dates should 88 s = siteSetup(t, 89 "buildDrafts", false, 90 "buildFuture", true) 91 92 if len(s.RegularPages()) != 2 { 93 t.Fatal("Draft posts published unexpectedly") 94 } 95 96 // all 4 should be included 97 s = siteSetup(t, 98 "buildDrafts", true, 99 "buildFuture", true) 100 101 if len(s.RegularPages()) != 4 { 102 t.Fatal("Drafts or Future posts not included as expected") 103 } 104 } 105 106 func TestFutureExpirationRender(t *testing.T) { 107 t.Parallel() 108 sources := [][2]string{ 109 {filepath.FromSlash("sect/doc3.md"), "---\ntitle: doc1\nexpirydate: \"2400-05-29\"\n---\n# doc1\n*some content*"}, 110 {filepath.FromSlash("sect/doc4.md"), "---\ntitle: doc2\nexpirydate: \"2000-05-29\"\n---\n# doc2\n*some content*"}, 111 } 112 113 siteSetup := func(t *testing.T) *Site { 114 cfg, fs := newTestCfg() 115 cfg.Set("baseURL", "http://auth/bub") 116 117 for _, src := range sources { 118 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 119 } 120 121 return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 122 } 123 124 s := siteSetup(t) 125 126 if len(s.AllPages()) != 1 { 127 if len(s.RegularPages()) > 1 { 128 t.Fatal("Expired content published unexpectedly") 129 } 130 131 if len(s.RegularPages()) < 1 { 132 t.Fatal("Valid content expired unexpectedly") 133 } 134 } 135 136 if s.AllPages()[0].Title() == "doc2" { 137 t.Fatal("Expired content published unexpectedly") 138 } 139 } 140 141 func TestLastChange(t *testing.T) { 142 t.Parallel() 143 144 cfg, fs := newTestCfg() 145 c := qt.New(t) 146 147 writeSource(t, fs, filepath.Join("content", "sect/doc1.md"), "---\ntitle: doc1\nweight: 1\ndate: 2014-05-29\n---\n# doc1\n*some content*") 148 writeSource(t, fs, filepath.Join("content", "sect/doc2.md"), "---\ntitle: doc2\nweight: 2\ndate: 2015-05-29\n---\n# doc2\n*some content*") 149 writeSource(t, fs, filepath.Join("content", "sect/doc3.md"), "---\ntitle: doc3\nweight: 3\ndate: 2017-05-29\n---\n# doc3\n*some content*") 150 writeSource(t, fs, filepath.Join("content", "sect/doc4.md"), "---\ntitle: doc4\nweight: 4\ndate: 2016-05-29\n---\n# doc4\n*some content*") 151 writeSource(t, fs, filepath.Join("content", "sect/doc5.md"), "---\ntitle: doc5\nweight: 3\n---\n# doc5\n*some content*") 152 153 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 154 155 c.Assert(s.Info.LastChange().IsZero(), qt.Equals, false) 156 c.Assert(s.Info.LastChange().Year(), qt.Equals, 2017) 157 } 158 159 // Issue #_index 160 func TestPageWithUnderScoreIndexInFilename(t *testing.T) { 161 t.Parallel() 162 163 cfg, fs := newTestCfg() 164 c := qt.New(t) 165 166 writeSource(t, fs, filepath.Join("content", "sect/my_index_file.md"), "---\ntitle: doc1\nweight: 1\ndate: 2014-05-29\n---\n# doc1\n*some content*") 167 168 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 169 170 c.Assert(len(s.RegularPages()), qt.Equals, 1) 171 } 172 173 // Issue #957 174 func TestCrossrefs(t *testing.T) { 175 t.Parallel() 176 for _, uglyURLs := range []bool{true, false} { 177 for _, relative := range []bool{true, false} { 178 doTestCrossrefs(t, relative, uglyURLs) 179 } 180 } 181 } 182 183 func doTestCrossrefs(t *testing.T, relative, uglyURLs bool) { 184 c := qt.New(t) 185 186 baseURL := "http://foo/bar" 187 188 var refShortcode string 189 var expectedBase string 190 var expectedURLSuffix string 191 var expectedPathSuffix string 192 193 if relative { 194 refShortcode = "relref" 195 expectedBase = "/bar" 196 } else { 197 refShortcode = "ref" 198 expectedBase = baseURL 199 } 200 201 if uglyURLs { 202 expectedURLSuffix = ".html" 203 expectedPathSuffix = ".html" 204 } else { 205 expectedURLSuffix = "/" 206 expectedPathSuffix = "/index.html" 207 } 208 209 doc3Slashed := filepath.FromSlash("/sect/doc3.md") 210 211 sources := [][2]string{ 212 { 213 filepath.FromSlash("sect/doc1.md"), 214 fmt.Sprintf(`Ref 2: {{< %s "sect/doc2.md" >}}`, refShortcode), 215 }, 216 // Issue #1148: Make sure that no P-tags is added around shortcodes. 217 { 218 filepath.FromSlash("sect/doc2.md"), 219 fmt.Sprintf(`**Ref 1:** 220 221 {{< %s "sect/doc1.md" >}} 222 223 THE END.`, refShortcode), 224 }, 225 // Issue #1753: Should not add a trailing newline after shortcode. 226 { 227 filepath.FromSlash("sect/doc3.md"), 228 fmt.Sprintf(`**Ref 1:** {{< %s "sect/doc3.md" >}}.`, refShortcode), 229 }, 230 // Issue #3703 231 { 232 filepath.FromSlash("sect/doc4.md"), 233 fmt.Sprintf(`**Ref 1:** {{< %s "%s" >}}.`, refShortcode, doc3Slashed), 234 }, 235 } 236 237 cfg, fs := newTestCfg() 238 239 cfg.Set("baseURL", baseURL) 240 cfg.Set("uglyURLs", uglyURLs) 241 cfg.Set("verbose", true) 242 243 for _, src := range sources { 244 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 245 } 246 247 s := buildSingleSite( 248 t, 249 deps.DepsCfg{ 250 Fs: fs, 251 Cfg: cfg, 252 WithTemplate: createWithTemplateFromNameValues("_default/single.html", "{{.Content}}"), 253 }, 254 BuildCfg{}) 255 256 c.Assert(len(s.RegularPages()), qt.Equals, 4) 257 258 th := newTestHelper(s.Cfg, s.Fs, t) 259 260 tests := []struct { 261 doc string 262 expected string 263 }{ 264 {filepath.FromSlash(fmt.Sprintf("public/sect/doc1%s", expectedPathSuffix)), fmt.Sprintf("<p>Ref 2: %s/sect/doc2%s</p>\n", expectedBase, expectedURLSuffix)}, 265 {filepath.FromSlash(fmt.Sprintf("public/sect/doc2%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong></p>\n%s/sect/doc1%s\n<p>THE END.</p>\n", expectedBase, expectedURLSuffix)}, 266 {filepath.FromSlash(fmt.Sprintf("public/sect/doc3%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong> %s/sect/doc3%s.</p>\n", expectedBase, expectedURLSuffix)}, 267 {filepath.FromSlash(fmt.Sprintf("public/sect/doc4%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong> %s/sect/doc3%s.</p>\n", expectedBase, expectedURLSuffix)}, 268 } 269 270 for _, test := range tests { 271 th.assertFileContent(test.doc, test.expected) 272 } 273 } 274 275 // Issue #939 276 // Issue #1923 277 func TestShouldAlwaysHaveUglyURLs(t *testing.T) { 278 t.Parallel() 279 for _, uglyURLs := range []bool{true, false} { 280 doTestShouldAlwaysHaveUglyURLs(t, uglyURLs) 281 } 282 } 283 284 func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) { 285 cfg, fs := newTestCfg() 286 c := qt.New(t) 287 288 cfg.Set("verbose", true) 289 cfg.Set("baseURL", "http://auth/bub") 290 cfg.Set("uglyURLs", uglyURLs) 291 292 sources := [][2]string{ 293 {filepath.FromSlash("sect/doc1.md"), "---\nmarkup: markdown\n---\n# title\nsome *content*"}, 294 {filepath.FromSlash("sect/doc2.md"), "---\nurl: /ugly.html\nmarkup: markdown\n---\n# title\ndoc2 *content*"}, 295 } 296 297 for _, src := range sources { 298 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 299 } 300 301 writeSource(t, fs, filepath.Join("layouts", "index.html"), "Home Sweet {{ if.IsHome }}Home{{ end }}.") 302 writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}{{ if.IsHome }}This is not home!{{ end }}") 303 writeSource(t, fs, filepath.Join("layouts", "404.html"), "Page Not Found.{{ if.IsHome }}This is not home!{{ end }}") 304 writeSource(t, fs, filepath.Join("layouts", "rss.xml"), "<root>RSS</root>") 305 writeSource(t, fs, filepath.Join("layouts", "sitemap.xml"), "<root>SITEMAP</root>") 306 307 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 308 309 var expectedPagePath string 310 if uglyURLs { 311 expectedPagePath = "public/sect/doc1.html" 312 } else { 313 expectedPagePath = "public/sect/doc1/index.html" 314 } 315 316 tests := []struct { 317 doc string 318 expected string 319 }{ 320 {filepath.FromSlash("public/index.html"), "Home Sweet Home."}, 321 {filepath.FromSlash(expectedPagePath), "<h1 id=\"title\">title</h1>\n<p>some <em>content</em></p>\n"}, 322 {filepath.FromSlash("public/404.html"), "Page Not Found."}, 323 {filepath.FromSlash("public/index.xml"), "<root>RSS</root>"}, 324 {filepath.FromSlash("public/sitemap.xml"), "<root>SITEMAP</root>"}, 325 // Issue #1923 326 {filepath.FromSlash("public/ugly.html"), "<h1 id=\"title\">title</h1>\n<p>doc2 <em>content</em></p>\n"}, 327 } 328 329 for _, p := range s.RegularPages() { 330 c.Assert(p.IsHome(), qt.Equals, false) 331 } 332 333 for _, test := range tests { 334 content := readWorkingDir(t, fs, test.doc) 335 336 if content != test.expected { 337 t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content) 338 } 339 } 340 } 341 342 // Issue #3355 343 func TestShouldNotWriteZeroLengthFilesToDestination(t *testing.T) { 344 cfg, fs := newTestCfg() 345 346 writeSource(t, fs, filepath.Join("content", "simple.html"), "simple") 347 writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}") 348 writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "") 349 350 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 351 th := newTestHelper(s.Cfg, s.Fs, t) 352 353 th.assertFileNotExist(filepath.Join("public", "index.html")) 354 } 355 356 func TestMainSections(t *testing.T) { 357 c := qt.New(t) 358 for _, paramSet := range []bool{false, true} { 359 c.Run(fmt.Sprintf("param-%t", paramSet), func(c *qt.C) { 360 v := config.NewWithTestDefaults() 361 if paramSet { 362 v.Set("params", map[string]any{ 363 "mainSections": []string{"a1", "a2"}, 364 }) 365 } 366 367 b := newTestSitesBuilder(c).WithViper(v) 368 369 for i := 0; i < 20; i++ { 370 b.WithContent(fmt.Sprintf("page%d.md", i), `--- 371 title: "Page" 372 --- 373 `) 374 } 375 376 for i := 0; i < 5; i++ { 377 b.WithContent(fmt.Sprintf("blog/page%d.md", i), `--- 378 title: "Page" 379 tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] 380 --- 381 `) 382 } 383 384 for i := 0; i < 3; i++ { 385 b.WithContent(fmt.Sprintf("docs/page%d.md", i), `--- 386 title: "Page" 387 --- 388 `) 389 } 390 391 b.WithTemplates("index.html", ` 392 mainSections: {{ .Site.Params.mainSections }} 393 394 {{ range (where .Site.RegularPages "Type" "in" .Site.Params.mainSections) }} 395 Main section page: {{ .RelPermalink }} 396 {{ end }} 397 `) 398 399 b.Build(BuildCfg{}) 400 401 if paramSet { 402 b.AssertFileContent("public/index.html", "mainSections: [a1 a2]") 403 } else { 404 b.AssertFileContent("public/index.html", "mainSections: [blog]", "Main section page: /blog/page3/") 405 } 406 }) 407 } 408 } 409 410 // Issue #1176 411 func TestSectionNaming(t *testing.T) { 412 for _, canonify := range []bool{true, false} { 413 for _, uglify := range []bool{true, false} { 414 for _, pluralize := range []bool{true, false} { 415 canonify := canonify 416 uglify := uglify 417 pluralize := pluralize 418 t.Run(fmt.Sprintf("canonify=%t,uglify=%t,pluralize=%t", canonify, uglify, pluralize), func(t *testing.T) { 419 t.Parallel() 420 doTestSectionNaming(t, canonify, uglify, pluralize) 421 }) 422 } 423 } 424 } 425 } 426 427 func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) { 428 c := qt.New(t) 429 430 var expectedPathSuffix string 431 432 if uglify { 433 expectedPathSuffix = ".html" 434 } else { 435 expectedPathSuffix = "/index.html" 436 } 437 438 sources := [][2]string{ 439 {filepath.FromSlash("sect/doc1.html"), "doc1"}, 440 // Add one more page to sect to make sure sect is picked in mainSections 441 {filepath.FromSlash("sect/sect.html"), "sect"}, 442 {filepath.FromSlash("Fish and Chips/doc2.html"), "doc2"}, 443 {filepath.FromSlash("ラーメン/doc3.html"), "doc3"}, 444 } 445 446 cfg, fs := newTestCfg() 447 448 cfg.Set("baseURL", "http://auth/sub/") 449 cfg.Set("uglyURLs", uglify) 450 cfg.Set("pluralizeListTitles", pluralize) 451 cfg.Set("canonifyURLs", canonify) 452 453 for _, src := range sources { 454 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 455 } 456 457 writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}") 458 writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "{{ .Kind }}|{{.Title}}") 459 460 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 461 462 mainSections, err := s.Info.Param("mainSections") 463 c.Assert(err, qt.IsNil) 464 c.Assert(mainSections, qt.DeepEquals, []string{"sect"}) 465 466 th := newTestHelper(s.Cfg, s.Fs, t) 467 tests := []struct { 468 doc string 469 pluralAware bool 470 expected string 471 }{ 472 {filepath.FromSlash(fmt.Sprintf("sect/doc1%s", expectedPathSuffix)), false, "doc1"}, 473 {filepath.FromSlash(fmt.Sprintf("sect%s", expectedPathSuffix)), true, "Sect"}, 474 {filepath.FromSlash(fmt.Sprintf("fish-and-chips/doc2%s", expectedPathSuffix)), false, "doc2"}, 475 {filepath.FromSlash(fmt.Sprintf("fish-and-chips%s", expectedPathSuffix)), true, "Fish and Chips"}, 476 {filepath.FromSlash(fmt.Sprintf("ラーメン/doc3%s", expectedPathSuffix)), false, "doc3"}, 477 {filepath.FromSlash(fmt.Sprintf("ラーメン%s", expectedPathSuffix)), true, "ラーメン"}, 478 } 479 480 for _, test := range tests { 481 482 if test.pluralAware && pluralize { 483 test.expected = flect.Pluralize(test.expected) 484 } 485 486 th.assertFileContent(filepath.Join("public", test.doc), test.expected) 487 } 488 } 489 490 func TestAbsURLify(t *testing.T) { 491 t.Parallel() 492 sources := [][2]string{ 493 {filepath.FromSlash("sect/doc1.html"), "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"}, 494 {filepath.FromSlash("blue/doc2.html"), "---\nf: t\n---\n<!doctype html><html><body>more content</body></html>"}, 495 } 496 for _, baseURL := range []string{"http://auth/bub", "http://base", "//base"} { 497 for _, canonify := range []bool{true, false} { 498 499 cfg, fs := newTestCfg() 500 501 cfg.Set("uglyURLs", true) 502 cfg.Set("canonifyURLs", canonify) 503 cfg.Set("baseURL", baseURL) 504 505 for _, src := range sources { 506 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 507 } 508 509 writeSource(t, fs, filepath.Join("layouts", "blue/single.html"), templateWithURLAbs) 510 511 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 512 th := newTestHelper(s.Cfg, s.Fs, t) 513 514 tests := []struct { 515 file, expected string 516 }{ 517 {"public/blue/doc2.html", "<a href=\"%s/foobar.jpg\">Going</a>"}, 518 {"public/sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"}, 519 } 520 521 for _, test := range tests { 522 523 expected := test.expected 524 525 if strings.Contains(expected, "%s") { 526 expected = fmt.Sprintf(expected, baseURL) 527 } 528 529 if !canonify { 530 expected = strings.Replace(expected, baseURL, "", -1) 531 } 532 533 th.assertFileContent(test.file, expected) 534 535 } 536 } 537 } 538 } 539 540 var weightedPage1 = `+++ 541 weight = "2" 542 title = "One" 543 my_param = "foo" 544 my_date = 1979-05-27T07:32:00Z 545 +++ 546 Front Matter with Ordered Pages` 547 548 var weightedPage2 = `+++ 549 weight = "6" 550 title = "Two" 551 publishdate = "2012-03-05" 552 my_param = "foo" 553 +++ 554 Front Matter with Ordered Pages 2` 555 556 var weightedPage3 = `+++ 557 weight = "4" 558 title = "Three" 559 date = "2012-04-06" 560 publishdate = "2012-04-06" 561 my_param = "bar" 562 only_one = "yes" 563 my_date = 2010-05-27T07:32:00Z 564 +++ 565 Front Matter with Ordered Pages 3` 566 567 var weightedPage4 = `+++ 568 weight = "4" 569 title = "Four" 570 date = "2012-01-01" 571 publishdate = "2012-01-01" 572 my_param = "baz" 573 my_date = 2010-05-27T07:32:00Z 574 summary = "A _custom_ summary" 575 categories = [ "hugo" ] 576 +++ 577 Front Matter with Ordered Pages 4. This is longer content` 578 579 var weightedPage5 = `+++ 580 weight = "5" 581 title = "Five" 582 583 [_build] 584 render = "never" 585 +++ 586 Front Matter with Ordered Pages 5` 587 588 var weightedSources = [][2]string{ 589 {filepath.FromSlash("sect/doc1.md"), weightedPage1}, 590 {filepath.FromSlash("sect/doc2.md"), weightedPage2}, 591 {filepath.FromSlash("sect/doc3.md"), weightedPage3}, 592 {filepath.FromSlash("sect/doc4.md"), weightedPage4}, 593 {filepath.FromSlash("sect/doc5.md"), weightedPage5}, 594 } 595 596 func TestOrderedPages(t *testing.T) { 597 t.Parallel() 598 cfg, fs := newTestCfg() 599 cfg.Set("baseURL", "http://auth/bub") 600 601 for _, src := range weightedSources { 602 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 603 } 604 605 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 606 607 if s.getPage(page.KindSection, "sect").Pages()[1].Title() != "Three" || s.getPage(page.KindSection, "sect").Pages()[2].Title() != "Four" { 608 t.Error("Pages in unexpected order.") 609 } 610 611 bydate := s.RegularPages().ByDate() 612 613 if bydate[0].Title() != "One" { 614 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title()) 615 } 616 617 rev := bydate.Reverse() 618 if rev[0].Title() != "Three" { 619 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title()) 620 } 621 622 bypubdate := s.RegularPages().ByPublishDate() 623 624 if bypubdate[0].Title() != "One" { 625 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bypubdate[0].Title()) 626 } 627 628 rbypubdate := bypubdate.Reverse() 629 if rbypubdate[0].Title() != "Three" { 630 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rbypubdate[0].Title()) 631 } 632 633 bylength := s.RegularPages().ByLength() 634 if bylength[0].Title() != "One" { 635 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title()) 636 } 637 638 rbylength := bylength.Reverse() 639 if rbylength[0].Title() != "Four" { 640 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title()) 641 } 642 } 643 644 var groupedSources = [][2]string{ 645 {filepath.FromSlash("sect1/doc1.md"), weightedPage1}, 646 {filepath.FromSlash("sect1/doc2.md"), weightedPage2}, 647 {filepath.FromSlash("sect2/doc3.md"), weightedPage3}, 648 {filepath.FromSlash("sect3/doc4.md"), weightedPage4}, 649 } 650 651 func TestGroupedPages(t *testing.T) { 652 t.Parallel() 653 defer func() { 654 if r := recover(); r != nil { 655 fmt.Println("Recovered in f", r) 656 } 657 }() 658 659 cfg, fs := newTestCfg() 660 cfg.Set("baseURL", "http://auth/bub") 661 662 writeSourcesToSource(t, "content", fs, groupedSources...) 663 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 664 665 rbysection, err := s.RegularPages().GroupBy("Section", "desc") 666 if err != nil { 667 t.Fatalf("Unable to make PageGroup array: %s", err) 668 } 669 670 if rbysection[0].Key != "sect3" { 671 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect3", rbysection[0].Key) 672 } 673 if rbysection[1].Key != "sect2" { 674 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "sect2", rbysection[1].Key) 675 } 676 if rbysection[2].Key != "sect1" { 677 t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "sect1", rbysection[2].Key) 678 } 679 if rbysection[0].Pages[0].Title() != "Four" { 680 t.Errorf("PageGroup has an unexpected page. First group's pages should have '%s', got '%s'", "Four", rbysection[0].Pages[0].Title()) 681 } 682 if len(rbysection[2].Pages) != 2 { 683 t.Errorf("PageGroup has unexpected number of pages. Third group should have '%d' pages, got '%d' pages", 2, len(rbysection[2].Pages)) 684 } 685 686 bytype, err := s.RegularPages().GroupBy("Type", "asc") 687 if err != nil { 688 t.Fatalf("Unable to make PageGroup array: %s", err) 689 } 690 if bytype[0].Key != "sect1" { 691 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect1", bytype[0].Key) 692 } 693 if bytype[1].Key != "sect2" { 694 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "sect2", bytype[1].Key) 695 } 696 if bytype[2].Key != "sect3" { 697 t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "sect3", bytype[2].Key) 698 } 699 if bytype[2].Pages[0].Title() != "Four" { 700 t.Errorf("PageGroup has an unexpected page. Third group's data should have '%s', got '%s'", "Four", bytype[0].Pages[0].Title()) 701 } 702 if len(bytype[0].Pages) != 2 { 703 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(bytype[2].Pages)) 704 } 705 706 bydate, err := s.RegularPages().GroupByDate("2006-01", "asc") 707 if err != nil { 708 t.Fatalf("Unable to make PageGroup array: %s", err) 709 } 710 if bydate[0].Key != "0001-01" { 711 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "0001-01", bydate[0].Key) 712 } 713 if bydate[1].Key != "2012-01" { 714 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "2012-01", bydate[1].Key) 715 } 716 717 bypubdate, err := s.RegularPages().GroupByPublishDate("2006") 718 if err != nil { 719 t.Fatalf("Unable to make PageGroup array: %s", err) 720 } 721 if bypubdate[0].Key != "2012" { 722 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "2012", bypubdate[0].Key) 723 } 724 if bypubdate[1].Key != "0001" { 725 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "0001", bypubdate[1].Key) 726 } 727 if bypubdate[0].Pages[0].Title() != "Three" { 728 t.Errorf("PageGroup has an unexpected page. Third group's pages should have '%s', got '%s'", "Three", bypubdate[0].Pages[0].Title()) 729 } 730 if len(bypubdate[0].Pages) != 3 { 731 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 3, len(bypubdate[0].Pages)) 732 } 733 734 byparam, err := s.RegularPages().GroupByParam("my_param", "desc") 735 if err != nil { 736 t.Fatalf("Unable to make PageGroup array: %s", err) 737 } 738 if byparam[0].Key != "foo" { 739 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "foo", byparam[0].Key) 740 } 741 if byparam[1].Key != "baz" { 742 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "baz", byparam[1].Key) 743 } 744 if byparam[2].Key != "bar" { 745 t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "bar", byparam[2].Key) 746 } 747 if byparam[2].Pages[0].Title() != "Three" { 748 t.Errorf("PageGroup has an unexpected page. Third group's pages should have '%s', got '%s'", "Three", byparam[2].Pages[0].Title()) 749 } 750 if len(byparam[0].Pages) != 2 { 751 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(byparam[0].Pages)) 752 } 753 754 _, err = s.RegularPages().GroupByParam("not_exist") 755 if err == nil { 756 t.Errorf("GroupByParam didn't return an expected error") 757 } 758 759 byOnlyOneParam, err := s.RegularPages().GroupByParam("only_one") 760 if err != nil { 761 t.Fatalf("Unable to make PageGroup array: %s", err) 762 } 763 if len(byOnlyOneParam) != 1 { 764 t.Errorf("PageGroup array has unexpected elements. Group length should be '%d', got '%d'", 1, len(byOnlyOneParam)) 765 } 766 if byOnlyOneParam[0].Key != "yes" { 767 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "yes", byOnlyOneParam[0].Key) 768 } 769 770 byParamDate, err := s.RegularPages().GroupByParamDate("my_date", "2006-01") 771 if err != nil { 772 t.Fatalf("Unable to make PageGroup array: %s", err) 773 } 774 if byParamDate[0].Key != "2010-05" { 775 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "2010-05", byParamDate[0].Key) 776 } 777 if byParamDate[1].Key != "1979-05" { 778 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "1979-05", byParamDate[1].Key) 779 } 780 if byParamDate[1].Pages[0].Title() != "One" { 781 t.Errorf("PageGroup has an unexpected page. Second group's pages should have '%s', got '%s'", "One", byParamDate[1].Pages[0].Title()) 782 } 783 if len(byParamDate[0].Pages) != 2 { 784 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(byParamDate[2].Pages)) 785 } 786 } 787 788 var pageWithWeightedTaxonomies1 = `+++ 789 tags = [ "a", "b", "c" ] 790 tags_weight = 22 791 categories = ["d"] 792 title = "foo" 793 categories_weight = 44 794 +++ 795 Front Matter with weighted tags and categories` 796 797 var pageWithWeightedTaxonomies2 = `+++ 798 tags = "a" 799 tags_weight = 33 800 title = "bar" 801 categories = [ "d", "e" ] 802 categories_weight = 11.0 803 alias = "spf13" 804 date = 1979-05-27T07:32:00Z 805 +++ 806 Front Matter with weighted tags and categories` 807 808 var pageWithWeightedTaxonomies3 = `+++ 809 title = "bza" 810 categories = [ "e" ] 811 categories_weight = 11 812 alias = "spf13" 813 date = 2010-05-27T07:32:00Z 814 +++ 815 Front Matter with weighted tags and categories` 816 817 func TestWeightedTaxonomies(t *testing.T) { 818 t.Parallel() 819 sources := [][2]string{ 820 {filepath.FromSlash("sect/doc1.md"), pageWithWeightedTaxonomies2}, 821 {filepath.FromSlash("sect/doc2.md"), pageWithWeightedTaxonomies1}, 822 {filepath.FromSlash("sect/doc3.md"), pageWithWeightedTaxonomies3}, 823 } 824 taxonomies := make(map[string]string) 825 826 taxonomies["tag"] = "tags" 827 taxonomies["category"] = "categories" 828 829 cfg, fs := newTestCfg() 830 831 cfg.Set("baseURL", "http://auth/bub") 832 cfg.Set("taxonomies", taxonomies) 833 834 writeSourcesToSource(t, "content", fs, sources...) 835 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 836 837 if s.Taxonomies()["tags"]["a"][0].Page.Title() != "foo" { 838 t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Taxonomies()["tags"]["a"][0].Page.Title()) 839 } 840 841 if s.Taxonomies()["categories"]["d"][0].Page.Title() != "bar" { 842 t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Taxonomies()["categories"]["d"][0].Page.Title()) 843 } 844 845 if s.Taxonomies()["categories"]["e"][0].Page.Title() != "bza" { 846 t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Taxonomies()["categories"]["e"][0].Page.Title()) 847 } 848 } 849 850 func setupLinkingMockSite(t *testing.T) *Site { 851 sources := [][2]string{ 852 {filepath.FromSlash("level2/unique.md"), ""}, 853 {filepath.FromSlash("_index.md"), ""}, 854 {filepath.FromSlash("common.md"), ""}, 855 {filepath.FromSlash("rootfile.md"), ""}, 856 {filepath.FromSlash("root-image.png"), ""}, 857 858 {filepath.FromSlash("level2/2-root.md"), ""}, 859 {filepath.FromSlash("level2/common.md"), ""}, 860 861 {filepath.FromSlash("level2/2-image.png"), ""}, 862 {filepath.FromSlash("level2/common.png"), ""}, 863 864 {filepath.FromSlash("level2/level3/start.md"), ""}, 865 {filepath.FromSlash("level2/level3/_index.md"), ""}, 866 {filepath.FromSlash("level2/level3/3-root.md"), ""}, 867 {filepath.FromSlash("level2/level3/common.md"), ""}, 868 {filepath.FromSlash("level2/level3/3-image.png"), ""}, 869 {filepath.FromSlash("level2/level3/common.png"), ""}, 870 871 {filepath.FromSlash("level2/level3/embedded.dot.md"), ""}, 872 873 {filepath.FromSlash("leafbundle/index.md"), ""}, 874 } 875 876 cfg, fs := newTestCfg() 877 878 cfg.Set("baseURL", "http://auth/") 879 cfg.Set("uglyURLs", false) 880 cfg.Set("outputs", map[string]any{ 881 "page": []string{"HTML", "AMP"}, 882 }) 883 cfg.Set("pluralizeListTitles", false) 884 cfg.Set("canonifyURLs", false) 885 writeSourcesToSource(t, "content", fs, sources...) 886 return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 887 } 888 889 func TestRefLinking(t *testing.T) { 890 t.Parallel() 891 site := setupLinkingMockSite(t) 892 893 currentPage := site.getPage(page.KindPage, "level2/level3/start.md") 894 if currentPage == nil { 895 t.Fatalf("failed to find current page in site") 896 } 897 898 for i, test := range []struct { 899 link string 900 outputFormat string 901 relative bool 902 expected string 903 }{ 904 // different refs resolving to the same unique filename: 905 {"/level2/unique.md", "", true, "/level2/unique/"}, 906 {"../unique.md", "", true, "/level2/unique/"}, 907 {"unique.md", "", true, "/level2/unique/"}, 908 909 {"level2/common.md", "", true, "/level2/common/"}, 910 {"3-root.md", "", true, "/level2/level3/3-root/"}, 911 {"../..", "", true, "/"}, 912 913 // different refs resolving to the same ambiguous top-level filename: 914 {"../../common.md", "", true, "/common/"}, 915 {"/common.md", "", true, "/common/"}, 916 917 // different refs resolving to the same ambiguous level-2 filename: 918 {"/level2/common.md", "", true, "/level2/common/"}, 919 {"../common.md", "", true, "/level2/common/"}, 920 {"common.md", "", true, "/level2/level3/common/"}, 921 922 // different refs resolving to the same section: 923 {"/level2", "", true, "/level2/"}, 924 {"..", "", true, "/level2/"}, 925 {"../", "", true, "/level2/"}, 926 927 // different refs resolving to the same subsection: 928 {"/level2/level3", "", true, "/level2/level3/"}, 929 {"/level2/level3/_index.md", "", true, "/level2/level3/"}, 930 {".", "", true, "/level2/level3/"}, 931 {"./", "", true, "/level2/level3/"}, 932 933 // try to confuse parsing 934 {"embedded.dot.md", "", true, "/level2/level3/embedded.dot/"}, 935 936 // test empty link, as well as fragment only link 937 {"", "", true, ""}, 938 } { 939 t.Run(fmt.Sprintf("t%dt", i), func(t *testing.T) { 940 checkLinkCase(site, test.link, currentPage, test.relative, test.outputFormat, test.expected, t, i) 941 942 // make sure fragment links are also handled 943 checkLinkCase(site, test.link+"#intro", currentPage, test.relative, test.outputFormat, test.expected+"#intro", t, i) 944 }) 945 } 946 947 // TODO: and then the failure cases. 948 } 949 950 func checkLinkCase(site *Site, link string, currentPage page.Page, relative bool, outputFormat string, expected string, t *testing.T, i int) { 951 t.Helper() 952 if out, err := site.refLink(link, currentPage, relative, outputFormat); err != nil || out != expected { 953 t.Fatalf("[%d] Expected %q from %q to resolve to %q, got %q - error: %s", i, link, currentPage.Pathc(), expected, out, err) 954 } 955 } 956 957 // https://github.com/gohugoio/hugo/issues/6952 958 func TestRefIssues(t *testing.T) { 959 b := newTestSitesBuilder(t) 960 b.WithContent( 961 "post/b1/index.md", "---\ntitle: pb1\n---\nRef: {{< ref \"b2\" >}}", 962 "post/b2/index.md", "---\ntitle: pb2\n---\n", 963 "post/nested-a/content-a.md", "---\ntitle: ca\n---\n{{< ref \"content-b\" >}}", 964 "post/nested-b/content-b.md", "---\ntitle: ca\n---\n", 965 ) 966 b.WithTemplates("index.html", `Home`) 967 b.WithTemplates("_default/single.html", `Content: {{ .Content }}`) 968 969 b.Build(BuildCfg{}) 970 971 b.AssertFileContent("public/post/b1/index.html", `Content: <p>Ref: http://example.com/post/b2/</p>`) 972 b.AssertFileContent("public/post/nested-a/content-a/index.html", `Content: http://example.com/post/nested-b/content-b/`) 973 } 974 975 func TestClassCollector(t *testing.T) { 976 for _, minify := range []bool{false, true} { 977 t.Run(fmt.Sprintf("minify-%t", minify), func(t *testing.T) { 978 statsFilename := "hugo_stats.json" 979 defer os.Remove(statsFilename) 980 981 b := newTestSitesBuilder(t) 982 b.WithConfigFile("toml", fmt.Sprintf(` 983 984 985 minify = %t 986 987 [build] 988 writeStats = true 989 990 `, minify)) 991 992 b.WithTemplates("index.html", ` 993 994 <div id="el1" class="a b c">Foo</div> 995 996 Some text. 997 998 <div class="c d e" id="el2">Foo</div> 999 1000 <span class=z>FOO</span> 1001 1002 <a class="text-base hover:text-gradient inline-block px-3 pb-1 rounded lowercase" href="{{ .RelPermalink }}">{{ .Title }}</a> 1003 1004 1005 `) 1006 1007 b.WithContent("p1.md", "") 1008 1009 b.Build(BuildCfg{}) 1010 1011 b.AssertFileContent("hugo_stats.json", ` 1012 { 1013 "htmlElements": { 1014 "tags": [ 1015 "a", 1016 "div", 1017 "span" 1018 ], 1019 "classes": [ 1020 "a", 1021 "b", 1022 "c", 1023 "d", 1024 "e", 1025 "hover:text-gradient", 1026 "inline-block", 1027 "lowercase", 1028 "pb-1", 1029 "px-3", 1030 "rounded", 1031 "text-base", 1032 "z" 1033 ], 1034 "ids": [ 1035 "el1", 1036 "el2" 1037 ] 1038 } 1039 } 1040 `) 1041 }) 1042 } 1043 } 1044 1045 func TestClassCollectorStress(t *testing.T) { 1046 statsFilename := "hugo_stats.json" 1047 defer os.Remove(statsFilename) 1048 1049 b := newTestSitesBuilder(t) 1050 b.WithConfigFile("toml", ` 1051 1052 disableKinds = ["home", "section", "term", "taxonomy" ] 1053 1054 [languages] 1055 [languages.en] 1056 [languages.nb] 1057 [languages.no] 1058 [languages.sv] 1059 1060 1061 [build] 1062 writeStats = true 1063 1064 `) 1065 1066 b.WithTemplates("_default/single.html", ` 1067 <div class="c d e" id="el2">Foo</div> 1068 1069 Some text. 1070 1071 {{ $n := index (shuffle (seq 1 20)) 0 }} 1072 1073 {{ "<span class=_a>Foo</span>" | strings.Repeat $n | safeHTML }} 1074 1075 <div class="{{ .Title }}"> 1076 ABC. 1077 </div> 1078 1079 <div class="f"></div> 1080 1081 {{ $n := index (shuffle (seq 1 5)) 0 }} 1082 1083 {{ "<hr class=p-3>" | safeHTML }} 1084 1085 `) 1086 1087 for _, lang := range []string{"en", "nb", "no", "sv"} { 1088 for i := 100; i <= 999; i++ { 1089 b.WithContent(fmt.Sprintf("p%d.%s.md", i, lang), fmt.Sprintf("---\ntitle: p%s%d\n---", lang, i)) 1090 } 1091 } 1092 1093 b.Build(BuildCfg{}) 1094 1095 contentMem := b.FileContent(statsFilename) 1096 cb, err := ioutil.ReadFile(statsFilename) 1097 b.Assert(err, qt.IsNil) 1098 contentFile := string(cb) 1099 1100 for _, content := range []string{contentMem, contentFile} { 1101 1102 stats := &publisher.PublishStats{} 1103 b.Assert(json.Unmarshal([]byte(content), stats), qt.IsNil) 1104 1105 els := stats.HTMLElements 1106 1107 b.Assert(els.Classes, qt.HasLen, 3606) // (4 * 900) + 4 +2 1108 b.Assert(els.Tags, qt.HasLen, 8) 1109 b.Assert(els.IDs, qt.HasLen, 1) 1110 } 1111 }