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  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  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 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 }