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