github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/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/rst"
    28  
    29  	"github.com/gohugoio/hugo/markup/asciidocext"
    30  
    31  	"github.com/gohugoio/hugo/config"
    32  
    33  	"github.com/gohugoio/hugo/common/loggers"
    34  
    35  	"github.com/gohugoio/hugo/hugofs"
    36  
    37  	"github.com/gohugoio/hugo/resources/page"
    38  	"github.com/gohugoio/hugo/resources/resource"
    39  	"github.com/spf13/afero"
    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  	engines := []struct {
   374  		ext           string
   375  		shouldExecute func() bool
   376  	}{
   377  		{"md", func() bool { return true }},
   378  		{"mmark", 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  		cfg, fs := newTestCfg(func(cfg config.Provider) error {
   389  			for k, v := range settings {
   390  				cfg.Set(k, v)
   391  			}
   392  			return nil
   393  		})
   394  
   395  		contentDir := "content"
   396  
   397  		if s := cfg.GetString("contentDir"); s != "" {
   398  			contentDir = s
   399  		}
   400  
   401  		var fileSourcePairs []string
   402  
   403  		for i, source := range pageSources {
   404  			fileSourcePairs = append(fileSourcePairs, fmt.Sprintf("p%d.%s", i, e.ext), source)
   405  		}
   406  
   407  		for i := 0; i < len(fileSourcePairs); i += 2 {
   408  			writeSource(t, fs, filepath.Join(contentDir, fileSourcePairs[i]), fileSourcePairs[i+1])
   409  		}
   410  
   411  		// Add a content page for the home page
   412  		homePath := fmt.Sprintf("_index.%s", e.ext)
   413  		writeSource(t, fs, filepath.Join(contentDir, homePath), homePage)
   414  
   415  		b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded()
   416  		b.Build(BuildCfg{SkipRender: true})
   417  
   418  		s := b.H.Sites[0]
   419  
   420  		b.Assert(len(s.RegularPages()), qt.Equals, len(pageSources))
   421  
   422  		assertFunc(t, e.ext, s.RegularPages())
   423  
   424  		home, err := s.Info.Home()
   425  		b.Assert(err, qt.IsNil)
   426  		b.Assert(home, qt.Not(qt.IsNil))
   427  		b.Assert(home.File().Path(), qt.Equals, homePath)
   428  		b.Assert(content(home), qt.Contains, "Home Page Content")
   429  
   430  	}
   431  }
   432  
   433  // Issue #1076
   434  func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) {
   435  	t.Parallel()
   436  	cfg, fs := newTestCfg()
   437  
   438  	c := qt.New(t)
   439  
   440  	writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder)
   441  
   442  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
   443  
   444  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
   445  
   446  	p := s.RegularPages()[0]
   447  
   448  	if p.Summary() != template.HTML(
   449  		"<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>") {
   450  		t.Fatalf("Got summary:\n%q", p.Summary())
   451  	}
   452  
   453  	cnt := content(p)
   454  	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.&#160;<a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;</a></p>\n</li>\n</ol>\n</section>" {
   455  		t.Fatalf("Got content:\n%q", cnt)
   456  	}
   457  }
   458  
   459  func TestPageDatesAllKinds(t *testing.T) {
   460  	t.Parallel()
   461  
   462  	pageContent := `
   463  ---
   464  title: Page
   465  date: 2017-01-15
   466  tags: ["hugo"]
   467  categories: ["cool stuff"]
   468  ---
   469  `
   470  
   471  	b := newTestSitesBuilder(t)
   472  	b.WithSimpleConfigFile().WithContent("page.md", pageContent)
   473  	b.WithContent("blog/page.md", pageContent)
   474  
   475  	b.CreateSites().Build(BuildCfg{})
   476  
   477  	b.Assert(len(b.H.Sites), qt.Equals, 1)
   478  	s := b.H.Sites[0]
   479  
   480  	checkDate := func(t time.Time, msg string) {
   481  		b.Assert(t.Year(), qt.Equals, 2017, qt.Commentf(msg))
   482  	}
   483  
   484  	checkDated := func(d resource.Dated, msg string) {
   485  		checkDate(d.Date(), "date: "+msg)
   486  		checkDate(d.Lastmod(), "lastmod: "+msg)
   487  	}
   488  	for _, p := range s.Pages() {
   489  		checkDated(p, p.Kind())
   490  	}
   491  	checkDate(s.Info.LastChange(), "site")
   492  }
   493  
   494  func TestPageDatesSections(t *testing.T) {
   495  	t.Parallel()
   496  
   497  	b := newTestSitesBuilder(t)
   498  	b.WithSimpleConfigFile().WithContent("no-index/page.md", `
   499  ---
   500  title: Page
   501  date: 2017-01-15
   502  ---
   503  `, "with-index-no-date/_index.md", `---
   504  title: No Date
   505  ---
   506  
   507  `,
   508  		// https://github.com/gohugoio/hugo/issues/5854
   509  		"with-index-date/_index.md", `---
   510  title: Date
   511  date: 2018-01-15
   512  ---
   513  
   514  `, "with-index-date/p1.md", `---
   515  title: Date
   516  date: 2018-01-15
   517  ---
   518  
   519  `, "with-index-date/p1.md", `---
   520  title: Date
   521  date: 2018-01-15
   522  ---
   523  
   524  `)
   525  
   526  	for i := 1; i <= 20; i++ {
   527  		b.WithContent(fmt.Sprintf("main-section/p%d.md", i), `---
   528  title: Date
   529  date: 2012-01-12
   530  ---
   531  
   532  `)
   533  	}
   534  
   535  	b.CreateSites().Build(BuildCfg{})
   536  
   537  	b.Assert(len(b.H.Sites), qt.Equals, 1)
   538  	s := b.H.Sites[0]
   539  
   540  	checkDate := func(p page.Page, year int) {
   541  		b.Assert(p.Date().Year(), qt.Equals, year)
   542  		b.Assert(p.Lastmod().Year(), qt.Equals, year)
   543  	}
   544  
   545  	checkDate(s.getPage("/"), 2018)
   546  	checkDate(s.getPage("/no-index"), 2017)
   547  	b.Assert(s.getPage("/with-index-no-date").Date().IsZero(), qt.Equals, true)
   548  	checkDate(s.getPage("/with-index-date"), 2018)
   549  
   550  	b.Assert(s.Site.LastChange().Year(), qt.Equals, 2018)
   551  }
   552  
   553  func TestCreateNewPage(t *testing.T) {
   554  	t.Parallel()
   555  	c := qt.New(t)
   556  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   557  		p := pages[0]
   558  
   559  		// issue #2290: Path is relative to the content dir and will continue to be so.
   560  		c.Assert(p.File().Path(), qt.Equals, fmt.Sprintf("p0.%s", ext))
   561  		c.Assert(p.IsHome(), qt.Equals, false)
   562  		checkPageTitle(t, p, "Simple")
   563  		checkPageContent(t, p, normalizeExpected(ext, "<p>Simple Page</p>\n"))
   564  		checkPageSummary(t, p, "Simple Page")
   565  		checkPageType(t, p, "page")
   566  	}
   567  
   568  	settings := map[string]interface{}{
   569  		"contentDir": "mycontent",
   570  	}
   571  
   572  	testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePage)
   573  }
   574  
   575  func TestPageSummary(t *testing.T) {
   576  	t.Parallel()
   577  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   578  		p := pages[0]
   579  		checkPageTitle(t, p, "SimpleWithoutSummaryDelimiter")
   580  		// Source is not Asciidoctor- or RST-compatible so don't test them
   581  		if ext != "ad" && ext != "rst" {
   582  			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)
   583  			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)
   584  		}
   585  		checkPageType(t, p, "page")
   586  	}
   587  
   588  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithoutSummaryDelimiter)
   589  }
   590  
   591  func TestPageWithDelimiter(t *testing.T) {
   592  	t.Parallel()
   593  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   594  		p := pages[0]
   595  		checkPageTitle(t, p, "Simple")
   596  		checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line</p>\n\n<p>Some more text</p>\n"), ext)
   597  		checkPageSummary(t, p, normalizeExpected(ext, "<p>Summary Next Line</p>"), ext)
   598  		checkPageType(t, p, "page")
   599  	}
   600  
   601  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiter)
   602  }
   603  
   604  func TestPageWithSummaryParameter(t *testing.T) {
   605  	t.Parallel()
   606  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   607  		p := pages[0]
   608  		checkPageTitle(t, p, "SimpleWithSummaryParameter")
   609  		checkPageContent(t, p, normalizeExpected(ext, "<p>Some text.</p>\n\n<p>Some more text.</p>\n"), ext)
   610  		// Summary is not Asciidoctor- or RST-compatible so don't test them
   611  		if ext != "ad" && ext != "rst" {
   612  			checkPageSummary(t, p, normalizeExpected(ext, "Page with summary parameter and <a href=\"http://www.example.com/\">a link</a>"), ext)
   613  		}
   614  		checkPageType(t, p, "page")
   615  	}
   616  
   617  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryParameter)
   618  }
   619  
   620  // Issue #3854
   621  // Also see https://github.com/gohugoio/hugo/issues/3977
   622  func TestPageWithDateFields(t *testing.T) {
   623  	c := qt.New(t)
   624  	pageWithDate := `---
   625  title: P%d
   626  weight: %d
   627  %s: 2017-10-13
   628  ---
   629  Simple Page With Some Date`
   630  
   631  	hasDate := func(p page.Page) bool {
   632  		return p.Date().Year() == 2017
   633  	}
   634  
   635  	datePage := func(field string, weight int) string {
   636  		return fmt.Sprintf(pageWithDate, weight, weight, field)
   637  	}
   638  
   639  	t.Parallel()
   640  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   641  		c.Assert(len(pages) > 0, qt.Equals, true)
   642  		for _, p := range pages {
   643  			c.Assert(hasDate(p), qt.Equals, true)
   644  		}
   645  	}
   646  
   647  	fields := []string{"date", "publishdate", "pubdate", "published"}
   648  	pageContents := make([]string, len(fields))
   649  	for i, field := range fields {
   650  		pageContents[i] = datePage(field, i+1)
   651  	}
   652  
   653  	testAllMarkdownEnginesForPages(t, assertFunc, nil, pageContents...)
   654  }
   655  
   656  // Issue #2601
   657  func TestPageRawContent(t *testing.T) {
   658  	t.Parallel()
   659  	cfg, fs := newTestCfg()
   660  	c := qt.New(t)
   661  
   662  	writeSource(t, fs, filepath.Join("content", "raw.md"), `---
   663  title: Raw
   664  ---
   665  **Raw**`)
   666  
   667  	writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .RawContent }}`)
   668  
   669  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
   670  
   671  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
   672  	p := s.RegularPages()[0]
   673  
   674  	c.Assert("**Raw**", qt.Equals, p.RawContent())
   675  }
   676  
   677  func TestPageWithShortCodeInSummary(t *testing.T) {
   678  	t.Parallel()
   679  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   680  		p := pages[0]
   681  		checkPageTitle(t, p, "Simple")
   682  		checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Next Line. <figure><img src=\"/not/real\"/> </figure> . More text here.</p><p>Some more text</p>"))
   683  		checkPageSummary(t, p, "Summary Next Line.  . More text here. Some more text")
   684  		checkPageType(t, p, "page")
   685  	}
   686  
   687  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithShortcodeInSummary)
   688  }
   689  
   690  func TestPageWithAdditionalExtension(t *testing.T) {
   691  	t.Parallel()
   692  	cfg, fs := newTestCfg()
   693  	cfg.Set("markup", map[string]interface{}{
   694  		"defaultMarkdownHandler": "blackfriday", // TODO(bep)
   695  	})
   696  
   697  	c := qt.New(t)
   698  
   699  	writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithAdditionalExtension)
   700  
   701  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
   702  
   703  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
   704  
   705  	p := s.RegularPages()[0]
   706  
   707  	checkPageContent(t, p, "<p>first line.<br />\nsecond line.</p>\n\n<p>fourth line.</p>\n")
   708  }
   709  
   710  func TestTableOfContents(t *testing.T) {
   711  	cfg, fs := newTestCfg()
   712  	c := qt.New(t)
   713  
   714  	writeSource(t, fs, filepath.Join("content", "tocpage.md"), pageWithToC)
   715  
   716  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
   717  
   718  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
   719  
   720  	p := s.RegularPages()[0]
   721  
   722  	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&ndash;as swift as the passage of light&ndash;would leap after me from the pit about the cylinder and strike me down. ## BB</p><h3 id=\"bbb\">BBB</h3> <p>&ldquo;You&rsquo;re a great Granser,&rdquo; he cried delightedly, &ldquo;always making believe them little marks mean something.&rdquo;</p>")
   723  	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>")
   724  }
   725  
   726  func TestPageWithMoreTag(t *testing.T) {
   727  	t.Parallel()
   728  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   729  		p := pages[0]
   730  		checkPageTitle(t, p, "Simple")
   731  		checkPageContent(t, p, normalizeExpected(ext, "<p>Summary Same Line</p>\n\n<p>Some more text</p>\n"))
   732  		checkPageSummary(t, p, normalizeExpected(ext, "<p>Summary Same Line</p>"))
   733  		checkPageType(t, p, "page")
   734  	}
   735  
   736  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithSummaryDelimiterSameLine)
   737  }
   738  
   739  // #2973
   740  func TestSummaryWithHTMLTagsOnNextLine(t *testing.T) {
   741  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   742  		c := qt.New(t)
   743  		p := pages[0]
   744  		s := string(p.Summary())
   745  		c.Assert(s, qt.Contains, "Happy new year everyone!")
   746  		c.Assert(s, qt.Not(qt.Contains), "User interface")
   747  	}
   748  
   749  	testAllMarkdownEnginesForPages(t, assertFunc, nil, `---
   750  title: Simple
   751  ---
   752  Happy new year everyone!
   753  
   754  Here is the last report for commits in the year 2016. It covers hrev50718-hrev50829.
   755  
   756  <!--more-->
   757  
   758  <h3>User interface</h3>
   759  
   760  `)
   761  }
   762  
   763  func TestPageWithDate(t *testing.T) {
   764  	t.Parallel()
   765  	cfg, fs := newTestCfg()
   766  	c := qt.New(t)
   767  
   768  	writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageRFC3339Date)
   769  
   770  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
   771  
   772  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
   773  
   774  	p := s.RegularPages()[0]
   775  	d, _ := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z")
   776  
   777  	checkPageDate(t, p, d)
   778  }
   779  
   780  func TestPageWithLastmodFromGitInfo(t *testing.T) {
   781  	if htesting.IsCI() {
   782  		// TODO(bep) figure out why this fails on GitHub actions.
   783  		t.Skip("Skip GitInfo test on CI")
   784  	}
   785  	c := qt.New(t)
   786  
   787  	// We need to use the OS fs for this.
   788  	cfg := config.New()
   789  	fs := hugofs.NewFrom(hugofs.Os, cfg)
   790  	fs.Destination = &afero.MemMapFs{}
   791  
   792  	wd, err := os.Getwd()
   793  	c.Assert(err, qt.IsNil)
   794  
   795  	cfg.Set("frontmatter", map[string]interface{}{
   796  		"lastmod": []string{":git", "lastmod"},
   797  	})
   798  	cfg.Set("defaultContentLanguage", "en")
   799  
   800  	langConfig := map[string]interface{}{
   801  		"en": map[string]interface{}{
   802  			"weight":       1,
   803  			"languageName": "English",
   804  			"contentDir":   "content",
   805  		},
   806  		"nn": map[string]interface{}{
   807  			"weight":       2,
   808  			"languageName": "Nynorsk",
   809  			"contentDir":   "content_nn",
   810  		},
   811  	}
   812  
   813  	cfg.Set("languages", langConfig)
   814  	cfg.Set("enableGitInfo", true)
   815  
   816  	cfg.Set("workingDir", filepath.Join(wd, "testsite"))
   817  
   818  	b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded()
   819  
   820  	b.Build(BuildCfg{SkipRender: true})
   821  	h := b.H
   822  
   823  	c.Assert(len(h.Sites), qt.Equals, 2)
   824  
   825  	enSite := h.Sites[0]
   826  	c.Assert(len(enSite.RegularPages()), qt.Equals, 1)
   827  
   828  	// 2018-03-11 is the Git author date for testsite/content/first-post.md
   829  	c.Assert(enSite.RegularPages()[0].Lastmod().Format("2006-01-02"), qt.Equals, "2018-03-11")
   830  
   831  	nnSite := h.Sites[1]
   832  	c.Assert(len(nnSite.RegularPages()), qt.Equals, 1)
   833  
   834  	// 2018-08-11 is the Git author date for testsite/content_nn/first-post.md
   835  	c.Assert(nnSite.RegularPages()[0].Lastmod().Format("2006-01-02"), qt.Equals, "2018-08-11")
   836  }
   837  
   838  func TestPageWithFrontMatterConfig(t *testing.T) {
   839  	for _, dateHandler := range []string{":filename", ":fileModTime"} {
   840  		dateHandler := dateHandler
   841  		t.Run(fmt.Sprintf("dateHandler=%q", dateHandler), func(t *testing.T) {
   842  			t.Parallel()
   843  			c := qt.New(t)
   844  			cfg, fs := newTestCfg()
   845  
   846  			pageTemplate := `
   847  ---
   848  title: Page
   849  weight: %d
   850  lastMod: 2018-02-28
   851  %s
   852  ---
   853  Content
   854  `
   855  
   856  			cfg.Set("frontmatter", map[string]interface{}{
   857  				"date": []string{dateHandler, "date"},
   858  			})
   859  
   860  			c1 := filepath.Join("content", "section", "2012-02-21-noslug.md")
   861  			c2 := filepath.Join("content", "section", "2012-02-22-slug.md")
   862  
   863  			writeSource(t, fs, c1, fmt.Sprintf(pageTemplate, 1, ""))
   864  			writeSource(t, fs, c2, fmt.Sprintf(pageTemplate, 2, "slug: aslug"))
   865  
   866  			c1fi, err := fs.Source.Stat(c1)
   867  			c.Assert(err, qt.IsNil)
   868  			c2fi, err := fs.Source.Stat(c2)
   869  			c.Assert(err, qt.IsNil)
   870  
   871  			b := newTestSitesBuilderFromDepsCfg(t, deps.DepsCfg{Fs: fs, Cfg: cfg}).WithNothingAdded()
   872  			b.Build(BuildCfg{SkipRender: true})
   873  
   874  			s := b.H.Sites[0]
   875  			c.Assert(len(s.RegularPages()), qt.Equals, 2)
   876  
   877  			noSlug := s.RegularPages()[0]
   878  			slug := s.RegularPages()[1]
   879  
   880  			c.Assert(noSlug.Lastmod().Day(), qt.Equals, 28)
   881  
   882  			switch strings.ToLower(dateHandler) {
   883  			case ":filename":
   884  				c.Assert(noSlug.Date().IsZero(), qt.Equals, false)
   885  				c.Assert(slug.Date().IsZero(), qt.Equals, false)
   886  				c.Assert(noSlug.Date().Year(), qt.Equals, 2012)
   887  				c.Assert(slug.Date().Year(), qt.Equals, 2012)
   888  				c.Assert(noSlug.Slug(), qt.Equals, "noslug")
   889  				c.Assert(slug.Slug(), qt.Equals, "aslug")
   890  			case ":filemodtime":
   891  				c.Assert(noSlug.Date().Year(), qt.Equals, c1fi.ModTime().Year())
   892  				c.Assert(slug.Date().Year(), qt.Equals, c2fi.ModTime().Year())
   893  				fallthrough
   894  			default:
   895  				c.Assert(noSlug.Slug(), qt.Equals, "")
   896  				c.Assert(slug.Slug(), qt.Equals, "aslug")
   897  
   898  			}
   899  		})
   900  	}
   901  }
   902  
   903  func TestWordCountWithAllCJKRunesWithoutHasCJKLanguage(t *testing.T) {
   904  	t.Parallel()
   905  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   906  		p := pages[0]
   907  		if p.WordCount() != 8 {
   908  			t.Fatalf("[%s] incorrect word count. expected %v, got %v", ext, 8, p.WordCount())
   909  		}
   910  	}
   911  
   912  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithAllCJKRunes)
   913  }
   914  
   915  func TestWordCountWithAllCJKRunesHasCJKLanguage(t *testing.T) {
   916  	t.Parallel()
   917  	settings := map[string]interface{}{"hasCJKLanguage": true}
   918  
   919  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   920  		p := pages[0]
   921  		if p.WordCount() != 15 {
   922  			t.Fatalf("[%s] incorrect word count, expected %v, got %v", ext, 15, p.WordCount())
   923  		}
   924  	}
   925  	testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithAllCJKRunes)
   926  }
   927  
   928  func TestWordCountWithMainEnglishWithCJKRunes(t *testing.T) {
   929  	t.Parallel()
   930  	settings := map[string]interface{}{"hasCJKLanguage": true}
   931  
   932  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   933  		p := pages[0]
   934  		if p.WordCount() != 74 {
   935  			t.Fatalf("[%s] incorrect word count, expected %v, got %v", ext, 74, p.WordCount())
   936  		}
   937  
   938  		if p.Summary() != simplePageWithMainEnglishWithCJKRunesSummary {
   939  			t.Fatalf("[%s] incorrect Summary for content '%s'. expected %v, got %v", ext, p.Plain(),
   940  				simplePageWithMainEnglishWithCJKRunesSummary, p.Summary())
   941  		}
   942  	}
   943  
   944  	testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithMainEnglishWithCJKRunes)
   945  }
   946  
   947  func TestWordCountWithIsCJKLanguageFalse(t *testing.T) {
   948  	t.Parallel()
   949  	settings := map[string]interface{}{
   950  		"hasCJKLanguage": true,
   951  	}
   952  
   953  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   954  		p := pages[0]
   955  		if p.WordCount() != 75 {
   956  			t.Fatalf("[%s] incorrect word count for content '%s'. expected %v, got %v", ext, p.Plain(), 74, p.WordCount())
   957  		}
   958  
   959  		if p.Summary() != simplePageWithIsCJKLanguageFalseSummary {
   960  			t.Fatalf("[%s] incorrect Summary for content '%s'. expected %v, got %v", ext, p.Plain(),
   961  				simplePageWithIsCJKLanguageFalseSummary, p.Summary())
   962  		}
   963  	}
   964  
   965  	testAllMarkdownEnginesForPages(t, assertFunc, settings, simplePageWithIsCJKLanguageFalse)
   966  }
   967  
   968  func TestWordCount(t *testing.T) {
   969  	t.Parallel()
   970  	assertFunc := func(t *testing.T, ext string, pages page.Pages) {
   971  		p := pages[0]
   972  		if p.WordCount() != 483 {
   973  			t.Fatalf("[%s] incorrect word count. expected %v, got %v", ext, 483, p.WordCount())
   974  		}
   975  
   976  		if p.FuzzyWordCount() != 500 {
   977  			t.Fatalf("[%s] incorrect word count. expected %v, got %v", ext, 500, p.FuzzyWordCount())
   978  		}
   979  
   980  		if p.ReadingTime() != 3 {
   981  			t.Fatalf("[%s] incorrect min read. expected %v, got %v", ext, 3, p.ReadingTime())
   982  		}
   983  	}
   984  
   985  	testAllMarkdownEnginesForPages(t, assertFunc, nil, simplePageWithLongContent)
   986  }
   987  
   988  func TestPagePaths(t *testing.T) {
   989  	t.Parallel()
   990  	c := qt.New(t)
   991  
   992  	siteParmalinksSetting := map[string]string{
   993  		"post": ":year/:month/:day/:title/",
   994  	}
   995  
   996  	tests := []struct {
   997  		content      string
   998  		path         string
   999  		hasPermalink bool
  1000  		expected     string
  1001  	}{
  1002  		{simplePage, "post/x.md", false, "post/x.html"},
  1003  		{simplePageWithURL, "post/x.md", false, "simple/url/index.html"},
  1004  		{simplePageWithSlug, "post/x.md", false, "post/simple-slug.html"},
  1005  		{simplePageWithDate, "post/x.md", true, "2013/10/15/simple/index.html"},
  1006  		{UTF8Page, "post/x.md", false, "post/x.html"},
  1007  		{UTF8PageWithURL, "post/x.md", false, "ラーメン/url/index.html"},
  1008  		{UTF8PageWithSlug, "post/x.md", false, "post/ラーメン-slug.html"},
  1009  		{UTF8PageWithDate, "post/x.md", true, "2013/10/15/ラーメン/index.html"},
  1010  	}
  1011  
  1012  	for _, test := range tests {
  1013  		cfg, fs := newTestCfg()
  1014  
  1015  		if test.hasPermalink {
  1016  			cfg.Set("permalinks", siteParmalinksSetting)
  1017  		}
  1018  
  1019  		writeSource(t, fs, filepath.Join("content", filepath.FromSlash(test.path)), test.content)
  1020  
  1021  		s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
  1022  		c.Assert(len(s.RegularPages()), qt.Equals, 1)
  1023  
  1024  	}
  1025  }
  1026  
  1027  func TestTranslationKey(t *testing.T) {
  1028  	t.Parallel()
  1029  	c := qt.New(t)
  1030  	cfg, fs := newTestCfg()
  1031  
  1032  	writeSource(t, fs, filepath.Join("content", filepath.FromSlash("sect/simple.no.md")), "---\ntitle: \"A1\"\ntranslationKey: \"k1\"\n---\nContent\n")
  1033  	writeSource(t, fs, filepath.Join("content", filepath.FromSlash("sect/simple.en.md")), "---\ntitle: \"A2\"\n---\nContent\n")
  1034  
  1035  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
  1036  
  1037  	c.Assert(len(s.RegularPages()), qt.Equals, 2)
  1038  
  1039  	home, _ := s.Info.Home()
  1040  	c.Assert(home, qt.Not(qt.IsNil))
  1041  	c.Assert(home.TranslationKey(), qt.Equals, "home")
  1042  	c.Assert(s.RegularPages()[0].TranslationKey(), qt.Equals, "page/k1")
  1043  	p2 := s.RegularPages()[1]
  1044  
  1045  	c.Assert(p2.TranslationKey(), qt.Equals, "page/sect/simple")
  1046  }
  1047  
  1048  func TestChompBOM(t *testing.T) {
  1049  	t.Parallel()
  1050  	c := qt.New(t)
  1051  	const utf8BOM = "\xef\xbb\xbf"
  1052  
  1053  	cfg, fs := newTestCfg()
  1054  
  1055  	writeSource(t, fs, filepath.Join("content", "simple.md"), utf8BOM+simplePage)
  1056  
  1057  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})
  1058  
  1059  	c.Assert(len(s.RegularPages()), qt.Equals, 1)
  1060  
  1061  	p := s.RegularPages()[0]
  1062  
  1063  	checkPageTitle(t, p, "Simple")
  1064  }
  1065  
  1066  func TestPageWithEmoji(t *testing.T) {
  1067  	for _, enableEmoji := range []bool{true, false} {
  1068  		v := config.New()
  1069  		v.Set("enableEmoji", enableEmoji)
  1070  
  1071  		b := newTestSitesBuilder(t).WithViper(v)
  1072  
  1073  		b.WithContent("page-emoji.md", `---
  1074  title: "Hugo Smile"
  1075  ---
  1076  This is a :smile:.
  1077  <!--more--> 
  1078  
  1079  Another :smile: This is :not: :an: :emoji:.
  1080  
  1081  O :christmas_tree:
  1082  
  1083  Write me an :e-mail: or :email:?
  1084  
  1085  Too many colons: :: ::: :::: :?: :!: :.:
  1086  
  1087  If you dislike this video, you can hit that :-1: button :stuck_out_tongue_winking_eye:,
  1088  but if you like it, hit :+1: and get subscribed!
  1089  `)
  1090  
  1091  		b.CreateSites().Build(BuildCfg{})
  1092  
  1093  		if enableEmoji {
  1094  			b.AssertFileContent("public/page-emoji/index.html",
  1095  				"This is a 😄",
  1096  				"Another 😄",
  1097  				"This is :not: :an: :emoji:.",
  1098  				"O 🎄",
  1099  				"Write me an 📧 or ✉️?",
  1100  				"Too many colons: :: ::: :::: :?: :!: :.:",
  1101  				"you can hit that 👎 button 😜,",
  1102  				"hit 👍 and get subscribed!",
  1103  			)
  1104  		} else {
  1105  			b.AssertFileContent("public/page-emoji/index.html",
  1106  				"This is a :smile:",
  1107  				"Another :smile:",
  1108  				"This is :not: :an: :emoji:.",
  1109  				"O :christmas_tree:",
  1110  				"Write me an :e-mail: or :email:?",
  1111  				"Too many colons: :: ::: :::: :?: :!: :.:",
  1112  				"you can hit that :-1: button :stuck_out_tongue_winking_eye:,",
  1113  				"hit :+1: and get subscribed!",
  1114  			)
  1115  		}
  1116  
  1117  	}
  1118  }
  1119  
  1120  func TestPageHTMLContent(t *testing.T) {
  1121  	b := newTestSitesBuilder(t)
  1122  	b.WithSimpleConfigFile()
  1123  
  1124  	frontmatter := `---
  1125  title: "HTML Content"
  1126  ---
  1127  `
  1128  	b.WithContent("regular.html", frontmatter+`<h1>Hugo</h1>`)
  1129  	b.WithContent("noblackfridayforyou.html", frontmatter+`**Hugo!**`)
  1130  	b.WithContent("manualsummary.html", frontmatter+`
  1131  <p>This is summary</p>
  1132  <!--more-->
  1133  <p>This is the main content.</p>`)
  1134  
  1135  	b.Build(BuildCfg{})
  1136  
  1137  	b.AssertFileContent(
  1138  		"public/regular/index.html",
  1139  		"Single: HTML Content|Hello|en|RelPermalink: /regular/|",
  1140  		"Summary: Hugo|Truncated: false")
  1141  
  1142  	b.AssertFileContent(
  1143  		"public/noblackfridayforyou/index.html",
  1144  		"Permalink: http://example.com/noblackfridayforyou/|**Hugo!**|",
  1145  	)
  1146  
  1147  	// https://github.com/gohugoio/hugo/issues/5723
  1148  	b.AssertFileContent(
  1149  		"public/manualsummary/index.html",
  1150  		"Single: HTML Content|Hello|en|RelPermalink: /manualsummary/|",
  1151  		"Summary: \n<p>This is summary</p>\n|Truncated: true",
  1152  		"|<p>This is the main content.</p>|",
  1153  	)
  1154  }
  1155  
  1156  // https://github.com/gohugoio/hugo/issues/5381
  1157  func TestPageManualSummary(t *testing.T) {
  1158  	b := newTestSitesBuilder(t)
  1159  	b.WithSimpleConfigFile()
  1160  
  1161  	b.WithContent("page-md-shortcode.md", `---
  1162  title: "Hugo"
  1163  ---
  1164  This is a {{< sc >}}.
  1165  <!--more--> 
  1166  Content.
  1167  `)
  1168  
  1169  	// https://github.com/gohugoio/hugo/issues/5464
  1170  	b.WithContent("page-md-only-shortcode.md", `---
  1171  title: "Hugo"
  1172  ---
  1173  {{< sc >}}
  1174  <!--more--> 
  1175  {{< sc >}}
  1176  `)
  1177  
  1178  	b.WithContent("page-md-shortcode-same-line.md", `---
  1179  title: "Hugo"
  1180  ---
  1181  This is a {{< sc >}}<!--more-->Same line.
  1182  `)
  1183  
  1184  	b.WithContent("page-md-shortcode-same-line-after.md", `---
  1185  title: "Hugo"
  1186  ---
  1187  Summary<!--more-->{{< sc >}}
  1188  `)
  1189  
  1190  	b.WithContent("page-org-shortcode.org", `#+TITLE: T1
  1191  #+AUTHOR: A1
  1192  #+DESCRIPTION: D1
  1193  This is a {{< sc >}}.
  1194  # more
  1195  Content.	
  1196  `)
  1197  
  1198  	b.WithContent("page-org-variant1.org", `#+TITLE: T1
  1199  Summary.
  1200  
  1201  # more
  1202  
  1203  Content.	
  1204  `)
  1205  
  1206  	b.WithTemplatesAdded("layouts/shortcodes/sc.html", "a shortcode")
  1207  	b.WithTemplatesAdded("layouts/_default/single.html", `
  1208  SUMMARY:{{ .Summary }}:END
  1209  --------------------------
  1210  CONTENT:{{ .Content }}
  1211  `)
  1212  
  1213  	b.CreateSites().Build(BuildCfg{})
  1214  
  1215  	b.AssertFileContent("public/page-md-shortcode/index.html",
  1216  		"SUMMARY:<p>This is a a shortcode.</p>:END",
  1217  		"CONTENT:<p>This is a a shortcode.</p>\n\n<p>Content.</p>\n",
  1218  	)
  1219  
  1220  	b.AssertFileContent("public/page-md-shortcode-same-line/index.html",
  1221  		"SUMMARY:<p>This is a a shortcode</p>:END",
  1222  		"CONTENT:<p>This is a a shortcode</p>\n\n<p>Same line.</p>\n",
  1223  	)
  1224  
  1225  	b.AssertFileContent("public/page-md-shortcode-same-line-after/index.html",
  1226  		"SUMMARY:<p>Summary</p>:END",
  1227  		"CONTENT:<p>Summary</p>\n\na shortcode",
  1228  	)
  1229  
  1230  	b.AssertFileContent("public/page-org-shortcode/index.html",
  1231  		"SUMMARY:<p>\nThis is a a shortcode.\n</p>:END",
  1232  		"CONTENT:<p>\nThis is a a shortcode.\n</p>\n<p>\nContent.\t\n</p>\n",
  1233  	)
  1234  	b.AssertFileContent("public/page-org-variant1/index.html",
  1235  		"SUMMARY:<p>\nSummary.\n</p>:END",
  1236  		"CONTENT:<p>\nSummary.\n</p>\n<p>\nContent.\t\n</p>\n",
  1237  	)
  1238  
  1239  	b.AssertFileContent("public/page-md-only-shortcode/index.html",
  1240  		"SUMMARY:a shortcode:END",
  1241  		"CONTENT:a shortcode\n\na shortcode\n",
  1242  	)
  1243  }
  1244  
  1245  // https://github.com/gohugoio/hugo/issues/5478
  1246  func TestPageWithCommentedOutFrontMatter(t *testing.T) {
  1247  	b := newTestSitesBuilder(t)
  1248  	b.WithSimpleConfigFile()
  1249  
  1250  	b.WithContent("page.md", `<!--
  1251  +++
  1252  title = "hello"
  1253  +++
  1254  -->
  1255  This is the content.
  1256  `)
  1257  
  1258  	b.WithTemplatesAdded("layouts/_default/single.html", `
  1259  Title: {{ .Title }}
  1260  Content:{{ .Content }}
  1261  `)
  1262  
  1263  	b.CreateSites().Build(BuildCfg{})
  1264  
  1265  	b.AssertFileContent("public/page/index.html",
  1266  		"Title: hello",
  1267  		"Content:<p>This is the content.</p>",
  1268  	)
  1269  }
  1270  
  1271  // https://github.com/gohugoio/hugo/issues/5781
  1272  func TestPageWithZeroFile(t *testing.T) {
  1273  	newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger()).WithSimpleConfigFile().
  1274  		WithTemplatesAdded("index.html", "{{ .File.Filename }}{{ with .File }}{{ .Dir }}{{ end }}").Build(BuildCfg{})
  1275  }
  1276  
  1277  func TestHomePageWithNoTitle(t *testing.T) {
  1278  	b := newTestSitesBuilder(t).WithConfigFile("toml", `
  1279  title = "Site Title"
  1280  `)
  1281  	b.WithTemplatesAdded("index.html", "Title|{{ with .Title }}{{ . }}{{ end }}|")
  1282  	b.WithContent("_index.md", `---
  1283  description: "No title for you!"
  1284  ---
  1285  
  1286  Content.
  1287  `)
  1288  
  1289  	b.Build(BuildCfg{})
  1290  	b.AssertFileContent("public/index.html", "Title||")
  1291  }
  1292  
  1293  func TestShouldBuild(t *testing.T) {
  1294  	t.Parallel()
  1295  	past := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
  1296  	future := time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC)
  1297  	zero := time.Time{}
  1298  
  1299  	publishSettings := []struct {
  1300  		buildFuture  bool
  1301  		buildExpired bool
  1302  		buildDrafts  bool
  1303  		draft        bool
  1304  		publishDate  time.Time
  1305  		expiryDate   time.Time
  1306  		out          bool
  1307  	}{
  1308  		// publishDate and expiryDate
  1309  		{false, false, false, false, zero, zero, true},
  1310  		{false, false, false, false, zero, future, true},
  1311  		{false, false, false, false, past, zero, true},
  1312  		{false, false, false, false, past, future, true},
  1313  		{false, false, false, false, past, past, false},
  1314  		{false, false, false, false, future, future, false},
  1315  		{false, false, false, false, future, past, false},
  1316  
  1317  		// buildFuture and buildExpired
  1318  		{false, true, false, false, past, past, true},
  1319  		{true, true, false, false, past, past, true},
  1320  		{true, false, false, false, past, past, false},
  1321  		{true, false, false, false, future, future, true},
  1322  		{true, true, false, false, future, future, true},
  1323  		{false, true, false, false, future, past, false},
  1324  
  1325  		// buildDrafts and draft
  1326  		{true, true, false, true, past, future, false},
  1327  		{true, true, true, true, past, future, true},
  1328  		{true, true, true, true, past, future, true},
  1329  	}
  1330  
  1331  	for _, ps := range publishSettings {
  1332  		s := shouldBuild(ps.buildFuture, ps.buildExpired, ps.buildDrafts, ps.draft,
  1333  			ps.publishDate, ps.expiryDate)
  1334  		if s != ps.out {
  1335  			t.Errorf("AssertShouldBuild unexpected output with params: %+v", ps)
  1336  		}
  1337  	}
  1338  }
  1339  
  1340  // "dot" in path: #1885 and #2110
  1341  // disablePathToLower regression: #3374
  1342  func TestPathIssues(t *testing.T) {
  1343  	for _, disablePathToLower := range []bool{false, true} {
  1344  		for _, uglyURLs := range []bool{false, true} {
  1345  			disablePathToLower := disablePathToLower
  1346  			uglyURLs := uglyURLs
  1347  			t.Run(fmt.Sprintf("disablePathToLower=%t,uglyURLs=%t", disablePathToLower, uglyURLs), func(t *testing.T) {
  1348  				t.Parallel()
  1349  				cfg, fs := newTestCfg()
  1350  				th := newTestHelper(cfg, fs, t)
  1351  				c := qt.New(t)
  1352  
  1353  				cfg.Set("permalinks", map[string]string{
  1354  					"post": ":section/:title",
  1355  				})
  1356  
  1357  				cfg.Set("uglyURLs", uglyURLs)
  1358  				cfg.Set("disablePathToLower", disablePathToLower)
  1359  				cfg.Set("paginate", 1)
  1360  
  1361  				writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), "<html><body>{{.Content}}</body></html>")
  1362  				writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"),
  1363  					"<html><body>P{{.Paginator.PageNumber}}|URL: {{.Paginator.URL}}|{{ if .Paginator.HasNext }}Next: {{.Paginator.Next.URL }}{{ end }}</body></html>")
  1364  
  1365  				for i := 0; i < 3; i++ {
  1366  					writeSource(t, fs, filepath.Join("content", "post", fmt.Sprintf("doc%d.md", i)),
  1367  						fmt.Sprintf(`---
  1368  title: "test%d.dot"
  1369  tags:
  1370  - ".net"
  1371  ---
  1372  # doc1
  1373  *some content*`, i))
  1374  				}
  1375  
  1376  				writeSource(t, fs, filepath.Join("content", "Blog", "Blog1.md"),
  1377  					fmt.Sprintf(`---
  1378  title: "testBlog"
  1379  tags:
  1380  - "Blog"
  1381  ---
  1382  # doc1
  1383  *some blog content*`))
  1384  
  1385  				s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
  1386  
  1387  				c.Assert(len(s.RegularPages()), qt.Equals, 4)
  1388  
  1389  				pathFunc := func(s string) string {
  1390  					if uglyURLs {
  1391  						return strings.Replace(s, "/index.html", ".html", 1)
  1392  					}
  1393  					return s
  1394  				}
  1395  
  1396  				blog := "blog"
  1397  
  1398  				if disablePathToLower {
  1399  					blog = "Blog"
  1400  				}
  1401  
  1402  				th.assertFileContent(pathFunc("public/"+blog+"/"+blog+"1/index.html"), "some blog content")
  1403  
  1404  				th.assertFileContent(pathFunc("public/post/test0.dot/index.html"), "some content")
  1405  
  1406  				if uglyURLs {
  1407  					th.assertFileContent("public/post/page/1.html", `canonical" href="/post.html"/`)
  1408  					th.assertFileContent("public/post.html", `<body>P1|URL: /post.html|Next: /post/page/2.html</body>`)
  1409  					th.assertFileContent("public/post/page/2.html", `<body>P2|URL: /post/page/2.html|Next: /post/page/3.html</body>`)
  1410  				} else {
  1411  					th.assertFileContent("public/post/page/1/index.html", `canonical" href="/post/"/`)
  1412  					th.assertFileContent("public/post/index.html", `<body>P1|URL: /post/|Next: /post/page/2/</body>`)
  1413  					th.assertFileContent("public/post/page/2/index.html", `<body>P2|URL: /post/page/2/|Next: /post/page/3/</body>`)
  1414  					th.assertFileContent("public/tags/.net/index.html", `<body>P1|URL: /tags/.net/|Next: /tags/.net/page/2/</body>`)
  1415  
  1416  				}
  1417  
  1418  				p := s.RegularPages()[0]
  1419  				if uglyURLs {
  1420  					c.Assert(p.RelPermalink(), qt.Equals, "/post/test0.dot.html")
  1421  				} else {
  1422  					c.Assert(p.RelPermalink(), qt.Equals, "/post/test0.dot/")
  1423  				}
  1424  			})
  1425  		}
  1426  	}
  1427  }
  1428  
  1429  // https://github.com/gohugoio/hugo/issues/4675
  1430  func TestWordCountAndSimilarVsSummary(t *testing.T) {
  1431  	t.Parallel()
  1432  	c := qt.New(t)
  1433  
  1434  	single := []string{"_default/single.html", `
  1435  WordCount: {{ .WordCount }}
  1436  FuzzyWordCount: {{ .FuzzyWordCount }}
  1437  ReadingTime: {{ .ReadingTime }}
  1438  Len Plain: {{ len .Plain }}
  1439  Len PlainWords: {{ len .PlainWords }}
  1440  Truncated: {{ .Truncated }}
  1441  Len Summary: {{ len .Summary }}
  1442  Len Content: {{ len .Content }}
  1443  
  1444  SUMMARY:{{ .Summary }}:{{ len .Summary }}:END
  1445  `}
  1446  
  1447  	b := newTestSitesBuilder(t)
  1448  	b.WithSimpleConfigFile().WithTemplatesAdded(single...).WithContent("p1.md", fmt.Sprintf(`---
  1449  title: p1	
  1450  ---
  1451  
  1452  %s
  1453  
  1454  `, strings.Repeat("word ", 510)),
  1455  
  1456  		"p2.md", fmt.Sprintf(`---
  1457  title: p2
  1458  ---
  1459  This is a summary.
  1460  
  1461  <!--more-->
  1462  
  1463  %s
  1464  
  1465  `, strings.Repeat("word ", 310)),
  1466  		"p3.md", fmt.Sprintf(`---
  1467  title: p3
  1468  isCJKLanguage: true
  1469  ---
  1470  Summary: In Chinese, 好 means good.
  1471  
  1472  <!--more-->
  1473  
  1474  %s
  1475  
  1476  `, strings.Repeat("好", 200)),
  1477  		"p4.md", fmt.Sprintf(`---
  1478  title: p4
  1479  isCJKLanguage: false
  1480  ---
  1481  Summary: In Chinese, 好 means good.
  1482  
  1483  <!--more-->
  1484  
  1485  %s
  1486  
  1487  `, strings.Repeat("好", 200)),
  1488  
  1489  		"p5.md", fmt.Sprintf(`---
  1490  title: p4
  1491  isCJKLanguage: true
  1492  ---
  1493  Summary: In Chinese, 好 means good.
  1494  
  1495  %s
  1496  
  1497  `, strings.Repeat("好", 200)),
  1498  		"p6.md", fmt.Sprintf(`---
  1499  title: p4
  1500  isCJKLanguage: false
  1501  ---
  1502  Summary: In Chinese, 好 means good.
  1503  
  1504  %s
  1505  
  1506  `, strings.Repeat("好", 200)),
  1507  	)
  1508  
  1509  	b.CreateSites().Build(BuildCfg{})
  1510  
  1511  	c.Assert(len(b.H.Sites), qt.Equals, 1)
  1512  	c.Assert(len(b.H.Sites[0].RegularPages()), qt.Equals, 6)
  1513  
  1514  	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")
  1515  
  1516  	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")
  1517  
  1518  	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")
  1519  	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")
  1520  	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")
  1521  	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")
  1522  }
  1523  
  1524  func TestScratchSite(t *testing.T) {
  1525  	t.Parallel()
  1526  
  1527  	b := newTestSitesBuilder(t)
  1528  	b.WithSimpleConfigFile().WithTemplatesAdded("index.html", `
  1529  {{ .Scratch.Set "b" "bv" }}
  1530  B: {{ .Scratch.Get "b" }}
  1531  `,
  1532  		"shortcodes/scratch.html", `
  1533  {{ .Scratch.Set "c" "cv" }}
  1534  C: {{ .Scratch.Get "c" }}
  1535  `,
  1536  	)
  1537  
  1538  	b.WithContentAdded("scratchme.md", `
  1539  ---
  1540  title: Scratch Me!
  1541  ---
  1542  
  1543  {{< scratch >}}
  1544  `)
  1545  	b.Build(BuildCfg{})
  1546  
  1547  	b.AssertFileContent("public/index.html", "B: bv")
  1548  	b.AssertFileContent("public/scratchme/index.html", "C: cv")
  1549  }
  1550  
  1551  func TestPageParam(t *testing.T) {
  1552  	t.Parallel()
  1553  
  1554  	b := newTestSitesBuilder(t).WithConfigFile("toml", `
  1555  
  1556  baseURL = "https://example.org"
  1557  
  1558  [params]
  1559  [params.author]
  1560    name = "Kurt Vonnegut"
  1561  
  1562  `)
  1563  	b.WithTemplatesAdded("index.html", `
  1564  
  1565  {{ $withParam := .Site.GetPage "withparam" }}
  1566  {{ $noParam := .Site.GetPage "noparam" }}
  1567  {{ $withStringParam := .Site.GetPage "withstringparam" }}
  1568  
  1569  Author page: {{ $withParam.Param "author.name" }}
  1570  Author name page string: {{ $withStringParam.Param "author.name" }}|
  1571  Author page string: {{ $withStringParam.Param "author" }}|
  1572  Author site config:  {{ $noParam.Param "author.name" }}
  1573  
  1574  `,
  1575  	)
  1576  
  1577  	b.WithContent("withparam.md", `
  1578  +++
  1579  title = "With Param!"
  1580  [author]
  1581    name = "Ernest Miller Hemingway"
  1582  
  1583  +++
  1584  
  1585  `,
  1586  
  1587  		"noparam.md", `
  1588  ---
  1589  title: "No Param!"
  1590  ---
  1591  `, "withstringparam.md", `
  1592  +++
  1593  title = "With string Param!"
  1594  author = "Jo Nesbø"
  1595  
  1596  +++
  1597  
  1598  `)
  1599  	b.Build(BuildCfg{})
  1600  
  1601  	b.AssertFileContent("public/index.html",
  1602  		"Author page: Ernest Miller Hemingway",
  1603  		"Author name page string: Kurt Vonnegut|",
  1604  		"Author page string: Jo Nesbø|",
  1605  		"Author site config:  Kurt Vonnegut")
  1606  }
  1607  
  1608  func TestGoldmark(t *testing.T) {
  1609  	t.Parallel()
  1610  
  1611  	b := newTestSitesBuilder(t).WithConfigFile("toml", `
  1612  baseURL = "https://example.org"
  1613  
  1614  [markup]
  1615  defaultMarkdownHandler="goldmark"
  1616  [markup.goldmark]
  1617  [markup.goldmark.renderer]
  1618  unsafe = false
  1619  [markup.highlight]
  1620  noClasses=false
  1621  
  1622  
  1623  `)
  1624  	b.WithTemplatesAdded("_default/single.html", `
  1625  Title: {{ .Title }}
  1626  ToC: {{ .TableOfContents }}
  1627  Content: {{ .Content }}
  1628  
  1629  `, "shortcodes/t.html", `T-SHORT`, "shortcodes/s.html", `## Code
  1630  {{ .Inner }}
  1631  `)
  1632  
  1633  	content := `
  1634  +++
  1635  title = "A Page!"
  1636  +++
  1637  
  1638  ## Shortcode {{% t %}} in header
  1639  
  1640  ## Code Fense in Shortcode
  1641  
  1642  {{% s %}}
  1643  $$$bash {hl_lines=[1]}
  1644  SHORT
  1645  $$$
  1646  {{% /s %}}
  1647  
  1648  ## Code Fence
  1649  
  1650  $$$bash {hl_lines=[1]}
  1651  MARKDOWN
  1652  $$$
  1653  
  1654  Link with URL as text
  1655  
  1656  [https://google.com](https://google.com)
  1657  
  1658  
  1659  `
  1660  	content = strings.ReplaceAll(content, "$$$", "```")
  1661  
  1662  	b.WithContent("page.md", content)
  1663  
  1664  	b.Build(BuildCfg{})
  1665  
  1666  	b.AssertFileContent("public/page/index.html",
  1667  		`<nav id="TableOfContents">
  1668  <li><a href="#shortcode-t-short-in-header">Shortcode T-SHORT in header</a></li>
  1669  <code class="language-bash" data-lang="bash"><span class="hl">SHORT
  1670  <code class="language-bash" data-lang="bash"><span class="hl">MARKDOWN
  1671  <p><a href="https://google.com">https://google.com</a></p>
  1672  `)
  1673  }
  1674  
  1675  func TestBlackfridayDefault(t *testing.T) {
  1676  	t.Parallel()
  1677  
  1678  	b := newTestSitesBuilder(t).WithConfigFile("toml", `
  1679  baseURL = "https://example.org"
  1680  
  1681  [markup]
  1682  defaultMarkdownHandler="blackfriday"
  1683  [markup.highlight]
  1684  noClasses=false
  1685  [markup.goldmark]
  1686  [markup.goldmark.renderer]
  1687  unsafe=true
  1688  
  1689  
  1690  `)
  1691  	// Use the new attribute syntax to make sure it's not Goldmark.
  1692  	b.WithTemplatesAdded("_default/single.html", `
  1693  Title: {{ .Title }}
  1694  Content: {{ .Content }}
  1695  
  1696  `, "shortcodes/s.html", `## Code
  1697  {{ .Inner }}
  1698  `)
  1699  
  1700  	content := `
  1701  +++
  1702  title = "A Page!"
  1703  +++
  1704  
  1705  
  1706  ## Code Fense in Shortcode
  1707  
  1708  {{% s %}}
  1709  S:
  1710  {{% s %}}
  1711  $$$bash {hl_lines=[1]}
  1712  SHORT
  1713  $$$
  1714  {{% /s %}}
  1715  {{% /s %}}
  1716  
  1717  ## Code Fence
  1718  
  1719  $$$bash {hl_lines=[1]}
  1720  MARKDOWN
  1721  $$$
  1722  
  1723  `
  1724  	content = strings.ReplaceAll(content, "$$$", "```")
  1725  
  1726  	for i, ext := range []string{"md", "html"} {
  1727  		b.WithContent(fmt.Sprintf("page%d.%s", i+1, ext), content)
  1728  	}
  1729  
  1730  	b.Build(BuildCfg{})
  1731  
  1732  	// Blackfriday does not support this extended attribute syntax.
  1733  	b.AssertFileContent("public/page1/index.html",
  1734  		`<pre tabindex="0"><code class="language-bash {hl_lines=[1]}" data-lang="bash {hl_lines=[1]}">SHORT</code></pre>`,
  1735  		`<pre tabindex="0"><code class="language-bash {hl_lines=[1]}" data-lang="bash {hl_lines=[1]}">MARKDOWN`,
  1736  	)
  1737  
  1738  	b.AssertFileContent("public/page2/index.html",
  1739  		`<pre tabindex="0"><code class="language-bash {hl_lines=[1]}" data-lang="bash {hl_lines=[1]}">SHORT`,
  1740  	)
  1741  }
  1742  
  1743  func TestPageCaseIssues(t *testing.T) {
  1744  	t.Parallel()
  1745  
  1746  	b := newTestSitesBuilder(t)
  1747  	b.WithConfigFile("toml", `defaultContentLanguage = "no"
  1748  [languages]
  1749  [languages.NO]
  1750  title = "Norsk"
  1751  `)
  1752  	b.WithContent("a/B/C/Page1.md", "---\ntitle: Page1\n---")
  1753  	b.WithTemplates("index.html", `
  1754  {{ $p1 := site.GetPage "a/B/C/Page1" }}
  1755  Lang: {{ .Lang }}
  1756  Page1: {{ $p1.Path }}
  1757  `)
  1758  
  1759  	b.Build(BuildCfg{})
  1760  
  1761  	b.AssertFileContent("public/index.html", "Lang: no", filepath.FromSlash("Page1: a/B/C/Page1.md"))
  1762  }