github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/hugolib/content_render_hooks_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 requiredF 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  	"testing"
    19  
    20  	qt "github.com/frankban/quicktest"
    21  	"github.com/gohugoio/hugo/common/loggers"
    22  )
    23  
    24  func TestRenderHookEditNestedPartial(t *testing.T) {
    25  	config := `
    26  baseURL="https://example.org"
    27  workingDir="/mywork"
    28  `
    29  	b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
    30  
    31  	b.WithTemplates("_default/single.html", "{{ .Content }}")
    32  	b.WithTemplates("partials/mypartial1.html", `PARTIAL1 {{ partial "mypartial2.html" }}`)
    33  	b.WithTemplates("partials/mypartial2.html", `PARTIAL2`)
    34  	b.WithTemplates("_default/_markup/render-link.html", `Link {{ .Text | safeHTML }}|{{ partial "mypartial1.html" . }}END`)
    35  
    36  	b.WithContent("p1.md", `---
    37  title: P1
    38  ---
    39  
    40  [First Link](https://www.google.com "Google's Homepage")
    41  
    42  `)
    43  	b.Build(BuildCfg{})
    44  
    45  	b.AssertFileContent("public/p1/index.html", `Link First Link|PARTIAL1 PARTIAL2END`)
    46  
    47  	b.EditFiles("layouts/partials/mypartial1.html", `PARTIAL1_EDITED {{ partial "mypartial2.html" }}`)
    48  
    49  	b.Build(BuildCfg{})
    50  
    51  	b.AssertFileContent("public/p1/index.html", `Link First Link|PARTIAL1_EDITED PARTIAL2END`)
    52  
    53  	b.EditFiles("layouts/partials/mypartial2.html", `PARTIAL2_EDITED`)
    54  
    55  	b.Build(BuildCfg{})
    56  
    57  	b.AssertFileContent("public/p1/index.html", `Link First Link|PARTIAL1_EDITED PARTIAL2_EDITEDEND`)
    58  }
    59  
    60  func TestRenderHooks(t *testing.T) {
    61  	config := `
    62  baseURL="https://example.org"
    63  workingDir="/mywork"
    64  
    65  [markup]
    66  [markup.goldmark]
    67  [markup.goldmark.parser]
    68  autoHeadingID = true
    69  autoHeadingIDType = "github"
    70  [markup.goldmark.parser.attribute]
    71  block = true
    72  title = true
    73  
    74  `
    75  	b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
    76  	b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`)
    77  	b.WithTemplatesAdded("shortcodes/myshortcode1.html", `{{ partial "mypartial1" }}`)
    78  	b.WithTemplatesAdded("shortcodes/myshortcode2.html", `{{ partial "mypartial2" }}`)
    79  	b.WithTemplatesAdded("shortcodes/myshortcode3.html", `SHORT3|`)
    80  	b.WithTemplatesAdded("shortcodes/myshortcode4.html", `
    81  <div class="foo">
    82  {{ .Inner | markdownify }}
    83  </div>
    84  `)
    85  	b.WithTemplatesAdded("shortcodes/myshortcode5.html", `
    86  Inner Inline: {{ .Inner | .Page.RenderString }}
    87  Inner Block: {{ .Inner | .Page.RenderString (dict "display" "block" ) }}
    88  `)
    89  
    90  	b.WithTemplatesAdded("shortcodes/myshortcode6.html", `.Render: {{ .Page.Render "myrender" }}`)
    91  	b.WithTemplatesAdded("partials/mypartial1.html", `PARTIAL1`)
    92  	b.WithTemplatesAdded("partials/mypartial2.html", `PARTIAL2  {{ partial "mypartial3.html" }}`)
    93  	b.WithTemplatesAdded("partials/mypartial3.html", `PARTIAL3`)
    94  	b.WithTemplatesAdded("partials/mypartial4.html", `PARTIAL4`)
    95  	b.WithTemplatesAdded("customview/myrender.html", `myrender: {{ .Title }}|P4: {{ partial "mypartial4" }}`)
    96  	b.WithTemplatesAdded("_default/_markup/render-link.html", `{{ with .Page }}{{ .Title }}{{ end }}|{{ .Destination | safeURL }}|Title: {{ .Title | safeHTML }}|Text: {{ .Text | safeHTML }}|END`)
    97  	b.WithTemplatesAdded("docs/_markup/render-link.html", `Link docs section: {{ .Text | safeHTML }}|END`)
    98  	b.WithTemplatesAdded("_default/_markup/render-image.html", `IMAGE: {{ .Page.Title }}||{{ .Destination | safeURL }}|Title: {{ .Title | safeHTML }}|Text: {{ .Text | safeHTML }}|END`)
    99  	b.WithTemplatesAdded("_default/_markup/render-heading.html", `HEADING: {{ .Page.Title }}||Level: {{ .Level }}|Anchor: {{ .Anchor | safeURL }}|Text: {{ .Text | safeHTML }}|Attributes: {{ .Attributes }}|END`)
   100  	b.WithTemplatesAdded("docs/_markup/render-heading.html", `Docs Level: {{ .Level }}|END`)
   101  
   102  	b.WithContent("customview/p1.md", `---
   103  title: Custom View
   104  ---
   105  
   106  {{< myshortcode6 >}}
   107  
   108  	`, "blog/p1.md", `---
   109  title: Cool Page
   110  ---
   111  
   112  [First Link](https://www.google.com "Google's Homepage")
   113  <https://foo.bar/>
   114  https://bar.baz/
   115  <fake@example.com>
   116  <mailto:fake2@example.com>
   117  
   118  {{< myshortcode3 >}}
   119  
   120  [Second Link](https://www.google.com "Google's Homepage")
   121  
   122  Image:
   123  
   124  ![Drag Racing](/images/Dragster.jpg "image title")
   125  
   126  Attributes:
   127  
   128  ## Some Heading {.text-serif #a-heading title="Hovered"} 
   129  
   130  
   131  `, "blog/p2.md", `---
   132  title: Cool Page2
   133  layout: mylayout
   134  ---
   135  
   136  {{< myshortcode1 >}}
   137  
   138  [Some Text](https://www.google.com "Google's Homepage")
   139  
   140  ,[No Whitespace Please](https://gohugo.io),
   141  
   142  
   143  
   144  `, "blog/p3.md", `---
   145  title: Cool Page3
   146  ---
   147  
   148  {{< myshortcode2 >}}
   149  
   150  
   151  `, "docs/docs1.md", `---
   152  title: Docs 1
   153  ---
   154  
   155  
   156  [Docs 1](https://www.google.com "Google's Homepage")
   157  
   158  
   159  `, "blog/p4.md", `---
   160  title: Cool Page With Image
   161  ---
   162  
   163  Image:
   164  
   165  ![Drag Racing](/images/Dragster.jpg "image title")
   166  
   167  
   168  `, "blog/p5.md", `---
   169  title: Cool Page With Markdownify
   170  ---
   171  
   172  {{< myshortcode4 >}}
   173  Inner Link: [Inner Link](https://www.google.com "Google's Homepage")
   174  {{< /myshortcode4 >}}
   175  
   176  `, "blog/p6.md", `---
   177  title: With RenderString
   178  ---
   179  
   180  {{< myshortcode5 >}}Inner Link: [Inner Link](https://www.gohugo.io "Hugo's Homepage"){{< /myshortcode5 >}}
   181  
   182  `, "blog/p7.md", `---
   183  title: With Headings
   184  ---
   185  
   186  # Heading Level 1
   187  some text
   188  
   189  ## Heading Level 2
   190  
   191  ### Heading Level 3
   192  `,
   193  		"docs/p8.md", `---
   194  title: Doc With Heading
   195  ---
   196  
   197  # Docs lvl 1
   198  
   199  `,
   200  	)
   201  
   202  	for i := 1; i <= 30; i++ {
   203  		// Add some content with no shortcodes or links, i.e no templates needed.
   204  		b.WithContent(fmt.Sprintf("blog/notempl%d.md", i), `---
   205  title: No Template
   206  ---
   207  
   208  ## Content
   209  `)
   210  	}
   211  	counters := &testCounters{}
   212  	b.Build(BuildCfg{testCounters: counters})
   213  	b.Assert(int(counters.contentRenderCounter), qt.Equals, 45)
   214  
   215  	b.AssertFileContent("public/blog/p1/index.html", `
   216  Cool Page|https://www.google.com|Title: Google's Homepage|Text: First Link|END
   217  Cool Page|https://foo.bar/|Title: |Text: https://foo.bar/|END
   218  Cool Page|https://bar.baz/|Title: |Text: https://bar.baz/|END
   219  Cool Page|mailto:fake@example.com|Title: |Text: fake@example.com|END
   220  Cool Page|mailto:fake2@example.com|Title: |Text: mailto:fake2@example.com|END
   221  Text: Second
   222  SHORT3|
   223  <p>IMAGE: Cool Page||/images/Dragster.jpg|Title: image title|Text: Drag Racing|END</p>
   224  `)
   225  
   226  	b.AssertFileContent("public/customview/p1/index.html", `.Render: myrender: Custom View|P4: PARTIAL4`)
   227  	b.AssertFileContent("public/blog/p2/index.html",
   228  		`PARTIAL
   229  ,Cool Page2|https://gohugo.io|Title: |Text: No Whitespace Please|END,`,
   230  	)
   231  	b.AssertFileContent("public/blog/p3/index.html", `PARTIAL3`)
   232  	// We may add type template support later, keep this for then. b.AssertFileContent("public/docs/docs1/index.html", `Link docs section: Docs 1|END`)
   233  	b.AssertFileContent("public/blog/p4/index.html", `<p>IMAGE: Cool Page With Image||/images/Dragster.jpg|Title: image title|Text: Drag Racing|END</p>`)
   234  	// markdownify
   235  	b.AssertFileContent("public/blog/p5/index.html", "Inner Link: |https://www.google.com|Title: Google's Homepage|Text: Inner Link|END")
   236  
   237  	b.AssertFileContent("public/blog/p6/index.html",
   238  		"Inner Inline: Inner Link: With RenderString|https://www.gohugo.io|Title: Hugo's Homepage|Text: Inner Link|END",
   239  		"Inner Block: <p>Inner Link: With RenderString|https://www.gohugo.io|Title: Hugo's Homepage|Text: Inner Link|END</p>",
   240  	)
   241  
   242  	b.EditFiles(
   243  		"layouts/_default/_markup/render-link.html", `EDITED: {{ .Destination | safeURL }}|`,
   244  		"layouts/_default/_markup/render-image.html", `IMAGE EDITED: {{ .Destination | safeURL }}|`,
   245  		"layouts/docs/_markup/render-link.html", `DOCS EDITED: {{ .Destination | safeURL }}|`,
   246  		"layouts/partials/mypartial1.html", `PARTIAL1_EDITED`,
   247  		"layouts/partials/mypartial3.html", `PARTIAL3_EDITED`,
   248  		"layouts/partials/mypartial4.html", `PARTIAL4_EDITED`,
   249  		"layouts/shortcodes/myshortcode3.html", `SHORT3_EDITED|`,
   250  	)
   251  
   252  	counters = &testCounters{}
   253  	b.Build(BuildCfg{testCounters: counters})
   254  	// Make sure that only content using the changed templates are re-rendered.
   255  	b.Assert(int(counters.contentRenderCounter), qt.Equals, 7)
   256  
   257  	b.AssertFileContent("public/customview/p1/index.html", `.Render: myrender: Custom View|P4: PARTIAL4_EDITED`)
   258  	b.AssertFileContent("public/blog/p1/index.html", `<p>EDITED: https://www.google.com|</p>`, "SHORT3_EDITED|")
   259  	b.AssertFileContent("public/blog/p2/index.html", `PARTIAL1_EDITED`)
   260  	b.AssertFileContent("public/blog/p3/index.html", `PARTIAL3_EDITED`)
   261  	// We may add type template support later, keep this for then. b.AssertFileContent("public/docs/docs1/index.html", `DOCS EDITED: https://www.google.com|</p>`)
   262  	b.AssertFileContent("public/blog/p4/index.html", `IMAGE EDITED: /images/Dragster.jpg|`)
   263  	b.AssertFileContent("public/blog/p6/index.html", "<p>Inner Link: EDITED: https://www.gohugo.io|</p>")
   264  	b.AssertFileContent("public/blog/p7/index.html", "HEADING: With Headings||Level: 1|Anchor: heading-level-1|Text: Heading Level 1|Attributes: map[id:heading-level-1]|END<p>some text</p>\nHEADING: With Headings||Level: 2|Anchor: heading-level-2|Text: Heading Level 2|Attributes: map[id:heading-level-2]|ENDHEADING: With Headings||Level: 3|Anchor: heading-level-3|Text: Heading Level 3|Attributes: map[id:heading-level-3]|END")
   265  
   266  	// https://github.com/gohugoio/hugo/issues/7349
   267  	b.AssertFileContent("public/docs/p8/index.html", "Docs Level: 1")
   268  }
   269  
   270  func TestRenderHooksDeleteTemplate(t *testing.T) {
   271  	config := `
   272  baseURL="https://example.org"
   273  workingDir="/mywork"
   274  `
   275  	b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
   276  	b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`)
   277  	b.WithTemplatesAdded("_default/_markup/render-link.html", `html-render-link`)
   278  
   279  	b.WithContent("p1.md", `---
   280  title: P1
   281  ---
   282  [First Link](https://www.google.com "Google's Homepage")
   283  
   284  `)
   285  	b.Build(BuildCfg{})
   286  
   287  	b.AssertFileContent("public/p1/index.html", `<p>html-render-link</p>`)
   288  
   289  	b.RemoveFiles(
   290  		"layouts/_default/_markup/render-link.html",
   291  	)
   292  
   293  	b.Build(BuildCfg{})
   294  	b.AssertFileContent("public/p1/index.html", `<p><a href="https://www.google.com" title="Google's Homepage">First Link</a></p>`)
   295  }
   296  
   297  func TestRenderHookAddTemplate(t *testing.T) {
   298  	config := `
   299  baseURL="https://example.org"
   300  workingDir="/mywork"
   301  `
   302  	b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
   303  	b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`)
   304  
   305  	b.WithContent("p1.md", `---
   306  title: P1
   307  ---
   308  [First Link](https://www.google.com "Google's Homepage")
   309  
   310  `)
   311  	b.Build(BuildCfg{})
   312  
   313  	b.AssertFileContent("public/p1/index.html", `<p><a href="https://www.google.com" title="Google's Homepage">First Link</a></p>`)
   314  
   315  	b.EditFiles("layouts/_default/_markup/render-link.html", `html-render-link`)
   316  
   317  	b.Build(BuildCfg{})
   318  
   319  	b.AssertFileContent("public/p1/index.html", `<p>html-render-link</p>`)
   320  }
   321  
   322  func TestRenderHooksRSS(t *testing.T) {
   323  	b := newTestSitesBuilder(t)
   324  
   325  	b.WithTemplates("index.html", `
   326  {{ $p := site.GetPage "p1.md" }}
   327  {{ $p2 := site.GetPage "p2.md" }}
   328  
   329  P1: {{ $p.Content }}
   330  P2: {{ $p2.Content }}
   331  	
   332  	`, "index.xml", `
   333  
   334  {{ $p2 := site.GetPage "p2.md" }}
   335  {{ $p3 := site.GetPage "p3.md" }}
   336  
   337  P2: {{ $p2.Content }}
   338  P3: {{ $p3.Content }}
   339  
   340  	
   341  	`,
   342  		"_default/_markup/render-link.html", `html-link: {{ .Destination | safeURL }}|`,
   343  		"_default/_markup/render-link.rss.xml", `xml-link: {{ .Destination | safeURL }}|`,
   344  		"_default/_markup/render-heading.html", `html-heading: {{ .Text }}|`,
   345  		"_default/_markup/render-heading.rss.xml", `xml-heading: {{ .Text }}|`,
   346  	)
   347  
   348  	b.WithContent("p1.md", `---
   349  title: "p1"
   350  ---
   351  P1. [I'm an inline-style link](https://www.gohugo.io)
   352  
   353  # Heading in p1
   354  
   355  `, "p2.md", `---
   356  title: "p2"
   357  ---
   358  P1. [I'm an inline-style link](https://www.bep.is)
   359  
   360  # Heading in p2
   361  
   362  `,
   363  		"p3.md", `---
   364  title: "p2"
   365  outputs: ["rss"]
   366  ---
   367  P3. [I'm an inline-style link](https://www.example.org)
   368  
   369  `,
   370  	)
   371  
   372  	b.Build(BuildCfg{})
   373  
   374  	b.AssertFileContent("public/index.html", `
   375  P1: <p>P1. html-link: https://www.gohugo.io|</p>
   376  html-heading: Heading in p1|
   377  html-heading: Heading in p2|
   378  `)
   379  	b.AssertFileContent("public/index.xml", `
   380  P2: <p>P1. xml-link: https://www.bep.is|</p>
   381  P3: <p>P3. xml-link: https://www.example.org|</p>
   382  xml-heading: Heading in p2|
   383  `)
   384  }
   385  
   386  // https://github.com/gohugoio/hugo/issues/6629
   387  func TestRenderLinkWithMarkupInText(t *testing.T) {
   388  	b := newTestSitesBuilder(t)
   389  	b.WithConfigFile("toml", `
   390  
   391  baseURL="https://example.org"
   392  
   393  [markup]
   394    [markup.goldmark]
   395      [markup.goldmark.renderer]
   396        unsafe = true
   397      
   398  `)
   399  
   400  	b.WithTemplates("index.html", `
   401  {{ $p := site.GetPage "p1.md" }}
   402  P1: {{ $p.Content }}
   403  
   404  	`,
   405  		"_default/_markup/render-link.html", `html-link: {{ .Destination | safeURL }}|Text: {{ .Text | safeHTML }}|Plain: {{ .PlainText | safeHTML }}`,
   406  		"_default/_markup/render-image.html", `html-image: {{ .Destination | safeURL }}|Text: {{ .Text | safeHTML }}|Plain: {{ .PlainText | safeHTML }}`,
   407  	)
   408  
   409  	b.WithContent("p1.md", `---
   410  title: "p1"
   411  ---
   412  
   413  START: [**should be bold**](https://gohugo.io)END
   414  
   415  Some regular **markup**.
   416  
   417  Image:
   418  
   419  ![Hello<br> Goodbye](image.jpg)END
   420  
   421  `)
   422  
   423  	b.Build(BuildCfg{})
   424  
   425  	b.AssertFileContent("public/index.html", `
   426    P1: <p>START: html-link: https://gohugo.io|Text: <strong>should be bold</strong>|Plain: should be boldEND</p>
   427  <p>Some regular <strong>markup</strong>.</p>
   428  <p>html-image: image.jpg|Text: Hello<br> Goodbye|Plain: Hello GoodbyeEND</p>
   429  `)
   430  }
   431  
   432  func TestRenderString(t *testing.T) {
   433  	b := newTestSitesBuilder(t)
   434  
   435  	b.WithTemplates("index.html", `
   436  {{ $p := site.GetPage "p1.md" }}
   437  {{ $optBlock := dict "display" "block" }}
   438  {{ $optOrg := dict "markup" "org" }}
   439  RSTART:{{ "**Bold Markdown**" | $p.RenderString }}:REND
   440  RSTART:{{  "**Bold Block Markdown**" | $p.RenderString  $optBlock }}:REND
   441  RSTART:{{  "/italic org mode/" | $p.RenderString  $optOrg }}:REND
   442  RSTART:{{ "## Header2" | $p.RenderString }}:REND
   443  
   444  
   445  `, "_default/_markup/render-heading.html", "Hook Heading: {{ .Level }}")
   446  
   447  	b.WithContent("p1.md", `---
   448  title: "p1"
   449  ---
   450  `,
   451  	)
   452  
   453  	b.Build(BuildCfg{})
   454  
   455  	b.AssertFileContent("public/index.html", `
   456  RSTART:<strong>Bold Markdown</strong>:REND
   457  RSTART:<p><strong>Bold Block Markdown</strong></p>
   458  RSTART:<em>italic org mode</em>:REND
   459  RSTART:Hook Heading: 2:REND
   460  `)
   461  }
   462  
   463  // https://github.com/gohugoio/hugo/issues/6882
   464  func TestRenderStringOnListPage(t *testing.T) {
   465  	renderStringTempl := `
   466  {{ .RenderString "**Hello**" }}
   467  `
   468  	b := newTestSitesBuilder(t)
   469  	b.WithContent("mysection/p1.md", `FOO`)
   470  	b.WithTemplates(
   471  		"index.html", renderStringTempl,
   472  		"_default/list.html", renderStringTempl,
   473  		"_default/single.html", renderStringTempl,
   474  	)
   475  
   476  	b.Build(BuildCfg{})
   477  
   478  	for _, filename := range []string{
   479  		"index.html",
   480  		"mysection/index.html",
   481  		"categories/index.html",
   482  		"tags/index.html",
   483  		"mysection/p1/index.html",
   484  	} {
   485  		b.AssertFileContent("public/"+filename, `<strong>Hello</strong>`)
   486  	}
   487  }
   488  
   489  // Issue 9433
   490  func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
   491  	t.Parallel()
   492  	logger := loggers.NewWarningLogger()
   493  	b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
   494  disableKinds = ["page", "section", "taxonomy", "term"]	
   495  `)
   496  	b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
   497  	b.BuildE(BuildCfg{})
   498  	b.Assert(int(logger.LogCounters().WarnCounter.Count()), qt.Equals, 0)
   499  }