github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/hugolib/menu_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 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  }