github.com/olliephillips/hugo@v0.42.2/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 "section" "l1" "l2") }} 108 <html>List|{{ .Title }}|L1/l2-IsActive: {{ .InSection $sect }} 109 {{ range .Paginator.Pages }} 110 PAG|{{ .Title }}|{{ $sect.InSection . }} 111 {{ end }} 112 </html>`) 113 114 cfg.Set("paginate", 2) 115 116 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 117 118 require.Len(t, s.RegularPages, 21) 119 120 tests := []struct { 121 sections string 122 verify func(p *Page) 123 }{ 124 {"elsewhere", func(p *Page) { 125 assert.Len(p.Pages, 1) 126 for _, p := range p.Pages { 127 assert.Equal([]string{"elsewhere"}, p.sections) 128 } 129 }}, 130 {"post", func(p *Page) { 131 assert.Len(p.Pages, 2) 132 for _, p := range p.Pages { 133 assert.Equal("post", p.Section()) 134 } 135 }}, 136 {"empty1", func(p *Page) { 137 // > b,c 138 assert.NotNil(p.s.getPage(KindSection, "empty1", "b")) 139 assert.NotNil(p.s.getPage(KindSection, "empty1", "b", "c")) 140 141 }}, 142 {"empty2", func(p *Page) { 143 // > b,c,d where b and d have content files. 144 b := p.s.getPage(KindSection, "empty2", "b") 145 assert.NotNil(b) 146 assert.Equal("T40_-1", b.title) 147 c := p.s.getPage(KindSection, "empty2", "b", "c") 148 assert.NotNil(c) 149 assert.Equal("Cs", c.title) 150 d := p.s.getPage(KindSection, "empty2", "b", "c", "d") 151 assert.NotNil(d) 152 assert.Equal("T41_-1", d.title) 153 154 assert.False(c.Eq(d)) 155 assert.True(c.Eq(c)) 156 assert.False(c.Eq("asdf")) 157 158 }}, 159 {"empty3", func(p *Page) { 160 // b,c,d with regular page in b 161 b := p.s.getPage(KindSection, "empty3", "b") 162 assert.NotNil(b) 163 assert.Len(b.Pages, 1) 164 assert.Equal("empty3.md", b.Pages[0].File.LogicalName()) 165 166 }}, 167 {"top", func(p *Page) { 168 assert.Equal("Tops", p.title) 169 assert.Len(p.Pages, 2) 170 assert.Equal("mypage2.md", p.Pages[0].LogicalName()) 171 assert.Equal("mypage3.md", p.Pages[1].LogicalName()) 172 home := p.Parent() 173 assert.True(home.IsHome()) 174 assert.Len(p.Sections(), 0) 175 assert.Equal(home, home.CurrentSection()) 176 active, err := home.InSection(home) 177 assert.NoError(err) 178 assert.True(active) 179 }}, 180 {"l1", func(p *Page) { 181 assert.Equal("L1s", p.title) 182 assert.Len(p.Pages, 2) 183 assert.True(p.Parent().IsHome()) 184 assert.Len(p.Sections(), 2) 185 }}, 186 {"l1,l2", func(p *Page) { 187 assert.Equal("T2_-1", p.title) 188 assert.Len(p.Pages, 3) 189 assert.Equal(p, p.Pages[0].Parent()) 190 assert.Equal("L1s", p.Parent().title) 191 assert.Equal("/l1/l2/", p.URLPath.URL) 192 assert.Equal("/l1/l2/", p.RelPermalink()) 193 assert.Len(p.Sections(), 1) 194 195 for _, child := range p.Pages { 196 assert.Equal(p, child.CurrentSection()) 197 active, err := child.InSection(p) 198 assert.NoError(err) 199 assert.True(active) 200 active, err = p.InSection(child) 201 assert.NoError(err) 202 assert.True(active) 203 active, err = p.InSection(p.s.getPage(KindHome)) 204 assert.NoError(err) 205 assert.False(active) 206 207 isAncestor, err := p.IsAncestor(child) 208 assert.NoError(err) 209 assert.True(isAncestor) 210 isAncestor, err = child.IsAncestor(p) 211 assert.NoError(err) 212 assert.False(isAncestor) 213 214 isDescendant, err := p.IsDescendant(child) 215 assert.NoError(err) 216 assert.False(isDescendant) 217 isDescendant, err = child.IsDescendant(p) 218 assert.NoError(err) 219 assert.True(isDescendant) 220 } 221 222 assert.Equal(p, p.CurrentSection()) 223 224 }}, 225 {"l1,l2_2", func(p *Page) { 226 assert.Equal("T22_-1", p.title) 227 assert.Len(p.Pages, 2) 228 assert.Equal(filepath.FromSlash("l1/l2_2/page_2_2_1.md"), p.Pages[0].Path()) 229 assert.Equal("L1s", p.Parent().title) 230 assert.Len(p.Sections(), 0) 231 }}, 232 {"l1,l2,l3", func(p *Page) { 233 assert.Equal("T3_-1", p.title) 234 assert.Len(p.Pages, 2) 235 assert.Equal("T2_-1", p.Parent().title) 236 assert.Len(p.Sections(), 0) 237 238 l1 := p.s.getPage(KindSection, "l1") 239 isDescendant, err := l1.IsDescendant(p) 240 assert.NoError(err) 241 assert.False(isDescendant) 242 isDescendant, err = p.IsDescendant(l1) 243 assert.NoError(err) 244 assert.True(isDescendant) 245 246 isAncestor, err := l1.IsAncestor(p) 247 assert.NoError(err) 248 assert.True(isAncestor) 249 isAncestor, err = p.IsAncestor(l1) 250 assert.NoError(err) 251 assert.False(isAncestor) 252 253 }}, 254 {"perm a,link", func(p *Page) { 255 assert.Equal("T9_-1", p.title) 256 assert.Equal("/perm-a/link/", p.RelPermalink()) 257 assert.Len(p.Pages, 4) 258 first := p.Pages[0] 259 assert.Equal("/perm-a/link/t1_1/", first.RelPermalink()) 260 th.assertFileContent("public/perm-a/link/t1_1/index.html", "Single|T1_1") 261 262 last := p.Pages[3] 263 assert.Equal("/perm-a/link/t1_5/", last.RelPermalink()) 264 265 }}, 266 } 267 268 home := s.getPage(KindHome) 269 270 for _, test := range tests { 271 sections := strings.Split(test.sections, ",") 272 p := s.getPage(KindSection, sections...) 273 assert.NotNil(p, fmt.Sprint(sections)) 274 275 if p.Pages != nil { 276 assert.Equal(p.Pages, p.Data["Pages"]) 277 } 278 assert.NotNil(p.Parent(), fmt.Sprintf("Parent nil: %q", test.sections)) 279 test.verify(p) 280 } 281 282 assert.NotNil(home) 283 284 assert.Len(home.Sections(), 9) 285 assert.Equal(home.Sections(), s.Info.Sections()) 286 287 rootPage := s.getPage(KindPage, "mypage.md") 288 assert.NotNil(rootPage) 289 assert.True(rootPage.Parent().IsHome()) 290 291 // Add a odd test for this as this looks a little bit off, but I'm not in the mood 292 // to think too hard a out this right now. It works, but people will have to spell 293 // out the directory name as is. 294 // If we later decide to do something about this, we will have to do some normalization in 295 // getPage. 296 // TODO(bep) 297 sectionWithSpace := s.getPage(KindSection, "Spaces in Section") 298 require.NotNil(t, sectionWithSpace) 299 require.Equal(t, "/spaces-in-section/", sectionWithSpace.RelPermalink()) 300 301 th.assertFileContent("public/l1/l2/page/2/index.html", "L1/l2-IsActive: true", "PAG|T2_3|true") 302 303 }