github.com/rabbouni145/gg@v0.47.1/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  	"github.com/gohugoio/hugo/common/loggers"
    18  
    19  	"os"
    20  	"runtime"
    21  	"testing"
    22  
    23  	"github.com/gohugoio/hugo/helpers"
    24  
    25  	"io"
    26  
    27  	"github.com/spf13/afero"
    28  
    29  	"github.com/gohugoio/hugo/media"
    30  
    31  	"path/filepath"
    32  
    33  	"fmt"
    34  
    35  	"github.com/gohugoio/hugo/deps"
    36  	"github.com/gohugoio/hugo/hugofs"
    37  	"github.com/spf13/viper"
    38  
    39  	"github.com/stretchr/testify/require"
    40  )
    41  
    42  func TestPageBundlerSiteRegular(t *testing.T) {
    43  	t.Parallel()
    44  
    45  	for _, ugly := range []bool{false, true} {
    46  		t.Run(fmt.Sprintf("ugly=%t", ugly),
    47  			func(t *testing.T) {
    48  
    49  				assert := require.New(t)
    50  				fs, cfg := newTestBundleSources(t)
    51  				assert.NoError(loadDefaultSettingsFor(cfg))
    52  				assert.NoError(loadLanguageSettings(cfg, nil))
    53  
    54  				cfg.Set("permalinks", map[string]string{
    55  					"a": ":sections/:filename",
    56  					"b": ":year/:slug/",
    57  					"c": ":sections/:slug",
    58  					"":  ":filename/",
    59  				})
    60  
    61  				cfg.Set("outputFormats", map[string]interface{}{
    62  					"CUSTOMO": map[string]interface{}{
    63  						"mediaType": media.HTMLType,
    64  						"baseName":  "cindex",
    65  						"path":      "cpath",
    66  					},
    67  				})
    68  
    69  				cfg.Set("outputs", map[string]interface{}{
    70  					"home":    []string{"HTML", "CUSTOMO"},
    71  					"page":    []string{"HTML", "CUSTOMO"},
    72  					"section": []string{"HTML", "CUSTOMO"},
    73  				})
    74  
    75  				cfg.Set("uglyURLs", ugly)
    76  
    77  				s := buildSingleSite(t, deps.DepsCfg{Logger: loggers.NewWarningLogger(), Fs: fs, Cfg: cfg}, BuildCfg{})
    78  
    79  				th := testHelper{s.Cfg, s.Fs, t}
    80  
    81  				assert.Len(s.RegularPages, 8)
    82  
    83  				singlePage := s.getPage(KindPage, "a/1.md")
    84  				assert.Equal("", singlePage.BundleType())
    85  
    86  				assert.NotNil(singlePage)
    87  				assert.Equal(singlePage, s.getPage("page", "a/1"))
    88  				assert.Equal(singlePage, s.getPage("page", "1"))
    89  
    90  				assert.Contains(singlePage.content(), "TheContent")
    91  
    92  				if ugly {
    93  					assert.Equal("/a/1.html", singlePage.RelPermalink())
    94  					th.assertFileContent(filepath.FromSlash("/work/public/a/1.html"), "TheContent")
    95  
    96  				} else {
    97  					assert.Equal("/a/1/", singlePage.RelPermalink())
    98  					th.assertFileContent(filepath.FromSlash("/work/public/a/1/index.html"), "TheContent")
    99  				}
   100  
   101  				th.assertFileContent(filepath.FromSlash("/work/public/images/hugo-logo.png"), "content")
   102  
   103  				// This should be just copied to destination.
   104  				th.assertFileContent(filepath.FromSlash("/work/public/assets/pic1.png"), "content")
   105  
   106  				leafBundle1 := s.getPage(KindPage, "b/my-bundle/index.md")
   107  				assert.NotNil(leafBundle1)
   108  				assert.Equal("leaf", leafBundle1.BundleType())
   109  				assert.Equal("b", leafBundle1.Section())
   110  				sectionB := s.getPage(KindSection, "b")
   111  				assert.NotNil(sectionB)
   112  				home, _ := s.Info.Home()
   113  				assert.Equal("branch", home.BundleType())
   114  
   115  				// This is a root bundle and should live in the "home section"
   116  				// See https://github.com/gohugoio/hugo/issues/4332
   117  				rootBundle := s.getPage(KindPage, "root")
   118  				assert.NotNil(rootBundle)
   119  				assert.True(rootBundle.Parent().IsHome())
   120  				if ugly {
   121  					assert.Equal("/root.html", rootBundle.RelPermalink())
   122  				} else {
   123  					assert.Equal("/root/", rootBundle.RelPermalink())
   124  				}
   125  
   126  				leafBundle2 := s.getPage(KindPage, "a/b/index.md")
   127  				assert.NotNil(leafBundle2)
   128  				unicodeBundle := s.getPage(KindPage, "c/bundle/index.md")
   129  				assert.NotNil(unicodeBundle)
   130  
   131  				pageResources := leafBundle1.Resources.ByType(pageResourceType)
   132  				assert.Len(pageResources, 2)
   133  				firstPage := pageResources[0].(*Page)
   134  				secondPage := pageResources[1].(*Page)
   135  				assert.Equal(filepath.FromSlash("b/my-bundle/1.md"), firstPage.pathOrTitle(), secondPage.pathOrTitle())
   136  				assert.Contains(firstPage.content(), "TheContent")
   137  				assert.Equal(6, len(leafBundle1.Resources))
   138  
   139  				// Verify shortcode in bundled page
   140  				assert.Contains(secondPage.content(), filepath.FromSlash("MyShort in b/my-bundle/2.md"))
   141  
   142  				// https://github.com/gohugoio/hugo/issues/4582
   143  				assert.Equal(leafBundle1, firstPage.Parent())
   144  				assert.Equal(leafBundle1, secondPage.Parent())
   145  
   146  				assert.Equal(firstPage, pageResources.GetMatch("1*"))
   147  				assert.Equal(secondPage, pageResources.GetMatch("2*"))
   148  				assert.Nil(pageResources.GetMatch("doesnotexist*"))
   149  
   150  				imageResources := leafBundle1.Resources.ByType("image")
   151  				assert.Equal(3, len(imageResources))
   152  				image := imageResources[0]
   153  
   154  				altFormat := leafBundle1.OutputFormats().Get("CUSTOMO")
   155  				assert.NotNil(altFormat)
   156  
   157  				assert.Equal("https://example.com/2017/pageslug/c/logo.png", image.Permalink())
   158  
   159  				th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/c/logo.png"), "content")
   160  				th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/c/logo.png"), "content")
   161  
   162  				// Custom media type defined in site config.
   163  				assert.Len(leafBundle1.Resources.ByType("bepsays"), 1)
   164  
   165  				if ugly {
   166  					assert.Equal("/2017/pageslug.html", leafBundle1.RelPermalink())
   167  					th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug.html"),
   168  						"TheContent",
   169  						"Sunset RelPermalink: /2017/pageslug/sunset1.jpg",
   170  						"Thumb Width: 123",
   171  						"Thumb Name: my-sunset-1",
   172  						"Short Sunset RelPermalink: /2017/pageslug/sunset2.jpg",
   173  						"Short Thumb Width: 56",
   174  						"1: Image Title: Sunset Galore 1",
   175  						"1: Image Params: map[myparam:My Sunny Param]",
   176  						"2: Image Title: Sunset Galore 2",
   177  						"2: Image Params: map[myparam:My Sunny Param]",
   178  						"1: Image myParam: Lower: My Sunny Param Caps: My Sunny Param",
   179  					)
   180  					th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug.html"), "TheContent")
   181  
   182  					assert.Equal("/a/b.html", leafBundle2.RelPermalink())
   183  
   184  					// 은행
   185  					assert.Equal("/c/%EC%9D%80%ED%96%89.html", unicodeBundle.RelPermalink())
   186  					th.assertFileContent(filepath.FromSlash("/work/public/c/은행.html"), "Content for 은행")
   187  					th.assertFileContent(filepath.FromSlash("/work/public/c/은행/logo-은행.png"), "은행 PNG")
   188  
   189  				} else {
   190  					assert.Equal("/2017/pageslug/", leafBundle1.RelPermalink())
   191  					th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "TheContent")
   192  					th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/cindex.html"), "TheContent")
   193  					th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "Single Title")
   194  					th.assertFileContent(filepath.FromSlash("/work/public/root/index.html"), "Single Title")
   195  
   196  					assert.Equal("/a/b/", leafBundle2.RelPermalink())
   197  
   198  				}
   199  
   200  			})
   201  	}
   202  
   203  }
   204  
   205  func TestPageBundlerSiteMultilingual(t *testing.T) {
   206  	t.Parallel()
   207  
   208  	for _, ugly := range []bool{false, true} {
   209  		t.Run(fmt.Sprintf("ugly=%t", ugly),
   210  			func(t *testing.T) {
   211  
   212  				assert := require.New(t)
   213  				fs, cfg := newTestBundleSourcesMultilingual(t)
   214  				cfg.Set("uglyURLs", ugly)
   215  
   216  				assert.NoError(loadDefaultSettingsFor(cfg))
   217  				assert.NoError(loadLanguageSettings(cfg, nil))
   218  				sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
   219  				assert.NoError(err)
   220  				assert.Equal(2, len(sites.Sites))
   221  
   222  				assert.NoError(sites.Build(BuildCfg{}))
   223  
   224  				s := sites.Sites[0]
   225  
   226  				assert.Equal(8, len(s.RegularPages))
   227  				assert.Equal(16, len(s.Pages))
   228  				assert.Equal(31, len(s.AllPages))
   229  
   230  				bundleWithSubPath := s.getPage(KindPage, "lb/index")
   231  				assert.NotNil(bundleWithSubPath)
   232  
   233  				// See https://github.com/gohugoio/hugo/issues/4312
   234  				// Before that issue:
   235  				// A bundle in a/b/index.en.md
   236  				// a/b/index.en.md => OK
   237  				// a/b/index => OK
   238  				// index.en.md => ambigous, but OK.
   239  				// With bundles, the file name has little meaning, the folder it lives in does. So this should also work:
   240  				// a/b
   241  				// and probably also just b (aka "my-bundle")
   242  				// These may also be translated, so we also need to test that.
   243  				//  "bf", "my-bf-bundle", "index.md + nn
   244  				bfBundle := s.getPage(KindPage, "bf/my-bf-bundle/index")
   245  				assert.NotNil(bfBundle)
   246  				assert.Equal("en", bfBundle.Lang())
   247  				assert.Equal(bfBundle, s.getPage(KindPage, "bf/my-bf-bundle/index.md"))
   248  				assert.Equal(bfBundle, s.getPage(KindPage, "bf/my-bf-bundle"))
   249  				assert.Equal(bfBundle, s.getPage(KindPage, "my-bf-bundle"))
   250  
   251  				nnSite := sites.Sites[1]
   252  				assert.Equal(7, len(nnSite.RegularPages))
   253  
   254  				bfBundleNN := nnSite.getPage(KindPage, "bf/my-bf-bundle/index")
   255  				assert.NotNil(bfBundleNN)
   256  				assert.Equal("nn", bfBundleNN.Lang())
   257  				assert.Equal(bfBundleNN, nnSite.getPage(KindPage, "bf/my-bf-bundle/index.nn.md"))
   258  				assert.Equal(bfBundleNN, nnSite.getPage(KindPage, "bf/my-bf-bundle"))
   259  				assert.Equal(bfBundleNN, nnSite.getPage(KindPage, "my-bf-bundle"))
   260  
   261  				// See https://github.com/gohugoio/hugo/issues/4295
   262  				// Every resource should have its Name prefixed with its base folder.
   263  				cBundleResources := bundleWithSubPath.Resources.Match("c/**")
   264  				assert.Equal(4, len(cBundleResources))
   265  				bundlePage := bundleWithSubPath.Resources.GetMatch("c/page*")
   266  				assert.NotNil(bundlePage)
   267  				assert.IsType(&Page{}, bundlePage)
   268  
   269  			})
   270  	}
   271  }
   272  
   273  func TestMultilingualDisableDefaultLanguage(t *testing.T) {
   274  	t.Parallel()
   275  
   276  	assert := require.New(t)
   277  	_, cfg := newTestBundleSourcesMultilingual(t)
   278  
   279  	cfg.Set("disableLanguages", []string{"en"})
   280  
   281  	err := loadDefaultSettingsFor(cfg)
   282  	assert.NoError(err)
   283  	err = loadLanguageSettings(cfg, nil)
   284  	assert.Error(err)
   285  	assert.Contains(err.Error(), "cannot disable default language")
   286  }
   287  
   288  func TestMultilingualDisableLanguage(t *testing.T) {
   289  	t.Parallel()
   290  
   291  	assert := require.New(t)
   292  	fs, cfg := newTestBundleSourcesMultilingual(t)
   293  	cfg.Set("disableLanguages", []string{"nn"})
   294  
   295  	assert.NoError(loadDefaultSettingsFor(cfg))
   296  	assert.NoError(loadLanguageSettings(cfg, nil))
   297  
   298  	sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg})
   299  	assert.NoError(err)
   300  	assert.Equal(1, len(sites.Sites))
   301  
   302  	assert.NoError(sites.Build(BuildCfg{}))
   303  
   304  	s := sites.Sites[0]
   305  
   306  	assert.Equal(8, len(s.RegularPages))
   307  	assert.Equal(16, len(s.Pages))
   308  	// No nn pages
   309  	assert.Equal(16, len(s.AllPages))
   310  	for _, p := range s.rawAllPages {
   311  		assert.True(p.Lang() != "nn")
   312  	}
   313  	for _, p := range s.AllPages {
   314  		assert.True(p.Lang() != "nn")
   315  	}
   316  
   317  }
   318  
   319  func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) {
   320  	if runtime.GOOS == "windows" && os.Getenv("CI") == "" {
   321  		t.Skip("Skip TestPageBundlerSiteWitSymbolicLinksInContent as os.Symlink needs administrator rights on Windows")
   322  	}
   323  
   324  	assert := require.New(t)
   325  	ps, clean, workDir := newTestBundleSymbolicSources(t)
   326  	defer clean()
   327  
   328  	cfg := ps.Cfg
   329  	fs := ps.Fs
   330  
   331  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg, Logger: loggers.NewErrorLogger()}, BuildCfg{})
   332  
   333  	th := testHelper{s.Cfg, s.Fs, t}
   334  
   335  	assert.Equal(7, len(s.RegularPages))
   336  	a1Bundle := s.getPage(KindPage, "symbolic2/a1/index.md")
   337  	assert.NotNil(a1Bundle)
   338  	assert.Equal(2, len(a1Bundle.Resources))
   339  	assert.Equal(1, len(a1Bundle.Resources.ByType(pageResourceType)))
   340  
   341  	th.assertFileContent(filepath.FromSlash(workDir+"/public/a/page/index.html"), "TheContent")
   342  	th.assertFileContent(filepath.FromSlash(workDir+"/public/symbolic1/s1/index.html"), "TheContent")
   343  	th.assertFileContent(filepath.FromSlash(workDir+"/public/symbolic2/a1/index.html"), "TheContent")
   344  
   345  }
   346  
   347  func TestPageBundlerHeadless(t *testing.T) {
   348  	t.Parallel()
   349  
   350  	cfg, fs := newTestCfg()
   351  	assert := require.New(t)
   352  
   353  	workDir := "/work"
   354  	cfg.Set("workingDir", workDir)
   355  	cfg.Set("contentDir", "base")
   356  	cfg.Set("baseURL", "https://example.com")
   357  
   358  	pageContent := `---
   359  title: "Bundle Galore"
   360  slug: s1
   361  date: 2017-01-23
   362  ---
   363  
   364  TheContent.
   365  
   366  {{< myShort >}}
   367  `
   368  
   369  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), "single {{ .Content }}")
   370  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), "list")
   371  	writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), "SHORTCODE")
   372  
   373  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "index.md"), pageContent)
   374  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "l1.png"), "PNG image")
   375  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "l2.png"), "PNG image")
   376  
   377  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "index.md"), `---
   378  title: "Headless Bundle in Topless Bar"
   379  slug: s2
   380  headless: true
   381  date: 2017-01-23
   382  ---
   383  
   384  TheContent.
   385  HEADLESS {{< myShort >}}
   386  `)
   387  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "l1.png"), "PNG image")
   388  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "l2.png"), "PNG image")
   389  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "p1.md"), pageContent)
   390  
   391  	s := buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   392  
   393  	assert.Equal(1, len(s.RegularPages))
   394  	assert.Equal(1, len(s.headlessPages))
   395  
   396  	regular := s.getPage(KindPage, "a/index")
   397  	assert.Equal("/a/s1/", regular.RelPermalink())
   398  
   399  	headless := s.getPage(KindPage, "b/index")
   400  	assert.NotNil(headless)
   401  	assert.True(headless.headless)
   402  	assert.Equal("Headless Bundle in Topless Bar", headless.Title())
   403  	assert.Equal("", headless.RelPermalink())
   404  	assert.Equal("", headless.Permalink())
   405  	assert.Contains(headless.content(), "HEADLESS SHORTCODE")
   406  
   407  	headlessResources := headless.Resources
   408  	assert.Equal(3, len(headlessResources))
   409  	assert.Equal(2, len(headlessResources.Match("l*")))
   410  	pageResource := headlessResources.GetMatch("p*")
   411  	assert.NotNil(pageResource)
   412  	assert.IsType(&Page{}, pageResource)
   413  	p := pageResource.(*Page)
   414  	assert.Contains(p.content(), "SHORTCODE")
   415  	assert.Equal("p1.md", p.Name())
   416  
   417  	th := testHelper{s.Cfg, s.Fs, t}
   418  
   419  	th.assertFileContent(filepath.FromSlash(workDir+"/public/a/s1/index.html"), "TheContent")
   420  	th.assertFileContent(filepath.FromSlash(workDir+"/public/a/s1/l1.png"), "PNG")
   421  
   422  	th.assertFileNotExist(workDir + "/public/b/s2/index.html")
   423  	// But the bundled resources needs to be published
   424  	th.assertFileContent(filepath.FromSlash(workDir+"/public/b/s2/l1.png"), "PNG")
   425  
   426  }
   427  
   428  func newTestBundleSources(t *testing.T) (*hugofs.Fs, *viper.Viper) {
   429  	cfg, fs := newTestCfg()
   430  	assert := require.New(t)
   431  
   432  	workDir := "/work"
   433  	cfg.Set("workingDir", workDir)
   434  	cfg.Set("contentDir", "base")
   435  	cfg.Set("baseURL", "https://example.com")
   436  	cfg.Set("mediaTypes", map[string]interface{}{
   437  		"text/bepsays": map[string]interface{}{
   438  			"suffix": "bep",
   439  		},
   440  	})
   441  
   442  	pageContent := `---
   443  title: "Bundle Galore"
   444  slug: pageslug
   445  date: 2017-10-09
   446  ---
   447  
   448  TheContent.
   449  `
   450  
   451  	pageContentShortcode := `---
   452  title: "Bundle Galore"
   453  slug: pageslug
   454  date: 2017-10-09
   455  ---
   456  
   457  TheContent.
   458  
   459  {{< myShort >}}
   460  `
   461  
   462  	pageWithImageShortcodeAndResourceMetadataContent := `---
   463  title: "Bundle Galore"
   464  slug: pageslug
   465  date: 2017-10-09
   466  resources:
   467  - src: "*.jpg"
   468    name: "my-sunset-:counter"
   469    title: "Sunset Galore :counter"
   470    params:
   471      myParam: "My Sunny Param"
   472  ---
   473  
   474  TheContent.
   475  
   476  {{< myShort >}}
   477  `
   478  
   479  	pageContentNoSlug := `---
   480  title: "Bundle Galore #2"
   481  date: 2017-10-09
   482  ---
   483  
   484  TheContent.
   485  `
   486  
   487  	singleLayout := `
   488  Single Title: {{ .Title }}
   489  Content: {{ .Content }}
   490  {{ $sunset := .Resources.GetMatch "my-sunset-1*" }}
   491  {{ with $sunset }}
   492  Sunset RelPermalink: {{ .RelPermalink }}
   493  {{ $thumb := .Fill "123x123" }}
   494  Thumb Width: {{ $thumb.Width }}
   495  Thumb Name: {{ $thumb.Name }}
   496  Thumb Title: {{ $thumb.Title }}
   497  Thumb RelPermalink: {{ $thumb.RelPermalink }}
   498  {{ end }}
   499  {{ range $i, $e := .Resources.ByType "image" }}
   500  {{ $i }}: Image Title: {{ .Title }}
   501  {{ $i }}: Image Name: {{ .Name }}
   502  {{ $i }}: Image Params: {{ printf "%v" .Params }}
   503  {{ $i }}: Image myParam: Lower: {{ .Params.myparam }} Caps: {{ .Params.MYPARAM }}
   504  {{ end }}
   505  `
   506  
   507  	myShort := `
   508  MyShort in {{ .Page.Path }}:
   509  {{ $sunset := .Page.Resources.GetMatch "my-sunset-2*" }}
   510  {{ with $sunset }}
   511  Short Sunset RelPermalink: {{ .RelPermalink }}
   512  {{ $thumb := .Fill "56x56" }}
   513  Short Thumb Width: {{ $thumb.Width }}
   514  {{ end }}
   515  `
   516  
   517  	listLayout := `{{ .Title }}|{{ .Content }}`
   518  
   519  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), singleLayout)
   520  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), listLayout)
   521  	writeSource(t, fs, filepath.Join(workDir, "layouts", "shortcodes", "myShort.html"), myShort)
   522  
   523  	writeSource(t, fs, filepath.Join(workDir, "base", "_index.md"), pageContent)
   524  	writeSource(t, fs, filepath.Join(workDir, "base", "_1.md"), pageContent)
   525  	writeSource(t, fs, filepath.Join(workDir, "base", "_1.png"), pageContent)
   526  
   527  	writeSource(t, fs, filepath.Join(workDir, "base", "images", "hugo-logo.png"), "content")
   528  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "2.md"), pageContent)
   529  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "1.md"), pageContent)
   530  
   531  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "index.md"), pageContentNoSlug)
   532  	writeSource(t, fs, filepath.Join(workDir, "base", "a", "b", "ab1.md"), pageContentNoSlug)
   533  
   534  	// Mostly plain static assets in a folder with a page in a sub folder thrown in.
   535  	writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic1.png"), "content")
   536  	writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pic2.png"), "content")
   537  	writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pages", "mypage.md"), pageContent)
   538  
   539  	// Bundle
   540  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
   541  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "1.md"), pageContent)
   542  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "2.md"), pageContentShortcode)
   543  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "custom-mime.bep"), "bepsays")
   544  	writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "c", "logo.png"), "content")
   545  
   546  	// Bundle with 은행 slug
   547  	// See https://github.com/gohugoio/hugo/issues/4241
   548  	writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "index.md"), `---
   549  title: "은행 은행"
   550  slug: 은행
   551  date: 2017-10-09
   552  ---
   553  
   554  Content for 은행.
   555  `)
   556  
   557  	// Bundle in root
   558  	writeSource(t, fs, filepath.Join(workDir, "base", "root", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
   559  	writeSource(t, fs, filepath.Join(workDir, "base", "root", "1.md"), pageContent)
   560  	writeSource(t, fs, filepath.Join(workDir, "base", "root", "c", "logo.png"), "content")
   561  
   562  	writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "logo-은행.png"), "은행 PNG")
   563  
   564  	// Write a real image into one of the bundle above.
   565  	src, err := os.Open("testdata/sunset.jpg")
   566  	assert.NoError(err)
   567  
   568  	// We need 2 to test https://github.com/gohugoio/hugo/issues/4202
   569  	out, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset1.jpg"))
   570  	assert.NoError(err)
   571  	out2, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset2.jpg"))
   572  	assert.NoError(err)
   573  
   574  	_, err = io.Copy(out, src)
   575  	out.Close()
   576  	src.Seek(0, 0)
   577  	_, err = io.Copy(out2, src)
   578  	out2.Close()
   579  	src.Close()
   580  	assert.NoError(err)
   581  
   582  	return fs, cfg
   583  
   584  }
   585  
   586  func newTestBundleSourcesMultilingual(t *testing.T) (*hugofs.Fs, *viper.Viper) {
   587  	cfg, fs := newTestCfg()
   588  
   589  	workDir := "/work"
   590  	cfg.Set("workingDir", workDir)
   591  	cfg.Set("contentDir", "base")
   592  	cfg.Set("baseURL", "https://example.com")
   593  	cfg.Set("defaultContentLanguage", "en")
   594  
   595  	langConfig := map[string]interface{}{
   596  		"en": map[string]interface{}{
   597  			"weight":       1,
   598  			"languageName": "English",
   599  		},
   600  		"nn": map[string]interface{}{
   601  			"weight":       2,
   602  			"languageName": "Nynorsk",
   603  		},
   604  	}
   605  
   606  	cfg.Set("languages", langConfig)
   607  
   608  	pageContent := `---
   609  slug: pageslug
   610  date: 2017-10-09
   611  ---
   612  
   613  TheContent.
   614  `
   615  
   616  	layout := `{{ .Title }}|{{ .Content }}|Lang: {{ .Site.Language.Lang }}`
   617  
   618  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), layout)
   619  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout)
   620  
   621  	writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.md"), pageContent)
   622  	writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mypage.nn.md"), pageContent)
   623  	writeSource(t, fs, filepath.Join(workDir, "base", "1s", "mylogo.png"), "content")
   624  
   625  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.md"), pageContent)
   626  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_index.nn.md"), pageContent)
   627  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "en.md"), pageContent)
   628  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.md"), pageContent)
   629  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "_1.nn.md"), pageContent)
   630  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "a.png"), "content")
   631  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.png"), "content")
   632  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b.nn.png"), "content")
   633  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "c.nn.png"), "content")
   634  	writeSource(t, fs, filepath.Join(workDir, "base", "bb", "b", "d.nn.png"), "content")
   635  
   636  	writeSource(t, fs, filepath.Join(workDir, "base", "bc", "_index.md"), pageContent)
   637  	writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.md"), pageContent)
   638  	writeSource(t, fs, filepath.Join(workDir, "base", "bc", "logo-bc.png"), pageContent)
   639  	writeSource(t, fs, filepath.Join(workDir, "base", "bc", "page.nn.md"), pageContent)
   640  
   641  	writeSource(t, fs, filepath.Join(workDir, "base", "bd", "index.md"), pageContent)
   642  	writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.md"), pageContent)
   643  	writeSource(t, fs, filepath.Join(workDir, "base", "bd", "page.nn.md"), pageContent)
   644  
   645  	writeSource(t, fs, filepath.Join(workDir, "base", "be", "_index.md"), pageContent)
   646  	writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.md"), pageContent)
   647  	writeSource(t, fs, filepath.Join(workDir, "base", "be", "page.nn.md"), pageContent)
   648  
   649  	// Bundle leaf,  multilingual
   650  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.md"), pageContent)
   651  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "index.nn.md"), pageContent)
   652  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "1.md"), pageContent)
   653  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.md"), pageContent)
   654  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "2.nn.md"), pageContent)
   655  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "page.md"), pageContent)
   656  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.png"), "content")
   657  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "logo.nn.png"), "content")
   658  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "one.png"), "content")
   659  	writeSource(t, fs, filepath.Join(workDir, "base", "lb", "c", "d", "deep.png"), "content")
   660  
   661  	//Translated bundle in some sensible sub path.
   662  	writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "index.md"), pageContent)
   663  	writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "index.nn.md"), pageContent)
   664  	writeSource(t, fs, filepath.Join(workDir, "base", "bf", "my-bf-bundle", "page.md"), pageContent)
   665  
   666  	return fs, cfg
   667  }
   668  
   669  func newTestBundleSymbolicSources(t *testing.T) (*helpers.PathSpec, func(), string) {
   670  	assert := require.New(t)
   671  	// We need to use the OS fs for this.
   672  	cfg := viper.New()
   673  	fs := hugofs.NewFrom(hugofs.Os, cfg)
   674  	fs.Destination = &afero.MemMapFs{}
   675  	loadDefaultSettingsFor(cfg)
   676  
   677  	workDir, clean, err := createTempDir("hugosym")
   678  	assert.NoError(err)
   679  
   680  	contentDir := "base"
   681  	cfg.Set("workingDir", workDir)
   682  	cfg.Set("contentDir", contentDir)
   683  	cfg.Set("baseURL", "https://example.com")
   684  
   685  	if err := loadLanguageSettings(cfg, nil); err != nil {
   686  		t.Fatal(err)
   687  	}
   688  
   689  	layout := `{{ .Title }}|{{ .Content }}`
   690  	pageContent := `---
   691  slug: %s
   692  date: 2017-10-09
   693  ---
   694  
   695  TheContent.
   696  `
   697  
   698  	fs.Source.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777)
   699  	fs.Source.MkdirAll(filepath.Join(workDir, contentDir), 0777)
   700  	fs.Source.MkdirAll(filepath.Join(workDir, contentDir, "a"), 0777)
   701  	for i := 1; i <= 3; i++ {
   702  		fs.Source.MkdirAll(filepath.Join(workDir, fmt.Sprintf("symcontent%d", i)), 0777)
   703  
   704  	}
   705  	fs.Source.MkdirAll(filepath.Join(workDir, "symcontent2", "a1"), 0777)
   706  
   707  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "single.html"), layout)
   708  	writeSource(t, fs, filepath.Join(workDir, "layouts", "_default", "list.html"), layout)
   709  
   710  	writeSource(t, fs, filepath.Join(workDir, contentDir, "a", "regular.md"), fmt.Sprintf(pageContent, "a1"))
   711  
   712  	// Regular files inside symlinked folder.
   713  	writeSource(t, fs, filepath.Join(workDir, "symcontent1", "s1.md"), fmt.Sprintf(pageContent, "s1"))
   714  	writeSource(t, fs, filepath.Join(workDir, "symcontent1", "s2.md"), fmt.Sprintf(pageContent, "s2"))
   715  
   716  	// A bundle
   717  	writeSource(t, fs, filepath.Join(workDir, "symcontent2", "a1", "index.md"), fmt.Sprintf(pageContent, ""))
   718  	writeSource(t, fs, filepath.Join(workDir, "symcontent2", "a1", "page.md"), fmt.Sprintf(pageContent, "page"))
   719  	writeSource(t, fs, filepath.Join(workDir, "symcontent2", "a1", "logo.png"), "image")
   720  
   721  	// Assets
   722  	writeSource(t, fs, filepath.Join(workDir, "symcontent3", "s1.png"), "image")
   723  	writeSource(t, fs, filepath.Join(workDir, "symcontent3", "s2.png"), "image")
   724  
   725  	wd, _ := os.Getwd()
   726  	defer func() {
   727  		os.Chdir(wd)
   728  	}()
   729  	// Symlinked sections inside content.
   730  	os.Chdir(filepath.Join(workDir, contentDir))
   731  	for i := 1; i <= 3; i++ {
   732  		assert.NoError(os.Symlink(filepath.FromSlash(fmt.Sprintf(("../symcontent%d"), i)), fmt.Sprintf("symbolic%d", i)))
   733  	}
   734  
   735  	os.Chdir(filepath.Join(workDir, contentDir, "a"))
   736  
   737  	// Create a symlink to one single content file
   738  	assert.NoError(os.Symlink(filepath.FromSlash("../../symcontent2/a1/page.md"), "page_s.md"))
   739  
   740  	os.Chdir(filepath.FromSlash("../../symcontent3"))
   741  
   742  	// Create a circular symlink. Will print some warnings.
   743  	assert.NoError(os.Symlink(filepath.Join("..", contentDir), filepath.FromSlash("circus")))
   744  
   745  	os.Chdir(workDir)
   746  	assert.NoError(err)
   747  
   748  	ps, _ := helpers.NewPathSpec(fs, cfg)
   749  
   750  	return ps, clean, workDir
   751  }