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 }