github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/hugolib/site_test.go (about) 1 // Copyright 2019 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package hugolib 15 16 import ( 17 "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 ...interface{}) *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("blackfriday", 291 map[string]interface{}{ 292 "plainIDAnchors": true, 293 }) 294 295 cfg.Set("uglyURLs", uglyURLs) 296 297 sources := [][2]string{ 298 {filepath.FromSlash("sect/doc1.md"), "---\nmarkup: markdown\n---\n# title\nsome *content*"}, 299 {filepath.FromSlash("sect/doc2.md"), "---\nurl: /ugly.html\nmarkup: markdown\n---\n# title\ndoc2 *content*"}, 300 } 301 302 for _, src := range sources { 303 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 304 } 305 306 writeSource(t, fs, filepath.Join("layouts", "index.html"), "Home Sweet {{ if.IsHome }}Home{{ end }}.") 307 writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}{{ if.IsHome }}This is not home!{{ end }}") 308 writeSource(t, fs, filepath.Join("layouts", "404.html"), "Page Not Found.{{ if.IsHome }}This is not home!{{ end }}") 309 writeSource(t, fs, filepath.Join("layouts", "rss.xml"), "<root>RSS</root>") 310 writeSource(t, fs, filepath.Join("layouts", "sitemap.xml"), "<root>SITEMAP</root>") 311 312 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 313 314 var expectedPagePath string 315 if uglyURLs { 316 expectedPagePath = "public/sect/doc1.html" 317 } else { 318 expectedPagePath = "public/sect/doc1/index.html" 319 } 320 321 tests := []struct { 322 doc string 323 expected string 324 }{ 325 {filepath.FromSlash("public/index.html"), "Home Sweet Home."}, 326 {filepath.FromSlash(expectedPagePath), "<h1 id=\"title\">title</h1>\n<p>some <em>content</em></p>\n"}, 327 {filepath.FromSlash("public/404.html"), "Page Not Found."}, 328 {filepath.FromSlash("public/index.xml"), "<root>RSS</root>"}, 329 {filepath.FromSlash("public/sitemap.xml"), "<root>SITEMAP</root>"}, 330 // Issue #1923 331 {filepath.FromSlash("public/ugly.html"), "<h1 id=\"title\">title</h1>\n<p>doc2 <em>content</em></p>\n"}, 332 } 333 334 for _, p := range s.RegularPages() { 335 c.Assert(p.IsHome(), qt.Equals, false) 336 } 337 338 for _, test := range tests { 339 content := readDestination(t, fs, test.doc) 340 341 if content != test.expected { 342 t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content) 343 } 344 } 345 } 346 347 // Issue #3355 348 func TestShouldNotWriteZeroLengthFilesToDestination(t *testing.T) { 349 cfg, fs := newTestCfg() 350 351 writeSource(t, fs, filepath.Join("content", "simple.html"), "simple") 352 writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}") 353 writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "") 354 355 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 356 th := newTestHelper(s.Cfg, s.Fs, t) 357 358 th.assertFileNotExist(filepath.Join("public", "index.html")) 359 } 360 361 func TestMainSections(t *testing.T) { 362 c := qt.New(t) 363 for _, paramSet := range []bool{false, true} { 364 c.Run(fmt.Sprintf("param-%t", paramSet), func(c *qt.C) { 365 v := config.New() 366 if paramSet { 367 v.Set("params", map[string]interface{}{ 368 "mainSections": []string{"a1", "a2"}, 369 }) 370 } 371 372 b := newTestSitesBuilder(c).WithViper(v) 373 374 for i := 0; i < 20; i++ { 375 b.WithContent(fmt.Sprintf("page%d.md", i), `--- 376 title: "Page" 377 --- 378 `) 379 } 380 381 for i := 0; i < 5; i++ { 382 b.WithContent(fmt.Sprintf("blog/page%d.md", i), `--- 383 title: "Page" 384 tags: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] 385 --- 386 `) 387 } 388 389 for i := 0; i < 3; i++ { 390 b.WithContent(fmt.Sprintf("docs/page%d.md", i), `--- 391 title: "Page" 392 --- 393 `) 394 } 395 396 b.WithTemplates("index.html", ` 397 mainSections: {{ .Site.Params.mainSections }} 398 399 {{ range (where .Site.RegularPages "Type" "in" .Site.Params.mainSections) }} 400 Main section page: {{ .RelPermalink }} 401 {{ end }} 402 `) 403 404 b.Build(BuildCfg{}) 405 406 if paramSet { 407 b.AssertFileContent("public/index.html", "mainSections: [a1 a2]") 408 } else { 409 b.AssertFileContent("public/index.html", "mainSections: [blog]", "Main section page: /blog/page3/") 410 } 411 }) 412 } 413 } 414 415 // Issue #1176 416 func TestSectionNaming(t *testing.T) { 417 for _, canonify := range []bool{true, false} { 418 for _, uglify := range []bool{true, false} { 419 for _, pluralize := range []bool{true, false} { 420 canonify := canonify 421 uglify := uglify 422 pluralize := pluralize 423 t.Run(fmt.Sprintf("canonify=%t,uglify=%t,pluralize=%t", canonify, uglify, pluralize), func(t *testing.T) { 424 t.Parallel() 425 doTestSectionNaming(t, canonify, uglify, pluralize) 426 }) 427 } 428 } 429 } 430 } 431 432 func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) { 433 c := qt.New(t) 434 435 var expectedPathSuffix string 436 437 if uglify { 438 expectedPathSuffix = ".html" 439 } else { 440 expectedPathSuffix = "/index.html" 441 } 442 443 sources := [][2]string{ 444 {filepath.FromSlash("sect/doc1.html"), "doc1"}, 445 // Add one more page to sect to make sure sect is picked in mainSections 446 {filepath.FromSlash("sect/sect.html"), "sect"}, 447 {filepath.FromSlash("Fish and Chips/doc2.html"), "doc2"}, 448 {filepath.FromSlash("ラーメン/doc3.html"), "doc3"}, 449 } 450 451 cfg, fs := newTestCfg() 452 453 cfg.Set("baseURL", "http://auth/sub/") 454 cfg.Set("uglyURLs", uglify) 455 cfg.Set("pluralizeListTitles", pluralize) 456 cfg.Set("canonifyURLs", canonify) 457 458 for _, src := range sources { 459 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 460 } 461 462 writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}") 463 writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "{{ .Kind }}|{{.Title}}") 464 465 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 466 467 mainSections, err := s.Info.Param("mainSections") 468 c.Assert(err, qt.IsNil) 469 c.Assert(mainSections, qt.DeepEquals, []string{"sect"}) 470 471 th := newTestHelper(s.Cfg, s.Fs, t) 472 tests := []struct { 473 doc string 474 pluralAware bool 475 expected string 476 }{ 477 {filepath.FromSlash(fmt.Sprintf("sect/doc1%s", expectedPathSuffix)), false, "doc1"}, 478 {filepath.FromSlash(fmt.Sprintf("sect%s", expectedPathSuffix)), true, "Sect"}, 479 {filepath.FromSlash(fmt.Sprintf("fish-and-chips/doc2%s", expectedPathSuffix)), false, "doc2"}, 480 {filepath.FromSlash(fmt.Sprintf("fish-and-chips%s", expectedPathSuffix)), true, "Fish and Chips"}, 481 {filepath.FromSlash(fmt.Sprintf("ラーメン/doc3%s", expectedPathSuffix)), false, "doc3"}, 482 {filepath.FromSlash(fmt.Sprintf("ラーメン%s", expectedPathSuffix)), true, "ラーメン"}, 483 } 484 485 for _, test := range tests { 486 487 if test.pluralAware && pluralize { 488 test.expected = flect.Pluralize(test.expected) 489 } 490 491 th.assertFileContent(filepath.Join("public", test.doc), test.expected) 492 } 493 } 494 495 func TestAbsURLify(t *testing.T) { 496 t.Parallel() 497 sources := [][2]string{ 498 {filepath.FromSlash("sect/doc1.html"), "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"}, 499 {filepath.FromSlash("blue/doc2.html"), "---\nf: t\n---\n<!doctype html><html><body>more content</body></html>"}, 500 } 501 for _, baseURL := range []string{"http://auth/bub", "http://base", "//base"} { 502 for _, canonify := range []bool{true, false} { 503 504 cfg, fs := newTestCfg() 505 506 cfg.Set("uglyURLs", true) 507 cfg.Set("canonifyURLs", canonify) 508 cfg.Set("baseURL", baseURL) 509 510 for _, src := range sources { 511 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 512 } 513 514 writeSource(t, fs, filepath.Join("layouts", "blue/single.html"), templateWithURLAbs) 515 516 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 517 th := newTestHelper(s.Cfg, s.Fs, t) 518 519 tests := []struct { 520 file, expected string 521 }{ 522 {"public/blue/doc2.html", "<a href=\"%s/foobar.jpg\">Going</a>"}, 523 {"public/sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"}, 524 } 525 526 for _, test := range tests { 527 528 expected := test.expected 529 530 if strings.Contains(expected, "%s") { 531 expected = fmt.Sprintf(expected, baseURL) 532 } 533 534 if !canonify { 535 expected = strings.Replace(expected, baseURL, "", -1) 536 } 537 538 th.assertFileContent(test.file, expected) 539 540 } 541 } 542 } 543 } 544 545 var weightedPage1 = `+++ 546 weight = "2" 547 title = "One" 548 my_param = "foo" 549 my_date = 1979-05-27T07:32:00Z 550 +++ 551 Front Matter with Ordered Pages` 552 553 var weightedPage2 = `+++ 554 weight = "6" 555 title = "Two" 556 publishdate = "2012-03-05" 557 my_param = "foo" 558 +++ 559 Front Matter with Ordered Pages 2` 560 561 var weightedPage3 = `+++ 562 weight = "4" 563 title = "Three" 564 date = "2012-04-06" 565 publishdate = "2012-04-06" 566 my_param = "bar" 567 only_one = "yes" 568 my_date = 2010-05-27T07:32:00Z 569 +++ 570 Front Matter with Ordered Pages 3` 571 572 var weightedPage4 = `+++ 573 weight = "4" 574 title = "Four" 575 date = "2012-01-01" 576 publishdate = "2012-01-01" 577 my_param = "baz" 578 my_date = 2010-05-27T07:32:00Z 579 summary = "A _custom_ summary" 580 categories = [ "hugo" ] 581 +++ 582 Front Matter with Ordered Pages 4. This is longer content` 583 584 var weightedPage5 = `+++ 585 weight = "5" 586 title = "Five" 587 588 [_build] 589 render = "never" 590 +++ 591 Front Matter with Ordered Pages 5` 592 593 var weightedSources = [][2]string{ 594 {filepath.FromSlash("sect/doc1.md"), weightedPage1}, 595 {filepath.FromSlash("sect/doc2.md"), weightedPage2}, 596 {filepath.FromSlash("sect/doc3.md"), weightedPage3}, 597 {filepath.FromSlash("sect/doc4.md"), weightedPage4}, 598 {filepath.FromSlash("sect/doc5.md"), weightedPage5}, 599 } 600 601 func TestOrderedPages(t *testing.T) { 602 t.Parallel() 603 cfg, fs := newTestCfg() 604 cfg.Set("baseURL", "http://auth/bub") 605 606 for _, src := range weightedSources { 607 writeSource(t, fs, filepath.Join("content", src[0]), src[1]) 608 } 609 610 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 611 612 if s.getPage(page.KindSection, "sect").Pages()[1].Title() != "Three" || s.getPage(page.KindSection, "sect").Pages()[2].Title() != "Four" { 613 t.Error("Pages in unexpected order.") 614 } 615 616 bydate := s.RegularPages().ByDate() 617 618 if bydate[0].Title() != "One" { 619 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title()) 620 } 621 622 rev := bydate.Reverse() 623 if rev[0].Title() != "Three" { 624 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title()) 625 } 626 627 bypubdate := s.RegularPages().ByPublishDate() 628 629 if bypubdate[0].Title() != "One" { 630 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bypubdate[0].Title()) 631 } 632 633 rbypubdate := bypubdate.Reverse() 634 if rbypubdate[0].Title() != "Three" { 635 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rbypubdate[0].Title()) 636 } 637 638 bylength := s.RegularPages().ByLength() 639 if bylength[0].Title() != "One" { 640 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title()) 641 } 642 643 rbylength := bylength.Reverse() 644 if rbylength[0].Title() != "Four" { 645 t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title()) 646 } 647 } 648 649 var groupedSources = [][2]string{ 650 {filepath.FromSlash("sect1/doc1.md"), weightedPage1}, 651 {filepath.FromSlash("sect1/doc2.md"), weightedPage2}, 652 {filepath.FromSlash("sect2/doc3.md"), weightedPage3}, 653 {filepath.FromSlash("sect3/doc4.md"), weightedPage4}, 654 } 655 656 func TestGroupedPages(t *testing.T) { 657 t.Parallel() 658 defer func() { 659 if r := recover(); r != nil { 660 fmt.Println("Recovered in f", r) 661 } 662 }() 663 664 cfg, fs := newTestCfg() 665 cfg.Set("baseURL", "http://auth/bub") 666 667 writeSourcesToSource(t, "content", fs, groupedSources...) 668 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 669 670 rbysection, err := s.RegularPages().GroupBy("Section", "desc") 671 if err != nil { 672 t.Fatalf("Unable to make PageGroup array: %s", err) 673 } 674 675 if rbysection[0].Key != "sect3" { 676 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect3", rbysection[0].Key) 677 } 678 if rbysection[1].Key != "sect2" { 679 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "sect2", rbysection[1].Key) 680 } 681 if rbysection[2].Key != "sect1" { 682 t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "sect1", rbysection[2].Key) 683 } 684 if rbysection[0].Pages[0].Title() != "Four" { 685 t.Errorf("PageGroup has an unexpected page. First group's pages should have '%s', got '%s'", "Four", rbysection[0].Pages[0].Title()) 686 } 687 if len(rbysection[2].Pages) != 2 { 688 t.Errorf("PageGroup has unexpected number of pages. Third group should have '%d' pages, got '%d' pages", 2, len(rbysection[2].Pages)) 689 } 690 691 bytype, err := s.RegularPages().GroupBy("Type", "asc") 692 if err != nil { 693 t.Fatalf("Unable to make PageGroup array: %s", err) 694 } 695 if bytype[0].Key != "sect1" { 696 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect1", bytype[0].Key) 697 } 698 if bytype[1].Key != "sect2" { 699 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "sect2", bytype[1].Key) 700 } 701 if bytype[2].Key != "sect3" { 702 t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "sect3", bytype[2].Key) 703 } 704 if bytype[2].Pages[0].Title() != "Four" { 705 t.Errorf("PageGroup has an unexpected page. Third group's data should have '%s', got '%s'", "Four", bytype[0].Pages[0].Title()) 706 } 707 if len(bytype[0].Pages) != 2 { 708 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(bytype[2].Pages)) 709 } 710 711 bydate, err := s.RegularPages().GroupByDate("2006-01", "asc") 712 if err != nil { 713 t.Fatalf("Unable to make PageGroup array: %s", err) 714 } 715 if bydate[0].Key != "0001-01" { 716 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "0001-01", bydate[0].Key) 717 } 718 if bydate[1].Key != "2012-01" { 719 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "2012-01", bydate[1].Key) 720 } 721 722 bypubdate, err := s.RegularPages().GroupByPublishDate("2006") 723 if err != nil { 724 t.Fatalf("Unable to make PageGroup array: %s", err) 725 } 726 if bypubdate[0].Key != "2012" { 727 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "2012", bypubdate[0].Key) 728 } 729 if bypubdate[1].Key != "0001" { 730 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "0001", bypubdate[1].Key) 731 } 732 if bypubdate[0].Pages[0].Title() != "Three" { 733 t.Errorf("PageGroup has an unexpected page. Third group's pages should have '%s', got '%s'", "Three", bypubdate[0].Pages[0].Title()) 734 } 735 if len(bypubdate[0].Pages) != 3 { 736 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 3, len(bypubdate[0].Pages)) 737 } 738 739 byparam, err := s.RegularPages().GroupByParam("my_param", "desc") 740 if err != nil { 741 t.Fatalf("Unable to make PageGroup array: %s", err) 742 } 743 if byparam[0].Key != "foo" { 744 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "foo", byparam[0].Key) 745 } 746 if byparam[1].Key != "baz" { 747 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "baz", byparam[1].Key) 748 } 749 if byparam[2].Key != "bar" { 750 t.Errorf("PageGroup array in unexpected order. Third group key should be '%s', got '%s'", "bar", byparam[2].Key) 751 } 752 if byparam[2].Pages[0].Title() != "Three" { 753 t.Errorf("PageGroup has an unexpected page. Third group's pages should have '%s', got '%s'", "Three", byparam[2].Pages[0].Title()) 754 } 755 if len(byparam[0].Pages) != 2 { 756 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(byparam[0].Pages)) 757 } 758 759 _, err = s.RegularPages().GroupByParam("not_exist") 760 if err == nil { 761 t.Errorf("GroupByParam didn't return an expected error") 762 } 763 764 byOnlyOneParam, err := s.RegularPages().GroupByParam("only_one") 765 if err != nil { 766 t.Fatalf("Unable to make PageGroup array: %s", err) 767 } 768 if len(byOnlyOneParam) != 1 { 769 t.Errorf("PageGroup array has unexpected elements. Group length should be '%d', got '%d'", 1, len(byOnlyOneParam)) 770 } 771 if byOnlyOneParam[0].Key != "yes" { 772 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "yes", byOnlyOneParam[0].Key) 773 } 774 775 byParamDate, err := s.RegularPages().GroupByParamDate("my_date", "2006-01") 776 if err != nil { 777 t.Fatalf("Unable to make PageGroup array: %s", err) 778 } 779 if byParamDate[0].Key != "2010-05" { 780 t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "2010-05", byParamDate[0].Key) 781 } 782 if byParamDate[1].Key != "1979-05" { 783 t.Errorf("PageGroup array in unexpected order. Second group key should be '%s', got '%s'", "1979-05", byParamDate[1].Key) 784 } 785 if byParamDate[1].Pages[0].Title() != "One" { 786 t.Errorf("PageGroup has an unexpected page. Second group's pages should have '%s', got '%s'", "One", byParamDate[1].Pages[0].Title()) 787 } 788 if len(byParamDate[0].Pages) != 2 { 789 t.Errorf("PageGroup has unexpected number of pages. First group should have '%d' pages, got '%d' pages", 2, len(byParamDate[2].Pages)) 790 } 791 } 792 793 var pageWithWeightedTaxonomies1 = `+++ 794 tags = [ "a", "b", "c" ] 795 tags_weight = 22 796 categories = ["d"] 797 title = "foo" 798 categories_weight = 44 799 +++ 800 Front Matter with weighted tags and categories` 801 802 var pageWithWeightedTaxonomies2 = `+++ 803 tags = "a" 804 tags_weight = 33 805 title = "bar" 806 categories = [ "d", "e" ] 807 categories_weight = 11.0 808 alias = "spf13" 809 date = 1979-05-27T07:32:00Z 810 +++ 811 Front Matter with weighted tags and categories` 812 813 var pageWithWeightedTaxonomies3 = `+++ 814 title = "bza" 815 categories = [ "e" ] 816 categories_weight = 11 817 alias = "spf13" 818 date = 2010-05-27T07:32:00Z 819 +++ 820 Front Matter with weighted tags and categories` 821 822 func TestWeightedTaxonomies(t *testing.T) { 823 t.Parallel() 824 sources := [][2]string{ 825 {filepath.FromSlash("sect/doc1.md"), pageWithWeightedTaxonomies2}, 826 {filepath.FromSlash("sect/doc2.md"), pageWithWeightedTaxonomies1}, 827 {filepath.FromSlash("sect/doc3.md"), pageWithWeightedTaxonomies3}, 828 } 829 taxonomies := make(map[string]string) 830 831 taxonomies["tag"] = "tags" 832 taxonomies["category"] = "categories" 833 834 cfg, fs := newTestCfg() 835 836 cfg.Set("baseURL", "http://auth/bub") 837 cfg.Set("taxonomies", taxonomies) 838 839 writeSourcesToSource(t, "content", fs, sources...) 840 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 841 842 if s.Taxonomies()["tags"]["a"][0].Page.Title() != "foo" { 843 t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Taxonomies()["tags"]["a"][0].Page.Title()) 844 } 845 846 if s.Taxonomies()["categories"]["d"][0].Page.Title() != "bar" { 847 t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Taxonomies()["categories"]["d"][0].Page.Title()) 848 } 849 850 if s.Taxonomies()["categories"]["e"][0].Page.Title() != "bza" { 851 t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Taxonomies()["categories"]["e"][0].Page.Title()) 852 } 853 } 854 855 func setupLinkingMockSite(t *testing.T) *Site { 856 sources := [][2]string{ 857 {filepath.FromSlash("level2/unique.md"), ""}, 858 {filepath.FromSlash("_index.md"), ""}, 859 {filepath.FromSlash("common.md"), ""}, 860 {filepath.FromSlash("rootfile.md"), ""}, 861 {filepath.FromSlash("root-image.png"), ""}, 862 863 {filepath.FromSlash("level2/2-root.md"), ""}, 864 {filepath.FromSlash("level2/common.md"), ""}, 865 866 {filepath.FromSlash("level2/2-image.png"), ""}, 867 {filepath.FromSlash("level2/common.png"), ""}, 868 869 {filepath.FromSlash("level2/level3/start.md"), ""}, 870 {filepath.FromSlash("level2/level3/_index.md"), ""}, 871 {filepath.FromSlash("level2/level3/3-root.md"), ""}, 872 {filepath.FromSlash("level2/level3/common.md"), ""}, 873 {filepath.FromSlash("level2/level3/3-image.png"), ""}, 874 {filepath.FromSlash("level2/level3/common.png"), ""}, 875 876 {filepath.FromSlash("level2/level3/embedded.dot.md"), ""}, 877 878 {filepath.FromSlash("leafbundle/index.md"), ""}, 879 } 880 881 cfg, fs := newTestCfg() 882 883 cfg.Set("baseURL", "http://auth/") 884 cfg.Set("uglyURLs", false) 885 cfg.Set("outputs", map[string]interface{}{ 886 "page": []string{"HTML", "AMP"}, 887 }) 888 cfg.Set("pluralizeListTitles", false) 889 cfg.Set("canonifyURLs", false) 890 cfg.Set("blackfriday", 891 map[string]interface{}{}) 892 writeSourcesToSource(t, "content", fs, sources...) 893 return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 894 } 895 896 func TestRefLinking(t *testing.T) { 897 t.Parallel() 898 site := setupLinkingMockSite(t) 899 900 currentPage := site.getPage(page.KindPage, "level2/level3/start.md") 901 if currentPage == nil { 902 t.Fatalf("failed to find current page in site") 903 } 904 905 for i, test := range []struct { 906 link string 907 outputFormat string 908 relative bool 909 expected string 910 }{ 911 // different refs resolving to the same unique filename: 912 {"/level2/unique.md", "", true, "/level2/unique/"}, 913 {"../unique.md", "", true, "/level2/unique/"}, 914 {"unique.md", "", true, "/level2/unique/"}, 915 916 {"level2/common.md", "", true, "/level2/common/"}, 917 {"3-root.md", "", true, "/level2/level3/3-root/"}, 918 {"../..", "", true, "/"}, 919 920 // different refs resolving to the same ambiguous top-level filename: 921 {"../../common.md", "", true, "/common/"}, 922 {"/common.md", "", true, "/common/"}, 923 924 // different refs resolving to the same ambiguous level-2 filename: 925 {"/level2/common.md", "", true, "/level2/common/"}, 926 {"../common.md", "", true, "/level2/common/"}, 927 {"common.md", "", true, "/level2/level3/common/"}, 928 929 // different refs resolving to the same section: 930 {"/level2", "", true, "/level2/"}, 931 {"..", "", true, "/level2/"}, 932 {"../", "", true, "/level2/"}, 933 934 // different refs resolving to the same subsection: 935 {"/level2/level3", "", true, "/level2/level3/"}, 936 {"/level2/level3/_index.md", "", true, "/level2/level3/"}, 937 {".", "", true, "/level2/level3/"}, 938 {"./", "", true, "/level2/level3/"}, 939 940 // try to confuse parsing 941 {"embedded.dot.md", "", true, "/level2/level3/embedded.dot/"}, 942 943 // test empty link, as well as fragment only link 944 {"", "", true, ""}, 945 } { 946 t.Run(fmt.Sprintf("t%dt", i), func(t *testing.T) { 947 checkLinkCase(site, test.link, currentPage, test.relative, test.outputFormat, test.expected, t, i) 948 949 // make sure fragment links are also handled 950 checkLinkCase(site, test.link+"#intro", currentPage, test.relative, test.outputFormat, test.expected+"#intro", t, i) 951 }) 952 } 953 954 // TODO: and then the failure cases. 955 } 956 957 func checkLinkCase(site *Site, link string, currentPage page.Page, relative bool, outputFormat string, expected string, t *testing.T, i int) { 958 t.Helper() 959 if out, err := site.refLink(link, currentPage, relative, outputFormat); err != nil || out != expected { 960 t.Fatalf("[%d] Expected %q from %q to resolve to %q, got %q - error: %s", i, link, currentPage.Path(), expected, out, err) 961 } 962 } 963 964 // https://github.com/gohugoio/hugo/issues/6952 965 func TestRefIssues(t *testing.T) { 966 b := newTestSitesBuilder(t) 967 b.WithContent( 968 "post/b1/index.md", "---\ntitle: pb1\n---\nRef: {{< ref \"b2\" >}}", 969 "post/b2/index.md", "---\ntitle: pb2\n---\n", 970 "post/nested-a/content-a.md", "---\ntitle: ca\n---\n{{< ref \"content-b\" >}}", 971 "post/nested-b/content-b.md", "---\ntitle: ca\n---\n", 972 ) 973 b.WithTemplates("index.html", `Home`) 974 b.WithTemplates("_default/single.html", `Content: {{ .Content }}`) 975 976 b.Build(BuildCfg{}) 977 978 b.AssertFileContent("public/post/b1/index.html", `Content: <p>Ref: http://example.com/post/b2/</p>`) 979 b.AssertFileContent("public/post/nested-a/content-a/index.html", `Content: http://example.com/post/nested-b/content-b/`) 980 } 981 982 func TestClassCollector(t *testing.T) { 983 for _, minify := range []bool{false, true} { 984 t.Run(fmt.Sprintf("minify-%t", minify), func(t *testing.T) { 985 statsFilename := "hugo_stats.json" 986 defer os.Remove(statsFilename) 987 988 b := newTestSitesBuilder(t) 989 b.WithConfigFile("toml", fmt.Sprintf(` 990 991 992 minify = %t 993 994 [build] 995 writeStats = true 996 997 `, minify)) 998 999 b.WithTemplates("index.html", ` 1000 1001 <div id="el1" class="a b c">Foo</div> 1002 1003 Some text. 1004 1005 <div class="c d e" id="el2">Foo</div> 1006 1007 <span class=z>FOO</span> 1008 1009 <a class="text-base hover:text-gradient inline-block px-3 pb-1 rounded lowercase" href="{{ .RelPermalink }}">{{ .Title }}</a> 1010 1011 1012 `) 1013 1014 b.WithContent("p1.md", "") 1015 1016 b.Build(BuildCfg{}) 1017 1018 b.AssertFileContent("hugo_stats.json", ` 1019 { 1020 "htmlElements": { 1021 "tags": [ 1022 "a", 1023 "div", 1024 "span" 1025 ], 1026 "classes": [ 1027 "a", 1028 "b", 1029 "c", 1030 "d", 1031 "e", 1032 "hover:text-gradient", 1033 "inline-block", 1034 "lowercase", 1035 "pb-1", 1036 "px-3", 1037 "rounded", 1038 "text-base", 1039 "z" 1040 ], 1041 "ids": [ 1042 "el1", 1043 "el2" 1044 ] 1045 } 1046 } 1047 `) 1048 }) 1049 } 1050 } 1051 1052 func TestClassCollectorStress(t *testing.T) { 1053 statsFilename := "hugo_stats.json" 1054 defer os.Remove(statsFilename) 1055 1056 b := newTestSitesBuilder(t) 1057 b.WithConfigFile("toml", ` 1058 1059 disableKinds = ["home", "section", "term", "taxonomy" ] 1060 1061 [languages] 1062 [languages.en] 1063 [languages.nb] 1064 [languages.no] 1065 [languages.sv] 1066 1067 1068 [build] 1069 writeStats = true 1070 1071 `) 1072 1073 b.WithTemplates("_default/single.html", ` 1074 <div class="c d e" id="el2">Foo</div> 1075 1076 Some text. 1077 1078 {{ $n := index (shuffle (seq 1 20)) 0 }} 1079 1080 {{ "<span class=_a>Foo</span>" | strings.Repeat $n | safeHTML }} 1081 1082 <div class="{{ .Title }}"> 1083 ABC. 1084 </div> 1085 1086 <div class="f"></div> 1087 1088 {{ $n := index (shuffle (seq 1 5)) 0 }} 1089 1090 {{ "<hr class=p-3>" | safeHTML }} 1091 1092 `) 1093 1094 for _, lang := range []string{"en", "nb", "no", "sv"} { 1095 for i := 100; i <= 999; i++ { 1096 b.WithContent(fmt.Sprintf("p%d.%s.md", i, lang), fmt.Sprintf("---\ntitle: p%s%d\n---", lang, i)) 1097 } 1098 } 1099 1100 b.Build(BuildCfg{}) 1101 1102 contentMem := b.FileContent(statsFilename) 1103 cb, err := ioutil.ReadFile(statsFilename) 1104 b.Assert(err, qt.IsNil) 1105 contentFile := string(cb) 1106 1107 for _, content := range []string{contentMem, contentFile} { 1108 1109 stats := &publisher.PublishStats{} 1110 b.Assert(json.Unmarshal([]byte(content), stats), qt.IsNil) 1111 1112 els := stats.HTMLElements 1113 1114 b.Assert(els.Classes, qt.HasLen, 3606) // (4 * 900) + 4 +2 1115 b.Assert(els.Tags, qt.HasLen, 8) 1116 b.Assert(els.IDs, qt.HasLen, 1) 1117 } 1118 }