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  }