github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/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  		}},
   185  		{"l1", func(c *qt.C, p page.Page) {
   186  			c.Assert(p.Title(), qt.Equals, "L1s")
   187  			c.Assert(len(p.Pages()), qt.Equals, 4) // 2 pages + 2 sections
   188  			c.Assert(p.Parent().IsHome(), qt.Equals, true)
   189  			c.Assert(len(p.Sections()), qt.Equals, 2)
   190  		}},
   191  		{"l1,l2", func(c *qt.C, p page.Page) {
   192  			c.Assert(p.Title(), qt.Equals, "T2_-1")
   193  			c.Assert(len(p.Pages()), qt.Equals, 4) // 3 pages + 1 section
   194  			c.Assert(p.Pages()[0].Parent(), qt.Equals, p)
   195  			c.Assert(p.Parent().Title(), qt.Equals, "L1s")
   196  			c.Assert(p.RelPermalink(), qt.Equals, "/l1/l2/")
   197  			c.Assert(len(p.Sections()), qt.Equals, 1)
   198  
   199  			for _, child := range p.Pages() {
   200  				if child.IsSection() {
   201  					c.Assert(child.CurrentSection(), qt.Equals, child)
   202  					continue
   203  				}
   204  
   205  				c.Assert(child.CurrentSection(), qt.Equals, p)
   206  				active, err := child.InSection(p)
   207  				c.Assert(err, qt.IsNil)
   208  
   209  				c.Assert(active, qt.Equals, true)
   210  				active, err = p.InSection(child)
   211  				c.Assert(err, qt.IsNil)
   212  				c.Assert(active, qt.Equals, true)
   213  				active, err = p.InSection(getPage(p, "/"))
   214  				c.Assert(err, qt.IsNil)
   215  				c.Assert(active, qt.Equals, false)
   216  
   217  				isAncestor, err := p.IsAncestor(child)
   218  				c.Assert(err, qt.IsNil)
   219  				c.Assert(isAncestor, qt.Equals, true)
   220  				isAncestor, err = child.IsAncestor(p)
   221  				c.Assert(err, qt.IsNil)
   222  				c.Assert(isAncestor, qt.Equals, false)
   223  
   224  				isDescendant, err := p.IsDescendant(child)
   225  				c.Assert(err, qt.IsNil)
   226  				c.Assert(isDescendant, qt.Equals, false)
   227  				isDescendant, err = child.IsDescendant(p)
   228  				c.Assert(err, qt.IsNil)
   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  		}},
   241  		{"l1,l2,l3", func(c *qt.C, p page.Page) {
   242  			nilp, _ := p.GetPage("this/does/not/exist")
   243  
   244  			c.Assert(p.Title(), qt.Equals, "T3_-1")
   245  			c.Assert(len(p.Pages()), qt.Equals, 2)
   246  			c.Assert(p.Parent().Title(), qt.Equals, "T2_-1")
   247  			c.Assert(len(p.Sections()), qt.Equals, 0)
   248  
   249  			l1 := getPage(p, "/l1")
   250  			isDescendant, err := l1.IsDescendant(p)
   251  			c.Assert(err, qt.IsNil)
   252  			c.Assert(isDescendant, qt.Equals, false)
   253  			isDescendant, err = l1.IsDescendant(nil)
   254  			c.Assert(err, qt.IsNil)
   255  			c.Assert(isDescendant, qt.Equals, false)
   256  			isDescendant, err = nilp.IsDescendant(p)
   257  			c.Assert(err, qt.IsNil)
   258  			c.Assert(isDescendant, qt.Equals, false)
   259  			isDescendant, err = p.IsDescendant(l1)
   260  			c.Assert(err, qt.IsNil)
   261  			c.Assert(isDescendant, qt.Equals, true)
   262  
   263  			isAncestor, err := l1.IsAncestor(p)
   264  			c.Assert(err, qt.IsNil)
   265  			c.Assert(isAncestor, qt.Equals, true)
   266  			isAncestor, err = p.IsAncestor(l1)
   267  			c.Assert(err, qt.IsNil)
   268  			c.Assert(isAncestor, qt.Equals, false)
   269  			c.Assert(p.FirstSection(), qt.Equals, l1)
   270  			isAncestor, err = p.IsAncestor(nil)
   271  			c.Assert(err, qt.IsNil)
   272  			c.Assert(isAncestor, qt.Equals, false)
   273  			isAncestor, err = nilp.IsAncestor(l1)
   274  			c.Assert(err, qt.IsNil)
   275  			c.Assert(isAncestor, qt.Equals, false)
   276  		}},
   277  		{"perm a,link", func(c *qt.C, p page.Page) {
   278  			c.Assert(p.Title(), qt.Equals, "T9_-1")
   279  			c.Assert(p.RelPermalink(), qt.Equals, "/perm-a/link/")
   280  			c.Assert(len(p.Pages()), qt.Equals, 4)
   281  			first := p.Pages()[0]
   282  			c.Assert(first.RelPermalink(), qt.Equals, "/perm-a/link/t1_1/")
   283  			th.assertFileContent("public/perm-a/link/t1_1/index.html", "Single|T1_1")
   284  
   285  			last := p.Pages()[3]
   286  			c.Assert(last.RelPermalink(), qt.Equals, "/perm-a/link/t1_5/")
   287  		}},
   288  	}
   289  
   290  	home := s.getPage(page.KindHome)
   291  
   292  	for _, test := range tests {
   293  		test := test
   294  		t.Run(fmt.Sprintf("sections %s", test.sections), func(t *testing.T) {
   295  			t.Parallel()
   296  			c := qt.New(t)
   297  			sections := strings.Split(test.sections, ",")
   298  			p := s.getPage(page.KindSection, sections...)
   299  			c.Assert(p, qt.Not(qt.IsNil), qt.Commentf(fmt.Sprint(sections)))
   300  
   301  			if p.Pages() != nil {
   302  				c.Assert(p.Data().(page.Data).Pages(), deepEqualsPages, p.Pages())
   303  			}
   304  			c.Assert(p.Parent(), qt.Not(qt.IsNil))
   305  			test.verify(c, p)
   306  		})
   307  	}
   308  
   309  	c.Assert(home, qt.Not(qt.IsNil))
   310  
   311  	c.Assert(len(home.Sections()), qt.Equals, 9)
   312  	c.Assert(s.Info.Sections(), deepEqualsPages, home.Sections())
   313  
   314  	rootPage := s.getPage(page.KindPage, "mypage.md")
   315  	c.Assert(rootPage, qt.Not(qt.IsNil))
   316  	c.Assert(rootPage.Parent().IsHome(), qt.Equals, true)
   317  	// https://github.com/gohugoio/hugo/issues/6365
   318  	c.Assert(rootPage.Sections(), qt.HasLen, 0)
   319  
   320  	// Add a odd test for this as this looks a little bit off, but I'm not in the mood
   321  	// to think too hard a out this right now. It works, but people will have to spell
   322  	// out the directory name as is.
   323  	// If we later decide to do something about this, we will have to do some normalization in
   324  	// getPage.
   325  	// TODO(bep)
   326  	sectionWithSpace := s.getPage(page.KindSection, "Spaces in Section")
   327  	c.Assert(sectionWithSpace, qt.Not(qt.IsNil))
   328  	c.Assert(sectionWithSpace.RelPermalink(), qt.Equals, "/spaces-in-section/")
   329  
   330  	th.assertFileContent("public/l1/l2/page/2/index.html", "L1/l2-IsActive: true", "PAG|T2_3|true")
   331  }
   332  
   333  func TestNextInSectionNested(t *testing.T) {
   334  	t.Parallel()
   335  
   336  	pageContent := `---
   337  title: "The Page"
   338  weight: %d
   339  ---
   340  Some content.
   341  `
   342  	createPageContent := func(weight int) string {
   343  		return fmt.Sprintf(pageContent, weight)
   344  	}
   345  
   346  	b := newTestSitesBuilder(t)
   347  	b.WithSimpleConfigFile()
   348  	b.WithTemplates("_default/single.html", `
   349  Prev: {{ with .PrevInSection }}{{ .RelPermalink }}{{ end }}|
   350  Next: {{ with .NextInSection }}{{ .RelPermalink }}{{ end }}|
   351  `)
   352  
   353  	b.WithContent("blog/page1.md", createPageContent(1))
   354  	b.WithContent("blog/page2.md", createPageContent(2))
   355  	b.WithContent("blog/cool/_index.md", createPageContent(1))
   356  	b.WithContent("blog/cool/cool1.md", createPageContent(1))
   357  	b.WithContent("blog/cool/cool2.md", createPageContent(2))
   358  	b.WithContent("root1.md", createPageContent(1))
   359  	b.WithContent("root2.md", createPageContent(2))
   360  
   361  	b.Build(BuildCfg{})
   362  
   363  	b.AssertFileContent("public/root1/index.html",
   364  		"Prev: /root2/|", "Next: |")
   365  	b.AssertFileContent("public/root2/index.html",
   366  		"Prev: |", "Next: /root1/|")
   367  	b.AssertFileContent("public/blog/page1/index.html",
   368  		"Prev: /blog/page2/|", "Next: |")
   369  	b.AssertFileContent("public/blog/page2/index.html",
   370  		"Prev: |", "Next: /blog/page1/|")
   371  	b.AssertFileContent("public/blog/cool/cool1/index.html",
   372  		"Prev: /blog/cool/cool2/|", "Next: |")
   373  	b.AssertFileContent("public/blog/cool/cool2/index.html",
   374  		"Prev: |", "Next: /blog/cool/cool1/|")
   375  }