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