github.com/rezahousseini/hugo@v0.32.3/hugolib/page_bundler_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 "io/ioutil" 18 "os" 19 "runtime" 20 "strings" 21 "testing" 22 23 "io" 24 25 "github.com/spf13/afero" 26 27 "github.com/gohugoio/hugo/media" 28 29 "path/filepath" 30 31 "fmt" 32 33 "github.com/gohugoio/hugo/deps" 34 "github.com/gohugoio/hugo/hugofs" 35 "github.com/gohugoio/hugo/resource" 36 "github.com/spf13/viper" 37 38 "github.com/stretchr/testify/require" 39 ) 40 41 func TestPageBundlerSite(t *testing.T) { 42 t.Parallel() 43 44 for _, ugly := range []bool{false, true} { 45 t.Run(fmt.Sprintf("ugly=%t", ugly), 46 func(t *testing.T) { 47 48 assert := require.New(t) 49 cfg, fs := newTestBundleSources(t) 50 51 cfg.Set("permalinks", map[string]string{ 52 "a": ":sections/:filename", 53 "b": ":year/:slug/", 54 }) 55 56 cfg.Set("outputFormats", map[string]interface{}{ 57 "CUSTOMO": map[string]interface{}{ 58 "mediaType": media.HTMLType, 59 "baseName": "cindex", 60 "path": "cpath", 61 }, 62 }) 63 64 cfg.Set("outputs", map[string]interface{}{ 65 "home": []string{"HTML", "CUSTOMO"}, 66 "page": []string{"HTML", "CUSTOMO"}, 67 "section": []string{"HTML", "CUSTOMO"}, 68 }) 69 70 cfg.Set("uglyURLs", ugly) 71 72 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{}) 73 74 th := testHelper{s.Cfg, s.Fs, t} 75 76 // Singles (2), Below home (1), Bundle (1) 77 assert.Len(s.RegularPages, 6) 78 79 singlePage := s.getPage(KindPage, "a/1.md") 80 81 assert.NotNil(singlePage) 82 assert.Contains(singlePage.Content, "TheContent") 83 84 if ugly { 85 assert.Equal("/a/1.html", singlePage.RelPermalink()) 86 th.assertFileContent(filepath.FromSlash("/work/public/a/1.html"), "TheContent") 87 88 } else { 89 assert.Equal("/a/1/", singlePage.RelPermalink()) 90 th.assertFileContent(filepath.FromSlash("/work/public/a/1/index.html"), "TheContent") 91 } 92 93 th.assertFileContent(filepath.FromSlash("/work/public/images/hugo-logo.png"), "content") 94 95 // This should be just copied to destination. 96 th.assertFileContent(filepath.FromSlash("/work/public/assets/pic1.png"), "content") 97 98 leafBundle1 := s.getPage(KindPage, "b/index.md") 99 assert.NotNil(leafBundle1) 100 leafBundle2 := s.getPage(KindPage, "a/b/index.md") 101 assert.NotNil(leafBundle2) 102 103 pageResources := leafBundle1.Resources.ByType(pageResourceType) 104 assert.Len(pageResources, 2) 105 firstPage := pageResources[0].(*Page) 106 secondPage := pageResources[1].(*Page) 107 assert.Equal(filepath.FromSlash("b/1.md"), firstPage.pathOrTitle(), secondPage.pathOrTitle()) 108 assert.Contains(firstPage.Content, "TheContent") 109 assert.Len(leafBundle1.Resources, 6) // 2 pages 3 images 1 custom mime type 110 111 imageResources := leafBundle1.Resources.ByType("image") 112 assert.Len(imageResources, 3) 113 image := imageResources[0] 114 115 altFormat := leafBundle1.OutputFormats().Get("CUSTOMO") 116 assert.NotNil(altFormat) 117 118 assert.Equal(filepath.FromSlash("/work/base/b/c/logo.png"), image.(resource.Source).AbsSourceFilename()) 119 assert.Equal("https://example.com/2017/pageslug/c/logo.png", image.Permalink()) 120 th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/c/logo.png"), "content") 121 th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/c/logo.png"), "content") 122 123 // Custom media type defined in site config. 124 assert.Len(leafBundle1.Resources.ByType("bepsays"), 1) 125 126 if ugly { 127 assert.Equal("/2017/pageslug.html", leafBundle1.RelPermalink()) 128 th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug.html"), 129 "TheContent", 130 "Sunset RelPermalink: /2017/pageslug/sunset1.jpg", 131 "Thumb Width: 123", 132 "Short Sunset RelPermalink: /2017/pageslug/sunset2.jpg", 133 "Short Thumb Width: 56", 134 ) 135 th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug.html"), "TheContent") 136 137 assert.Equal("/a/b.html", leafBundle2.RelPermalink()) 138 139 } else { 140 assert.Equal("/2017/pageslug/", leafBundle1.RelPermalink()) 141 th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "TheContent") 142 th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/cindex.html"), "TheContent") 143 144 assert.Equal("/a/b/", leafBundle2.RelPermalink()) 145 } 146 147 }) 148 } 149 150 } 151 152 func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) { 153 assert := require.New(t) 154 cfg, fs, workDir := newTestBundleSymbolicSources(t) 155 156 s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, Logger: newWarningLogger()}, BuildCfg{}) 157 158 th := testHelper{s.Cfg, s.Fs, t} 159 160 assert.Equal(7, len(s.RegularPages)) 161 a1Bundle := s.getPage(KindPage, "symbolic2/a1/index.md") 162 assert.NotNil(a1Bundle) 163 assert.Equal(2, len(a1Bundle.Resources)) 164 assert.Equal(1, len(a1Bundle.Resources.ByType(pageResourceType))) 165 166 th.assertFileContent(filepath.FromSlash(workDir+"/public/a/page/index.html"), "TheContent") 167 th.assertFileContent(filepath.FromSlash(workDir+"/public/symbolic1/s1/index.html"), "TheContent") 168 th.assertFileContent(filepath.FromSlash(workDir+"/public/symbolic2/a1/index.html"), "TheContent") 169 170 } 171 172 func newTestBundleSources(t *testing.T) (*viper.Viper, *hugofs.Fs) { 173 cfg, fs := newTestCfg() 174 assert := require.New(t) 175 176 workDir := "/work" 177 cfg.Set("workingDir", workDir) 178 cfg.Set("contentDir", "base") 179 cfg.Set("baseURL", "https://example.com") 180 cfg.Set("mediaTypes", map[string]interface{}{ 181 "text/bepsays": map[string]interface{}{ 182 "suffix": "bep", 183 }, 184 }) 185 186 pageContent := `--- 187 title: "Bundle Galore" 188 slug: pageslug 189 date: 2017-10-09 190 --- 191 192 TheContent. 193 ` 194 195 pageWithImageShortcodeContent := `--- 196 title: "Bundle Galore" 197 slug: pageslug 198 date: 2017-10-09 199 --- 200 201 TheContent. 202 203 {{< myShort >}} 204 ` 205 206 pageContentNoSlug := `--- 207 title: "Bundle Galore #2" 208 date: 2017-10-09 209 --- 210 211 TheContent. 212 ` 213 214 singleLayout := ` 215 Title: {{ .Title }} 216 Content: {{ .Content }} 217 {{ $sunset := .Resources.GetByPrefix "sunset1" }} 218 {{ with $sunset }} 219 Sunset RelPermalink: {{ .RelPermalink }} 220 {{ $thumb := .Fill "123x123" }} 221 Thumb Width: {{ $thumb.Width }} 222 {{ end }} 223 224 ` 225 226 myShort := ` 227 {{ $sunset := .Page.Resources.GetByPrefix "sunset2" }} 228 {{ with $sunset }} 229 Short Sunset RelPermalink: {{ .RelPermalink }} 230 {{ $thumb := .Fill "56x56" }} 231 Short Thumb Width: {{ $thumb.Width }} 232 {{ end }} 233 ` 234 235 listLayout := `{{ .Title }}|{{ .Content }}` 236 237 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), singleLayout) 238 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), listLayout) 239 writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), myShort) 240 241 writeSource(t, fs, filepath.Join(workDir, "base", "_index.md"), pageContent) 242 writeSource(t, fs, filepath.Join(workDir, "base", "_1.md"), pageContent) 243 writeSource(t, fs, filepath.Join(workDir, "base", "_1.png"), pageContent) 244 245 writeSource(t, fs, filepath.Join(workDir, "base", "images", "hugo-logo.png"), "content") 246 writeSource(t, fs, filepath.Join(workDir, "base", "a", "2.md"), pageContent) 247 writeSource(t, fs, filepath.Join(workDir, "base", "a", "1.md"), pageContent) 248 249 writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "index.md"), pageContentNoSlug) 250 writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "ab1.md"), pageContentNoSlug) 251 252 // Mostly plain static assets in a folder with a page in a sub folder thrown in. 253 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic1.png"), "content") 254 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic2.png"), "content") 255 writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pages", "mypage.md"), pageContent) 256 257 // Bundle 258 writeSource(t, fs, filepath.Join(workDir, "base", "b", "index.md"), pageWithImageShortcodeContent) 259 writeSource(t, fs, filepath.Join(workDir, "base", "b", "1.md"), pageContent) 260 writeSource(t, fs, filepath.Join(workDir, "base", "b", "2.md"), pageContent) 261 writeSource(t, fs, filepath.Join(workDir, "base", "b", "custom-mime.bep"), "bepsays") 262 writeSource(t, fs, filepath.Join(workDir, "base", "b", "c", "logo.png"), "content") 263 264 // Write a real image into one of the bundle above. 265 src, err := os.Open("testdata/sunset.jpg") 266 assert.NoError(err) 267 268 // We need 2 to test https://github.com/gohugoio/hugo/issues/4202 269 out, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "sunset1.jpg")) 270 assert.NoError(err) 271 out2, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "sunset2.jpg")) 272 assert.NoError(err) 273 274 _, err = io.Copy(out, src) 275 out.Close() 276 src.Seek(0, 0) 277 _, err = io.Copy(out2, src) 278 out2.Close() 279 src.Close() 280 assert.NoError(err) 281 282 return cfg, fs 283 } 284 285 func newTestBundleSourcesMultilingual(t *testing.T) (*viper.Viper, *hugofs.Fs) { 286 cfg, fs := newTestCfg() 287 288 workDir := "/work" 289 cfg.Set("workingDir", workDir) 290 cfg.Set("contentDir", "base") 291 cfg.Set("baseURL", "https://example.com") 292 cfg.Set("defaultContentLanguage", "en") 293 294 langConfig := map[string]interface{}{ 295 "en": map[string]interface{}{ 296 "weight": 1, 297 "languageName": "English", 298 }, 299 "nn": map[string]interface{}{ 300 "weight": 2, 301 "languageName": "Nynorsk", 302 }, 303 } 304 305 cfg.Set("languages", langConfig) 306 307 pageContent := `--- 308 slug: pageslug 309 date: 2017-10-09 310 --- 311 312 TheContent. 313 ` 314 315 layout := `{{ .Title }}|{{ .Content }}|Lang: {{ .Site.Language.Lang }}` 316 317 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), layout) 318 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout) 319 320 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.md"), pageContent) 321 writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mylogo.png"), "content") 322 323 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.md"), pageContent) 324 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.nn.md"), pageContent) 325 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "en.md"), pageContent) 326 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.md"), pageContent) 327 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.nn.md"), pageContent) 328 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "a.png"), "content") 329 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.png"), "content") 330 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.nn.png"), "content") 331 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "c.nn.png"), "content") 332 writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b", "d.nn.png"), "content") 333 334 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "_index.md"), pageContent) 335 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.md"), pageContent) 336 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "logo-bc.png"), pageContent) 337 writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.nn.md"), pageContent) 338 339 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "index.md"), pageContent) 340 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.md"), pageContent) 341 writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.nn.md"), pageContent) 342 343 writeSource(t, fs, filepath.Join(workDir, "base", "be", "_index.md"), pageContent) 344 writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.md"), pageContent) 345 writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.nn.md"), pageContent) 346 347 // Bundle leaf, multilingual 348 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.md"), pageContent) 349 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.nn.md"), pageContent) 350 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "1.md"), pageContent) 351 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.md"), pageContent) 352 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.nn.md"), pageContent) 353 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.png"), "content") 354 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.nn.png"), "content") 355 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "one.png"), "content") 356 writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "d", "deep.png"), "content") 357 358 return cfg, fs 359 } 360 361 func newTestBundleSymbolicSources(t *testing.T) (*viper.Viper, *hugofs.Fs, string) { 362 assert := require.New(t) 363 // We need to use the OS fs for this. 364 cfg := viper.New() 365 fs := hugofs.NewFrom(hugofs.Os, cfg) 366 fs.Destination = &afero.MemMapFs{} 367 loadDefaultSettingsFor(cfg) 368 369 workDir, err := ioutil.TempDir("", "hugosym") 370 371 if runtime.GOOS == "darwin" && !strings.HasPrefix(workDir, "/private") { 372 // To get the entry folder in line with the rest. This its a little bit 373 // mysterious, but so be it. 374 workDir = "/private" + workDir 375 } 376 377 contentDir := "base" 378 cfg.Set("workingDir", workDir) 379 cfg.Set("contentDir", contentDir) 380 cfg.Set("baseURL", "https://example.com") 381 382 layout := `{{ .Title }}|{{ .Content }}` 383 pageContent := `--- 384 slug: %s 385 date: 2017-10-09 386 --- 387 388 TheContent. 389 ` 390 391 fs.Source.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777) 392 fs.Source.MkdirAll(filepath.Join(workDir, contentDir), 0777) 393 fs.Source.MkdirAll(filepath.Join(workDir, contentDir, "a"), 0777) 394 for i := 1; i <= 3; i++ { 395 fs.Source.MkdirAll(filepath.Join(workDir, fmt.Sprintf("symcontent%d", i)), 0777) 396 397 } 398 fs.Source.MkdirAll(filepath.Join(workDir, "symcontent2", "a1"), 0777) 399 400 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), layout) 401 writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout) 402 403 writeSource(t, fs, filepath.Join(workDir, contentDir, "a", "regular.md"), fmt.Sprintf(pageContent, "a1")) 404 405 // Regular files inside symlinked folder. 406 writeSource(t, fs, filepath.Join(workDir, "symcontent1", "s1.md"), fmt.Sprintf(pageContent, "s1")) 407 writeSource(t, fs, filepath.Join(workDir, "symcontent1", "s2.md"), fmt.Sprintf(pageContent, "s2")) 408 409 // A bundle 410 writeSource(t, fs, filepath.Join(workDir, "symcontent2", "a1", "index.md"), fmt.Sprintf(pageContent, "")) 411 writeSource(t, fs, filepath.Join(workDir, "symcontent2", "a1", "page.md"), fmt.Sprintf(pageContent, "page")) 412 writeSource(t, fs, filepath.Join(workDir, "symcontent2", "a1", "logo.png"), "image") 413 414 // Assets 415 writeSource(t, fs, filepath.Join(workDir, "symcontent3", "s1.png"), "image") 416 writeSource(t, fs, filepath.Join(workDir, "symcontent3", "s2.png"), "image") 417 418 wd, _ := os.Getwd() 419 defer func() { 420 os.Chdir(wd) 421 }() 422 // Symlinked sections inside content. 423 os.Chdir(filepath.Join(workDir, contentDir)) 424 for i := 1; i <= 3; i++ { 425 assert.NoError(os.Symlink(filepath.FromSlash(fmt.Sprintf(("../symcontent%d"), i)), fmt.Sprintf("symbolic%d", i))) 426 } 427 428 os.Chdir(filepath.Join(workDir, contentDir, "a")) 429 430 // Create a symlink to one single content file 431 assert.NoError(os.Symlink(filepath.FromSlash("../../symcontent2/a1/page.md"), "page_s.md")) 432 433 os.Chdir(filepath.FromSlash("../../symcontent3")) 434 435 // Create a circular symlink. Will print some warnings. 436 assert.NoError(os.Symlink(filepath.Join("..", contentDir), filepath.FromSlash("circus"))) 437 438 os.Chdir(workDir) 439 assert.NoError(err) 440 441 return cfg, fs, workDir 442 }