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