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