github.com/kristoff-it/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 }