github.com/neohugo/neohugo@v0.123.8/hugolib/site_sections_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  	"path/filepath"
    19  	"strings"
    20  	"testing"
    21  
    22  	qt "github.com/frankban/quicktest"
    23  	"github.com/neohugo/neohugo/deps"
    24  	"github.com/neohugo/neohugo/htesting"
    25  	"github.com/neohugo/neohugo/resources/kinds"
    26  	"github.com/neohugo/neohugo/resources/page"
    27  )
    28  
    29  func TestNestedSections(t *testing.T) {
    30  	var (
    31  		c       = qt.New(t)
    32  		cfg, fs = newTestCfg()
    33  	)
    34  
    35  	tt := htesting.NewPinnedRunner(c, "")
    36  
    37  	cfg.Set("permalinks", map[string]string{
    38  		"perm-a": ":sections/:title",
    39  	})
    40  
    41  	pageTemplate := `---
    42  title: T%d_%d
    43  ---
    44  Content
    45  `
    46  
    47  	// Home page
    48  	writeSource(t, fs, filepath.Join("content", "_index.md"), fmt.Sprintf(pageTemplate, -1, -1))
    49  
    50  	// Top level content page
    51  	writeSource(t, fs, filepath.Join("content", "mypage.md"), fmt.Sprintf(pageTemplate, 1234, 5))
    52  
    53  	// Top level section without index content page
    54  	writeSource(t, fs, filepath.Join("content", "top", "mypage2.md"), fmt.Sprintf(pageTemplate, 12345, 6))
    55  	// Just a page in a subfolder, i.e. not a section.
    56  	writeSource(t, fs, filepath.Join("content", "top", "folder", "mypage3.md"), fmt.Sprintf(pageTemplate, 12345, 67))
    57  
    58  	for level1 := 1; level1 < 3; level1++ {
    59  		writeSource(t, fs, filepath.Join("content", "l1", fmt.Sprintf("page_1_%d.md", level1)),
    60  			fmt.Sprintf(pageTemplate, 1, level1))
    61  	}
    62  
    63  	// Issue #3586
    64  	writeSource(t, fs, filepath.Join("content", "post", "0000.md"), fmt.Sprintf(pageTemplate, 1, 2))
    65  	writeSource(t, fs, filepath.Join("content", "post", "0000", "0001.md"), fmt.Sprintf(pageTemplate, 1, 3))
    66  	writeSource(t, fs, filepath.Join("content", "elsewhere", "0003.md"), fmt.Sprintf(pageTemplate, 1, 4))
    67  
    68  	// Empty nested section, i.e. no regular content pages.
    69  	writeSource(t, fs, filepath.Join("content", "empty1", "b", "c", "_index.md"), fmt.Sprintf(pageTemplate, 33, -1))
    70  	// Index content file a the end and in the middle.
    71  	writeSource(t, fs, filepath.Join("content", "empty2", "b", "_index.md"), fmt.Sprintf(pageTemplate, 40, -1))
    72  	writeSource(t, fs, filepath.Join("content", "empty2", "b", "c", "d", "_index.md"), fmt.Sprintf(pageTemplate, 41, -1))
    73  
    74  	// Empty with content file in the middle.
    75  	writeSource(t, fs, filepath.Join("content", "empty3", "b", "c", "d", "_index.md"), fmt.Sprintf(pageTemplate, 41, -1))
    76  	writeSource(t, fs, filepath.Join("content", "empty3", "b", "empty3.md"), fmt.Sprintf(pageTemplate, 3, -1))
    77  
    78  	// Section with permalink config
    79  	writeSource(t, fs, filepath.Join("content", "perm a", "link", "_index.md"), fmt.Sprintf(pageTemplate, 9, -1))
    80  	for i := 1; i < 4; i++ {
    81  		writeSource(t, fs, filepath.Join("content", "perm a", "link", fmt.Sprintf("page_%d.md", i)),
    82  			fmt.Sprintf(pageTemplate, 1, i))
    83  	}
    84  	writeSource(t, fs, filepath.Join("content", "perm a", "link", "regular", fmt.Sprintf("page_%d.md", 5)),
    85  		fmt.Sprintf(pageTemplate, 1, 5))
    86  
    87  	writeSource(t, fs, filepath.Join("content", "l1", "l2", "_index.md"), fmt.Sprintf(pageTemplate, 2, -1))
    88  	writeSource(t, fs, filepath.Join("content", "l1", "l2_2", "_index.md"), fmt.Sprintf(pageTemplate, 22, -1))
    89  	writeSource(t, fs, filepath.Join("content", "l1", "l2", "l3", "_index.md"), fmt.Sprintf(pageTemplate, 3, -1))
    90  
    91  	for level2 := 1; level2 < 4; level2++ {
    92  		writeSource(t, fs, filepath.Join("content", "l1", "l2", fmt.Sprintf("page_2_%d.md", level2)),
    93  			fmt.Sprintf(pageTemplate, 2, level2))
    94  	}
    95  	for level2 := 1; level2 < 3; level2++ {
    96  		writeSource(t, fs, filepath.Join("content", "l1", "l2_2", fmt.Sprintf("page_2_2_%d.md", level2)),
    97  			fmt.Sprintf(pageTemplate, 2, level2))
    98  	}
    99  	for level3 := 1; level3 < 3; level3++ {
   100  		writeSource(t, fs, filepath.Join("content", "l1", "l2", "l3", fmt.Sprintf("page_3_%d.md", level3)),
   101  			fmt.Sprintf(pageTemplate, 3, level3))
   102  	}
   103  
   104  	writeSource(t, fs, filepath.Join("content", "Spaces in Section", "page100.md"), fmt.Sprintf(pageTemplate, 10, 0))
   105  
   106  	writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), "<html>Single|{{ .Title }}</html>")
   107  	writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"),
   108  		`
   109  {{ $sect := (.Site.GetPage "l1/l2") }}
   110  <html>List|{{ .Title }}|L1/l2-IsActive: {{ .InSection $sect }}
   111  {{ range .Paginator.Pages }}
   112  PAG|{{ .Title }}|{{ $sect.InSection . }}
   113  {{ end }}
   114  {{/* https://github.com/gohugoio/hugo/issues/4989 */}}
   115  {{ $sections := (.Site.GetPage "section" .Section).Sections.ByWeight }}
   116  </html>`)
   117  
   118  	cfg.Set("paginate", 2)
   119  
   120  	th, configs := newTestHelperFromProvider(cfg, fs, t)
   121  
   122  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Configs: configs}, BuildCfg{})
   123  
   124  	c.Assert(len(s.RegularPages()), qt.Equals, 21)
   125  
   126  	tests := []struct {
   127  		sections string
   128  		verify   func(c *qt.C, p page.Page)
   129  	}{
   130  		{"elsewhere", func(c *qt.C, p page.Page) {
   131  			c.Assert(len(p.Pages()), qt.Equals, 1)
   132  			for _, p := range p.Pages() {
   133  				c.Assert(p.SectionsPath(), qt.Equals, "/elsewhere")
   134  			}
   135  		}},
   136  		{"post", func(c *qt.C, p page.Page) {
   137  			c.Assert(len(p.Pages()), qt.Equals, 2)
   138  			for _, p := range p.Pages() {
   139  				c.Assert(p.Section(), qt.Equals, "post")
   140  			}
   141  		}},
   142  		{"empty1", func(c *qt.C, p page.Page) {
   143  			// > b,c
   144  			c.Assert(getPage(p, "/empty1/b"), qt.IsNil) // No _index.md page.
   145  			c.Assert(getPage(p, "/empty1/b/c"), qt.Not(qt.IsNil))
   146  		}},
   147  		{"empty2", func(c *qt.C, p page.Page) {
   148  			// > b,c,d where b and d have _index.md files.
   149  			b := getPage(p, "/empty2/b")
   150  			c.Assert(b, qt.Not(qt.IsNil))
   151  			c.Assert(b.Title(), qt.Equals, "T40_-1")
   152  
   153  			cp := getPage(p, "/empty2/b/c")
   154  			c.Assert(cp, qt.IsNil) // No _index.md
   155  
   156  			d := getPage(p, "/empty2/b/c/d")
   157  			c.Assert(d, qt.Not(qt.IsNil))
   158  			c.Assert(d.Title(), qt.Equals, "T41_-1")
   159  
   160  			c.Assert(cp.Eq(d), qt.Equals, false)
   161  			c.Assert(cp.Eq(cp), qt.Equals, true)
   162  			c.Assert(cp.Eq("asdf"), qt.Equals, false)
   163  		}},
   164  		{"empty3", func(c *qt.C, p page.Page) {
   165  			// b,c,d with regular page in b
   166  			b := getPage(p, "/empty3/b")
   167  			c.Assert(b, qt.IsNil) // No _index.md
   168  			e3 := getPage(p, "/empty3/b/empty3")
   169  			c.Assert(e3, qt.Not(qt.IsNil))
   170  			c.Assert(e3.File().LogicalName(), qt.Equals, "empty3.md")
   171  		}},
   172  		{"empty3", func(c *qt.C, p page.Page) {
   173  			xxx := getPage(p, "/empty3/nil")
   174  			c.Assert(xxx, qt.IsNil)
   175  		}},
   176  		{"top", func(c *qt.C, p page.Page) {
   177  			c.Assert(p.Title(), qt.Equals, "Tops")
   178  			c.Assert(len(p.Pages()), qt.Equals, 2)
   179  			c.Assert(p.Pages()[0].File().LogicalName(), qt.Equals, "mypage2.md")
   180  			c.Assert(p.Pages()[1].File().LogicalName(), qt.Equals, "mypage3.md")
   181  			home := p.Parent()
   182  			c.Assert(home.IsHome(), qt.Equals, true)
   183  			c.Assert(len(p.Sections()), qt.Equals, 0)
   184  			c.Assert(home.CurrentSection(), qt.Equals, home)
   185  			active := home.InSection(home)
   186  			c.Assert(active, qt.Equals, true)
   187  			c.Assert(p.FirstSection(), qt.Equals, p)
   188  			c.Assert(len(p.Ancestors()), qt.Equals, 1)
   189  		}},
   190  		{"l1", func(c *qt.C, p page.Page) {
   191  			c.Assert(p.Title(), qt.Equals, "L1s")
   192  			c.Assert(len(p.Pages()), qt.Equals, 4) // 2 pages + 2 sections
   193  			c.Assert(p.Parent().IsHome(), qt.Equals, true)
   194  			c.Assert(len(p.Sections()), qt.Equals, 2)
   195  			c.Assert(len(p.Ancestors()), qt.Equals, 1)
   196  		}},
   197  		{"l1,l2", func(c *qt.C, p page.Page) {
   198  			c.Assert(p.Title(), qt.Equals, "T2_-1")
   199  			c.Assert(len(p.Pages()), qt.Equals, 4) // 3 pages + 1 section
   200  			c.Assert(p.Pages()[0].Parent(), qt.Equals, p)
   201  			c.Assert(p.Parent().Title(), qt.Equals, "L1s")
   202  			c.Assert(p.RelPermalink(), qt.Equals, "/l1/l2/")
   203  			c.Assert(len(p.Sections()), qt.Equals, 1)
   204  			c.Assert(len(p.Ancestors()), qt.Equals, 2)
   205  
   206  			for _, child := range p.Pages() {
   207  				if child.IsSection() {
   208  					c.Assert(child.CurrentSection(), qt.Equals, child)
   209  					continue
   210  				}
   211  
   212  				c.Assert(child.CurrentSection(), qt.Equals, p)
   213  				active := child.InSection(p)
   214  
   215  				c.Assert(active, qt.Equals, true)
   216  				active = p.InSection(child)
   217  				c.Assert(active, qt.Equals, true)
   218  				active = p.InSection(getPage(p, "/"))
   219  				c.Assert(active, qt.Equals, false)
   220  
   221  				isAncestor := p.IsAncestor(child)
   222  				c.Assert(isAncestor, qt.Equals, true)
   223  				isAncestor = child.IsAncestor(p)
   224  				c.Assert(isAncestor, qt.Equals, false)
   225  
   226  				isDescendant := p.IsDescendant(child)
   227  				c.Assert(isDescendant, qt.Equals, false)
   228  				isDescendant = child.IsDescendant(p)
   229  				c.Assert(isDescendant, qt.Equals, true)
   230  			}
   231  
   232  			c.Assert(p.Eq(p.CurrentSection()), qt.Equals, true)
   233  		}},
   234  		{"l1,l2_2", func(c *qt.C, p page.Page) {
   235  			c.Assert(p.Title(), qt.Equals, "T22_-1")
   236  			c.Assert(len(p.Pages()), qt.Equals, 2)
   237  			c.Assert(p.Pages()[0].File().Path(), qt.Equals, filepath.FromSlash("l1/l2_2/page_2_2_1.md"))
   238  			c.Assert(p.Parent().Title(), qt.Equals, "L1s")
   239  			c.Assert(len(p.Sections()), qt.Equals, 0)
   240  			c.Assert(len(p.Ancestors()), qt.Equals, 2)
   241  		}},
   242  		{"l1,l2,l3", func(c *qt.C, p page.Page) {
   243  			nilp, _ := p.GetPage("this/does/not/exist")
   244  
   245  			c.Assert(p.Title(), qt.Equals, "T3_-1")
   246  			c.Assert(len(p.Pages()), qt.Equals, 2)
   247  			c.Assert(p.Parent().Title(), qt.Equals, "T2_-1")
   248  			c.Assert(len(p.Sections()), qt.Equals, 0)
   249  			c.Assert(len(p.Ancestors()), qt.Equals, 3)
   250  
   251  			l1 := getPage(p, "/l1")
   252  			isDescendant := l1.IsDescendant(p)
   253  			c.Assert(isDescendant, qt.Equals, false)
   254  			isDescendant = l1.IsDescendant(nil)
   255  			c.Assert(isDescendant, qt.Equals, false)
   256  			isDescendant = nilp.IsDescendant(p)
   257  			c.Assert(isDescendant, qt.Equals, false)
   258  			isDescendant = p.IsDescendant(l1)
   259  			c.Assert(isDescendant, qt.Equals, true)
   260  
   261  			isAncestor := l1.IsAncestor(p)
   262  			c.Assert(isAncestor, qt.Equals, true)
   263  			isAncestor = p.IsAncestor(l1)
   264  			c.Assert(isAncestor, qt.Equals, false)
   265  			c.Assert(p.FirstSection(), qt.Equals, l1)
   266  			isAncestor = p.IsAncestor(nil)
   267  			c.Assert(isAncestor, qt.Equals, false)
   268  			c.Assert(isAncestor, qt.Equals, false)
   269  
   270  			l3 := getPage(p, "/l1/l2/l3")
   271  			c.Assert(l3.FirstSection(), qt.Equals, l1)
   272  		}},
   273  		{"perm a,link", func(c *qt.C, p page.Page) {
   274  			c.Assert(p.Title(), qt.Equals, "T9_-1")
   275  			c.Assert(p.RelPermalink(), qt.Equals, "/perm-a/link/")
   276  			c.Assert(len(p.Pages()), qt.Equals, 4)
   277  			first := p.Pages()[0]
   278  			c.Assert(first.RelPermalink(), qt.Equals, "/perm-a/link/t1_1/")
   279  			th.assertFileContent("public/perm-a/link/t1_1/index.html", "Single|T1_1")
   280  
   281  			last := p.Pages()[3]
   282  			c.Assert(last.RelPermalink(), qt.Equals, "/perm-a/link/t1_5/")
   283  		}},
   284  	}
   285  
   286  	home := s.getPageOldVersion(kinds.KindHome)
   287  
   288  	for _, test := range tests {
   289  		test := test
   290  		tt.Run(fmt.Sprintf("sections %s", test.sections), func(c *qt.C) {
   291  			c.Parallel()
   292  			sections := strings.Split(test.sections, ",")
   293  			p := s.getPageOldVersion(kinds.KindSection, sections...)
   294  			c.Assert(p, qt.Not(qt.IsNil), qt.Commentf(fmt.Sprint(sections)))
   295  
   296  			if p.Pages() != nil {
   297  				c.Assert(p.Data().(page.Data).Pages(), deepEqualsPages, p.Pages())
   298  			}
   299  			c.Assert(p.Parent(), qt.Not(qt.IsNil))
   300  			test.verify(c, p)
   301  		})
   302  	}
   303  
   304  	c.Assert(home, qt.Not(qt.IsNil))
   305  	c.Assert(len(home.Ancestors()), qt.Equals, 0)
   306  
   307  	c.Assert(len(home.Sections()), qt.Equals, 9)
   308  	c.Assert(s.Sections(), deepEqualsPages, home.Sections())
   309  
   310  	rootPage := s.getPageOldVersion(kinds.KindPage, "mypage.md")
   311  	c.Assert(rootPage, qt.Not(qt.IsNil))
   312  	c.Assert(rootPage.Parent().IsHome(), qt.Equals, true)
   313  	// https://github.com/gohugoio/hugo/issues/6365
   314  	c.Assert(rootPage.Sections(), qt.HasLen, 0)
   315  
   316  	sectionWithSpace := s.getPageOldVersion(kinds.KindSection, "Spaces in Section")
   317  	// s.h.pageTrees.debugPrint()
   318  	c.Assert(sectionWithSpace, qt.Not(qt.IsNil))
   319  	c.Assert(sectionWithSpace.RelPermalink(), qt.Equals, "/spaces-in-section/")
   320  
   321  	th.assertFileContent("public/l1/l2/page/2/index.html", "L1/l2-IsActive: true", "PAG|T2_3|true")
   322  }
   323  
   324  func TestNextInSectionNested(t *testing.T) {
   325  	t.Parallel()
   326  
   327  	pageContent := `---
   328  title: "The Page"
   329  weight: %d
   330  ---
   331  Some content.
   332  `
   333  	createPageContent := func(weight int) string {
   334  		return fmt.Sprintf(pageContent, weight)
   335  	}
   336  
   337  	b := newTestSitesBuilder(t)
   338  	b.WithSimpleConfigFile()
   339  	b.WithTemplates("_default/single.html", `
   340  Prev: {{ with .PrevInSection }}{{ .RelPermalink }}{{ end }}|
   341  Next: {{ with .NextInSection }}{{ .RelPermalink }}{{ end }}|
   342  `)
   343  
   344  	b.WithContent("blog/page1.md", createPageContent(1))
   345  	b.WithContent("blog/page2.md", createPageContent(2))
   346  	b.WithContent("blog/cool/_index.md", createPageContent(1))
   347  	b.WithContent("blog/cool/cool1.md", createPageContent(1))
   348  	b.WithContent("blog/cool/cool2.md", createPageContent(2))
   349  	b.WithContent("root1.md", createPageContent(1))
   350  	b.WithContent("root2.md", createPageContent(2))
   351  
   352  	b.Build(BuildCfg{})
   353  
   354  	b.AssertFileContent("public/root1/index.html",
   355  		"Prev: /root2/|", "Next: |")
   356  	b.AssertFileContent("public/root2/index.html",
   357  		"Prev: |", "Next: /root1/|")
   358  	b.AssertFileContent("public/blog/page1/index.html",
   359  		"Prev: /blog/page2/|", "Next: |")
   360  	b.AssertFileContent("public/blog/page2/index.html",
   361  		"Prev: |", "Next: /blog/page1/|")
   362  	b.AssertFileContent("public/blog/cool/cool1/index.html",
   363  		"Prev: /blog/cool/cool2/|", "Next: |")
   364  	b.AssertFileContent("public/blog/cool/cool2/index.html",
   365  		"Prev: |", "Next: /blog/cool/cool1/|")
   366  }
   367  
   368  func TestSectionEntries(t *testing.T) {
   369  	t.Parallel()
   370  
   371  	files := `
   372  -- hugo.toml --
   373  baseURL = "https://example.com/"
   374  -- content/myfirstsection/p1.md --
   375  ---
   376  title: "P1"
   377  ---
   378  P1
   379  -- content/a/b/c/_index.md --
   380  ---
   381  title: "C"
   382  ---
   383  C
   384  -- content/a/b/c/mybundle/index.md --
   385  ---
   386  title: "My Bundle"
   387  ---
   388  -- layouts/_default/list.html --
   389  Kind: {{ .Kind }}|RelPermalink: {{ .RelPermalink }}|SectionsPath: {{ .SectionsPath }}|SectionsEntries: {{ .SectionsEntries }}|Len: {{ len .SectionsEntries }}|
   390  -- layouts/_default/single.html --
   391  Kind: {{ .Kind }}|RelPermalink: {{ .RelPermalink }}|SectionsPath: {{ .SectionsPath }}|SectionsEntries: {{ .SectionsEntries }}|Len: {{ len .SectionsEntries }}|
   392  `
   393  
   394  	b := Test(t, files)
   395  
   396  	b.AssertFileContent("public/myfirstsection/p1/index.html", "RelPermalink: /myfirstsection/p1/|SectionsPath: /myfirstsection|SectionsEntries: [myfirstsection]|Len: 1")
   397  	b.AssertFileContent("public/a/b/c/index.html", "RelPermalink: /a/b/c/|SectionsPath: /a/b/c|SectionsEntries: [a b c]|Len: 3")
   398  	b.AssertFileContent("public/a/b/c/mybundle/index.html", "Kind: page|RelPermalink: /a/b/c/mybundle/|SectionsPath: /a/b/c|SectionsEntries: [a b c]|Len: 3")
   399  	b.AssertFileContent("public/index.html", "Kind: home|RelPermalink: /|SectionsPath: /|SectionsEntries: []|Len: 0")
   400  }