github.com/SDLMoe/hugo@v0.47.1/hugolib/site_sections_test.go (about)

     1  // Copyright 2017-present 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  	"github.com/gohugoio/hugo/deps"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func TestNestedSections(t *testing.T) {
    27  	t.Parallel()
    28  
    29  	var (
    30  		assert  = require.New(t)
    31  		cfg, fs = newTestCfg()
    32  		th      = testHelper{cfg, fs, t}
    33  	)
    34  
    35  	cfg.Set("permalinks", map[string]string{
    36  		"perm a": ":sections/:title",
    37  	})
    38  
    39  	pageTemplate := `---
    40  title: T%d_%d
    41  ---
    42  Content
    43  `
    44  
    45  	// Home page
    46  	writeSource(t, fs, filepath.Join("content", "_index.md"), fmt.Sprintf(pageTemplate, -1, -1))
    47  
    48  	// Top level content page
    49  	writeSource(t, fs, filepath.Join("content", "mypage.md"), fmt.Sprintf(pageTemplate, 1234, 5))
    50  
    51  	// Top level section without index content page
    52  	writeSource(t, fs, filepath.Join("content", "top", "mypage2.md"), fmt.Sprintf(pageTemplate, 12345, 6))
    53  	// Just a page in a subfolder, i.e. not a section.
    54  	writeSource(t, fs, filepath.Join("content", "top", "folder", "mypage3.md"), fmt.Sprintf(pageTemplate, 12345, 67))
    55  
    56  	for level1 := 1; level1 < 3; level1++ {
    57  		writeSource(t, fs, filepath.Join("content", "l1", fmt.Sprintf("page_1_%d.md", level1)),
    58  			fmt.Sprintf(pageTemplate, 1, level1))
    59  	}
    60  
    61  	// Issue #3586
    62  	writeSource(t, fs, filepath.Join("content", "post", "0000.md"), fmt.Sprintf(pageTemplate, 1, 2))
    63  	writeSource(t, fs, filepath.Join("content", "post", "0000", "0001.md"), fmt.Sprintf(pageTemplate, 1, 3))
    64  	writeSource(t, fs, filepath.Join("content", "elsewhere", "0003.md"), fmt.Sprintf(pageTemplate, 1, 4))
    65  
    66  	// Empty nested section, i.e. no regular content pages.
    67  	writeSource(t, fs, filepath.Join("content", "empty1", "b", "c", "_index.md"), fmt.Sprintf(pageTemplate, 33, -1))
    68  	// Index content file a the end and in the middle.
    69  	writeSource(t, fs, filepath.Join("content", "empty2", "b", "_index.md"), fmt.Sprintf(pageTemplate, 40, -1))
    70  	writeSource(t, fs, filepath.Join("content", "empty2", "b", "c", "d", "_index.md"), fmt.Sprintf(pageTemplate, 41, -1))
    71  
    72  	// Empty with content file in the middle.
    73  	writeSource(t, fs, filepath.Join("content", "empty3", "b", "c", "d", "_index.md"), fmt.Sprintf(pageTemplate, 41, -1))
    74  	writeSource(t, fs, filepath.Join("content", "empty3", "b", "empty3.md"), fmt.Sprintf(pageTemplate, 3, -1))
    75  
    76  	// Section with permalink config
    77  	writeSource(t, fs, filepath.Join("content", "perm a", "link", "_index.md"), fmt.Sprintf(pageTemplate, 9, -1))
    78  	for i := 1; i < 4; i++ {
    79  		writeSource(t, fs, filepath.Join("content", "perm a", "link", fmt.Sprintf("page_%d.md", i)),
    80  			fmt.Sprintf(pageTemplate, 1, i))
    81  	}
    82  	writeSource(t, fs, filepath.Join("content", "perm a", "link", "regular", fmt.Sprintf("page_%d.md", 5)),
    83  		fmt.Sprintf(pageTemplate, 1, 5))
    84  
    85  	writeSource(t, fs, filepath.Join("content", "l1", "l2", "_index.md"), fmt.Sprintf(pageTemplate, 2, -1))
    86  	writeSource(t, fs, filepath.Join("content", "l1", "l2_2", "_index.md"), fmt.Sprintf(pageTemplate, 22, -1))
    87  	writeSource(t, fs, filepath.Join("content", "l1", "l2", "l3", "_index.md"), fmt.Sprintf(pageTemplate, 3, -1))
    88  
    89  	for level2 := 1; level2 < 4; level2++ {
    90  		writeSource(t, fs, filepath.Join("content", "l1", "l2", fmt.Sprintf("page_2_%d.md", level2)),
    91  			fmt.Sprintf(pageTemplate, 2, level2))
    92  	}
    93  	for level2 := 1; level2 < 3; level2++ {
    94  		writeSource(t, fs, filepath.Join("content", "l1", "l2_2", fmt.Sprintf("page_2_2_%d.md", level2)),
    95  			fmt.Sprintf(pageTemplate, 2, level2))
    96  	}
    97  	for level3 := 1; level3 < 3; level3++ {
    98  		writeSource(t, fs, filepath.Join("content", "l1", "l2", "l3", fmt.Sprintf("page_3_%d.md", level3)),
    99  			fmt.Sprintf(pageTemplate, 3, level3))
   100  	}
   101  
   102  	writeSource(t, fs, filepath.Join("content", "Spaces in Section", "page100.md"), fmt.Sprintf(pageTemplate, 10, 0))
   103  
   104  	writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), "<html>Single|{{ .Title }}</html>")
   105  	writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"),
   106  		`
   107  {{ $sect := (.Site.GetPage "l1/l2") }}
   108  <html>List|{{ .Title }}|L1/l2-IsActive: {{ .InSection $sect }}
   109  {{ range .Paginator.Pages }}
   110  PAG|{{ .Title }}|{{ $sect.InSection . }}
   111  {{ end }}
   112  {{/* https://github.com/gohugoio/hugo/issues/4989 */}}
   113  {{ $sections := (.Site.GetPage "section" .Section).Sections.ByWeight }}
   114  </html>`)
   115  
   116  	cfg.Set("paginate", 2)
   117  
   118  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   119  
   120  	require.Len(t, s.RegularPages, 21)
   121  
   122  	tests := []struct {
   123  		sections string
   124  		verify   func(p *Page)
   125  	}{
   126  		{"elsewhere", func(p *Page) {
   127  			assert.Len(p.Pages, 1)
   128  			for _, p := range p.Pages {
   129  				assert.Equal([]string{"elsewhere"}, p.sections)
   130  			}
   131  		}},
   132  		{"post", func(p *Page) {
   133  			assert.Len(p.Pages, 2)
   134  			for _, p := range p.Pages {
   135  				assert.Equal("post", p.Section())
   136  			}
   137  		}},
   138  		{"empty1", func(p *Page) {
   139  			// > b,c
   140  			assert.NotNil(p.s.getPage(KindSection, "empty1", "b"))
   141  			assert.NotNil(p.s.getPage(KindSection, "empty1", "b", "c"))
   142  
   143  		}},
   144  		{"empty2", func(p *Page) {
   145  			// > b,c,d where b and d have content files.
   146  			b := p.s.getPage(KindSection, "empty2", "b")
   147  			assert.NotNil(b)
   148  			assert.Equal("T40_-1", b.title)
   149  			c := p.s.getPage(KindSection, "empty2", "b", "c")
   150  			assert.NotNil(c)
   151  			assert.Equal("Cs", c.title)
   152  			d := p.s.getPage(KindSection, "empty2", "b", "c", "d")
   153  			assert.NotNil(d)
   154  			assert.Equal("T41_-1", d.title)
   155  
   156  			assert.False(c.Eq(d))
   157  			assert.True(c.Eq(c))
   158  			assert.False(c.Eq("asdf"))
   159  
   160  		}},
   161  		{"empty3", func(p *Page) {
   162  			// b,c,d with regular page in b
   163  			b := p.s.getPage(KindSection, "empty3", "b")
   164  			assert.NotNil(b)
   165  			assert.Len(b.Pages, 1)
   166  			assert.Equal("empty3.md", b.Pages[0].File.LogicalName())
   167  
   168  		}},
   169  		{"top", func(p *Page) {
   170  			assert.Equal("Tops", p.title)
   171  			assert.Len(p.Pages, 2)
   172  			assert.Equal("mypage2.md", p.Pages[0].LogicalName())
   173  			assert.Equal("mypage3.md", p.Pages[1].LogicalName())
   174  			home := p.Parent()
   175  			assert.True(home.IsHome())
   176  			assert.Len(p.Sections(), 0)
   177  			assert.Equal(home, home.CurrentSection())
   178  			active, err := home.InSection(home)
   179  			assert.NoError(err)
   180  			assert.True(active)
   181  			assert.Equal(p, p.FirstSection())
   182  		}},
   183  		{"l1", func(p *Page) {
   184  			assert.Equal("L1s", p.title)
   185  			assert.Len(p.Pages, 2)
   186  			assert.True(p.Parent().IsHome())
   187  			assert.Len(p.Sections(), 2)
   188  		}},
   189  		{"l1,l2", func(p *Page) {
   190  			assert.Equal("T2_-1", p.title)
   191  			assert.Len(p.Pages, 3)
   192  			assert.Equal(p, p.Pages[0].Parent())
   193  			assert.Equal("L1s", p.Parent().title)
   194  			assert.Equal("/l1/l2/", p.URLPath.URL)
   195  			assert.Equal("/l1/l2/", p.RelPermalink())
   196  			assert.Len(p.Sections(), 1)
   197  
   198  			for _, child := range p.Pages {
   199  				assert.Equal(p, child.CurrentSection())
   200  				active, err := child.InSection(p)
   201  				assert.NoError(err)
   202  				assert.True(active)
   203  				active, err = p.InSection(child)
   204  				assert.NoError(err)
   205  				assert.True(active)
   206  				active, err = p.InSection(p.s.getPage(KindHome))
   207  				assert.NoError(err)
   208  				assert.False(active)
   209  
   210  				isAncestor, err := p.IsAncestor(child)
   211  				assert.NoError(err)
   212  				assert.True(isAncestor)
   213  				isAncestor, err = child.IsAncestor(p)
   214  				assert.NoError(err)
   215  				assert.False(isAncestor)
   216  
   217  				isDescendant, err := p.IsDescendant(child)
   218  				assert.NoError(err)
   219  				assert.False(isDescendant)
   220  				isDescendant, err = child.IsDescendant(p)
   221  				assert.NoError(err)
   222  				assert.True(isDescendant)
   223  			}
   224  
   225  			assert.Equal(p, p.CurrentSection())
   226  
   227  		}},
   228  		{"l1,l2_2", func(p *Page) {
   229  			assert.Equal("T22_-1", p.title)
   230  			assert.Len(p.Pages, 2)
   231  			assert.Equal(filepath.FromSlash("l1/l2_2/page_2_2_1.md"), p.Pages[0].Path())
   232  			assert.Equal("L1s", p.Parent().title)
   233  			assert.Len(p.Sections(), 0)
   234  		}},
   235  		{"l1,l2,l3", func(p *Page) {
   236  			assert.Equal("T3_-1", p.title)
   237  			assert.Len(p.Pages, 2)
   238  			assert.Equal("T2_-1", p.Parent().title)
   239  			assert.Len(p.Sections(), 0)
   240  
   241  			l1 := p.s.getPage(KindSection, "l1")
   242  			isDescendant, err := l1.IsDescendant(p)
   243  			assert.NoError(err)
   244  			assert.False(isDescendant)
   245  			isDescendant, err = p.IsDescendant(l1)
   246  			assert.NoError(err)
   247  			assert.True(isDescendant)
   248  
   249  			isAncestor, err := l1.IsAncestor(p)
   250  			assert.NoError(err)
   251  			assert.True(isAncestor)
   252  			isAncestor, err = p.IsAncestor(l1)
   253  			assert.NoError(err)
   254  			assert.False(isAncestor)
   255  			assert.Equal(l1, p.FirstSection())
   256  
   257  		}},
   258  		{"perm a,link", func(p *Page) {
   259  			assert.Equal("T9_-1", p.title)
   260  			assert.Equal("/perm-a/link/", p.RelPermalink())
   261  			assert.Len(p.Pages, 4)
   262  			first := p.Pages[0]
   263  			assert.Equal("/perm-a/link/t1_1/", first.RelPermalink())
   264  			th.assertFileContent("public/perm-a/link/t1_1/index.html", "Single|T1_1")
   265  
   266  			last := p.Pages[3]
   267  			assert.Equal("/perm-a/link/t1_5/", last.RelPermalink())
   268  
   269  		}},
   270  	}
   271  
   272  	home := s.getPage(KindHome)
   273  
   274  	for _, test := range tests {
   275  		sections := strings.Split(test.sections, ",")
   276  		p := s.getPage(KindSection, sections...)
   277  		assert.NotNil(p, fmt.Sprint(sections))
   278  
   279  		if p.Pages != nil {
   280  			assert.Equal(p.Pages, p.data["Pages"])
   281  		}
   282  		assert.NotNil(p.Parent(), fmt.Sprintf("Parent nil: %q", test.sections))
   283  		test.verify(p)
   284  	}
   285  
   286  	assert.NotNil(home)
   287  
   288  	assert.Len(home.Sections(), 9)
   289  	assert.Equal(home.Sections(), s.Info.Sections())
   290  
   291  	rootPage := s.getPage(KindPage, "mypage.md")
   292  	assert.NotNil(rootPage)
   293  	assert.True(rootPage.Parent().IsHome())
   294  
   295  	// Add a odd test for this as this looks a little bit off, but I'm not in the mood
   296  	// to think too hard a out this right now. It works, but people will have to spell
   297  	// out the directory name as is.
   298  	// If we later decide to do something about this, we will have to do some normalization in
   299  	// getPage.
   300  	// TODO(bep)
   301  	sectionWithSpace := s.getPage(KindSection, "Spaces in Section")
   302  	require.NotNil(t, sectionWithSpace)
   303  	require.Equal(t, "/spaces-in-section/", sectionWithSpace.RelPermalink())
   304  
   305  	th.assertFileContent("public/l1/l2/page/2/index.html", "L1/l2-IsActive: true", "PAG|T2_3|true")
   306  
   307  }