page_test.go (57555B)
1 // Copyright 2019 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package hugolib 15 16 import ( 17 "fmt" 18 "html/template" 19 "os" 20 "path/filepath" 21 "strings" 22 "testing" 23 "time" 24 25 "github.com/bep/clock" 26 "github.com/gohugoio/hugo/htesting" 27 "github.com/gohugoio/hugo/markup/asciidocext" 28 "github.com/gohugoio/hugo/markup/rst" 29 "github.com/gohugoio/hugo/tpl" 30 31 "github.com/gohugoio/hugo/config" 32 33 "github.com/gohugoio/hugo/common/htime" 34 "github.com/gohugoio/hugo/common/loggers" 35 36 "github.com/gohugoio/hugo/hugofs" 37 38 "github.com/gohugoio/hugo/resources/page" 39 "github.com/gohugoio/hugo/resources/resource" 40 "github.com/spf13/jwalterweatherman" 41 42 qt "github.com/frankban/quicktest" 43 "github.com/gohugoio/hugo/deps" 44 ) 45 46 const ( 47 homePage = "---\ntitle: Home\n---\nHome Page Content\n" 48 simplePage = "---\ntitle: Simple\n---\nSimple Page\n" 49 50 simplePageRFC3339Date = "---\ntitle: RFC3339 Date\ndate: \"2013-05-17T16:59:30Z\"\n---\nrfc3339 content" 51 52 simplePageWithoutSummaryDelimiter = `--- 53 title: SimpleWithoutSummaryDelimiter 54 --- 55 [Lorem ipsum](https://lipsum.com/) dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 56 57 Additional text. 58 59 Further text. 60 ` 61 62 simplePageWithSummaryDelimiter = `--- 63 title: Simple 64 --- 65 Summary Next Line 66 67 <!--more--> 68 Some more text 69 ` 70 71 simplePageWithSummaryParameter = `--- 72 title: SimpleWithSummaryParameter 73 summary: "Page with summary parameter and [a link](http://www.example.com/)" 74 --- 75 76 Some text. 77 78 Some more text. 79 ` 80 81 simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder = `--- 82 title: Simple 83 --- 84 The [best static site generator][hugo].[^1] 85 <!--more--> 86 [hugo]: http://gohugo.io/ 87 [^1]: Many people say so. 88 ` 89 simplePageWithShortcodeInSummary = `--- 90 title: Simple 91 --- 92 Summary Next Line. {{<figure src="/not/real" >}}. 93 More text here. 94 95 Some more text 96 ` 97 98 simplePageWithSummaryDelimiterSameLine = `--- 99 title: Simple 100 --- 101 Summary Same Line<!--more--> 102 103 Some more text 104 ` 105 106 simplePageWithAllCJKRunes = `--- 107 title: Simple 108 --- 109 110 111 € € € € € 112 你好 113 도형이 114 カテゴリー 115 116 117 ` 118 119 simplePageWithMainEnglishWithCJKRunes = `--- 120 title: Simple 121 --- 122 123 124 In Chinese, 好 means good. In Chinese, 好 means good. 125 In Chinese, 好 means good. In Chinese, 好 means good. 126 In Chinese, 好 means good. In Chinese, 好 means good. 127 In Chinese, 好 means good. In Chinese, 好 means good. 128 In Chinese, 好 means good. In Chinese, 好 means good. 129 In Chinese, 好 means good. In Chinese, 好 means good. 130 In Chinese, 好 means good. In Chinese, 好 means good. 131 More then 70 words. 132 133 134 ` 135 simplePageWithMainEnglishWithCJKRunesSummary = "In Chinese, 好 means good. In Chinese, 好 means good. " + 136 "In Chinese, 好 means good. In Chinese, 好 means good. " + 137 "In Chinese, 好 means good. In Chinese, 好 means good. " + 138 "In Chinese, 好 means good. In Chinese, 好 means good. " + 139 "In Chinese, 好 means good. In Chinese, 好 means good. " + 140 "In Chinese, 好 means good. In Chinese, 好 means good. " + 141 "In Chinese, 好 means good. In Chinese, 好 means good." 142 143 simplePageWithIsCJKLanguageFalse = `--- 144 title: Simple 145 isCJKLanguage: false 146 --- 147 148 In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. 149 In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. 150 In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. 151 In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. 152 In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. 153 In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. 154 In Chinese, 好的啊 means good. In Chinese, 好的呀呀 means good enough. 155 More then 70 words. 156 157 158 ` 159 simplePageWithIsCJKLanguageFalseSummary = "In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. " + 160 "In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. " + 161 "In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. " + 162 "In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. " + 163 "In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. " + 164 "In Chinese, 好的啊 means good. In Chinese, 好的呀 means good. " + 165 "In Chinese, 好的啊 means good. In Chinese, 好的呀呀 means good enough." 166 167 simplePageWithLongContent = `--- 168 title: Simple 169 --- 170 171 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 172 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 173 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 174 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 175 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 176 culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit 177 amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore 178 et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation 179 ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor 180 in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 181 pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui 182 officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, 183 consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 184 dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco 185 laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in 186 reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. 187 Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia 188 deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur 189 adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna 190 aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi 191 ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in 192 voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 193 occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim 194 id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed 195 do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim 196 veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 197 consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 198 cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 199 proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem 200 ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 201 incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis 202 nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 203 Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 204 fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 205 culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit 206 amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore 207 et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation 208 ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor 209 in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 210 pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui 211 officia deserunt mollit anim id est laborum.` 212 213 pageWithToC = `--- 214 title: TOC 215 --- 216 For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke. 217 218 ## AA 219 220 I have no idea, of course, how long it took me to reach the limit of the plain, 221 but at last I entered the foothills, following a pretty little canyon upward 222 toward the mountains. Beside me frolicked a laughing brooklet, hurrying upon 223 its noisy way down to the silent sea. In its quieter pools I discovered many 224 small fish, of four-or five-pound weight I should imagine. In appearance, 225 except as to size and color, they were not unlike the whale of our own seas. As 226 I watched them playing about I discovered, not only that they suckled their 227 young, but that at intervals they rose to the surface to breathe as well as to 228 feed upon certain grasses and a strange, scarlet lichen which grew upon the 229 rocks just above the water line. 230 231 ### AAA 232 233 I remember I felt an extraordinary persuasion that I was being played with, 234 that presently, when I was upon the very verge of safety, this mysterious 235 death--as swift as the passage of light--would leap after me from the pit about 236 the cylinder and strike me down. ## BB 237 238 ### BBB 239 240 "You're a great Granser," he cried delightedly, "always making believe them little marks mean something." 241 ` 242 243 simplePageWithURL = `--- 244 title: Simple 245 url: simple/url/ 246 --- 247 Simple Page With URL` 248 249 simplePageWithSlug = `--- 250 title: Simple 251 slug: simple-slug 252 --- 253 Simple Page With Slug` 254 255 simplePageWithDate = `--- 256 title: Simple 257 date: '2013-10-15T06:16:13' 258 --- 259 Simple Page With Date` 260 261 UTF8Page = `--- 262 title: ラーメン 263 --- 264 UTF8 Page` 265 266 UTF8PageWithURL = `--- 267 title: ラーメン 268 url: ラーメン/url/ 269 --- 270 UTF8 Page With URL` 271 272 UTF8PageWithSlug = `--- 273 title: ラーメン 274 slug: ラーメン-slug 275 --- 276 UTF8 Page With Slug` 277 278 UTF8PageWithDate = `--- 279 title: ラーメン 280 date: '2013-10-15T06:16:13' 281 --- 282 UTF8 Page With Date` 283 ) 284 285 func checkPageTitle(t *testing.T, page page.Page, title string) { 286 if page.Title() != title { 287 t.Fatalf("Page title is: %s. Expected %s", page.Title(), title) 288 } 289 } 290 291 func checkPageContent(t *testing.T, page page.Page, expected string, msg ...any) { 292 t.Helper() 293 a := normalizeContent(expected) 294 b := normalizeContent(content(page)) 295 if a != b { 296 t.Fatalf("Page content is:\n%q\nExpected:\n%q (%q)", b, a, msg) 297 } 298 } 299 300 func normalizeContent(c string) string { 301 norm := c 302 norm = strings.Replace(norm, "\n", " ", -1) 303 norm = strings.Replace(norm, " ", " ", -1) 304 norm = strings.Replace(norm, " ", " ", -1) 305 norm = strings.Replace(norm, " ", " ", -1) 306 norm = strings.Replace(norm, "p> ", "p>", -1) 307 norm = strings.Replace(norm, "> <", "> <", -1) 308 return strings.TrimSpace(norm) 309 } 310 311 func checkPageTOC(t *testing.T, page page.Page, toc string) { 312 t.Helper() 313 if page.TableOfContents() != template.HTML(toc) { 314 t.Fatalf("Page TableOfContents is:\n%q.\nExpected %q", page.TableOfContents(), toc) 315 } 316 } 317 318 func checkPageSummary(t *testing.T, page page.Page, summary string, msg ...any) { 319 a := normalizeContent(string(page.Summary())) 320 b := normalizeContent(summary) 321 if a != b { 322 t.Fatalf("Page summary is:\n%q.\nExpected\n%q (%q)", a, b, msg) 323 } 324 } 325 326 func checkPageType(t *testing.T, page page.Page, pageType string) { 327 if page.Type() != pageType { 328 t.Fatalf("Page type is: %s. Expected: %s", page.Type(), pageType) 329 } 330 } 331 332 func checkPageDate(t *testing.T, page page.Page, time time.Time) { 333 if page.Date() != time { 334 t.Fatalf("Page date is: %s. Expected: %s", page.Date(), time) 335 } 336 } 337 338 func normalizeExpected(ext, str string) string { 339 str = normalizeContent(str) 340 switch ext { 341 default: 342 return str 343 case "html": 344 return strings.Trim(tpl.StripHTML(str), " ") 345 case "ad": 346 paragraphs := strings.Split(str, "</p>") 347 expected := "" 348 for _, para := range paragraphs { 349 if para == "" { 350 continue 351 } 352 expected += fmt.Sprintf("<div class=\"paragraph\">\n%s</p></div>\n", para) 353 } 354 355 return expected 356 case "rst": 357 return fmt.Sprintf("<div class=\"document\">\n\n\n%s</div>", str) 358 } 359 } 360 361 func testAllMarkdownEnginesForPages(t *testing.T, 362 assertFunc func(t *testing.T, ext string, pages page.Pages), settings map[string]any, pageSources ...string) { 363 364 engines := []struct { 365 ext string 366 shouldExecute func() bool 367 }{ 368 {"md", func() bool { return true }}, 369 {"ad", func() bool { return asciidocext.Supports() }}, 370 {"rst", func() bool { return rst.Supports() }}, 371 } 372 373 for _, e := range engines { 374 if !e.shouldExecute() { 375 continue 376 } 377 378 t.Run(e.ext, func(t *testing.T) { 379 cfg, fs := newTestCfg(func(cfg config.Provider) error { 380 for k, v := range settings { 381 cfg.Set(k, v) 382 } 383 return nil 384 }) 385 386 contentDir := "content" 387 388 if s := cfg.GetString("contentDir"); s != "" { 389 contentDir = s 390 } 391 392 cfg.Set("security", map[string]any{ 393 "exec": map[string]any{ 394 "allow": []string{"^python$", "^rst2html.*", "^asciidoctor$"}, 395 }, 396 }) 397 398 var fileSourcePairs []string 399 400 for i, source := range pageSources { 401 fileSourcePairs = append(fileSourcePairs, fmt.Sprintf("p%d.%s", i, e.ext), source) 402 } 403 404 for i := 0; i < len(fileSourcePairs); i += 2 { 405 writeSource(t, fs, filepath.Join(contentDir, fileSourcePairs[i]), fileSourcePairs[i+1]) 406 } 407 408 // Add a content page for the home page 409 homePath := fmt.Sprintf("_index.%s", e.ext) 410 writeSource(t, fs, filepath.Join(contentDir, homePath), homePage) 411 412 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded() 413 b.Build(BuildCfg{}) 414 415 s := b.H.Sites[0] 416 417 b.Assert(len(s.RegularPages()), qt.Equals, len(pageSources)) 418 419 assertFunc(t, e.ext, s.RegularPages()) 420 421 home := s.Info.Home() 422 b.Assert(home, qt.Not(qt.IsNil)) 423 b.Assert(home.File().Path(), qt.Equals, homePath) 424 b.Assert(content(home), qt.Contains, "Home Page Content") 425 }) 426 427 } 428 } 429 430 // Issue #1076 431 func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) { 432 t.Parallel() 433 cfg, fs := newTestCfg() 434 435 c := qt.New(t) 436 437 writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder) 438 439 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 440 441 c.Assert(len(s.RegularPages()), qt.Equals, 1) 442 443 p := s.RegularPages()[0] 444 445 if p.Summary() != template.HTML( 446 "<p>The <a href=\"http://gohugo.io/\">best static site generator</a>.<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1</a></sup></p>") { 447 t.Fatalf("Got summary:\n%q", p.Summary()) 448 } 449 450 cnt := content(p) 451 if cnt != "<p>The <a href=\"http://gohugo.io/\">best static site generator</a>.<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1</a></sup></p>\n<div class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn:1\">\n<p>Many people say so. <a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">↩︎</a></p>\n</li>\n</ol>\n</div>" { 452 t.Fatalf("Got content:\n%q", cnt) 453 } 454 } 455 456 func TestPageDatesAllKinds(t *testing.T) { 457 t.Parallel() 458 459 pageContent := ` 460 --- 461 title: Page 462 date: 2017-01-15 463 tags: ["hugo"] 464 categories: ["cool stuff"] 465 --- 466 ` 467 468 b := newTestSitesBuilder(t) 469 b.WithSimpleConfigFile().WithContent("page.md", pageContent) 470 b.WithContent("blog/page.md", pageContent) 471 472 b.CreateSites().Build(BuildCfg{}) 473 474 b.Assert(len(b.H.Sites), qt.Equals, 1) 475 s := b.H.Sites[0] 476 477 checkDate := func(t time.Time, msg string) { 478 b.Assert(t.Year(), qt.Equals, 2017, qt.Commentf(msg)) 479 } 480 481 checkDated := func(d resource.Dated, msg string) { 482 checkDate(d.Date(), "date: "+msg) 483 checkDate(d.Lastmod(), "lastmod: "+msg) 484 } 485 for _, p := range s.Pages() { 486 checkDated(p, p.Kind()) 487 } 488 checkDate(s.Info.LastChange(), "site") 489 } 490 491 func TestPageDatesSections(t *testing.T) { 492 t.Parallel() 493 494 b := newTestSitesBuilder(t) 495 b.WithSimpleConfigFile().WithContent("no-index/page.md", ` 496 --- 497 title: Page 498 date: 2017-01-15 499 --- 500 `, "with-index-no-date/_index.md", `--- 501 title: No Date 502 --- 503 504 `, 505 // https://github.com/gohugoio/hugo/issues/5854 506 "with-index-date/_index.md", `--- 507 title: Date 508 date: 2018-01-15 509 --- 510 511 `, "with-index-date/p1.md", `--- 512 title: Date 513 date: 2018-01-15 514 --- 515 516 `, "with-index-date/p1.md", `--- 517 title: Date 518 date: 2018-01-15 519 --- 520 521 `) 522 523 for i := 1; i <= 20; i++ { 524 b.WithContent(fmt.Sprintf("main-section/p%d.md", i), `--- 525 title: Date 526 date: 2012-01-12 527 --- 528 529 `) 530 } 531 532 b.CreateSites().Build(BuildCfg{}) 533 534 b.Assert(len(b.H.Sites), qt.Equals, 1) 535 s := b.H.Sites[0] 536 537 checkDate := func(p page.Page, year int) { 538 b.Assert(p.Date().Year(), qt.Equals, year) 539 b.Assert(p.Lastmod().Year(), qt.Equals, year) 540 } 541 542 checkDate(s.getPage("/"), 2018) 543 checkDate(s.getPage("/no-index"), 2017) 544 b.Assert(s.getPage("/with-index-no-date").Date().IsZero(), qt.Equals, true) 545 checkDate(s.getPage("/with-index-date"), 2018) 546 547 b.Assert(s.Site.LastChange().Year(), qt.Equals, 2018) 548 } 549 550 func TestCreateNewPage(t *testing.T) { 551 t.Parallel() 552 c := qt.New(t) 553 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 554 p := pages[0] 555 556 // issue #2290: Path is relative to the content dir and will continue to be so. 557 c.Assert(p.File().Path(), qt.Equals, fmt.Sprintf("p0.%s", ext)) 558 c.Assert(p.IsHome(), qt.Equals, false) 559 checkPageTitle(t, p, "Simple") 560 checkPageContent(t, p, normalizeExpected(ext, "<p>Simple Page</p>\n")) 561 checkPageSummary(t, p, "Simple Page") 562 checkPageType(t, p, "page") 563 } 564 565 settings := map[string]any{ 566 "contentDir": "mycontent", 567 } 568 569 testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePage) 570 } 571 572 func TestPageSummary(t *testing.T) { 573 t.Parallel() 574 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 575 p := pages[0] 576 checkPageTitle(t, p, "SimpleWithoutSummaryDelimiter") 577 // Source is not Asciidoctor- or RST-compatible so don't test them 578 if ext != "ad" && ext != "rst" { 579 checkPageContent(t, p, normalizeExpected(ext, "<p><a href=\"https://lipsum.com/\">Lorem ipsum</a> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>\n\n<p>Additional text.</p>\n\n<p>Further text.</p>\n"), ext) 580 checkPageSummary(t, p, normalizeExpected(ext, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Additional text."), ext) 581 } 582 checkPageType(t, p, "page") 583 } 584 585 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithoutSummaryDelimiter) 586 } 587 588 func TestPageWithDelimiter(t *testing.T) { 589 t.Parallel() 590 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 591 p := pages[0] 592 checkPageTitle(t, p, "Simple") 593 checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line</p>\n\n<p>Some more text</p>\n"), ext) 594 checkPageSummary(t, p, normalizeExpected(ext, "<p>Summary Next Line</p>"), ext) 595 checkPageType(t, p, "page") 596 } 597 598 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiter) 599 } 600 601 func TestPageWithSummaryParameter(t *testing.T) { 602 t.Parallel() 603 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 604 p := pages[0] 605 checkPageTitle(t, p, "SimpleWithSummaryParameter") 606 checkPageContent(t, p, normalizeExpected(ext, "<p>Some text.</p>\n\n<p>Some more text.</p>\n"), ext) 607 // Summary is not Asciidoctor- or RST-compatible so don't test them 608 if ext != "ad" && ext != "rst" { 609 checkPageSummary(t, p, normalizeExpected(ext, "Page with summary parameter and <a href=\"http://www.example.com/\">a link</a>"), ext) 610 } 611 checkPageType(t, p, "page") 612 } 613 614 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryParameter) 615 } 616 617 // Issue #3854 618 // Also see https://github.com/gohugoio/hugo/issues/3977 619 func TestPageWithDateFields(t *testing.T) { 620 c := qt.New(t) 621 pageWithDate := `--- 622 title: P%d 623 weight: %d 624 %s: 2017-10-13 625 --- 626 Simple Page With Some Date` 627 628 hasDate := func(p page.Page) bool { 629 return p.Date().Year() == 2017 630 } 631 632 datePage := func(field string, weight int) string { 633 return fmt.Sprintf(pageWithDate, weight, weight, field) 634 } 635 636 t.Parallel() 637 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 638 c.Assert(len(pages) > 0, qt.Equals, true) 639 for _, p := range pages { 640 c.Assert(hasDate(p), qt.Equals, true) 641 } 642 } 643 644 fields := []string{"date", "publishdate", "pubdate", "published"} 645 pageContents := make([]string, len(fields)) 646 for i, field := range fields { 647 pageContents[i] = datePage(field, i+1) 648 } 649 650 testAllMarkdownEnginesForPages(t, assertFunc, nil, pageContents...) 651 } 652 653 // Issue #2601 654 func TestPageRawContent(t *testing.T) { 655 t.Parallel() 656 cfg, fs := newTestCfg() 657 c := qt.New(t) 658 659 writeSource(t, fs, filepath.Join("content", "raw.md"), `--- 660 title: Raw 661 --- 662 **Raw**`) 663 664 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .RawContent }}`) 665 666 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 667 668 c.Assert(len(s.RegularPages()), qt.Equals, 1) 669 p := s.RegularPages()[0] 670 671 c.Assert("**Raw**", qt.Equals, p.RawContent()) 672 } 673 674 func TestPageWithShortCodeInSummary(t *testing.T) { 675 t.Parallel() 676 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 677 p := pages[0] 678 checkPageTitle(t, p, "Simple") 679 checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line. <figure><img src=\"/not/real\"/> </figure> . More text here.</p><p>Some more text</p>")) 680 checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text") 681 checkPageType(t, p, "page") 682 } 683 684 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithShortcodeInSummary) 685 } 686 687 func TestTableOfContents(t *testing.T) { 688 cfg, fs := newTestCfg() 689 c := qt.New(t) 690 691 writeSource(t, fs, filepath.Join("content", "tocpage.md"), pageWithToC) 692 693 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 694 695 c.Assert(len(s.RegularPages()), qt.Equals, 1) 696 697 p := s.RegularPages()[0] 698 699 checkPageContent(t, p, "<p>For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.</p><h2 id=\"aa\">AA</h2> <p>I have no idea, of course, how long it took me to reach the limit of the plain, but at last I entered the foothills, following a pretty little canyon upward toward the mountains. Beside me frolicked a laughing brooklet, hurrying upon its noisy way down to the silent sea. In its quieter pools I discovered many small fish, of four-or five-pound weight I should imagine. In appearance, except as to size and color, they were not unlike the whale of our own seas. As I watched them playing about I discovered, not only that they suckled their young, but that at intervals they rose to the surface to breathe as well as to feed upon certain grasses and a strange, scarlet lichen which grew upon the rocks just above the water line.</p><h3 id=\"aaa\">AAA</h3> <p>I remember I felt an extraordinary persuasion that I was being played with, that presently, when I was upon the very verge of safety, this mysterious death–as swift as the passage of light–would leap after me from the pit about the cylinder and strike me down. ## BB</p><h3 id=\"bbb\">BBB</h3> <p>“You’re a great Granser,” he cried delightedly, “always making believe them little marks mean something.”</p>") 700 checkPageTOC(t, p, "<nav id=\"TableOfContents\">\n <ul>\n <li><a href=\"#aa\">AA</a>\n <ul>\n <li><a href=\"#aaa\">AAA</a></li>\n <li><a href=\"#bbb\">BBB</a></li>\n </ul>\n </li>\n </ul>\n</nav>") 701 } 702 703 func TestPageWithMoreTag(t *testing.T) { 704 t.Parallel() 705 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 706 p := pages[0] 707 checkPageTitle(t, p, "Simple") 708 checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Same Line</p>\n\n<p>Some more text</p>\n")) 709 checkPageSummary(t, p, normalizeExpected(ext, "<p>Summary Same Line</p>")) 710 checkPageType(t, p, "page") 711 } 712 713 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiterSameLine) 714 } 715 716 // #2973 717 func TestSummaryWithHTMLTagsOnNextLine(t *testing.T) { 718 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 719 c := qt.New(t) 720 p := pages[0] 721 s := string(p.Summary()) 722 c.Assert(s, qt.Contains, "Happy new year everyone!") 723 c.Assert(s, qt.Not(qt.Contains), "User interface") 724 } 725 726 testAllMarkdownEnginesForPages(t, assertFunc, nil, `--- 727 title: Simple 728 --- 729 Happy new year everyone! 730 731 Here is the last report for commits in the year 2016. It covers hrev50718-hrev50829. 732 733 <!--more--> 734 735 <h3>User interface</h3> 736 737 `) 738 } 739 740 // Issue 9383 741 func TestRenderStringForRegularPageTranslations(t *testing.T) { 742 c := qt.New(t) 743 b := newTestSitesBuilder(t) 744 b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelError, os.Stderr)) 745 746 b.WithConfigFile("toml", 747 `baseurl = "https://example.org/" 748 title = "My Site" 749 750 defaultContentLanguage = "ru" 751 defaultContentLanguageInSubdir = true 752 753 [languages.ru] 754 contentDir = 'content/ru' 755 weight = 1 756 757 [languages.en] 758 weight = 2 759 contentDir = 'content/en' 760 761 [outputs] 762 home = ["HTML", "JSON"]`) 763 764 b.WithTemplates("index.html", ` 765 {{- range .Site.Home.Translations -}} 766 <p>{{- .RenderString "foo" -}}</p> 767 {{- end -}} 768 {{- range .Site.Home.AllTranslations -}} 769 <p>{{- .RenderString "bar" -}}</p> 770 {{- end -}} 771 `, "_default/single.html", 772 `{{ .Content }}`, 773 "index.json", 774 `{"Title": "My Site"}`, 775 ) 776 777 b.WithContent( 778 "ru/a.md", 779 "", 780 "en/a.md", 781 "", 782 ) 783 784 err := b.BuildE(BuildCfg{}) 785 c.Assert(err, qt.Equals, nil) 786 787 b.AssertFileContent("public/ru/index.html", ` 788 <p>foo</p> 789 <p>foo</p> 790 <p>bar</p> 791 <p>bar</p> 792 `) 793 794 b.AssertFileContent("public/en/index.html", ` 795 <p>foo</p> 796 <p>foo</p> 797 <p>bar</p> 798 <p>bar</p> 799 `) 800 } 801 802 // Issue 8919 803 func TestContentProviderWithCustomOutputFormat(t *testing.T) { 804 b := newTestSitesBuilder(t) 805 b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelDebug, os.Stderr)) 806 b.WithConfigFile("toml", `baseURL = 'http://example.org/' 807 title = 'My New Hugo Site' 808 809 timeout = 600000 # ten minutes in case we want to pause and debug 810 811 defaultContentLanguage = "en" 812 813 [languages] 814 [languages.en] 815 title = "Repro" 816 languageName = "English" 817 contentDir = "content/en" 818 819 [languages.zh_CN] 820 title = "Repro" 821 languageName = "简体中文" 822 contentDir = "content/zh_CN" 823 824 [outputFormats] 825 [outputFormats.metadata] 826 baseName = "metadata" 827 mediaType = "text/html" 828 isPlainText = true 829 notAlternative = true 830 831 [outputs] 832 home = ["HTML", "metadata"]`) 833 834 b.WithTemplates("home.metadata.html", `<h2>Translations metadata</h2> 835 <ul> 836 {{ $p := .Page }} 837 {{ range $p.Translations}} 838 <li>Title: {{ .Title }}, {{ .Summary }}</li> 839 <li>Content: {{ .Content }}</li> 840 <li>Plain: {{ .Plain }}</li> 841 <li>PlainWords: {{ .PlainWords }}</li> 842 <li>Summary: {{ .Summary }}</li> 843 <li>Truncated: {{ .Truncated }}</li> 844 <li>FuzzyWordCount: {{ .FuzzyWordCount }}</li> 845 <li>ReadingTime: {{ .ReadingTime }}</li> 846 <li>Len: {{ .Len }}</li> 847 {{ end }} 848 </ul>`) 849 850 b.WithTemplates("_default/baseof.html", `<html> 851 852 <body> 853 {{ block "main" . }}{{ end }} 854 </body> 855 856 </html>`) 857 858 b.WithTemplates("_default/home.html", `{{ define "main" }} 859 <h2>Translations</h2> 860 <ul> 861 {{ $p := .Page }} 862 {{ range $p.Translations}} 863 <li>Title: {{ .Title }}, {{ .Summary }}</li> 864 <li>Content: {{ .Content }}</li> 865 <li>Plain: {{ .Plain }}</li> 866 <li>PlainWords: {{ .PlainWords }}</li> 867 <li>Summary: {{ .Summary }}</li> 868 <li>Truncated: {{ .Truncated }}</li> 869 <li>FuzzyWordCount: {{ .FuzzyWordCount }}</li> 870 <li>ReadingTime: {{ .ReadingTime }}</li> 871 <li>Len: {{ .Len }}</li> 872 {{ end }} 873 </ul> 874 {{ end }}`) 875 876 b.WithContent("en/_index.md", `--- 877 title: Title (en) 878 summary: Summary (en) 879 --- 880 881 Here is some content. 882 `) 883 884 b.WithContent("zh_CN/_index.md", `--- 885 title: Title (zh) 886 summary: Summary (zh) 887 --- 888 889 这是一些内容 890 `) 891 892 b.Build(BuildCfg{}) 893 894 b.AssertFileContent("public/index.html", `<html> 895 896 <body> 897 898 <h2>Translations</h2> 899 <ul> 900 901 902 <li>Title: Title (zh), Summary (zh)</li> 903 <li>Content: <p>这是一些内容</p> 904 </li> 905 <li>Plain: 这是一些内容 906 </li> 907 <li>PlainWords: [这是一些内容]</li> 908 <li>Summary: Summary (zh)</li> 909 <li>Truncated: false</li> 910 <li>FuzzyWordCount: 100</li> 911 <li>ReadingTime: 1</li> 912 <li>Len: 26</li> 913 914 </ul> 915 916 </body> 917 918 </html>`) 919 b.AssertFileContent("public/metadata.html", `<h2>Translations metadata</h2> 920 <ul> 921 922 923 <li>Title: Title (zh), Summary (zh)</li> 924 <li>Content: <p>这是一些内容</p> 925 </li> 926 <li>Plain: 这是一些内容 927 </li> 928 <li>PlainWords: [这是一些内容]</li> 929 <li>Summary: Summary (zh)</li> 930 <li>Truncated: false</li> 931 <li>FuzzyWordCount: 100</li> 932 <li>ReadingTime: 1</li> 933 <li>Len: 26</li> 934 935 </ul>`) 936 b.AssertFileContent("public/zh_cn/index.html", `<html> 937 938 <body> 939 940 <h2>Translations</h2> 941 <ul> 942 943 944 <li>Title: Title (en), Summary (en)</li> 945 <li>Content: <p>Here is some content.</p> 946 </li> 947 <li>Plain: Here is some content. 948 </li> 949 <li>PlainWords: [Here is some content.]</li> 950 <li>Summary: Summary (en)</li> 951 <li>Truncated: false</li> 952 <li>FuzzyWordCount: 100</li> 953 <li>ReadingTime: 1</li> 954 <li>Len: 29</li> 955 956 </ul> 957 958 </body> 959 960 </html>`) 961 b.AssertFileContent("public/zh_cn/metadata.html", `<h2>Translations metadata</h2> 962 <ul> 963 964 965 <li>Title: Title (en), Summary (en)</li> 966 <li>Content: <p>Here is some content.</p> 967 </li> 968 <li>Plain: Here is some content. 969 </li> 970 <li>PlainWords: [Here is some content.]</li> 971 <li>Summary: Summary (en)</li> 972 <li>Truncated: false</li> 973 <li>FuzzyWordCount: 100</li> 974 <li>ReadingTime: 1</li> 975 <li>Len: 29</li> 976 977 </ul>`) 978 } 979 980 func TestPageWithDate(t *testing.T) { 981 t.Parallel() 982 cfg, fs := newTestCfg() 983 c := qt.New(t) 984 985 writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageRFC3339Date) 986 987 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 988 989 c.Assert(len(s.RegularPages()), qt.Equals, 1) 990 991 p := s.RegularPages()[0] 992 d, _ := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z") 993 994 checkPageDate(t, p, d) 995 } 996 997 func TestPageWithLastmodFromGitInfo(t *testing.T) { 998 if htesting.IsCI() { 999 // TODO(bep) figure out why this fails on GitHub actions. 1000 t.Skip("Skip GitInfo test on CI") 1001 } 1002 c := qt.New(t) 1003 1004 wd, err := os.Getwd() 1005 c.Assert(err, qt.IsNil) 1006 1007 // We need to use the OS fs for this. 1008 cfg := config.NewWithTestDefaults() 1009 cfg.Set("workingDir", filepath.Join(wd, "testsite")) 1010 fs := hugofs.NewFrom(hugofs.Os, cfg) 1011 1012 cfg.Set("frontmatter", map[string]any{ 1013 "lastmod": []string{":git", "lastmod"}, 1014 }) 1015 cfg.Set("defaultContentLanguage", "en") 1016 1017 langConfig := map[string]any{ 1018 "en": map[string]any{ 1019 "weight": 1, 1020 "languageName": "English", 1021 "contentDir": "content", 1022 }, 1023 "nn": map[string]any{ 1024 "weight": 2, 1025 "languageName": "Nynorsk", 1026 "contentDir": "content_nn", 1027 }, 1028 } 1029 1030 cfg.Set("languages", langConfig) 1031 cfg.Set("enableGitInfo", true) 1032 1033 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded() 1034 1035 b.Build(BuildCfg{SkipRender: true}) 1036 h := b.H 1037 1038 c.Assert(len(h.Sites), qt.Equals, 2) 1039 1040 enSite := h.Sites[0] 1041 c.Assert(len(enSite.RegularPages()), qt.Equals, 1) 1042 1043 // 2018-03-11 is the Git author date for testsite/content/first-post.md 1044 c.Assert(enSite.RegularPages()[0].Lastmod().Format("2006-01-02"), qt.Equals, "2018-03-11") 1045 c.Assert(enSite.RegularPages()[0].CodeOwners()[0], qt.Equals, "@bep") 1046 1047 nnSite := h.Sites[1] 1048 c.Assert(len(nnSite.RegularPages()), qt.Equals, 1) 1049 1050 // 2018-08-11 is the Git author date for testsite/content_nn/first-post.md 1051 c.Assert(nnSite.RegularPages()[0].Lastmod().Format("2006-01-02"), qt.Equals, "2018-08-11") 1052 c.Assert(enSite.RegularPages()[0].CodeOwners()[0], qt.Equals, "@bep") 1053 } 1054 1055 func TestPageWithFrontMatterConfig(t *testing.T) { 1056 for _, dateHandler := range []string{":filename", ":fileModTime"} { 1057 dateHandler := dateHandler 1058 t.Run(fmt.Sprintf("dateHandler=%q", dateHandler), func(t *testing.T) { 1059 t.Parallel() 1060 c := qt.New(t) 1061 cfg, fs := newTestCfg() 1062 1063 pageTemplate := ` 1064 --- 1065 title: Page 1066 weight: %d 1067 lastMod: 2018-02-28 1068 %s 1069 --- 1070 Content 1071 ` 1072 1073 cfg.Set("frontmatter", map[string]any{ 1074 "date": []string{dateHandler, "date"}, 1075 }) 1076 1077 c1 := filepath.Join("content", "section", "2012-02-21-noslug.md") 1078 c2 := filepath.Join("content", "section", "2012-02-22-slug.md") 1079 1080 writeSource(t, fs, c1, fmt.Sprintf(pageTemplate, 1, "")) 1081 writeSource(t, fs, c2, fmt.Sprintf(pageTemplate, 2, "slug: aslug")) 1082 1083 c1fi, err := fs.Source.Stat(c1) 1084 c.Assert(err, qt.IsNil) 1085 c2fi, err := fs.Source.Stat(c2) 1086 c.Assert(err, qt.IsNil) 1087 1088 b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded() 1089 b.Build(BuildCfg{SkipRender: true}) 1090 1091 s := b.H.Sites[0] 1092 c.Assert(len(s.RegularPages()), qt.Equals, 2) 1093 1094 noSlug := s.RegularPages()[0] 1095 slug := s.RegularPages()[1] 1096 1097 c.Assert(noSlug.Lastmod().Day(), qt.Equals, 28) 1098 1099 switch strings.ToLower(dateHandler) { 1100 case ":filename": 1101 c.Assert(noSlug.Date().IsZero(), qt.Equals, false) 1102 c.Assert(slug.Date().IsZero(), qt.Equals, false) 1103 c.Assert(noSlug.Date().Year(), qt.Equals, 2012) 1104 c.Assert(slug.Date().Year(), qt.Equals, 2012) 1105 c.Assert(noSlug.Slug(), qt.Equals, "noslug") 1106 c.Assert(slug.Slug(), qt.Equals, "aslug") 1107 case ":filemodtime": 1108 c.Assert(noSlug.Date().Year(), qt.Equals, c1fi.ModTime().Year()) 1109 c.Assert(slug.Date().Year(), qt.Equals, c2fi.ModTime().Year()) 1110 fallthrough 1111 default: 1112 c.Assert(noSlug.Slug(), qt.Equals, "") 1113 c.Assert(slug.Slug(), qt.Equals, "aslug") 1114 1115 } 1116 }) 1117 } 1118 } 1119 1120 func TestWordCountWithAllCJKRunesWithoutHasCJKLanguage(t *testing.T) { 1121 t.Parallel() 1122 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 1123 p := pages[0] 1124 if p.WordCount() != 8 { 1125 t.Fatalf("[%s] incorrect word count. expected %v, got %v", ext, 8, p.WordCount()) 1126 } 1127 } 1128 1129 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithAllCJKRunes) 1130 } 1131 1132 func TestWordCountWithAllCJKRunesHasCJKLanguage(t *testing.T) { 1133 t.Parallel() 1134 settings := map[string]any{"hasCJKLanguage": true} 1135 1136 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 1137 p := pages[0] 1138 if p.WordCount() != 15 { 1139 t.Fatalf("[%s] incorrect word count, expected %v, got %v", ext, 15, p.WordCount()) 1140 } 1141 } 1142 testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithAllCJKRunes) 1143 } 1144 1145 func TestWordCountWithMainEnglishWithCJKRunes(t *testing.T) { 1146 t.Parallel() 1147 settings := map[string]any{"hasCJKLanguage": true} 1148 1149 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 1150 p := pages[0] 1151 if p.WordCount() != 74 { 1152 t.Fatalf("[%s] incorrect word count, expected %v, got %v", ext, 74, p.WordCount()) 1153 } 1154 1155 if p.Summary() != simplePageWithMainEnglishWithCJKRunesSummary { 1156 t.Fatalf("[%s] incorrect Summary for content '%s'. expected %v, got %v", ext, p.Plain(), 1157 simplePageWithMainEnglishWithCJKRunesSummary, p.Summary()) 1158 } 1159 } 1160 1161 testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithMainEnglishWithCJKRunes) 1162 } 1163 1164 func TestWordCountWithIsCJKLanguageFalse(t *testing.T) { 1165 t.Parallel() 1166 settings := map[string]any{ 1167 "hasCJKLanguage": true, 1168 } 1169 1170 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 1171 p := pages[0] 1172 if p.WordCount() != 75 { 1173 t.Fatalf("[%s] incorrect word count for content '%s'. expected %v, got %v", ext, p.Plain(), 74, p.WordCount()) 1174 } 1175 1176 if p.Summary() != simplePageWithIsCJKLanguageFalseSummary { 1177 t.Fatalf("[%s] incorrect Summary for content '%s'. expected %v, got %v", ext, p.Plain(), 1178 simplePageWithIsCJKLanguageFalseSummary, p.Summary()) 1179 } 1180 } 1181 1182 testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithIsCJKLanguageFalse) 1183 } 1184 1185 func TestWordCount(t *testing.T) { 1186 t.Parallel() 1187 assertFunc := func(t *testing.T, ext string, pages page.Pages) { 1188 p := pages[0] 1189 if p.WordCount() != 483 { 1190 t.Fatalf("[%s] incorrect word count. expected %v, got %v", ext, 483, p.WordCount()) 1191 } 1192 1193 if p.FuzzyWordCount() != 500 { 1194 t.Fatalf("[%s] incorrect word count. expected %v, got %v", ext, 500, p.FuzzyWordCount()) 1195 } 1196 1197 if p.ReadingTime() != 3 { 1198 t.Fatalf("[%s] incorrect min read. expected %v, got %v", ext, 3, p.ReadingTime()) 1199 } 1200 } 1201 1202 testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithLongContent) 1203 } 1204 1205 func TestPagePaths(t *testing.T) { 1206 t.Parallel() 1207 c := qt.New(t) 1208 1209 siteParmalinksSetting := map[string]string{ 1210 "post": ":year/:month/:day/:title/", 1211 } 1212 1213 tests := []struct { 1214 content string 1215 path string 1216 hasPermalink bool 1217 expected string 1218 }{ 1219 {simplePage, "post/x.md", false, "post/x.html"}, 1220 {simplePageWithURL, "post/x.md", false, "simple/url/index.html"}, 1221 {simplePageWithSlug, "post/x.md", false, "post/simple-slug.html"}, 1222 {simplePageWithDate, "post/x.md", true, "2013/10/15/simple/index.html"}, 1223 {UTF8Page, "post/x.md", false, "post/x.html"}, 1224 {UTF8PageWithURL, "post/x.md", false, "ラーメン/url/index.html"}, 1225 {UTF8PageWithSlug, "post/x.md", false, "post/ラーメン-slug.html"}, 1226 {UTF8PageWithDate, "post/x.md", true, "2013/10/15/ラーメン/index.html"}, 1227 } 1228 1229 for _, test := range tests { 1230 cfg, fs := newTestCfg() 1231 1232 if test.hasPermalink { 1233 cfg.Set("permalinks", siteParmalinksSetting) 1234 } 1235 1236 writeSource(t, fs, filepath.Join("content", filepath.FromSlash(test.path)), test.content) 1237 1238 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 1239 c.Assert(len(s.RegularPages()), qt.Equals, 1) 1240 1241 } 1242 } 1243 1244 func TestTranslationKey(t *testing.T) { 1245 t.Parallel() 1246 c := qt.New(t) 1247 cfg, fs := newTestCfg() 1248 1249 writeSource(t, fs, filepath.Join("content", filepath.FromSlash("sect/simple.no.md")), "---\ntitle: \"A1\"\ntranslationKey: \"k1\"\n---\nContent\n") 1250 writeSource(t, fs, filepath.Join("content", filepath.FromSlash("sect/simple.en.md")), "---\ntitle: \"A2\"\n---\nContent\n") 1251 1252 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 1253 1254 c.Assert(len(s.RegularPages()), qt.Equals, 2) 1255 1256 home := s.Info.Home() 1257 c.Assert(home, qt.Not(qt.IsNil)) 1258 c.Assert(home.TranslationKey(), qt.Equals, "home") 1259 c.Assert(s.RegularPages()[0].TranslationKey(), qt.Equals, "page/k1") 1260 p2 := s.RegularPages()[1] 1261 1262 c.Assert(p2.TranslationKey(), qt.Equals, "page/sect/simple") 1263 } 1264 1265 func TestChompBOM(t *testing.T) { 1266 t.Parallel() 1267 c := qt.New(t) 1268 const utf8BOM = "\xef\xbb\xbf" 1269 1270 cfg, fs := newTestCfg() 1271 1272 writeSource(t, fs, filepath.Join("content", "simple.md"), utf8BOM+simplePage) 1273 1274 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true}) 1275 1276 c.Assert(len(s.RegularPages()), qt.Equals, 1) 1277 1278 p := s.RegularPages()[0] 1279 1280 checkPageTitle(t, p, "Simple") 1281 } 1282 1283 func TestPageWithEmoji(t *testing.T) { 1284 for _, enableEmoji := range []bool{true, false} { 1285 v := config.NewWithTestDefaults() 1286 v.Set("enableEmoji", enableEmoji) 1287 1288 b := newTestSitesBuilder(t).WithViper(v) 1289 1290 b.WithContent("page-emoji.md", `--- 1291 title: "Hugo Smile" 1292 --- 1293 This is a :smile:. 1294 <!--more--> 1295 1296 Another :smile: This is :not: :an: :emoji:. 1297 1298 O :christmas_tree: 1299 1300 Write me an :e-mail: or :email:? 1301 1302 Too many colons: :: ::: :::: :?: :!: :.: 1303 1304 If you dislike this video, you can hit that :-1: button :stuck_out_tongue_winking_eye:, 1305 but if you like it, hit :+1: and get subscribed! 1306 `) 1307 1308 b.CreateSites().Build(BuildCfg{}) 1309 1310 if enableEmoji { 1311 b.AssertFileContent("public/page-emoji/index.html", 1312 "This is a 😄", 1313 "Another 😄", 1314 "This is :not: :an: :emoji:.", 1315 "O 🎄", 1316 "Write me an 📧 or ✉️?", 1317 "Too many colons: :: ::: :::: :?: :!: :.:", 1318 "you can hit that 👎 button 😜,", 1319 "hit 👍 and get subscribed!", 1320 ) 1321 } else { 1322 b.AssertFileContent("public/page-emoji/index.html", 1323 "This is a :smile:", 1324 "Another :smile:", 1325 "This is :not: :an: :emoji:.", 1326 "O :christmas_tree:", 1327 "Write me an :e-mail: or :email:?", 1328 "Too many colons: :: ::: :::: :?: :!: :.:", 1329 "you can hit that :-1: button :stuck_out_tongue_winking_eye:,", 1330 "hit :+1: and get subscribed!", 1331 ) 1332 } 1333 1334 } 1335 } 1336 1337 func TestPageHTMLContent(t *testing.T) { 1338 b := newTestSitesBuilder(t) 1339 b.WithSimpleConfigFile() 1340 1341 frontmatter := `--- 1342 title: "HTML Content" 1343 --- 1344 ` 1345 b.WithContent("regular.html", frontmatter+`<h1>Hugo</h1>`) 1346 b.WithContent("nomarkdownforyou.html", frontmatter+`**Hugo!**`) 1347 b.WithContent("manualsummary.html", frontmatter+` 1348 <p>This is summary</p> 1349 <!--more--> 1350 <p>This is the main content.</p>`) 1351 1352 b.Build(BuildCfg{}) 1353 1354 b.AssertFileContent( 1355 "public/regular/index.html", 1356 "Single: HTML Content|Hello|en|RelPermalink: /regular/|", 1357 "Summary: Hugo|Truncated: false") 1358 1359 b.AssertFileContent( 1360 "public/nomarkdownforyou/index.html", 1361 "Permalink: http://example.com/nomarkdownforyou/|**Hugo!**|", 1362 ) 1363 1364 // https://github.com/gohugoio/hugo/issues/5723 1365 b.AssertFileContent( 1366 "public/manualsummary/index.html", 1367 "Single: HTML Content|Hello|en|RelPermalink: /manualsummary/|", 1368 "Summary: \n<p>This is summary</p>\n|Truncated: true", 1369 "|<p>This is the main content.</p>|", 1370 ) 1371 } 1372 1373 // https://github.com/gohugoio/hugo/issues/5381 1374 func TestPageManualSummary(t *testing.T) { 1375 b := newTestSitesBuilder(t) 1376 b.WithSimpleConfigFile() 1377 1378 b.WithContent("page-md-shortcode.md", `--- 1379 title: "Hugo" 1380 --- 1381 This is a {{< sc >}}. 1382 <!--more--> 1383 Content. 1384 `) 1385 1386 // https://github.com/gohugoio/hugo/issues/5464 1387 b.WithContent("page-md-only-shortcode.md", `--- 1388 title: "Hugo" 1389 --- 1390 {{< sc >}} 1391 <!--more--> 1392 {{< sc >}} 1393 `) 1394 1395 b.WithContent("page-md-shortcode-same-line.md", `--- 1396 title: "Hugo" 1397 --- 1398 This is a {{< sc >}}<!--more-->Same line. 1399 `) 1400 1401 b.WithContent("page-md-shortcode-same-line-after.md", `--- 1402 title: "Hugo" 1403 --- 1404 Summary<!--more-->{{< sc >}} 1405 `) 1406 1407 b.WithContent("page-org-shortcode.org", `#+TITLE: T1 1408 #+AUTHOR: A1 1409 #+DESCRIPTION: D1 1410 This is a {{< sc >}}. 1411 # more 1412 Content. 1413 `) 1414 1415 b.WithContent("page-org-variant1.org", `#+TITLE: T1 1416 Summary. 1417 1418 # more 1419 1420 Content. 1421 `) 1422 1423 b.WithTemplatesAdded("layouts/shortcodes/sc.html", "a shortcode") 1424 b.WithTemplatesAdded("layouts/_default/single.html", ` 1425 SUMMARY:{{ .Summary }}:END 1426 -------------------------- 1427 CONTENT:{{ .Content }} 1428 `) 1429 1430 b.CreateSites().Build(BuildCfg{}) 1431 1432 b.AssertFileContent("public/page-md-shortcode/index.html", 1433 "SUMMARY:<p>This is a a shortcode.</p>:END", 1434 "CONTENT:<p>This is a a shortcode.</p>\n\n<p>Content.</p>\n", 1435 ) 1436 1437 b.AssertFileContent("public/page-md-shortcode-same-line/index.html", 1438 "SUMMARY:<p>This is a a shortcode</p>:END", 1439 "CONTENT:<p>This is a a shortcode</p>\n\n<p>Same line.</p>\n", 1440 ) 1441 1442 b.AssertFileContent("public/page-md-shortcode-same-line-after/index.html", 1443 "SUMMARY:<p>Summary</p>:END", 1444 "CONTENT:<p>Summary</p>\n\na shortcode", 1445 ) 1446 1447 b.AssertFileContent("public/page-org-shortcode/index.html", 1448 "SUMMARY:<p>\nThis is a a shortcode.\n</p>:END", 1449 "CONTENT:<p>\nThis is a a shortcode.\n</p>\n<p>\nContent.\t\n</p>\n", 1450 ) 1451 b.AssertFileContent("public/page-org-variant1/index.html", 1452 "SUMMARY:<p>\nSummary.\n</p>:END", 1453 "CONTENT:<p>\nSummary.\n</p>\n<p>\nContent.\t\n</p>\n", 1454 ) 1455 1456 b.AssertFileContent("public/page-md-only-shortcode/index.html", 1457 "SUMMARY:a shortcode:END", 1458 "CONTENT:a shortcode\n\na shortcode\n", 1459 ) 1460 } 1461 1462 // https://github.com/gohugoio/hugo/issues/5478 1463 func TestPageWithCommentedOutFrontMatter(t *testing.T) { 1464 b := newTestSitesBuilder(t) 1465 b.WithSimpleConfigFile() 1466 1467 b.WithContent("page.md", `<!-- 1468 +++ 1469 title = "hello" 1470 +++ 1471 --> 1472 This is the content. 1473 `) 1474 1475 b.WithTemplatesAdded("layouts/_default/single.html", ` 1476 Title: {{ .Title }} 1477 Content:{{ .Content }} 1478 `) 1479 1480 b.CreateSites().Build(BuildCfg{}) 1481 1482 b.AssertFileContent("public/page/index.html", 1483 "Title: hello", 1484 "Content:<p>This is the content.</p>", 1485 ) 1486 } 1487 1488 // https://github.com/gohugoio/hugo/issues/5781 1489 func TestPageWithZeroFile(t *testing.T) { 1490 newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger()).WithSimpleConfigFile(). 1491 WithTemplatesAdded("index.html", "{{ .File.Filename }}{{ with .File }}{{ .Dir }}{{ end }}").Build(BuildCfg{}) 1492 } 1493 1494 func TestHomePageWithNoTitle(t *testing.T) { 1495 b := newTestSitesBuilder(t).WithConfigFile("toml", ` 1496 title = "Site Title" 1497 `) 1498 b.WithTemplatesAdded("index.html", "Title|{{ with .Title }}{{ . }}{{ end }}|") 1499 b.WithContent("_index.md", `--- 1500 description: "No title for you!" 1501 --- 1502 1503 Content. 1504 `) 1505 1506 b.Build(BuildCfg{}) 1507 b.AssertFileContent("public/index.html", "Title||") 1508 } 1509 1510 func TestShouldBuild(t *testing.T) { 1511 past := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 1512 future := time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC) 1513 zero := time.Time{} 1514 1515 publishSettings := []struct { 1516 buildFuture bool 1517 buildExpired bool 1518 buildDrafts bool 1519 draft bool 1520 publishDate time.Time 1521 expiryDate time.Time 1522 out bool 1523 }{ 1524 // publishDate and expiryDate 1525 {false, false, false, false, zero, zero, true}, 1526 {false, false, false, false, zero, future, true}, 1527 {false, false, false, false, past, zero, true}, 1528 {false, false, false, false, past, future, true}, 1529 {false, false, false, false, past, past, false}, 1530 {false, false, false, false, future, future, false}, 1531 {false, false, false, false, future, past, false}, 1532 1533 // buildFuture and buildExpired 1534 {false, true, false, false, past, past, true}, 1535 {true, true, false, false, past, past, true}, 1536 {true, false, false, false, past, past, false}, 1537 {true, false, false, false, future, future, true}, 1538 {true, true, false, false, future, future, true}, 1539 {false, true, false, false, future, past, false}, 1540 1541 // buildDrafts and draft 1542 {true, true, false, true, past, future, false}, 1543 {true, true, true, true, past, future, true}, 1544 {true, true, true, true, past, future, true}, 1545 } 1546 1547 for _, ps := range publishSettings { 1548 s := shouldBuild(ps.buildFuture, ps.buildExpired, ps.buildDrafts, ps.draft, 1549 ps.publishDate, ps.expiryDate) 1550 if s != ps.out { 1551 t.Errorf("AssertShouldBuild unexpected output with params: %+v", ps) 1552 } 1553 } 1554 } 1555 1556 func TestShouldBuildWithClock(t *testing.T) { 1557 htime.Clock = clock.Start(time.Date(2021, 11, 17, 20, 34, 58, 651387237, time.UTC)) 1558 t.Cleanup(func() { htime.Clock = clock.System() }) 1559 past := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 1560 future := time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC) 1561 zero := time.Time{} 1562 1563 publishSettings := []struct { 1564 buildFuture bool 1565 buildExpired bool 1566 buildDrafts bool 1567 draft bool 1568 publishDate time.Time 1569 expiryDate time.Time 1570 out bool 1571 }{ 1572 // publishDate and expiryDate 1573 {false, false, false, false, zero, zero, true}, 1574 {false, false, false, false, zero, future, true}, 1575 {false, false, false, false, past, zero, true}, 1576 {false, false, false, false, past, future, true}, 1577 {false, false, false, false, past, past, false}, 1578 {false, false, false, false, future, future, false}, 1579 {false, false, false, false, future, past, false}, 1580 1581 // buildFuture and buildExpired 1582 {false, true, false, false, past, past, true}, 1583 {true, true, false, false, past, past, true}, 1584 {true, false, false, false, past, past, false}, 1585 {true, false, false, false, future, future, true}, 1586 {true, true, false, false, future, future, true}, 1587 {false, true, false, false, future, past, false}, 1588 1589 // buildDrafts and draft 1590 {true, true, false, true, past, future, false}, 1591 {true, true, true, true, past, future, true}, 1592 {true, true, true, true, past, future, true}, 1593 } 1594 1595 for _, ps := range publishSettings { 1596 s := shouldBuild(ps.buildFuture, ps.buildExpired, ps.buildDrafts, ps.draft, 1597 ps.publishDate, ps.expiryDate) 1598 if s != ps.out { 1599 t.Errorf("AssertShouldBuildWithClock unexpected output with params: %+v", ps) 1600 } 1601 } 1602 } 1603 1604 // "dot" in path: #1885 and #2110 1605 // disablePathToLower regression: #3374 1606 func TestPathIssues(t *testing.T) { 1607 for _, disablePathToLower := range []bool{false, true} { 1608 for _, uglyURLs := range []bool{false, true} { 1609 disablePathToLower := disablePathToLower 1610 uglyURLs := uglyURLs 1611 t.Run(fmt.Sprintf("disablePathToLower=%t,uglyURLs=%t", disablePathToLower, uglyURLs), func(t *testing.T) { 1612 t.Parallel() 1613 cfg, fs := newTestCfg() 1614 th := newTestHelper(cfg, fs, t) 1615 c := qt.New(t) 1616 1617 cfg.Set("permalinks", map[string]string{ 1618 "post": ":section/:title", 1619 }) 1620 1621 cfg.Set("uglyURLs", uglyURLs) 1622 cfg.Set("disablePathToLower", disablePathToLower) 1623 cfg.Set("paginate", 1) 1624 1625 writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), "<html><body>{{.Content}}</body></html>") 1626 writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"), 1627 "<html><body>P{{.Paginator.PageNumber}}|URL: {{.Paginator.URL}}|{{ if .Paginator.HasNext }}Next: {{.Paginator.Next.URL }}{{ end }}</body></html>") 1628 1629 for i := 0; i < 3; i++ { 1630 writeSource(t, fs, filepath.Join("content", "post", fmt.Sprintf("doc%d.md", i)), 1631 fmt.Sprintf(`--- 1632 title: "test%d.dot" 1633 tags: 1634 - ".net" 1635 --- 1636 # doc1 1637 *some content*`, i)) 1638 } 1639 1640 writeSource(t, fs, filepath.Join("content", "Blog", "Blog1.md"), 1641 fmt.Sprintf(`--- 1642 title: "testBlog" 1643 tags: 1644 - "Blog" 1645 --- 1646 # doc1 1647 *some blog content*`)) 1648 1649 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 1650 1651 c.Assert(len(s.RegularPages()), qt.Equals, 4) 1652 1653 pathFunc := func(s string) string { 1654 if uglyURLs { 1655 return strings.Replace(s, "/index.html", ".html", 1) 1656 } 1657 return s 1658 } 1659 1660 blog := "blog" 1661 1662 if disablePathToLower { 1663 blog = "Blog" 1664 } 1665 1666 th.assertFileContent(pathFunc("public/"+blog+"/"+blog+"1/index.html"), "some blog content") 1667 1668 th.assertFileContent(pathFunc("public/post/test0.dot/index.html"), "some content") 1669 1670 if uglyURLs { 1671 th.assertFileContent("public/post/page/1.html", `canonical" href="/post.html"`) 1672 th.assertFileContent("public/post.html", `<body>P1|URL: /post.html|Next: /post/page/2.html</body>`) 1673 th.assertFileContent("public/post/page/2.html", `<body>P2|URL: /post/page/2.html|Next: /post/page/3.html</body>`) 1674 } else { 1675 th.assertFileContent("public/post/page/1/index.html", `canonical" href="/post/"`) 1676 th.assertFileContent("public/post/index.html", `<body>P1|URL: /post/|Next: /post/page/2/</body>`) 1677 th.assertFileContent("public/post/page/2/index.html", `<body>P2|URL: /post/page/2/|Next: /post/page/3/</body>`) 1678 th.assertFileContent("public/tags/.net/index.html", `<body>P1|URL: /tags/.net/|Next: /tags/.net/page/2/</body>`) 1679 1680 } 1681 1682 p := s.RegularPages()[0] 1683 if uglyURLs { 1684 c.Assert(p.RelPermalink(), qt.Equals, "/post/test0.dot.html") 1685 } else { 1686 c.Assert(p.RelPermalink(), qt.Equals, "/post/test0.dot/") 1687 } 1688 }) 1689 } 1690 } 1691 } 1692 1693 // https://github.com/gohugoio/hugo/issues/4675 1694 func TestWordCountAndSimilarVsSummary(t *testing.T) { 1695 t.Parallel() 1696 c := qt.New(t) 1697 1698 single := []string{"_default/single.html", ` 1699 WordCount: {{ .WordCount }} 1700 FuzzyWordCount: {{ .FuzzyWordCount }} 1701 ReadingTime: {{ .ReadingTime }} 1702 Len Plain: {{ len .Plain }} 1703 Len PlainWords: {{ len .PlainWords }} 1704 Truncated: {{ .Truncated }} 1705 Len Summary: {{ len .Summary }} 1706 Len Content: {{ len .Content }} 1707 1708 SUMMARY:{{ .Summary }}:{{ len .Summary }}:END 1709 1710 `} 1711 1712 b := newTestSitesBuilder(t) 1713 b.WithSimpleConfigFile().WithTemplatesAdded(single...).WithContent("p1.md", fmt.Sprintf(`--- 1714 title: p1 1715 --- 1716 1717 %s 1718 1719 `, strings.Repeat("word ", 510)), 1720 1721 "p2.md", fmt.Sprintf(`--- 1722 title: p2 1723 --- 1724 This is a summary. 1725 1726 <!--more--> 1727 1728 %s 1729 1730 `, strings.Repeat("word ", 310)), 1731 "p3.md", fmt.Sprintf(`--- 1732 title: p3 1733 isCJKLanguage: true 1734 --- 1735 Summary: In Chinese, 好 means good. 1736 1737 <!--more--> 1738 1739 %s 1740 1741 `, strings.Repeat("好", 200)), 1742 "p4.md", fmt.Sprintf(`--- 1743 title: p4 1744 isCJKLanguage: false 1745 --- 1746 Summary: In Chinese, 好 means good. 1747 1748 <!--more--> 1749 1750 %s 1751 1752 `, strings.Repeat("好", 200)), 1753 1754 "p5.md", fmt.Sprintf(`--- 1755 title: p4 1756 isCJKLanguage: true 1757 --- 1758 Summary: In Chinese, 好 means good. 1759 1760 %s 1761 1762 `, strings.Repeat("好", 200)), 1763 "p6.md", fmt.Sprintf(`--- 1764 title: p4 1765 isCJKLanguage: false 1766 --- 1767 Summary: In Chinese, 好 means good. 1768 1769 %s 1770 1771 `, strings.Repeat("好", 200)), 1772 ) 1773 1774 b.CreateSites().Build(BuildCfg{}) 1775 1776 c.Assert(len(b.H.Sites), qt.Equals, 1) 1777 c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 6) 1778 1779 b.AssertFileContent("public/p1/index.html", "WordCount: 510\nFuzzyWordCount: 600\nReadingTime: 3\nLen Plain: 2550\nLen PlainWords: 510\nTruncated: false\nLen Summary: 2549\nLen Content: 2557") 1780 1781 b.AssertFileContent("public/p2/index.html", "WordCount: 314\nFuzzyWordCount: 400\nReadingTime: 2\nLen Plain: 1569\nLen PlainWords: 314\nTruncated: true\nLen Summary: 25\nLen Content: 1582") 1782 1783 b.AssertFileContent("public/p3/index.html", "WordCount: 206\nFuzzyWordCount: 300\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 43\nLen Content: 651") 1784 b.AssertFileContent("public/p4/index.html", "WordCount: 7\nFuzzyWordCount: 100\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 43\nLen Content: 651") 1785 b.AssertFileContent("public/p5/index.html", "WordCount: 206\nFuzzyWordCount: 300\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 229\nLen Content: 652") 1786 b.AssertFileContent("public/p6/index.html", "WordCount: 7\nFuzzyWordCount: 100\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: false\nLen Summary: 637\nLen Content: 652") 1787 } 1788 1789 func TestScratch(t *testing.T) { 1790 t.Parallel() 1791 1792 b := newTestSitesBuilder(t) 1793 b.WithSimpleConfigFile().WithTemplatesAdded("index.html", ` 1794 {{ .Scratch.Set "b" "bv" }} 1795 B: {{ .Scratch.Get "b" }} 1796 `, 1797 "shortcodes/scratch.html", ` 1798 {{ .Scratch.Set "c" "cv" }} 1799 C: {{ .Scratch.Get "c" }} 1800 `, 1801 ) 1802 1803 b.WithContentAdded("scratchme.md", ` 1804 --- 1805 title: Scratch Me! 1806 --- 1807 1808 {{< scratch >}} 1809 `) 1810 b.Build(BuildCfg{}) 1811 1812 b.AssertFileContent("public/index.html", "B: bv") 1813 b.AssertFileContent("public/scratchme/index.html", "C: cv") 1814 } 1815 1816 func TestScratchRebuild(t *testing.T) { 1817 t.Parallel() 1818 1819 files := ` 1820 -- config.toml -- 1821 -- content/p1.md -- 1822 --- 1823 title: "p1" 1824 --- 1825 {{< scratchme >}} 1826 -- layouts/shortcodes/foo.html -- 1827 notused 1828 -- layouts/shortcodes/scratchme.html -- 1829 {{ .Page.Scratch.Set "scratch" "foo" }} 1830 {{ .Page.Store.Set "scratch" "bar" }} 1831 -- layouts/_default/single.html -- 1832 {{ .Content }} 1833 Scratch: {{ .Scratch.Get "scratch" }}| 1834 Store: {{ .Store.Get "scratch" }}| 1835 ` 1836 1837 b := NewIntegrationTestBuilder( 1838 IntegrationTestConfig{ 1839 T: t, 1840 TxtarString: files, 1841 Running: true, 1842 }, 1843 ).Build() 1844 1845 b.AssertFileContent("public/p1/index.html", ` 1846 Scratch: foo| 1847 Store: bar| 1848 `) 1849 1850 b.EditFiles("layouts/shortcodes/foo.html", "edit") 1851 1852 b.Build() 1853 1854 b.AssertFileContent("public/p1/index.html", ` 1855 Scratch: | 1856 Store: bar| 1857 `) 1858 } 1859 1860 func TestPageParam(t *testing.T) { 1861 t.Parallel() 1862 1863 b := newTestSitesBuilder(t).WithConfigFile("toml", ` 1864 1865 baseURL = "https://example.org" 1866 1867 [params] 1868 [params.author] 1869 name = "Kurt Vonnegut" 1870 1871 `) 1872 b.WithTemplatesAdded("index.html", ` 1873 1874 {{ $withParam := .Site.GetPage "withparam" }} 1875 {{ $noParam := .Site.GetPage "noparam" }} 1876 {{ $withStringParam := .Site.GetPage "withstringparam" }} 1877 1878 Author page: {{ $withParam.Param "author.name" }} 1879 Author name page string: {{ $withStringParam.Param "author.name" }}| 1880 Author page string: {{ $withStringParam.Param "author" }}| 1881 Author site config: {{ $noParam.Param "author.name" }} 1882 1883 `, 1884 ) 1885 1886 b.WithContent("withparam.md", ` 1887 +++ 1888 title = "With Param!" 1889 [author] 1890 name = "Ernest Miller Hemingway" 1891 1892 +++ 1893 1894 `, 1895 1896 "noparam.md", ` 1897 --- 1898 title: "No Param!" 1899 --- 1900 `, "withstringparam.md", ` 1901 +++ 1902 title = "With string Param!" 1903 author = "Jo Nesbø" 1904 1905 +++ 1906 1907 `) 1908 b.Build(BuildCfg{}) 1909 1910 b.AssertFileContent("public/index.html", 1911 "Author page: Ernest Miller Hemingway", 1912 "Author name page string: Kurt Vonnegut|", 1913 "Author page string: Jo Nesbø|", 1914 "Author site config: Kurt Vonnegut") 1915 } 1916 1917 func TestGoldmark(t *testing.T) { 1918 t.Parallel() 1919 1920 b := newTestSitesBuilder(t).WithConfigFile("toml", ` 1921 baseURL = "https://example.org" 1922 1923 [markup] 1924 defaultMarkdownHandler="goldmark" 1925 [markup.goldmark] 1926 [markup.goldmark.renderer] 1927 unsafe = false 1928 [markup.highlight] 1929 noClasses=false 1930 1931 1932 `) 1933 b.WithTemplatesAdded("_default/single.html", ` 1934 Title: {{ .Title }} 1935 ToC: {{ .TableOfContents }} 1936 Content: {{ .Content }} 1937 1938 `, "shortcodes/t.html", `T-SHORT`, "shortcodes/s.html", `## Code 1939 {{ .Inner }} 1940 `) 1941 1942 content := ` 1943 +++ 1944 title = "A Page!" 1945 +++ 1946 1947 ## Shortcode {{% t %}} in header 1948 1949 ## Code Fense in Shortcode 1950 1951 {{% s %}} 1952 $$$bash {hl_lines=[1]} 1953 SHORT 1954 $$$ 1955 {{% /s %}} 1956 1957 ## Code Fence 1958 1959 $$$bash {hl_lines=[1]} 1960 MARKDOWN 1961 $$$ 1962 1963 Link with URL as text 1964 1965 [https://google.com](https://google.com) 1966 1967 1968 ` 1969 content = strings.ReplaceAll(content, "$$$", "```") 1970 1971 b.WithContent("page.md", content) 1972 1973 b.Build(BuildCfg{}) 1974 1975 b.AssertFileContent("public/page/index.html", 1976 `<nav id="TableOfContents"> 1977 <li><a href="#shortcode-t-short-in-header">Shortcode T-SHORT in header</a></li> 1978 <code class="language-bash" data-lang="bash"><span class="line hl"><span class="cl">SHORT 1979 <code class="language-bash" data-lang="bash"><span class="line hl"><span class="cl">MARKDOWN 1980 <p><a href="https://google.com">https://google.com</a></p> 1981 `) 1982 } 1983 1984 func TestPageCaseIssues(t *testing.T) { 1985 t.Parallel() 1986 1987 b := newTestSitesBuilder(t) 1988 b.WithConfigFile("toml", `defaultContentLanguage = "no" 1989 [languages] 1990 [languages.NO] 1991 title = "Norsk" 1992 `) 1993 b.WithContent("a/B/C/Page1.md", "---\ntitle: Page1\n---") 1994 b.WithTemplates("index.html", ` 1995 {{ $p1 := site.GetPage "a/B/C/Page1" }} 1996 Lang: {{ .Lang }} 1997 Page1: {{ $p1.Path }} 1998 `) 1999 2000 b.Build(BuildCfg{}) 2001 2002 b.AssertFileContent("public/index.html", "Lang: no", filepath.FromSlash("Page1: a/B/C/Page1.md")) 2003 }