ref: a3af4fe46e1e1d184538a83bc8375154a9669316
parent: 2a6b26a7a520bf88af1413f3f00c7e2782abe8cd
author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
date: Mon Feb 20 04:33:35 EST 2017
hugolib: Finish menu vs section content pages This commit also fixes the default menu sort when the weight is 0. Closes #2974
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -167,6 +167,7 @@
}
for _, s := range h.Sites {+ s.assembleMenus()
s.refreshPageCaches()
s.setupSitePages()
}
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -1235,11 +1235,11 @@
return sites
}
-func writeSource(t *testing.T, fs *hugofs.Fs, filename, content string) {+func writeSource(t testing.TB, fs *hugofs.Fs, filename, content string) {writeToFs(t, fs.Source, filename, content)
}
-func writeToFs(t *testing.T, fs afero.Fs, filename, content string) {+func writeToFs(t testing.TB, fs afero.Fs, filename, content string) { if err := afero.WriteFile(fs, filepath.FromSlash(filename), []byte(content), 0755); err != nil { t.Fatalf("Failed to write file: %s", err)}
--- a/hugolib/menu.go
+++ b/hugolib/menu.go
@@ -157,6 +157,15 @@
}
return m1.Name < m2.Name
}
+
+ if m2.Weight == 0 {+ return true
+ }
+
+ if m1.Weight == 0 {+ return false
+ }
+
return m1.Weight < m2.Weight
}
--- /dev/null
+++ b/hugolib/menu_old_test.go
@@ -1,0 +1,693 @@
+// Copyright 2016 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hugolib
+
+// TODO(bep) remove this file when the reworked tests in menu_test.go is done.
+// NOTE: Do not add more tests to this file!
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/spf13/hugo/deps"
+
+ "path/filepath"
+
+ toml "github.com/pelletier/go-toml"
+ "github.com/spf13/hugo/source"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+const (
+ confMenu1 = `
+[[menu.main]]
+ name = "Go Home"
+ url = "/"
+ weight = 1
+ pre = "<div>"
+ post = "</div>"
+[[menu.main]]
+ name = "Blog"
+ url = "/posts"
+[[menu.main]]
+ name = "ext"
+ url = "http://gohugo.io"
+ identifier = "ext"
+[[menu.main]]
+ name = "ext2"
+ url = "http://foo.local/Zoo/foo"
+ identifier = "ext2"
+[[menu.grandparent]]
+ name = "grandparent"
+ url = "/grandparent"
+ identifier = "grandparentId"
+[[menu.grandparent]]
+ name = "parent"
+ url = "/parent"
+ identifier = "parentId"
+ parent = "grandparentId"
+[[menu.grandparent]]
+ name = "Go Home3"
+ url = "/"
+ identifier = "grandchildId"
+ parent = "parentId"
+[[menu.tax]]
+ name = "Tax1"
+ url = "/two/key/"
+ identifier="1"
+[[menu.tax]]
+ name = "Tax2"
+ url = "/two/key/"
+ identifier="2"
+[[menu.tax]]
+ name = "Tax RSS"
+ url = "/two/key.xml"
+ identifier="xml"
+[[menu.hash]]
+ name = "Tax With #"
+ url = "/resource#anchor"
+ identifier="hash"
+[[menu.unicode]]
+ name = "Unicode Russian"
+ identifier = "unicode-russian"
+ url = "/новости-проекта"` // Russian => "news-project"
+)
+
+var menuPage1 = []byte(`+++
+title = "One"
+weight = 1
+[menu]
+ [menu.p_one]
++++
+Front Matter with Menu Pages`)
+
+var menuPage2 = []byte(`+++
+title = "Two"
+weight = 2
+[menu]
+ [menu.p_one]
+ [menu.p_two]
+ identifier = "Two"
+
++++
+Front Matter with Menu Pages`)
+
+var menuPage3 = []byte(`+++
+title = "Three"
+weight = 3
+[menu]
+ [menu.p_two]
+ Name = "Three"
+ Parent = "Two"
++++
+Front Matter with Menu Pages`)
+
+var menuPage4 = []byte(`+++
+title = "Four"
+weight = 4
+[menu]
+ [menu.p_two]
+ Name = "Four"
+ Parent = "Three"
++++
+Front Matter with Menu Pages`)
+
+var menuPageSources = []source.ByteSource{+ {Name: filepath.FromSlash("sect/doc1.md"), Content: menuPage1},+ {Name: filepath.FromSlash("sect/doc2.md"), Content: menuPage2},+ {Name: filepath.FromSlash("sect/doc3.md"), Content: menuPage3},+}
+
+var menuPageSectionsSources = []source.ByteSource{+ {Name: filepath.FromSlash("first/doc1.md"), Content: menuPage1},+ {Name: filepath.FromSlash("first/doc2.md"), Content: menuPage2},+ {Name: filepath.FromSlash("second-section/doc3.md"), Content: menuPage3},+ {Name: filepath.FromSlash("Fish and Chips/doc4.md"), Content: menuPage4},+}
+
+func tstCreateMenuPageWithNameTOML(title, menu, name string) []byte {+ return []byte(fmt.Sprintf(`+++
+title = "%s"
+weight = 1
+[menu]
+ [menu.%s]
+ name = "%s"
++++
+Front Matter with Menu with Name`, title, menu, name))
+}
+
+func tstCreateMenuPageWithIdentifierTOML(title, menu, identifier string) []byte {+ return []byte(fmt.Sprintf(`+++
+title = "%s"
+weight = 1
+[menu]
+ [menu.%s]
+ identifier = "%s"
+ name = "somename"
++++
+Front Matter with Menu with Identifier`, title, menu, identifier))
+}
+
+func tstCreateMenuPageWithNameYAML(title, menu, name string) []byte {+ return []byte(fmt.Sprintf(`---
+title: "%s"
+weight: 1
+menu:
+ %s:
+ name: "%s"
+---
+Front Matter with Menu with Name`, title, menu, name))
+}
+
+func tstCreateMenuPageWithIdentifierYAML(title, menu, identifier string) []byte {+ return []byte(fmt.Sprintf(`---
+title: "%s"
+weight: 1
+menu:
+ %s:
+ identifier: "%s"
+ name: "somename"
+---
+Front Matter with Menu with Identifier`, title, menu, identifier))
+}
+
+// Issue 817 - identifier should trump everything
+func TestPageMenuWithIdentifier(t *testing.T) {+ t.Parallel()
+ toml := []source.ByteSource{+ {Name: "sect/doc1.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i1")},+ {Name: "sect/doc2.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i2")},+ {Name: "sect/doc3.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i2")}, // duplicate+ }
+
+ yaml := []source.ByteSource{+ {Name: "sect/doc1.md", Content: tstCreateMenuPageWithIdentifierYAML("t1", "m1", "i1")},+ {Name: "sect/doc2.md", Content: tstCreateMenuPageWithIdentifierYAML("t1", "m1", "i2")},+ {Name: "sect/doc3.md", Content: tstCreateMenuPageWithIdentifierYAML("t1", "m1", "i2")}, // duplicate+ }
+
+ doTestPageMenuWithIdentifier(t, toml)
+ doTestPageMenuWithIdentifier(t, yaml)
+
+}
+
+func doTestPageMenuWithIdentifier(t *testing.T, menuPageSources []source.ByteSource) {+
+ s := setupMenuTests(t, menuPageSources)
+
+ assert.Equal(t, 3, len(s.RegularPages), "Not enough pages")
+
+ me1 := findTestMenuEntryByID(s, "m1", "i1")
+ me2 := findTestMenuEntryByID(s, "m1", "i2")
+
+ require.NotNil(t, me1)
+ require.NotNil(t, me2)
+
+ assert.True(t, strings.Contains(me1.URL, "doc1"), me1.URL)
+ assert.True(t, strings.Contains(me2.URL, "doc2") || strings.Contains(me2.URL, "doc3"), me2.URL)
+
+}
+
+// Issue 817 contd - name should be second identifier in
+func TestPageMenuWithDuplicateName(t *testing.T) {+ t.Parallel()
+ toml := []source.ByteSource{+ {Name: "sect/doc1.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n1")},+ {Name: "sect/doc2.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n2")},+ {Name: "sect/doc3.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n2")}, // duplicate+ }
+
+ yaml := []source.ByteSource{+ {Name: "sect/doc1.md", Content: tstCreateMenuPageWithNameYAML("t1", "m1", "n1")},+ {Name: "sect/doc2.md", Content: tstCreateMenuPageWithNameYAML("t1", "m1", "n2")},+ {Name: "sect/doc3.md", Content: tstCreateMenuPageWithNameYAML("t1", "m1", "n2")}, // duplicate+ }
+
+ doTestPageMenuWithDuplicateName(t, toml)
+ doTestPageMenuWithDuplicateName(t, yaml)
+
+}
+
+func doTestPageMenuWithDuplicateName(t *testing.T, menuPageSources []source.ByteSource) {+
+ s := setupMenuTests(t, menuPageSources)
+
+ assert.Equal(t, 3, len(s.RegularPages), "Not enough pages")
+
+ me1 := findTestMenuEntryByName(s, "m1", "n1")
+ me2 := findTestMenuEntryByName(s, "m1", "n2")
+
+ require.NotNil(t, me1)
+ require.NotNil(t, me2)
+
+ assert.True(t, strings.Contains(me1.URL, "doc1"), me1.URL)
+ assert.True(t, strings.Contains(me2.URL, "doc2") || strings.Contains(me2.URL, "doc3"), me2.URL)
+
+}
+
+func TestPageMenu(t *testing.T) {+ t.Parallel()
+ s := setupMenuTests(t, menuPageSources)
+
+ if len(s.RegularPages) != 3 {+ t.Fatalf("Posts not created, expected 3 got %d", len(s.RegularPages))+ }
+
+ first := s.RegularPages[0]
+ second := s.RegularPages[1]
+ third := s.RegularPages[2]
+
+ pOne := findTestMenuEntryByName(s, "p_one", "One")
+ pTwo := findTestMenuEntryByID(s, "p_two", "Two")
+
+ for i, this := range []struct {+ menu string
+ page *Page
+ menuItem *MenuEntry
+ isMenuCurrent bool
+ hasMenuCurrent bool
+ }{+ {"p_one", first, pOne, true, false},+ {"p_one", first, pTwo, false, false},+ {"p_one", second, pTwo, false, false},+ {"p_two", second, pTwo, true, false},+ {"p_two", third, pTwo, false, true},+ {"p_one", third, pTwo, false, false},+ } {+
+ if i != 4 {+ continue
+ }
+
+ isMenuCurrent := this.page.IsMenuCurrent(this.menu, this.menuItem)
+ hasMenuCurrent := this.page.HasMenuCurrent(this.menu, this.menuItem)
+
+ if isMenuCurrent != this.isMenuCurrent {+ t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)+ }
+
+ if hasMenuCurrent != this.hasMenuCurrent {+ t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)+ }
+
+ }
+
+}
+
+func TestMenuURL(t *testing.T) {+ t.Parallel()
+ s := setupMenuTests(t, menuPageSources)
+
+ for i, this := range []struct {+ me *MenuEntry
+ expectedURL string
+ }{+ // issue #888
+ {findTestMenuEntryByID(s, "hash", "hash"), "/Zoo/resource#anchor"},+ // issue #1774
+ {findTestMenuEntryByID(s, "main", "ext"), "http://gohugo.io"},+ {findTestMenuEntryByID(s, "main", "ext2"), "http://foo.local/Zoo/foo"},+ } {+
+ if this.me == nil {+ t.Errorf("[%d] MenuEntry not found", i)+ continue
+ }
+
+ if this.me.URL != this.expectedURL {+ t.Errorf("[%d] Got URL %s expected %s", i, this.me.URL, this.expectedURL)+ }
+
+ }
+
+}
+
+// Issue #1934
+func TestYAMLMenuWithMultipleEntries(t *testing.T) {+ t.Parallel()
+ ps1 := []byte(`---
+title: "Yaml 1"
+weight: 5
+menu: ["p_one", "p_two"]
+---
+Yaml Front Matter with Menu Pages`)
+
+ ps2 := []byte(`---
+title: "Yaml 2"
+weight: 5
+menu:
+ p_three:
+ p_four:
+---
+Yaml Front Matter with Menu Pages`)
+
+ s := setupMenuTests(t, []source.ByteSource{+ {Name: filepath.FromSlash("sect/yaml1.md"), Content: ps1},+ {Name: filepath.FromSlash("sect/yaml2.md"), Content: ps2}})+
+ p1 := s.RegularPages[0]
+ assert.Len(t, p1.Menus(), 2, "List YAML")
+ p2 := s.RegularPages[1]
+ assert.Len(t, p2.Menus(), 2, "Map YAML")
+
+}
+
+// issue #719
+func TestMenuWithUnicodeURLs(t *testing.T) {+ t.Parallel()
+ for _, canonifyURLs := range []bool{true, false} {+ doTestMenuWithUnicodeURLs(t, canonifyURLs)
+ }
+}
+
+func doTestMenuWithUnicodeURLs(t *testing.T, canonifyURLs bool) {+
+ s := setupMenuTests(t, menuPageSources, "canonifyURLs", canonifyURLs)
+
+ unicodeRussian := findTestMenuEntryByID(s, "unicode", "unicode-russian")
+
+ expected := "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0"
+
+ if !canonifyURLs {+ expected = "/Zoo" + expected
+ }
+
+ assert.Equal(t, expected, unicodeRussian.URL)
+}
+
+// Issue #1114
+func TestSectionPagesMenu2(t *testing.T) {+ t.Parallel()
+ doTestSectionPagesMenu(true, t)
+ doTestSectionPagesMenu(false, t)
+}
+
+func doTestSectionPagesMenu(canonifyURLs bool, t *testing.T) {+
+ s := setupMenuTests(t, menuPageSectionsSources,
+ "sectionPagesMenu", "spm",
+ "canonifyURLs", canonifyURLs,
+ )
+
+ require.Equal(t, 3, len(s.Sections))
+
+ firstSectionPages := s.Sections["first"]
+ require.Equal(t, 2, len(firstSectionPages))
+ secondSectionPages := s.Sections["second-section"]
+ require.Equal(t, 1, len(secondSectionPages))
+ fishySectionPages := s.Sections["fish-and-chips"]
+ require.Equal(t, 1, len(fishySectionPages))
+
+ nodeFirst := s.getPage(KindSection, "first")
+ require.NotNil(t, nodeFirst)
+ nodeSecond := s.getPage(KindSection, "second-section")
+ require.NotNil(t, nodeSecond)
+ nodeFishy := s.getPage(KindSection, "fish-and-chips")
+ require.Equal(t, "fish-and-chips", nodeFishy.sections[0])
+
+ firstSectionMenuEntry := findTestMenuEntryByID(s, "spm", "first")
+ secondSectionMenuEntry := findTestMenuEntryByID(s, "spm", "second-section")
+ fishySectionMenuEntry := findTestMenuEntryByID(s, "spm", "fish-and-chips")
+
+ require.NotNil(t, firstSectionMenuEntry)
+ require.NotNil(t, secondSectionMenuEntry)
+ require.NotNil(t, nodeFirst)
+ require.NotNil(t, nodeSecond)
+ require.NotNil(t, fishySectionMenuEntry)
+ require.NotNil(t, nodeFishy)
+
+ require.True(t, nodeFirst.IsMenuCurrent("spm", firstSectionMenuEntry))+ require.False(t, nodeFirst.IsMenuCurrent("spm", secondSectionMenuEntry))+ require.False(t, nodeFirst.IsMenuCurrent("spm", fishySectionMenuEntry))+ require.True(t, nodeFishy.IsMenuCurrent("spm", fishySectionMenuEntry))+ require.Equal(t, "Fish and Chips", fishySectionMenuEntry.Name)
+
+ for _, p := range firstSectionPages {+ require.True(t, p.Page.HasMenuCurrent("spm", firstSectionMenuEntry))+ require.False(t, p.Page.HasMenuCurrent("spm", secondSectionMenuEntry))+ }
+
+ for _, p := range secondSectionPages {+ require.False(t, p.Page.HasMenuCurrent("spm", firstSectionMenuEntry))+ require.True(t, p.Page.HasMenuCurrent("spm", secondSectionMenuEntry))+ }
+
+ for _, p := range fishySectionPages {+ require.False(t, p.Page.HasMenuCurrent("spm", firstSectionMenuEntry))+ require.False(t, p.Page.HasMenuCurrent("spm", secondSectionMenuEntry))+ require.True(t, p.Page.HasMenuCurrent("spm", fishySectionMenuEntry))+ }
+}
+
+func TestTaxonomyNodeMenu(t *testing.T) {+ t.Parallel()
+
+ type taxRenderInfo struct {+ key string
+ singular string
+ plural string
+ }
+
+ s := setupMenuTests(t, menuPageSources, "canonifyURLs", true)
+
+ for i, this := range []struct {+ menu string
+ taxInfo taxRenderInfo
+ menuItem *MenuEntry
+ isMenuCurrent bool
+ hasMenuCurrent bool
+ }{+ {"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},+ findTestMenuEntryByID(s, "tax", "1"), true, false},
+ {"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},+ findTestMenuEntryByID(s, "tax", "2"), true, false},
+ {"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},+ &MenuEntry{Name: "Somewhere else", URL: "/somewhereelse"}, false, false},+ } {+
+ p := s.newTaxonomyPage(this.taxInfo.plural, this.taxInfo.key)
+
+ isMenuCurrent := p.IsMenuCurrent(this.menu, this.menuItem)
+ hasMenuCurrent := p.HasMenuCurrent(this.menu, this.menuItem)
+
+ if isMenuCurrent != this.isMenuCurrent {+ t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)+ }
+
+ if hasMenuCurrent != this.hasMenuCurrent {+ t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)+ }
+
+ }
+
+ menuEntryXML := findTestMenuEntryByID(s, "tax", "xml")
+
+ if strings.HasSuffix(menuEntryXML.URL, "/") {+ t.Error("RSS menu item should not be padded with trailing slash")+ }
+}
+
+func TestMenuLimit(t *testing.T) {+ t.Parallel()
+ s := setupMenuTests(t, menuPageSources)
+ m := *s.Menus["main"]
+
+ // main menu has 4 entries
+ firstTwo := m.Limit(2)
+ assert.Equal(t, 2, len(firstTwo))
+ for i := 0; i < 2; i++ {+ assert.Equal(t, m[i], firstTwo[i])
+ }
+ assert.Equal(t, m, m.Limit(4))
+ assert.Equal(t, m, m.Limit(5))
+}
+
+func TestMenuSortByN(t *testing.T) {+ t.Parallel()
+ for i, this := range []struct {+ sortFunc func(p Menu) Menu
+ assertFunc func(p Menu) bool
+ }{+ {(Menu).Sort, func(p Menu) bool { return p[0].Weight == 1 && p[1].Name == "nx" && p[2].Identifier == "ib" }},+ {(Menu).ByWeight, func(p Menu) bool { return p[0].Weight == 1 && p[1].Name == "nx" && p[2].Identifier == "ib" }},+ {(Menu).ByName, func(p Menu) bool { return p[0].Name == "na" }},+ {(Menu).Reverse, func(p Menu) bool { return p[0].Identifier == "ib" && p[len(p)-1].Identifier == "ia" }},+ } {+ menu := Menu{&MenuEntry{Weight: 3, Name: "nb", Identifier: "ia"},+ &MenuEntry{Weight: 1, Name: "na", Identifier: "ic"},+ &MenuEntry{Weight: 1, Name: "nx", Identifier: "ic"},+ &MenuEntry{Weight: 2, Name: "nb", Identifier: "ix"},+ &MenuEntry{Weight: 2, Name: "nb", Identifier: "ib"}}+
+ sorted := this.sortFunc(menu)
+
+ if !this.assertFunc(sorted) {+ t.Errorf("[%d] sort error", i)+ }
+ }
+
+}
+
+func TestHomeNodeMenu(t *testing.T) {+ t.Parallel()
+ s := setupMenuTests(t, menuPageSources,
+ "canonifyURLs", true,
+ "uglyURLs", false,
+ )
+
+ home := s.getPage(KindHome)
+ homeMenuEntry := &MenuEntry{Name: home.Title, URL: home.URL()}+
+ for i, this := range []struct {+ menu string
+ menuItem *MenuEntry
+ isMenuCurrent bool
+ hasMenuCurrent bool
+ }{+ {"main", homeMenuEntry, true, false},+ {"doesnotexist", homeMenuEntry, false, false},+ {"main", &MenuEntry{Name: "Somewhere else", URL: "/somewhereelse"}, false, false},+ {"grandparent", findTestMenuEntryByID(s, "grandparent", "grandparentId"), false, true},+ {"grandparent", findTestMenuEntryByID(s, "grandparent", "parentId"), false, true},+ {"grandparent", findTestMenuEntryByID(s, "grandparent", "grandchildId"), true, false},+ } {+
+ isMenuCurrent := home.IsMenuCurrent(this.menu, this.menuItem)
+ hasMenuCurrent := home.HasMenuCurrent(this.menu, this.menuItem)
+
+ if isMenuCurrent != this.isMenuCurrent {+ fmt.Println("isMenuCurrent", isMenuCurrent)+ fmt.Printf("this: %#v\n", this)+ t.Errorf("[%d] Wrong result from IsMenuCurrent: %v for %q", i, isMenuCurrent, this.menuItem)+ }
+
+ if hasMenuCurrent != this.hasMenuCurrent {+ fmt.Println("hasMenuCurrent", hasMenuCurrent)+ fmt.Printf("this: %#v\n", this)+ t.Errorf("[%d] Wrong result for menu %q menuItem %v for HasMenuCurrent: %v", i, this.menu, this.menuItem, hasMenuCurrent)+ }
+ }
+}
+
+func TestHopefullyUniqueID(t *testing.T) {+ t.Parallel()
+ assert.Equal(t, "i", (&MenuEntry{Identifier: "i", URL: "u", Name: "n"}).hopefullyUniqueID())+ assert.Equal(t, "u", (&MenuEntry{Identifier: "", URL: "u", Name: "n"}).hopefullyUniqueID())+ assert.Equal(t, "n", (&MenuEntry{Identifier: "", URL: "", Name: "n"}).hopefullyUniqueID())+}
+
+func TestAddMenuEntryChild(t *testing.T) {+ t.Parallel()
+ root := &MenuEntry{Weight: 1}+ root.addChild(&MenuEntry{Weight: 2})+ root.addChild(&MenuEntry{Weight: 1})+ assert.Equal(t, 2, len(root.Children))
+ assert.Equal(t, 1, root.Children[0].Weight)
+}
+
+var testMenuIdentityMatcher = func(me *MenuEntry, id string) bool { return me.Identifier == id }+var testMenuNameMatcher = func(me *MenuEntry, id string) bool { return me.Name == id }+
+func findTestMenuEntryByID(s *Site, mn string, id string) *MenuEntry {+ return findTestMenuEntry(s, mn, id, testMenuIdentityMatcher)
+}
+func findTestMenuEntryByName(s *Site, mn string, id string) *MenuEntry {+ return findTestMenuEntry(s, mn, id, testMenuNameMatcher)
+}
+
+func findTestMenuEntry(s *Site, mn string, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {+ var found *MenuEntry
+ if menu, ok := s.Menus[mn]; ok {+ for _, me := range *menu {+
+ if matcher(me, id) {+ if found != nil {+ panic(fmt.Sprintf("Duplicate menu entry in menu %s with id/name %s", mn, id))+ }
+ found = me
+ }
+
+ descendant := findDescendantTestMenuEntry(me, id, matcher)
+ if descendant != nil {+ if found != nil {+ panic(fmt.Sprintf("Duplicate menu entry in menu %s with id/name %s", mn, id))+ }
+ found = descendant
+ }
+ }
+ }
+ return found
+}
+
+func findDescendantTestMenuEntry(parent *MenuEntry, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {+ var found *MenuEntry
+ if parent.HasChildren() {+ for _, child := range parent.Children {+
+ if matcher(child, id) {+ if found != nil {+ panic(fmt.Sprintf("Duplicate menu entry in menuitem %s with id/name %s", parent.KeyName(), id))+ }
+ found = child
+ }
+
+ descendant := findDescendantTestMenuEntry(child, id, matcher)
+ if descendant != nil {+ if found != nil {+ panic(fmt.Sprintf("Duplicate menu entry in menuitem %s with id/name %s", parent.KeyName(), id))+ }
+ found = descendant
+ }
+ }
+ }
+ return found
+}
+
+func setupMenuTests(t *testing.T, pageSources []source.ByteSource, configKeyValues ...interface{}) *Site {+
+ var (
+ cfg, fs = newTestCfg()
+ )
+
+ menus, err := tomlToMap(confMenu1)
+ require.NoError(t, err)
+
+ cfg.Set("menu", menus["menu"])+ cfg.Set("baseURL", "http://foo.local/Zoo/")+
+ for i := 0; i < len(configKeyValues); i += 2 {+ cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
+ }
+
+ for _, src := range pageSources {+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))+
+ }
+
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})+
+}
+
+func tomlToMap(s string) (map[string]interface{}, error) {+ tree, err := toml.Load(s)
+
+ if err != nil {+ return nil, err
+ }
+
+ return tree.ToMap(), nil
+
+}
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Hugo Authors. All rights reserved.
+// Copyright 2017 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,677 +14,81 @@
package hugolib
import (
- "fmt"
- "strings"
"testing"
- "github.com/spf13/hugo/deps"
+ "fmt"
- "path/filepath"
-
- toml "github.com/pelletier/go-toml"
- "github.com/spf13/hugo/source"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
- confMenu1 = `
-[[menu.main]]
- name = "Go Home"
- url = "/"
- weight = 1
- pre = "<div>"
- post = "</div>"
-[[menu.main]]
- name = "Blog"
- url = "/posts"
-[[menu.main]]
- name = "ext"
- url = "http://gohugo.io"
- identifier = "ext"
-[[menu.main]]
- name = "ext2"
- url = "http://foo.local/Zoo/foo"
- identifier = "ext2"
-[[menu.grandparent]]
- name = "grandparent"
- url = "/grandparent"
- identifier = "grandparentId"
-[[menu.grandparent]]
- name = "parent"
- url = "/parent"
- identifier = "parentId"
- parent = "grandparentId"
-[[menu.grandparent]]
- name = "Go Home3"
- url = "/"
- identifier = "grandchildId"
- parent = "parentId"
-[[menu.tax]]
- name = "Tax1"
- url = "/two/key/"
- identifier="1"
-[[menu.tax]]
- name = "Tax2"
- url = "/two/key/"
- identifier="2"
-[[menu.tax]]
- name = "Tax RSS"
- url = "/two/key.xml"
- identifier="xml"
-[[menu.hash]]
- name = "Tax With #"
- url = "/resource#anchor"
- identifier="hash"
-[[menu.unicode]]
- name = "Unicode Russian"
- identifier = "unicode-russian"
- url = "/новости-проекта"` // Russian => "news-project"
-)
-
-var menuPage1 = []byte(`+++
-title = "One"
-weight = 1
-[menu]
- [menu.p_one]
-+++
-Front Matter with Menu Pages`)
-
-var menuPage2 = []byte(`+++
-title = "Two"
-weight = 2
-[menu]
- [menu.p_one]
- [menu.p_two]
- identifier = "Two"
-
-+++
-Front Matter with Menu Pages`)
-
-var menuPage3 = []byte(`+++
-title = "Three"
-weight = 3
-[menu]
- [menu.p_two]
- Name = "Three"
- Parent = "Two"
-+++
-Front Matter with Menu Pages`)
-
-var menuPage4 = []byte(`+++
-title = "Four"
-weight = 4
-[menu]
- [menu.p_two]
- Name = "Four"
- Parent = "Three"
-+++
-Front Matter with Menu Pages`)
-
-var menuPageSources = []source.ByteSource{- {Name: filepath.FromSlash("sect/doc1.md"), Content: menuPage1},- {Name: filepath.FromSlash("sect/doc2.md"), Content: menuPage2},- {Name: filepath.FromSlash("sect/doc3.md"), Content: menuPage3},-}
-
-var menuPageSectionsSources = []source.ByteSource{- {Name: filepath.FromSlash("first/doc1.md"), Content: menuPage1},- {Name: filepath.FromSlash("first/doc2.md"), Content: menuPage2},- {Name: filepath.FromSlash("second-section/doc3.md"), Content: menuPage3},- {Name: filepath.FromSlash("Fish and Chips/doc4.md"), Content: menuPage4},-}
-
-func tstCreateMenuPageWithNameTOML(title, menu, name string) []byte {- return []byte(fmt.Sprintf(`+++
-title = "%s"
-weight = 1
-[menu]
- [menu.%s]
- name = "%s"
-+++
-Front Matter with Menu with Name`, title, menu, name))
-}
-
-func tstCreateMenuPageWithIdentifierTOML(title, menu, identifier string) []byte {- return []byte(fmt.Sprintf(`+++
-title = "%s"
-weight = 1
-[menu]
- [menu.%s]
- identifier = "%s"
- name = "somename"
-+++
-Front Matter with Menu with Identifier`, title, menu, identifier))
-}
-
-func tstCreateMenuPageWithNameYAML(title, menu, name string) []byte {- return []byte(fmt.Sprintf(`---
-title: "%s"
-weight: 1
+ menuPageTemplate = `---
+title: %q
+weight: %d
menu:
- %s:
- name: "%s"
+ %s:
+ weight: %d
---
-Front Matter with Menu with Name`, title, menu, name))
-}
+# Doc Menu
+`
+)
-func tstCreateMenuPageWithIdentifierYAML(title, menu, identifier string) []byte {- return []byte(fmt.Sprintf(`---
-title: "%s"
-weight: 1
-menu:
- %s:
- identifier: "%s"
- name: "somename"
----
-Front Matter with Menu with Identifier`, title, menu, identifier))
-}
-
-// Issue 817 - identifier should trump everything
-func TestPageMenuWithIdentifier(t *testing.T) {- t.Parallel()
- toml := []source.ByteSource{- {Name: "sect/doc1.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i1")},- {Name: "sect/doc2.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i2")},- {Name: "sect/doc3.md", Content: tstCreateMenuPageWithIdentifierTOML("t1", "m1", "i2")}, // duplicate- }
-
- yaml := []source.ByteSource{- {Name: "sect/doc1.md", Content: tstCreateMenuPageWithIdentifierYAML("t1", "m1", "i1")},- {Name: "sect/doc2.md", Content: tstCreateMenuPageWithIdentifierYAML("t1", "m1", "i2")},- {Name: "sect/doc3.md", Content: tstCreateMenuPageWithIdentifierYAML("t1", "m1", "i2")}, // duplicate- }
-
- doTestPageMenuWithIdentifier(t, toml)
- doTestPageMenuWithIdentifier(t, yaml)
-
-}
-
-func doTestPageMenuWithIdentifier(t *testing.T, menuPageSources []source.ByteSource) {-
- s := setupMenuTests(t, menuPageSources)
-
- assert.Equal(t, 3, len(s.RegularPages), "Not enough pages")
-
- me1 := findTestMenuEntryByID(s, "m1", "i1")
- me2 := findTestMenuEntryByID(s, "m1", "i2")
-
- require.NotNil(t, me1)
- require.NotNil(t, me2)
-
- assert.True(t, strings.Contains(me1.URL, "doc1"), me1.URL)
- assert.True(t, strings.Contains(me2.URL, "doc2") || strings.Contains(me2.URL, "doc3"), me2.URL)
-
-}
-
-// Issue 817 contd - name should be second identifier in
-func TestPageMenuWithDuplicateName(t *testing.T) {- t.Parallel()
- toml := []source.ByteSource{- {Name: "sect/doc1.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n1")},- {Name: "sect/doc2.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n2")},- {Name: "sect/doc3.md", Content: tstCreateMenuPageWithNameTOML("t1", "m1", "n2")}, // duplicate- }
-
- yaml := []source.ByteSource{- {Name: "sect/doc1.md", Content: tstCreateMenuPageWithNameYAML("t1", "m1", "n1")},- {Name: "sect/doc2.md", Content: tstCreateMenuPageWithNameYAML("t1", "m1", "n2")},- {Name: "sect/doc3.md", Content: tstCreateMenuPageWithNameYAML("t1", "m1", "n2")}, // duplicate- }
-
- doTestPageMenuWithDuplicateName(t, toml)
- doTestPageMenuWithDuplicateName(t, yaml)
-
-}
-
-func doTestPageMenuWithDuplicateName(t *testing.T, menuPageSources []source.ByteSource) {-
- s := setupMenuTests(t, menuPageSources)
-
- assert.Equal(t, 3, len(s.RegularPages), "Not enough pages")
-
- me1 := findTestMenuEntryByName(s, "m1", "n1")
- me2 := findTestMenuEntryByName(s, "m1", "n2")
-
- require.NotNil(t, me1)
- require.NotNil(t, me2)
-
- assert.True(t, strings.Contains(me1.URL, "doc1"), me1.URL)
- assert.True(t, strings.Contains(me2.URL, "doc2") || strings.Contains(me2.URL, "doc3"), me2.URL)
-
-}
-
-func TestPageMenu(t *testing.T) {- t.Parallel()
- s := setupMenuTests(t, menuPageSources)
-
- if len(s.RegularPages) != 3 {- t.Fatalf("Posts not created, expected 3 got %d", len(s.RegularPages))- }
-
- first := s.RegularPages[0]
- second := s.RegularPages[1]
- third := s.RegularPages[2]
-
- pOne := findTestMenuEntryByName(s, "p_one", "One")
- pTwo := findTestMenuEntryByID(s, "p_two", "Two")
-
- for i, this := range []struct {- menu string
- page *Page
- menuItem *MenuEntry
- isMenuCurrent bool
- hasMenuCurrent bool
- }{- {"p_one", first, pOne, true, false},- {"p_one", first, pTwo, false, false},- {"p_one", second, pTwo, false, false},- {"p_two", second, pTwo, true, false},- {"p_two", third, pTwo, false, true},- {"p_one", third, pTwo, false, false},- } {-
- if i != 4 {- continue
- }
-
- isMenuCurrent := this.page.IsMenuCurrent(this.menu, this.menuItem)
- hasMenuCurrent := this.page.HasMenuCurrent(this.menu, this.menuItem)
-
- if isMenuCurrent != this.isMenuCurrent {- t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)- }
-
- if hasMenuCurrent != this.hasMenuCurrent {- t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)- }
-
- }
-
-}
-
-func TestMenuURL(t *testing.T) {- t.Parallel()
- s := setupMenuTests(t, menuPageSources)
-
- for i, this := range []struct {- me *MenuEntry
- expectedURL string
- }{- // issue #888
- {findTestMenuEntryByID(s, "hash", "hash"), "/Zoo/resource#anchor"},- // issue #1774
- {findTestMenuEntryByID(s, "main", "ext"), "http://gohugo.io"},- {findTestMenuEntryByID(s, "main", "ext2"), "http://foo.local/Zoo/foo"},- } {-
- if this.me == nil {- t.Errorf("[%d] MenuEntry not found", i)- continue
- }
-
- if this.me.URL != this.expectedURL {- t.Errorf("[%d] Got URL %s expected %s", i, this.me.URL, this.expectedURL)- }
-
- }
-
-}
-
-// Issue #1934
-func TestYAMLMenuWithMultipleEntries(t *testing.T) {- t.Parallel()
- ps1 := []byte(`---
-title: "Yaml 1"
-weight: 5
-menu: ["p_one", "p_two"]
----
-Yaml Front Matter with Menu Pages`)
-
- ps2 := []byte(`---
-title: "Yaml 2"
-weight: 5
-menu:
- p_three:
- p_four:
----
-Yaml Front Matter with Menu Pages`)
-
- s := setupMenuTests(t, []source.ByteSource{- {Name: filepath.FromSlash("sect/yaml1.md"), Content: ps1},- {Name: filepath.FromSlash("sect/yaml2.md"), Content: ps2}})-
- p1 := s.RegularPages[0]
- assert.Len(t, p1.Menus(), 2, "List YAML")
- p2 := s.RegularPages[1]
- assert.Len(t, p2.Menus(), 2, "Map YAML")
-
-}
-
-// issue #719
-func TestMenuWithUnicodeURLs(t *testing.T) {- t.Parallel()
- for _, canonifyURLs := range []bool{true, false} {- doTestMenuWithUnicodeURLs(t, canonifyURLs)
- }
-}
-
-func doTestMenuWithUnicodeURLs(t *testing.T, canonifyURLs bool) {-
- s := setupMenuTests(t, menuPageSources, "canonifyURLs", canonifyURLs)
-
- unicodeRussian := findTestMenuEntryByID(s, "unicode", "unicode-russian")
-
- expected := "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0"
-
- if !canonifyURLs {- expected = "/Zoo" + expected
- }
-
- assert.Equal(t, expected, unicodeRussian.URL)
-}
-
-// Issue #1114
func TestSectionPagesMenu(t *testing.T) {t.Parallel()
- doTestSectionPagesMenu(true, t)
- doTestSectionPagesMenu(false, t)
-}
-func doTestSectionPagesMenu(canonifyURLs bool, t *testing.T) {+ siteConfig := `
+baseurl = "http://example.com/"
+title = "Section Menu"
+sectionPagesMenu = "sect"
+`
- s := setupMenuTests(t, menuPageSectionsSources,
- "sectionPagesMenu", "spm",
- "canonifyURLs", canonifyURLs,
+ th, h := newTestSitesFromConfig(t, siteConfig,
+ "layouts/partials/menu.html", `{{- $p := .page -}}+{{- $m := .menu -}}+{{ range (index $p.Site.Menus $m) -}}+{{- .URL }}|{{ .Name }}|{{ .Weight -}}|+{{- if $p.IsMenuCurrent $m . }}IsMenuCurrent{{ else }}-{{ end -}}|+{{- if $p.HasMenuCurrent $m . }}HasMenuCurrent{{ else }}-{{ end -}}|+{{- end -}}+`,
+ "layouts/_default/single.html",
+ `Single|{{ .Title }}+Menu Sect: {{ partial "menu.html" (dict "page" . "menu" "sect") }}+Menu Main: {{ partial "menu.html" (dict "page" . "menu" "main") }}`,+ "layouts/_default/list.html", "List|{{ .Title }}|{{ .Content }}",)
+ require.Len(t, h.Sites, 1)
- require.Equal(t, 3, len(s.Sections))
+ fs := th.Fs
- firstSectionPages := s.Sections["first"]
- require.Equal(t, 2, len(firstSectionPages))
- secondSectionPages := s.Sections["second-section"]
- require.Equal(t, 1, len(secondSectionPages))
- fishySectionPages := s.Sections["fish-and-chips"]
- require.Equal(t, 1, len(fishySectionPages))
+ writeSource(t, fs, "content/sect1/p1.md", fmt.Sprintf(menuPageTemplate, "p1", 1, "main", 40))
+ writeSource(t, fs, "content/sect1/p2.md", fmt.Sprintf(menuPageTemplate, "p2", 2, "main", 30))
+ writeSource(t, fs, "content/sect2/p3.md", fmt.Sprintf(menuPageTemplate, "p3", 3, "main", 20))
+ writeSource(t, fs, "content/sect2/p4.md", fmt.Sprintf(menuPageTemplate, "p4", 4, "main", 10))
+ writeSource(t, fs, "content/sect3/p5.md", fmt.Sprintf(menuPageTemplate, "p5", 5, "main", 5))
- nodeFirst := s.getPage(KindSection, "first")
- require.NotNil(t, nodeFirst)
- nodeSecond := s.getPage(KindSection, "second-section")
- require.NotNil(t, nodeSecond)
- nodeFishy := s.getPage(KindSection, "fish-and-chips")
- require.Equal(t, "fish-and-chips", nodeFishy.sections[0])
+ writeNewContentFile(t, fs, "Section One", "2017-01-01", "content/sect1/_index.md", 100)
+ writeNewContentFile(t, fs, "Section Five", "2017-01-01", "content/sect5/_index.md", 10)
- firstSectionMenuEntry := findTestMenuEntryByID(s, "spm", "first")
- secondSectionMenuEntry := findTestMenuEntryByID(s, "spm", "second-section")
- fishySectionMenuEntry := findTestMenuEntryByID(s, "spm", "Fish and Chips")
+ err := h.Build(BuildCfg{})- require.NotNil(t, firstSectionMenuEntry)
- require.NotNil(t, secondSectionMenuEntry)
- require.NotNil(t, nodeFirst)
- require.NotNil(t, nodeSecond)
- require.NotNil(t, fishySectionMenuEntry)
- require.NotNil(t, nodeFishy)
+ require.NoError(t, err)
- require.True(t, nodeFirst.IsMenuCurrent("spm", firstSectionMenuEntry))- require.False(t, nodeFirst.IsMenuCurrent("spm", secondSectionMenuEntry))- require.False(t, nodeFirst.IsMenuCurrent("spm", fishySectionMenuEntry))- require.True(t, nodeFishy.IsMenuCurrent("spm", fishySectionMenuEntry))- require.Equal(t, "Fish and Chips", fishySectionMenuEntry.Name)
+ s := h.Sites[0]
- for _, p := range firstSectionPages {- require.True(t, p.Page.HasMenuCurrent("spm", firstSectionMenuEntry))- require.False(t, p.Page.HasMenuCurrent("spm", secondSectionMenuEntry))- }
+ require.Len(t, s.Menus, 2)
- for _, p := range secondSectionPages {- require.False(t, p.Page.HasMenuCurrent("spm", firstSectionMenuEntry))- require.True(t, p.Page.HasMenuCurrent("spm", secondSectionMenuEntry))- }
+ p1 := s.RegularPages[0].Menus()
- for _, p := range fishySectionPages {- require.False(t, p.Page.HasMenuCurrent("spm", firstSectionMenuEntry))- require.False(t, p.Page.HasMenuCurrent("spm", secondSectionMenuEntry))- require.True(t, p.Page.HasMenuCurrent("spm", fishySectionMenuEntry))- }
-}
+ // There is only one menu in the page, but it is "member of" 2
+ require.Len(t, p1, 1)
-func TestTaxonomyNodeMenu(t *testing.T) {- t.Parallel()
-
- type taxRenderInfo struct {- key string
- singular string
- plural string
- }
-
- s := setupMenuTests(t, menuPageSources, "canonifyURLs", true)
-
- for i, this := range []struct {- menu string
- taxInfo taxRenderInfo
- menuItem *MenuEntry
- isMenuCurrent bool
- hasMenuCurrent bool
- }{- {"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},- findTestMenuEntryByID(s, "tax", "1"), true, false},
- {"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},- findTestMenuEntryByID(s, "tax", "2"), true, false},
- {"tax", taxRenderInfo{key: "key", singular: "one", plural: "two"},- &MenuEntry{Name: "Somewhere else", URL: "/somewhereelse"}, false, false},- } {-
- p := s.newTaxonomyPage(this.taxInfo.plural, this.taxInfo.key)
-
- isMenuCurrent := p.IsMenuCurrent(this.menu, this.menuItem)
- hasMenuCurrent := p.HasMenuCurrent(this.menu, this.menuItem)
-
- if isMenuCurrent != this.isMenuCurrent {- t.Errorf("[%d] Wrong result from IsMenuCurrent: %v", i, isMenuCurrent)- }
-
- if hasMenuCurrent != this.hasMenuCurrent {- t.Errorf("[%d] Wrong result for menuItem %v for HasMenuCurrent: %v", i, this.menuItem, hasMenuCurrent)- }
-
- }
-
- menuEntryXML := findTestMenuEntryByID(s, "tax", "xml")
-
- if strings.HasSuffix(menuEntryXML.URL, "/") {- t.Error("RSS menu item should not be padded with trailing slash")- }
-}
-
-func TestMenuLimit(t *testing.T) {- t.Parallel()
- s := setupMenuTests(t, menuPageSources)
- m := *s.Menus["main"]
-
- // main menu has 4 entries
- firstTwo := m.Limit(2)
- assert.Equal(t, 2, len(firstTwo))
- for i := 0; i < 2; i++ {- assert.Equal(t, m[i], firstTwo[i])
- }
- assert.Equal(t, m, m.Limit(4))
- assert.Equal(t, m, m.Limit(5))
-}
-
-func TestMenuSortByN(t *testing.T) {- t.Parallel()
- for i, this := range []struct {- sortFunc func(p Menu) Menu
- assertFunc func(p Menu) bool
- }{- {(Menu).Sort, func(p Menu) bool { return p[0].Weight == 1 && p[1].Name == "nx" && p[2].Identifier == "ib" }},- {(Menu).ByWeight, func(p Menu) bool { return p[0].Weight == 1 && p[1].Name == "nx" && p[2].Identifier == "ib" }},- {(Menu).ByName, func(p Menu) bool { return p[0].Name == "na" }},- {(Menu).Reverse, func(p Menu) bool { return p[0].Identifier == "ib" && p[len(p)-1].Identifier == "ia" }},- } {- menu := Menu{&MenuEntry{Weight: 3, Name: "nb", Identifier: "ia"},- &MenuEntry{Weight: 1, Name: "na", Identifier: "ic"},- &MenuEntry{Weight: 1, Name: "nx", Identifier: "ic"},- &MenuEntry{Weight: 2, Name: "nb", Identifier: "ix"},- &MenuEntry{Weight: 2, Name: "nb", Identifier: "ib"}}-
- sorted := this.sortFunc(menu)
-
- if !this.assertFunc(sorted) {- t.Errorf("[%d] sort error", i)- }
- }
-
-}
-
-func TestHomeNodeMenu(t *testing.T) {- t.Parallel()
- s := setupMenuTests(t, menuPageSources,
- "canonifyURLs", true,
- "uglyURLs", false,
+ th.assertFileContent("public/sect1/p1/index.html", "Single",+ "Menu Sect: /sect5/|Section Five|10|-|-|/sect1/|Section One|100|-|HasMenuCurrent|/sect2/|Sect2s|0|-|-|/sect3/|Sect3s|0|-|-|",
+ "Menu Main: /sect3/p5/|p5|5|-|-|/sect2/p4/|p4|10|-|-|/sect2/p3/|p3|20|-|-|/sect1/p2/|p2|30|-|-|/sect1/p1/|p1|40|IsMenuCurrent|-|",
)
- home := s.getPage(KindHome)
- homeMenuEntry := &MenuEntry{Name: home.Title, URL: home.URL()}-
- for i, this := range []struct {- menu string
- menuItem *MenuEntry
- isMenuCurrent bool
- hasMenuCurrent bool
- }{- {"main", homeMenuEntry, true, false},- {"doesnotexist", homeMenuEntry, false, false},- {"main", &MenuEntry{Name: "Somewhere else", URL: "/somewhereelse"}, false, false},- {"grandparent", findTestMenuEntryByID(s, "grandparent", "grandparentId"), false, true},- {"grandparent", findTestMenuEntryByID(s, "grandparent", "parentId"), false, true},- {"grandparent", findTestMenuEntryByID(s, "grandparent", "grandchildId"), true, false},- } {-
- isMenuCurrent := home.IsMenuCurrent(this.menu, this.menuItem)
- hasMenuCurrent := home.HasMenuCurrent(this.menu, this.menuItem)
-
- if isMenuCurrent != this.isMenuCurrent {- fmt.Println("isMenuCurrent", isMenuCurrent)- fmt.Printf("this: %#v\n", this)- t.Errorf("[%d] Wrong result from IsMenuCurrent: %v for %q", i, isMenuCurrent, this.menuItem)- }
-
- if hasMenuCurrent != this.hasMenuCurrent {- fmt.Println("hasMenuCurrent", hasMenuCurrent)- fmt.Printf("this: %#v\n", this)- t.Errorf("[%d] Wrong result for menu %q menuItem %v for HasMenuCurrent: %v", i, this.menu, this.menuItem, hasMenuCurrent)- }
- }
-}
-
-func TestHopefullyUniqueID(t *testing.T) {- t.Parallel()
- assert.Equal(t, "i", (&MenuEntry{Identifier: "i", URL: "u", Name: "n"}).hopefullyUniqueID())- assert.Equal(t, "u", (&MenuEntry{Identifier: "", URL: "u", Name: "n"}).hopefullyUniqueID())- assert.Equal(t, "n", (&MenuEntry{Identifier: "", URL: "", Name: "n"}).hopefullyUniqueID())-}
-
-func TestAddMenuEntryChild(t *testing.T) {- t.Parallel()
- root := &MenuEntry{Weight: 1}- root.addChild(&MenuEntry{Weight: 2})- root.addChild(&MenuEntry{Weight: 1})- assert.Equal(t, 2, len(root.Children))
- assert.Equal(t, 1, root.Children[0].Weight)
-}
-
-var testMenuIdentityMatcher = func(me *MenuEntry, id string) bool { return me.Identifier == id }-var testMenuNameMatcher = func(me *MenuEntry, id string) bool { return me.Name == id }-
-func findTestMenuEntryByID(s *Site, mn string, id string) *MenuEntry {- return findTestMenuEntry(s, mn, id, testMenuIdentityMatcher)
-}
-func findTestMenuEntryByName(s *Site, mn string, id string) *MenuEntry {- return findTestMenuEntry(s, mn, id, testMenuNameMatcher)
-}
-
-func findTestMenuEntry(s *Site, mn string, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {- var found *MenuEntry
- if menu, ok := s.Menus[mn]; ok {- for _, me := range *menu {-
- if matcher(me, id) {- if found != nil {- panic(fmt.Sprintf("Duplicate menu entry in menu %s with id/name %s", mn, id))- }
- found = me
- }
-
- descendant := findDescendantTestMenuEntry(me, id, matcher)
- if descendant != nil {- if found != nil {- panic(fmt.Sprintf("Duplicate menu entry in menu %s with id/name %s", mn, id))- }
- found = descendant
- }
- }
- }
- return found
-}
-
-func findDescendantTestMenuEntry(parent *MenuEntry, id string, matcher func(me *MenuEntry, id string) bool) *MenuEntry {- var found *MenuEntry
- if parent.HasChildren() {- for _, child := range parent.Children {-
- if matcher(child, id) {- if found != nil {- panic(fmt.Sprintf("Duplicate menu entry in menuitem %s with id/name %s", parent.KeyName(), id))- }
- found = child
- }
-
- descendant := findDescendantTestMenuEntry(child, id, matcher)
- if descendant != nil {- if found != nil {- panic(fmt.Sprintf("Duplicate menu entry in menuitem %s with id/name %s", parent.KeyName(), id))- }
- found = descendant
- }
- }
- }
- return found
-}
-
-func setupMenuTests(t *testing.T, pageSources []source.ByteSource, configKeyValues ...interface{}) *Site {-
- var (
- cfg, fs = newTestCfg()
- )
-
- menus, err := tomlToMap(confMenu1)
- require.NoError(t, err)
-
- cfg.Set("menu", menus["menu"])- cfg.Set("baseURL", "http://foo.local/Zoo/")-
- for i := 0; i < len(configKeyValues); i += 2 {- cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
- }
-
- for _, src := range pageSources {- writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))-
- }
-
- return buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{SkipRender: true})-
-}
-
-func tomlToMap(s string) (map[string]interface{}, error) {- tree, err := toml.Load(s)
-
- if err != nil {- return nil, err
- }
-
- return tree.ToMap(), nil
+ th.assertFileContent("public/sect2/p3/index.html", "Single",+ "Menu Sect: /sect5/|Section Five|10|-|-|/sect1/|Section One|100|-|-|/sect2/|Sect2s|0|-|HasMenuCurrent|/sect3/|Sect3s|0|-|-|")
}
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -621,6 +621,9 @@
}
func (p *Page) Section() string {+ if p.Kind == KindSection {+ return p.sections[0]
+ }
return p.Source.Section()
}
@@ -1167,8 +1170,16 @@
sectionPagesMenu := p.Site.sectionPagesMenu
// page is labeled as "shadow-member" of the menu with the same identifier as the section
- if sectionPagesMenu != "" && p.Section() != "" && sectionPagesMenu == menuID && p.Section() == me.Identifier {- return true
+ if sectionPagesMenu != "" {+ section := p.Section()
+
+ if !p.s.Info.preserveTaxonomyNames {+ section = p.s.PathSpec.MakePathSanitized(section)
+ }
+
+ if section != "" && sectionPagesMenu == menuID && section == me.Identifier {+ return true
+ }
}
if !me.HasChildren() {@@ -1537,7 +1548,7 @@
case KindHome:
pages = s.RegularPages
case KindSection:
- sectionData, ok := s.Sections[p.sections[0]]
+ sectionData, ok := s.Sections[p.Section()]
if !ok { return fmt.Errorf("Data for section %s not found", p.Section())}
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -1366,8 +1366,6 @@
s.assembleSections()
- s.assembleMenus()
-
return
}
@@ -1442,42 +1440,20 @@
pages := s.Pages
if sectionPagesMenu != "" {- // Create menu entries for section pages with content
for _, p := range pages { if p.Kind == KindSection {- // menu with same id defined in config, let that one win
- if _, ok := flat[twoD{sectionPagesMenu, p.Section()}]; ok {+ id := p.Section()
+ if _, ok := flat[twoD{sectionPagesMenu, id}]; ok {continue
}
- me := MenuEntry{Identifier: p.Section(),+ me := MenuEntry{Identifier: id,Name: p.LinkTitle(),
Weight: p.Weight,
URL: p.RelPermalink()}
-
flat[twoD{sectionPagesMenu, me.KeyName()}] = &me}
}
-
- // Create entries for remaining content-less section pages
- sectionPagesMenus := make(map[string]interface{})- for _, p := range pages {- if _, ok := sectionPagesMenus[p.Section()]; !ok {- if p.Section() != "" {- // menu with same id defined in config, let that one win
- if _, ok := flat[twoD{sectionPagesMenu, p.Section()}]; ok {- continue
- }
-
- me := MenuEntry{Identifier: p.Section(),- Name: helpers.MakeTitle(helpers.FirstUpper(p.Section())),
- URL: s.Info.createNodeMenuEntryURL(p.addLangPathPrefix("/"+p.Section()) + "/")}-
- flat[twoD{sectionPagesMenu, me.KeyName()}] = &me- sectionPagesMenus[p.Section()] = true
- }
- }
- }
}
// Add menu entries provided by pages
@@ -1610,7 +1586,8 @@
sectionPages := s.findPagesByKind(KindSection)
for i, p := range regularPages {- s.Sections.add(s.getTaxonomyKey(p.Section()), WeightedPage{regularPages[i].Weight, regularPages[i]})+ section := s.getTaxonomyKey(p.Section())
+ s.Sections.add(section, WeightedPage{regularPages[i].Weight, regularPages[i]})}
// Add sections without regular pages, but with a content page
@@ -2135,7 +2112,6 @@
}
func (s *Site) newSectionPage(name string, section WeightedPages) *Page {-
p := s.newNodePage(KindSection)
p.sections = []string{name}--- a/hugolib/taxonomy_test.go
+++ b/hugolib/taxonomy_test.go
@@ -19,11 +19,9 @@
"reflect"
"testing"
- "github.com/spf13/afero"
"github.com/stretchr/testify/require"
"github.com/spf13/hugo/deps"
- "github.com/spf13/hugo/hugofs"
)
func TestByCountOrderOfTaxonomies(t *testing.T) {@@ -79,20 +77,11 @@
# Doc
`
- mf := afero.NewMemMapFs()
+ th, h := newTestSitesFromConfigWithDefaultTemplates(t, siteConfig)
+ require.Len(t, h.Sites, 1)
- writeToFs(t, mf, "config.toml", siteConfig)
+ fs := th.Fs
- cfg, err := LoadConfig(mf, "", "config.toml")
- require.NoError(t, err)
-
- fs := hugofs.NewFrom(mf, cfg)
- th := testHelper{cfg, fs, t}-
- writeSource(t, fs, "layouts/_default/single.html", "Single|{{ .Title }}|{{ .Content }}")- writeSource(t, fs, "layouts/_default/list.html", "List|{{ .Title }}|{{ .Content }}")- writeSource(t, fs, "layouts/_default/terms.html", "Terms List|{{ .Title }}|{{ .Content }}")-
writeSource(t, fs, "content/p1.md", fmt.Sprintf(pageTemplate, "t1/c1", "- tag1", "- cat1", "- o1"))
writeSource(t, fs, "content/p2.md", fmt.Sprintf(pageTemplate, "t2/c1", "- tag2", "- cat1", "- o1"))
@@ -99,12 +88,7 @@
writeNewContentFile(t, fs, "Category Terms", "2017-01-01", "content/categories/_index.md", 10)
writeNewContentFile(t, fs, "Tag1 List", "2017-01-01", "content/tags/tag1/_index.md", 10)
- h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})-
- require.NoError(t, err)
- require.Len(t, h.Sites, 1)
-
- err = h.Build(BuildCfg{})+ err := h.Build(BuildCfg{})require.NoError(t, err)
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -6,12 +6,13 @@
"regexp"
- "github.com/spf13/hugo/config"
- "github.com/spf13/hugo/deps"
-
"fmt"
"strings"
+ "github.com/spf13/afero"
+ "github.com/spf13/hugo/config"
+ "github.com/spf13/hugo/deps"
+
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
@@ -111,6 +112,39 @@
t.Fatalf("Failed to create Site: %s", err)}
return s
+}
+
+func newTestSitesFromConfig(t testing.TB, tomlConfig string, layoutPathContentPairs ...string) (testHelper, *HugoSites) {+ if len(layoutPathContentPairs)%2 != 0 {+ t.Fatalf("Layouts must be provided in pairs")+ }
+ mf := afero.NewMemMapFs()
+
+ writeToFs(t, mf, "config.toml", tomlConfig)
+
+ cfg, err := LoadConfig(mf, "", "config.toml")
+ require.NoError(t, err)
+
+ fs := hugofs.NewFrom(mf, cfg)
+ th := testHelper{cfg, fs, t}+
+ for i := 0; i < len(layoutPathContentPairs); i += 2 {+ writeSource(t, fs, layoutPathContentPairs[i], layoutPathContentPairs[i+1])
+ }
+
+ h, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})+
+ require.NoError(t, err)
+
+ return th, h
+}
+
+func newTestSitesFromConfigWithDefaultTemplates(t testing.TB, tomlConfig string) (testHelper, *HugoSites) {+ return newTestSitesFromConfig(t, tomlConfig,
+ "layouts/_default/single.html", "Single|{{ .Title }}|{{ .Content }}",+ "layouts/_default/list.html", "List|{{ .Title }}|{{ .Content }}",+ "layouts/_default/terms.html", "Terms List|{{ .Title }}|{{ .Content }}",+ )
}
func newDebugLogger() *jww.Notepad {--
⑨