content_render_hooks_test.go (13420B)
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 }