menu_test.go (13540B)
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 required 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 const (
24 menuPageTemplate = `---
25 title: %q
26 weight: %d
27 menu:
28 %s:
29 title: %s
30 weight: %d
31 ---
32 # Doc Menu
33 `
34 )
35
36 func TestMenusSectionPagesMenu(t *testing.T) {
37 t.Parallel()
38
39 siteConfig := `
40 baseurl = "http://example.com/"
41 title = "Section Menu"
42 sectionPagesMenu = "sect"
43 `
44
45 b := newTestSitesBuilder(t).WithConfigFile("toml", siteConfig)
46
47 b.WithTemplates(
48 "partials/menu.html",
49 `{{- $p := .page -}}
50 {{- $m := .menu -}}
51 {{ range (index $p.Site.Menus $m) -}}
52 {{- .URL }}|{{ .Name }}|{{ .Title }}|{{ .Weight -}}|
53 {{- if $p.IsMenuCurrent $m . }}IsMenuCurrent{{ else }}-{{ end -}}|
54 {{- if $p.HasMenuCurrent $m . }}HasMenuCurrent{{ else }}-{{ end -}}|
55 {{- end -}}
56 `,
57 "_default/single.html",
58 `Single|{{ .Title }}
59 Menu Sect: {{ partial "menu.html" (dict "page" . "menu" "sect") }}
60 Menu Main: {{ partial "menu.html" (dict "page" . "menu" "main") }}`,
61 "_default/list.html", "List|{{ .Title }}|{{ .Content }}",
62 )
63
64 b.WithContent(
65 "sect1/p1.md", fmt.Sprintf(menuPageTemplate, "p1", 1, "main", "atitle1", 40),
66 "sect1/p2.md", fmt.Sprintf(menuPageTemplate, "p2", 2, "main", "atitle2", 30),
67 "sect2/p3.md", fmt.Sprintf(menuPageTemplate, "p3", 3, "main", "atitle3", 20),
68 "sect2/p4.md", fmt.Sprintf(menuPageTemplate, "p4", 4, "main", "atitle4", 10),
69 "sect3/p5.md", fmt.Sprintf(menuPageTemplate, "p5", 5, "main", "atitle5", 5),
70 "sect1/_index.md", newTestPage("Section One", "2017-01-01", 100),
71 "sect5/_index.md", newTestPage("Section Five", "2017-01-01", 10),
72 )
73
74 b.Build(BuildCfg{})
75 h := b.H
76
77 s := h.Sites[0]
78
79 b.Assert(len(s.Menus()), qt.Equals, 2)
80
81 p1 := s.RegularPages()[0].Menus()
82
83 // There is only one menu in the page, but it is "member of" 2
84 b.Assert(len(p1), qt.Equals, 1)
85
86 b.AssertFileContent("public/sect1/p1/index.html", "Single",
87 "Menu Sect: "+
88 "/sect5/|Section Five|Section Five|10|-|-|"+
89 "/sect1/|Section One|Section One|100|-|HasMenuCurrent|"+
90 "/sect2/|Sect2s|Sect2s|0|-|-|"+
91 "/sect3/|Sect3s|Sect3s|0|-|-|",
92 "Menu Main: "+
93 "/sect3/p5/|p5|atitle5|5|-|-|"+
94 "/sect2/p4/|p4|atitle4|10|-|-|"+
95 "/sect2/p3/|p3|atitle3|20|-|-|"+
96 "/sect1/p2/|p2|atitle2|30|-|-|"+
97 "/sect1/p1/|p1|atitle1|40|IsMenuCurrent|-|",
98 )
99
100 b.AssertFileContent("public/sect2/p3/index.html", "Single",
101 "Menu Sect: "+
102 "/sect5/|Section Five|Section Five|10|-|-|"+
103 "/sect1/|Section One|Section One|100|-|-|"+
104 "/sect2/|Sect2s|Sect2s|0|-|HasMenuCurrent|"+
105 "/sect3/|Sect3s|Sect3s|0|-|-|")
106 }
107
108 // related issue #7594
109 func TestMenusSort(t *testing.T) {
110 b := newTestSitesBuilder(t).WithSimpleConfigFile()
111
112 b.WithTemplatesAdded("index.html", `
113 {{ range $k, $v := .Site.Menus.main }}
114 Default1|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
115 {{ range $k, $v := .Site.Menus.main.ByWeight }}
116 ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
117 {{ range $k, $v := (.Site.Menus.main.ByWeight).Reverse }}
118 Reverse|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
119 {{ range $k, $v := .Site.Menus.main }}
120 Default2|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
121 {{ range $k, $v := .Site.Menus.main.ByWeight }}
122 ByWeight|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
123 {{ range $k, $v := .Site.Menus.main }}
124 Default3|{{ $k }}|{{ $v.Weight }}|{{ $v.Name }}|{{ .URL }}|{{ $v.Page }}{{ end }}
125 `)
126
127 b.WithContent("_index.md", `
128 ---
129 title: Home
130 menu:
131 main:
132 weight: 100
133 ---`)
134
135 b.WithContent("blog/A.md", `
136 ---
137 title: "A"
138 menu:
139 main:
140 weight: 10
141 ---
142 `)
143
144 b.WithContent("blog/B.md", `
145 ---
146 title: "B"
147 menu:
148 main:
149 weight: 20
150 ---
151 `)
152 b.WithContent("blog/C.md", `
153 ---
154 title: "C"
155 menu:
156 main:
157 weight: 30
158 ---
159 `)
160
161 b.Build(BuildCfg{})
162
163 b.AssertFileContent("public/index.html",
164 `Default1|0|10|A|/blog/a/|Page(/blog/A.md)
165 Default1|1|20|B|/blog/b/|Page(/blog/B.md)
166 Default1|2|30|C|/blog/c/|Page(/blog/C.md)
167 Default1|3|100|Home|/|Page(/_index.md)
168
169 ByWeight|0|10|A|/blog/a/|Page(/blog/A.md)
170 ByWeight|1|20|B|/blog/b/|Page(/blog/B.md)
171 ByWeight|2|30|C|/blog/c/|Page(/blog/C.md)
172 ByWeight|3|100|Home|/|Page(/_index.md)
173
174 Reverse|0|100|Home|/|Page(/_index.md)
175 Reverse|1|30|C|/blog/c/|Page(/blog/C.md)
176 Reverse|2|20|B|/blog/b/|Page(/blog/B.md)
177 Reverse|3|10|A|/blog/a/|Page(/blog/A.md)
178
179 Default2|0|10|A|/blog/a/|Page(/blog/A.md)
180 Default2|1|20|B|/blog/b/|Page(/blog/B.md)
181 Default2|2|30|C|/blog/c/|Page(/blog/C.md)
182 Default2|3|100|Home|/|Page(/_index.md)
183
184 ByWeight|0|10|A|/blog/a/|Page(/blog/A.md)
185 ByWeight|1|20|B|/blog/b/|Page(/blog/B.md)
186 ByWeight|2|30|C|/blog/c/|Page(/blog/C.md)
187 ByWeight|3|100|Home|/|Page(/_index.md)
188
189 Default3|0|10|A|/blog/a/|Page(/blog/A.md)
190 Default3|1|20|B|/blog/b/|Page(/blog/B.md)
191 Default3|2|30|C|/blog/c/|Page(/blog/C.md)
192 Default3|3|100|Home|/|Page(/_index.md)`,
193 )
194 }
195
196 func TestMenusFrontMatter(t *testing.T) {
197 b := newTestSitesBuilder(t).WithSimpleConfigFile()
198
199 b.WithTemplatesAdded("index.html", `
200 Main: {{ len .Site.Menus.main }}
201 Other: {{ len .Site.Menus.other }}
202 {{ range .Site.Menus.main }}
203 * Main|{{ .Name }}: {{ .URL }}
204 {{ end }}
205 {{ range .Site.Menus.other }}
206 * Other|{{ .Name }}: {{ .URL }}
207 {{ end }}
208 `)
209
210 // Issue #5828
211 b.WithContent("blog/page1.md", `
212 ---
213 title: "P1"
214 menu: main
215 ---
216
217 `)
218
219 b.WithContent("blog/page2.md", `
220 ---
221 title: "P2"
222 menu: [main,other]
223 ---
224
225 `)
226
227 b.WithContent("blog/page3.md", `
228 ---
229 title: "P3"
230 menu:
231 main:
232 weight: 30
233 ---
234 `)
235
236 b.Build(BuildCfg{})
237
238 b.AssertFileContent("public/index.html",
239 "Main: 3", "Other: 1",
240 "Main|P1: /blog/page1/",
241 "Other|P2: /blog/page2/",
242 )
243 }
244
245 // https://github.com/gohugoio/hugo/issues/5849
246 func TestMenusPageMultipleOutputFormats(t *testing.T) {
247 config := `
248 baseURL = "https://example.com"
249
250 # DAMP is similar to AMP, but not permalinkable.
251 [outputFormats]
252 [outputFormats.damp]
253 mediaType = "text/html"
254 path = "damp"
255
256 `
257
258 b := newTestSitesBuilder(t).WithConfigFile("toml", config)
259 b.WithContent("_index.md", `
260 ---
261 Title: Home Sweet Home
262 outputs: [ "html", "amp" ]
263 menu: "main"
264 ---
265
266 `)
267
268 b.WithContent("blog/html-amp.md", `
269 ---
270 Title: AMP and HTML
271 outputs: [ "html", "amp" ]
272 menu: "main"
273 ---
274
275 `)
276
277 b.WithContent("blog/html.md", `
278 ---
279 Title: HTML only
280 outputs: [ "html" ]
281 menu: "main"
282 ---
283
284 `)
285
286 b.WithContent("blog/amp.md", `
287 ---
288 Title: AMP only
289 outputs: [ "amp" ]
290 menu: "main"
291 ---
292
293 `)
294
295 b.WithTemplatesAdded("index.html", `{{ range .Site.Menus.main }}{{ .Title }}|{{ .URL }}|{{ end }}`)
296
297 b.Build(BuildCfg{})
298
299 b.AssertFileContent("public/index.html", "AMP and HTML|/blog/html-amp/|AMP only|/amp/blog/amp/|Home Sweet Home|/|HTML only|/blog/html/|")
300 b.AssertFileContent("public/amp/index.html", "AMP and HTML|/amp/blog/html-amp/|AMP only|/amp/blog/amp/|Home Sweet Home|/amp/|HTML only|/blog/html/|")
301 }
302
303 // https://github.com/gohugoio/hugo/issues/5989
304 func TestMenusPageSortByDate(t *testing.T) {
305 b := newTestSitesBuilder(t).WithSimpleConfigFile()
306
307 b.WithContent("blog/a.md", `
308 ---
309 Title: A
310 date: 2019-01-01
311 menu:
312 main:
313 identifier: "a"
314 weight: 1
315 ---
316
317 `)
318
319 b.WithContent("blog/b.md", `
320 ---
321 Title: B
322 date: 2018-01-02
323 menu:
324 main:
325 parent: "a"
326 weight: 100
327 ---
328
329 `)
330
331 b.WithContent("blog/c.md", `
332 ---
333 Title: C
334 date: 2019-01-03
335 menu:
336 main:
337 parent: "a"
338 weight: 10
339 ---
340
341 `)
342
343 b.WithTemplatesAdded("index.html", `{{ range .Site.Menus.main }}{{ .Title }}|Children:
344 {{- $children := sort .Children ".Page.Date" "desc" }}{{ range $children }}{{ .Title }}|{{ end }}{{ end }}
345
346 `)
347
348 b.Build(BuildCfg{})
349
350 b.AssertFileContent("public/index.html", "A|Children:C|B|")
351 }
352
353 // Issue #8825
354 func TestMenuParamsEmptyYaml(t *testing.T) {
355 b := newTestSitesBuilder(t).WithConfigFile("yaml", `
356
357 `)
358
359 b.WithTemplates("index.html", `{{ site.Menus }}`)
360
361 b.WithContent("p1.md", `---
362 menus:
363 main:
364 identity: journal
365 weight: 2
366 params:
367 ---
368 `)
369 b.Build(BuildCfg{})
370 }
371
372 func TestMenuParams(t *testing.T) {
373 b := newTestSitesBuilder(t).WithConfigFile("toml", `
374 [[menus.main]]
375 identifier = "contact"
376 title = "Contact Us"
377 url = "mailto:noreply@example.com"
378 weight = 300
379 [menus.main.params]
380 foo = "foo_config"
381 key2 = "key2_config"
382 camelCase = "camelCase_config"
383 `)
384
385 b.WithTemplatesAdded("index.html", `
386 Main: {{ len .Site.Menus.main }}
387 {{ range .Site.Menus.main }}
388 foo: {{ .Params.foo }}
389 key2: {{ .Params.KEy2 }}
390 camelCase: {{ .Params.camelcase }}
391 {{ end }}
392 `)
393
394 b.WithContent("_index.md", `
395 ---
396 title: "Home"
397 menu:
398 main:
399 weight: 10
400 params:
401 foo: "foo_content"
402 key2: "key2_content"
403 camelCase: "camelCase_content"
404 ---
405 `)
406
407 b.Build(BuildCfg{})
408
409 b.AssertFileContent("public/index.html", `
410 Main: 2
411
412 foo: foo_content
413 key2: key2_content
414 camelCase: camelCase_content
415
416 foo: foo_config
417 key2: key2_config
418 camelCase: camelCase_config
419 `)
420 }
421
422 func TestMenusShadowMembers(t *testing.T) {
423 b := newTestSitesBuilder(t).WithConfigFile("toml", `
424 [[menus.main]]
425 identifier = "contact"
426 pageRef = "contact"
427 title = "Contact Us"
428 url = "mailto:noreply@example.com"
429 weight = 1
430 [[menus.main]]
431 pageRef = "/blog/post3"
432 title = "My Post 3"
433 url = "/blog/post3"
434
435 `)
436
437 commonTempl := `
438 Main: {{ len .Site.Menus.main }}
439 {{ range .Site.Menus.main }}
440 {{ .Title }}|HasMenuCurrent: {{ $.HasMenuCurrent "main" . }}|Page: {{ .Page }}
441 {{ .Title }}|IsMenuCurrent: {{ $.IsMenuCurrent "main" . }}|Page: {{ .Page }}
442 {{ end }}
443 `
444
445 b.WithTemplatesAdded("index.html", commonTempl)
446 b.WithTemplatesAdded("_default/single.html", commonTempl)
447
448 b.WithContent("_index.md", `
449 ---
450 title: "Home"
451 menu:
452 main:
453 weight: 10
454 ---
455 `)
456
457 b.WithContent("blog/_index.md", `
458 ---
459 title: "Blog"
460 menu:
461 main:
462 weight: 20
463 ---
464 `)
465
466 b.WithContent("blog/post1.md", `
467 ---
468 title: "My Post 1: With No Menu Defined"
469 ---
470 `)
471
472 b.WithContent("blog/post2.md", `
473 ---
474 title: "My Post 2: With Menu Defined"
475 menu:
476 main:
477 weight: 30
478 ---
479 `)
480
481 b.WithContent("blog/post3.md", `
482 ---
483 title: "My Post 2: With No Menu Defined"
484 ---
485 `)
486
487 b.WithContent("contact.md", `
488 ---
489 title: "Contact: With No Menu Defined"
490 ---
491 `)
492
493 b.Build(BuildCfg{})
494
495 b.AssertFileContent("public/index.html", `
496 Main: 5
497 Home|HasMenuCurrent: false|Page: Page(/_index.md)
498 Blog|HasMenuCurrent: false|Page: Page(/blog/_index.md)
499 My Post 2: With Menu Defined|HasMenuCurrent: false|Page: Page(/blog/post2.md)
500 My Post 3|HasMenuCurrent: false|Page: Page(/blog/post3.md)
501 Contact Us|HasMenuCurrent: false|Page: Page(/contact.md)
502 `)
503
504 b.AssertFileContent("public/blog/post1/index.html", `
505 Home|HasMenuCurrent: false|Page: Page(/_index.md)
506 Blog|HasMenuCurrent: true|Page: Page(/blog/_index.md)
507 `)
508
509 b.AssertFileContent("public/blog/post2/index.html", `
510 Home|HasMenuCurrent: false|Page: Page(/_index.md)
511 Blog|HasMenuCurrent: true|Page: Page(/blog/_index.md)
512 Blog|IsMenuCurrent: false|Page: Page(/blog/_index.md)
513 `)
514
515 b.AssertFileContent("public/blog/post3/index.html", `
516 Home|HasMenuCurrent: false|Page: Page(/_index.md)
517 Blog|HasMenuCurrent: true|Page: Page(/blog/_index.md)
518 `)
519
520 b.AssertFileContent("public/contact/index.html", `
521 Contact Us|HasMenuCurrent: false|Page: Page(/contact.md)
522 Contact Us|IsMenuCurrent: true|Page: Page(/contact.md)
523 Blog|HasMenuCurrent: false|Page: Page(/blog/_index.md)
524 Blog|IsMenuCurrent: false|Page: Page(/blog/_index.md)
525 `)
526 }
527
528 // Issue 9846
529 func TestMenuHasMenuCurrentSection(t *testing.T) {
530 t.Parallel()
531
532 files := `
533 -- config.toml --
534 disableKinds = ['RSS','sitemap','taxonomy','term']
535 [[menu.main]]
536 name = 'Home'
537 pageRef = '/'
538 weight = 1
539
540 [[menu.main]]
541 name = 'Tests'
542 pageRef = '/tests'
543 weight = 2
544 [[menu.main]]
545 name = 'Test 1'
546 pageRef = '/tests/test-1'
547 parent = 'Tests'
548 weight = 1
549
550 -- content/tests/test-1.md --
551 ---
552 title: "Test 1"
553 ---
554 -- layouts/_default/list.html --
555 {{ range site.Menus.main }}
556 {{ .Name }}|{{ .URL }}|IsMenuCurrent = {{ $.IsMenuCurrent "main" . }}|HasMenuCurrent = {{ $.HasMenuCurrent "main" . }}|
557 {{ range .Children }}
558 {{ .Name }}|{{ .URL }}|IsMenuCurrent = {{ $.IsMenuCurrent "main" . }}|HasMenuCurrent = {{ $.HasMenuCurrent "main" . }}|
559 {{ end }}
560 {{ end }}
561
562 {{/* Some tests for issue 9925 */}}
563 {{ $page := .Site.GetPage "tests/test-1" }}
564 {{ $section := site.GetPage "tests" }}
565
566 Home IsAncestor Self: {{ site.Home.IsAncestor site.Home }}
567 Home IsDescendant Self: {{ site.Home.IsDescendant site.Home }}
568 Section IsAncestor Self: {{ $section.IsAncestor $section }}
569 Section IsDescendant Self: {{ $section.IsDescendant $section}}
570 Page IsAncestor Self: {{ $page.IsAncestor $page }}
571 Page IsDescendant Self: {{ $page.IsDescendant $page}}
572 `
573
574 b := NewIntegrationTestBuilder(
575 IntegrationTestConfig{
576 T: t,
577 TxtarString: files,
578 },
579 ).Build()
580
581 b.AssertFileContent("public/tests/index.html", `
582 Tests|/tests/|IsMenuCurrent = true|HasMenuCurrent = false
583 Home IsAncestor Self: false
584 Home IsDescendant Self: false
585 Section IsAncestor Self: false
586 Section IsDescendant Self: false
587 Page IsAncestor Self: false
588 Page IsDescendant Self: false
589 `)
590 }