github.com/fighterlyt/hugo@v0.47.1/hugolib/hugo_sites_build_test.go (about)

     1  package hugolib
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  
     9  	"html/template"
    10  	"os"
    11  	"path/filepath"
    12  	"time"
    13  
    14  	"github.com/gohugoio/hugo/langs"
    15  
    16  	"github.com/fortytw2/leaktest"
    17  	"github.com/fsnotify/fsnotify"
    18  	"github.com/gohugoio/hugo/helpers"
    19  	"github.com/gohugoio/hugo/hugofs"
    20  	"github.com/spf13/afero"
    21  	"github.com/spf13/viper"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestMultiSitesMainLangInRoot(t *testing.T) {
    26  	t.Parallel()
    27  	for _, b := range []bool{false} {
    28  		doTestMultiSitesMainLangInRoot(t, b)
    29  	}
    30  }
    31  
    32  func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
    33  	assert := require.New(t)
    34  
    35  	siteConfig := map[string]interface{}{
    36  		"DefaultContentLanguage":         "fr",
    37  		"DefaultContentLanguageInSubdir": defaultInSubDir,
    38  	}
    39  
    40  	b := newMultiSiteTestBuilder(t, "toml", multiSiteTOMLConfigTemplate, siteConfig)
    41  
    42  	pathMod := func(s string) string {
    43  		return s
    44  	}
    45  
    46  	if !defaultInSubDir {
    47  		pathMod = func(s string) string {
    48  			return strings.Replace(s, "/fr/", "/", -1)
    49  		}
    50  	}
    51  
    52  	b.CreateSites()
    53  	b.Build(BuildCfg{})
    54  
    55  	sites := b.H.Sites
    56  
    57  	require.Len(t, sites, 4)
    58  
    59  	enSite := sites[0]
    60  	frSite := sites[1]
    61  
    62  	assert.Equal("/en", enSite.Info.LanguagePrefix)
    63  
    64  	if defaultInSubDir {
    65  		assert.Equal("/fr", frSite.Info.LanguagePrefix)
    66  	} else {
    67  		assert.Equal("", frSite.Info.LanguagePrefix)
    68  	}
    69  
    70  	assert.Equal("/blog/en/foo", enSite.PathSpec.RelURL("foo", true))
    71  
    72  	doc1en := enSite.RegularPages[0]
    73  	doc1fr := frSite.RegularPages[0]
    74  
    75  	enPerm := doc1en.Permalink()
    76  	enRelPerm := doc1en.RelPermalink()
    77  	assert.Equal("http://example.com/blog/en/sect/doc1-slug/", enPerm)
    78  	assert.Equal("/blog/en/sect/doc1-slug/", enRelPerm)
    79  
    80  	frPerm := doc1fr.Permalink()
    81  	frRelPerm := doc1fr.RelPermalink()
    82  
    83  	b.AssertFileContent(pathMod("public/fr/sect/doc1/index.html"), "Single", "Bonjour")
    84  	b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Hello")
    85  
    86  	if defaultInSubDir {
    87  		assert.Equal("http://example.com/blog/fr/sect/doc1/", frPerm)
    88  		assert.Equal("/blog/fr/sect/doc1/", frRelPerm)
    89  
    90  		// should have a redirect on top level.
    91  		b.AssertFileContent("public/index.html", `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr" />`)
    92  	} else {
    93  		// Main language in root
    94  		assert.Equal("http://example.com/blog/sect/doc1/", frPerm)
    95  		assert.Equal("/blog/sect/doc1/", frRelPerm)
    96  
    97  		// should have redirect back to root
    98  		b.AssertFileContent("public/fr/index.html", `<meta http-equiv="refresh" content="0; url=http://example.com/blog" />`)
    99  	}
   100  	b.AssertFileContent(pathMod("public/fr/index.html"), "Home", "Bonjour")
   101  	b.AssertFileContent("public/en/index.html", "Home", "Hello")
   102  
   103  	// Check list pages
   104  	b.AssertFileContent(pathMod("public/fr/sect/index.html"), "List", "Bonjour")
   105  	b.AssertFileContent("public/en/sect/index.html", "List", "Hello")
   106  	b.AssertFileContent(pathMod("public/fr/plaques/frtag1/index.html"), "List", "Bonjour")
   107  	b.AssertFileContent("public/en/tags/tag1/index.html", "List", "Hello")
   108  
   109  	// Check sitemaps
   110  	// Sitemaps behaves different: In a multilanguage setup there will always be a index file and
   111  	// one sitemap in each lang folder.
   112  	b.AssertFileContent("public/sitemap.xml",
   113  		"<loc>http://example.com/blog/en/sitemap.xml</loc>",
   114  		"<loc>http://example.com/blog/fr/sitemap.xml</loc>")
   115  
   116  	if defaultInSubDir {
   117  		b.AssertFileContent("public/fr/sitemap.xml", "<loc>http://example.com/blog/fr/</loc>")
   118  	} else {
   119  		b.AssertFileContent("public/fr/sitemap.xml", "<loc>http://example.com/blog/</loc>")
   120  	}
   121  	b.AssertFileContent("public/en/sitemap.xml", "<loc>http://example.com/blog/en/</loc>")
   122  
   123  	// Check rss
   124  	b.AssertFileContent(pathMod("public/fr/index.xml"), pathMod(`<atom:link href="http://example.com/blog/fr/index.xml"`),
   125  		`rel="self" type="application/rss+xml"`)
   126  	b.AssertFileContent("public/en/index.xml", `<atom:link href="http://example.com/blog/en/index.xml"`)
   127  	b.AssertFileContent(
   128  		pathMod("public/fr/sect/index.xml"),
   129  		pathMod(`<atom:link href="http://example.com/blog/fr/sect/index.xml"`))
   130  	b.AssertFileContent("public/en/sect/index.xml", `<atom:link href="http://example.com/blog/en/sect/index.xml"`)
   131  	b.AssertFileContent(
   132  		pathMod("public/fr/plaques/frtag1/index.xml"),
   133  		pathMod(`<atom:link href="http://example.com/blog/fr/plaques/frtag1/index.xml"`))
   134  	b.AssertFileContent("public/en/tags/tag1/index.xml", `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`)
   135  
   136  	// Check paginators
   137  	b.AssertFileContent(pathMod("public/fr/page/1/index.html"), pathMod(`refresh" content="0; url=http://example.com/blog/fr/"`))
   138  	b.AssertFileContent("public/en/page/1/index.html", `refresh" content="0; url=http://example.com/blog/en/"`)
   139  	b.AssertFileContent(pathMod("public/fr/page/2/index.html"), "Home Page 2", "Bonjour", pathMod("http://example.com/blog/fr/"))
   140  	b.AssertFileContent("public/en/page/2/index.html", "Home Page 2", "Hello", "http://example.com/blog/en/")
   141  	b.AssertFileContent(pathMod("public/fr/sect/page/1/index.html"), pathMod(`refresh" content="0; url=http://example.com/blog/fr/sect/"`))
   142  	b.AssertFileContent("public/en/sect/page/1/index.html", `refresh" content="0; url=http://example.com/blog/en/sect/"`)
   143  	b.AssertFileContent(pathMod("public/fr/sect/page/2/index.html"), "List Page 2", "Bonjour", pathMod("http://example.com/blog/fr/sect/"))
   144  	b.AssertFileContent("public/en/sect/page/2/index.html", "List Page 2", "Hello", "http://example.com/blog/en/sect/")
   145  	b.AssertFileContent(
   146  		pathMod("public/fr/plaques/frtag1/page/1/index.html"),
   147  		pathMod(`refresh" content="0; url=http://example.com/blog/fr/plaques/frtag1/"`))
   148  	b.AssertFileContent("public/en/tags/tag1/page/1/index.html", `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`)
   149  	b.AssertFileContent(
   150  		pathMod("public/fr/plaques/frtag1/page/2/index.html"), "List Page 2", "Bonjour",
   151  		pathMod("http://example.com/blog/fr/plaques/frtag1/"))
   152  	b.AssertFileContent("public/en/tags/tag1/page/2/index.html", "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/")
   153  	// nn (Nynorsk) and nb (Bokmål) have custom pagePath: side ("page" in Norwegian)
   154  	b.AssertFileContent("public/nn/side/1/index.html", `refresh" content="0; url=http://example.com/blog/nn/"`)
   155  	b.AssertFileContent("public/nb/side/1/index.html", `refresh" content="0; url=http://example.com/blog/nb/"`)
   156  }
   157  
   158  func TestMultiSitesWithTwoLanguages(t *testing.T) {
   159  	t.Parallel()
   160  
   161  	assert := require.New(t)
   162  	b := newTestSitesBuilder(t).WithConfigFile("toml", `
   163  
   164  defaultContentLanguage = "nn"
   165  
   166  [languages]
   167  [languages.nn]
   168  languageName = "Nynorsk"
   169  weight = 1
   170  title = "Tittel på Nynorsk"
   171  [languages.nn.params]
   172  p1 = "p1nn"
   173  
   174  [languages.en]
   175  title = "Title in English"
   176  languageName = "English"
   177  weight = 2
   178  [languages.en.params]
   179  p1 = "p1en"
   180  `)
   181  
   182  	b.CreateSites()
   183  	b.Build(BuildCfg{SkipRender: true})
   184  	sites := b.H.Sites
   185  
   186  	assert.Len(sites, 2)
   187  
   188  	nnSite := sites[0]
   189  	nnHome := nnSite.getPage(KindHome)
   190  	assert.Len(nnHome.AllTranslations(), 2)
   191  	assert.Len(nnHome.Translations(), 1)
   192  	assert.True(nnHome.IsTranslated())
   193  
   194  	enHome := sites[1].getPage(KindHome)
   195  
   196  	p1, err := enHome.Param("p1")
   197  	assert.NoError(err)
   198  	assert.Equal("p1en", p1)
   199  
   200  	p1, err = nnHome.Param("p1")
   201  	assert.NoError(err)
   202  	assert.Equal("p1nn", p1)
   203  }
   204  
   205  //
   206  func TestMultiSitesBuild(t *testing.T) {
   207  	t.Parallel()
   208  
   209  	for _, config := range []struct {
   210  		content string
   211  		suffix  string
   212  	}{
   213  		{multiSiteTOMLConfigTemplate, "toml"},
   214  		{multiSiteYAMLConfigTemplate, "yml"},
   215  		{multiSiteJSONConfigTemplate, "json"},
   216  	} {
   217  		doTestMultiSitesBuild(t, config.content, config.suffix)
   218  	}
   219  }
   220  
   221  func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
   222  	assert := require.New(t)
   223  
   224  	b := newMultiSiteTestBuilder(t, configSuffix, configTemplate, nil)
   225  	b.CreateSites()
   226  
   227  	sites := b.H.Sites
   228  	assert.Equal(4, len(sites))
   229  
   230  	b.Build(BuildCfg{})
   231  
   232  	// Check site config
   233  	for _, s := range sites {
   234  		require.True(t, s.Info.defaultContentLanguageInSubdir, s.Info.Title)
   235  		require.NotNil(t, s.disabledKinds)
   236  	}
   237  
   238  	gp1 := b.H.GetContentPage(filepath.FromSlash("content/sect/doc1.en.md"))
   239  	require.NotNil(t, gp1)
   240  	require.Equal(t, "doc1", gp1.title)
   241  	gp2 := b.H.GetContentPage(filepath.FromSlash("content/dummysect/notfound.md"))
   242  	require.Nil(t, gp2)
   243  
   244  	enSite := sites[0]
   245  	enSiteHome := enSite.getPage(KindHome)
   246  	require.True(t, enSiteHome.IsTranslated())
   247  
   248  	require.Equal(t, "en", enSite.Language.Lang)
   249  
   250  	assert.Equal(5, len(enSite.RegularPages))
   251  	assert.Equal(32, len(enSite.AllPages))
   252  
   253  	doc1en := enSite.RegularPages[0]
   254  	permalink := doc1en.Permalink()
   255  	require.Equal(t, "http://example.com/blog/en/sect/doc1-slug/", permalink, "invalid doc1.en permalink")
   256  	require.Len(t, doc1en.Translations(), 1, "doc1-en should have one translation, excluding itself")
   257  
   258  	doc2 := enSite.RegularPages[1]
   259  	permalink = doc2.Permalink()
   260  	require.Equal(t, "http://example.com/blog/en/sect/doc2/", permalink, "invalid doc2 permalink")
   261  
   262  	doc3 := enSite.RegularPages[2]
   263  	permalink = doc3.Permalink()
   264  	// Note that /superbob is a custom URL set in frontmatter.
   265  	// We respect that URL literally (it can be /search.json)
   266  	// and do no not do any language code prefixing.
   267  	require.Equal(t, "http://example.com/blog/superbob/", permalink, "invalid doc3 permalink")
   268  
   269  	require.Equal(t, "/superbob", doc3.URL(), "invalid url, was specified on doc3")
   270  	b.AssertFileContent("public/superbob/index.html", "doc3|Hello|en")
   271  	require.Equal(t, doc2.Next, doc3, "doc3 should follow doc2, in .Next")
   272  
   273  	doc1fr := doc1en.Translations()[0]
   274  	permalink = doc1fr.Permalink()
   275  	require.Equal(t, "http://example.com/blog/fr/sect/doc1/", permalink, "invalid doc1fr permalink")
   276  
   277  	require.Equal(t, doc1en.Translations()[0], doc1fr, "doc1-en should have doc1-fr as translation")
   278  	require.Equal(t, doc1fr.Translations()[0], doc1en, "doc1-fr should have doc1-en as translation")
   279  	require.Equal(t, "fr", doc1fr.Language().Lang)
   280  
   281  	doc4 := enSite.AllPages[4]
   282  	permalink = doc4.Permalink()
   283  	require.Equal(t, "http://example.com/blog/fr/sect/doc4/", permalink, "invalid doc4 permalink")
   284  	require.Equal(t, "/blog/fr/sect/doc4/", doc4.URL())
   285  
   286  	require.Len(t, doc4.Translations(), 0, "found translations for doc4")
   287  
   288  	doc5 := enSite.AllPages[5]
   289  	permalink = doc5.Permalink()
   290  	require.Equal(t, "http://example.com/blog/fr/somewhere/else/doc5/", permalink, "invalid doc5 permalink")
   291  
   292  	// Taxonomies and their URLs
   293  	require.Len(t, enSite.Taxonomies, 1, "should have 1 taxonomy")
   294  	tags := enSite.Taxonomies["tags"]
   295  	require.Len(t, tags, 2, "should have 2 different tags")
   296  	require.Equal(t, tags["tag1"][0].Page, doc1en, "first tag1 page should be doc1")
   297  
   298  	frSite := sites[1]
   299  
   300  	require.Equal(t, "fr", frSite.Language.Lang)
   301  	require.Len(t, frSite.RegularPages, 4, "should have 3 pages")
   302  	require.Len(t, frSite.AllPages, 32, "should have 32 total pages (including translations and nodes)")
   303  
   304  	for _, frenchPage := range frSite.RegularPages {
   305  		require.Equal(t, "fr", frenchPage.Lang())
   306  	}
   307  
   308  	// See https://github.com/gohugoio/hugo/issues/4285
   309  	// Before Hugo 0.33 you had to be explicit with the content path to get the correct Page, which
   310  	// isn't ideal in a multilingual setup. You want a way to get the current language version if available.
   311  	// Now you can do lookups with translation base name to get that behaviour.
   312  	// Let us test all the regular page variants:
   313  	getPageDoc1En := enSite.getPage(KindPage, filepath.ToSlash(doc1en.Path()))
   314  	getPageDoc1EnBase := enSite.getPage(KindPage, "sect/doc1")
   315  	getPageDoc1Fr := frSite.getPage(KindPage, filepath.ToSlash(doc1fr.Path()))
   316  	getPageDoc1FrBase := frSite.getPage(KindPage, "sect/doc1")
   317  	require.Equal(t, doc1en, getPageDoc1En)
   318  	require.Equal(t, doc1fr, getPageDoc1Fr)
   319  	require.Equal(t, doc1en, getPageDoc1EnBase)
   320  	require.Equal(t, doc1fr, getPageDoc1FrBase)
   321  
   322  	// Check redirect to main language, French
   323  	b.AssertFileContent("public/index.html", "0; url=http://example.com/blog/fr")
   324  
   325  	// check home page content (including data files rendering)
   326  	b.AssertFileContent("public/en/index.html", "Default Home Page 1", "Hello", "Hugo Rocks!")
   327  	b.AssertFileContent("public/fr/index.html", "French Home Page 1", "Bonjour", "Hugo Rocks!")
   328  
   329  	// check single page content
   330  	b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Shortcode: Bonjour", "LingoFrench")
   331  	b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Shortcode: Hello", "LingoDefault")
   332  
   333  	// Check node translations
   334  	homeEn := enSite.getPage(KindHome)
   335  	require.NotNil(t, homeEn)
   336  	require.Len(t, homeEn.Translations(), 3)
   337  	require.Equal(t, "fr", homeEn.Translations()[0].Lang())
   338  	require.Equal(t, "nn", homeEn.Translations()[1].Lang())
   339  	require.Equal(t, "På nynorsk", homeEn.Translations()[1].title)
   340  	require.Equal(t, "nb", homeEn.Translations()[2].Lang())
   341  	require.Equal(t, "På bokmål", homeEn.Translations()[2].title, configSuffix)
   342  	require.Equal(t, "Bokmål", homeEn.Translations()[2].Language().LanguageName, configSuffix)
   343  
   344  	sectFr := frSite.getPage(KindSection, "sect")
   345  	require.NotNil(t, sectFr)
   346  
   347  	require.Equal(t, "fr", sectFr.Lang())
   348  	require.Len(t, sectFr.Translations(), 1)
   349  	require.Equal(t, "en", sectFr.Translations()[0].Lang())
   350  	require.Equal(t, "Sects", sectFr.Translations()[0].title)
   351  
   352  	nnSite := sites[2]
   353  	require.Equal(t, "nn", nnSite.Language.Lang)
   354  	taxNn := nnSite.getPage(KindTaxonomyTerm, "lag")
   355  	require.NotNil(t, taxNn)
   356  	require.Len(t, taxNn.Translations(), 1)
   357  	require.Equal(t, "nb", taxNn.Translations()[0].Lang())
   358  
   359  	taxTermNn := nnSite.getPage(KindTaxonomy, "lag", "sogndal")
   360  	require.NotNil(t, taxTermNn)
   361  	require.Len(t, taxTermNn.Translations(), 1)
   362  	require.Equal(t, "nb", taxTermNn.Translations()[0].Lang())
   363  
   364  	// Check sitemap(s)
   365  	b.AssertFileContent("public/sitemap.xml",
   366  		"<loc>http://example.com/blog/en/sitemap.xml</loc>",
   367  		"<loc>http://example.com/blog/fr/sitemap.xml</loc>")
   368  	b.AssertFileContent("public/en/sitemap.xml", "http://example.com/blog/en/sect/doc2/")
   369  	b.AssertFileContent("public/fr/sitemap.xml", "http://example.com/blog/fr/sect/doc1/")
   370  
   371  	// Check taxonomies
   372  	enTags := enSite.Taxonomies["tags"]
   373  	frTags := frSite.Taxonomies["plaques"]
   374  	require.Len(t, enTags, 2, fmt.Sprintf("Tags in en: %v", enTags))
   375  	require.Len(t, frTags, 2, fmt.Sprintf("Tags in fr: %v", frTags))
   376  	require.NotNil(t, enTags["tag1"])
   377  	require.NotNil(t, frTags["frtag1"])
   378  	b.AssertFileContent("public/fr/plaques/frtag1/index.html", "Frtag1|Bonjour|http://example.com/blog/fr/plaques/frtag1/")
   379  	b.AssertFileContent("public/en/tags/tag1/index.html", "Tag1|Hello|http://example.com/blog/en/tags/tag1/")
   380  
   381  	// Check Blackfriday config
   382  	require.True(t, strings.Contains(string(doc1fr.content()), "&laquo;"), string(doc1fr.content()))
   383  	require.False(t, strings.Contains(string(doc1en.content()), "&laquo;"), string(doc1en.content()))
   384  	require.True(t, strings.Contains(string(doc1en.content()), "&ldquo;"), string(doc1en.content()))
   385  
   386  	// Check that the drafts etc. are not built/processed/rendered.
   387  	assertShouldNotBuild(t, b.H)
   388  
   389  	// en and nn have custom site menus
   390  	require.Len(t, frSite.Menus, 0, "fr: "+configSuffix)
   391  	require.Len(t, enSite.Menus, 1, "en: "+configSuffix)
   392  	require.Len(t, nnSite.Menus, 1, "nn: "+configSuffix)
   393  
   394  	require.Equal(t, "Home", enSite.Menus["main"].ByName()[0].Name)
   395  	require.Equal(t, "Heim", nnSite.Menus["main"].ByName()[0].Name)
   396  
   397  	// Issue #1302
   398  	require.Equal(t, template.URL(""), enSite.RegularPages[0].RSSLink())
   399  
   400  	// Issue #3108
   401  	next := enSite.RegularPages[0].Next
   402  	require.NotNil(t, next)
   403  	require.Equal(t, KindPage, next.Kind)
   404  
   405  	for {
   406  		if next == nil {
   407  			break
   408  		}
   409  		require.Equal(t, KindPage, next.Kind)
   410  		next = next.Next
   411  	}
   412  
   413  	// Check bundles
   414  	bundleFr := frSite.getPage(KindPage, "bundles/b1/index.md")
   415  	require.NotNil(t, bundleFr)
   416  	require.Equal(t, "/blog/fr/bundles/b1/", bundleFr.RelPermalink())
   417  	require.Equal(t, 1, len(bundleFr.Resources))
   418  	logoFr := bundleFr.Resources.GetMatch("logo*")
   419  	require.NotNil(t, logoFr)
   420  	require.Equal(t, "/blog/fr/bundles/b1/logo.png", logoFr.RelPermalink())
   421  	b.AssertFileContent("public/fr/bundles/b1/logo.png", "PNG Data")
   422  
   423  	bundleEn := enSite.getPage(KindPage, "bundles/b1/index.en.md")
   424  	require.NotNil(t, bundleEn)
   425  	require.Equal(t, "/blog/en/bundles/b1/", bundleEn.RelPermalink())
   426  	require.Equal(t, 1, len(bundleEn.Resources))
   427  	logoEn := bundleEn.Resources.GetMatch("logo*")
   428  	require.NotNil(t, logoEn)
   429  	require.Equal(t, "/blog/en/bundles/b1/logo.png", logoEn.RelPermalink())
   430  	b.AssertFileContent("public/en/bundles/b1/logo.png", "PNG Data")
   431  
   432  }
   433  
   434  func TestMultiSitesRebuild(t *testing.T) {
   435  	// t.Parallel() not supported, see https://github.com/fortytw2/leaktest/issues/4
   436  	// This leaktest seems to be a little bit shaky on Travis.
   437  	if !isCI() {
   438  		defer leaktest.CheckTimeout(t, 10*time.Second)()
   439  	}
   440  
   441  	assert := require.New(t)
   442  
   443  	b := newMultiSiteTestDefaultBuilder(t).Running().CreateSites().Build(BuildCfg{})
   444  
   445  	sites := b.H.Sites
   446  	fs := b.Fs
   447  
   448  	b.AssertFileContent("public/en/sect/doc2/index.html", "Single: doc2|Hello|en|\n\n<h1 id=\"doc2\">doc2</h1>\n\n<p><em>some content</em>")
   449  
   450  	enSite := sites[0]
   451  	frSite := sites[1]
   452  
   453  	assert.Len(enSite.RegularPages, 5)
   454  	assert.Len(frSite.RegularPages, 4)
   455  
   456  	// Verify translations
   457  	b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Hello")
   458  	b.AssertFileContent("public/fr/sect/doc1/index.html", "Bonjour")
   459  
   460  	// check single page content
   461  	b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Shortcode: Bonjour")
   462  	b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Shortcode: Hello")
   463  
   464  	contentFs := b.H.BaseFs.Content.Fs
   465  
   466  	for i, this := range []struct {
   467  		preFunc    func(t *testing.T)
   468  		events     []fsnotify.Event
   469  		assertFunc func(t *testing.T)
   470  	}{
   471  		// * Remove doc
   472  		// * Add docs existing languages
   473  		// (Add doc new language: TODO(bep) we should load config.toml as part of these so we can add languages).
   474  		// * Rename file
   475  		// * Change doc
   476  		// * Change a template
   477  		// * Change language file
   478  		{
   479  			func(t *testing.T) {
   480  				fs.Source.Remove("content/sect/doc2.en.md")
   481  			},
   482  			[]fsnotify.Event{{Name: filepath.FromSlash("content/sect/doc2.en.md"), Op: fsnotify.Remove}},
   483  			func(t *testing.T) {
   484  				assert.Len(enSite.RegularPages, 4, "1 en removed")
   485  
   486  				// Check build stats
   487  				require.Equal(t, 1, enSite.draftCount, "Draft")
   488  				require.Equal(t, 1, enSite.futureCount, "Future")
   489  				require.Equal(t, 1, enSite.expiredCount, "Expired")
   490  				require.Equal(t, 0, frSite.draftCount, "Draft")
   491  				require.Equal(t, 1, frSite.futureCount, "Future")
   492  				require.Equal(t, 1, frSite.expiredCount, "Expired")
   493  			},
   494  		},
   495  		{
   496  			func(t *testing.T) {
   497  				writeNewContentFile(t, contentFs, "new_en_1", "2016-07-31", "new1.en.md", -5)
   498  				writeNewContentFile(t, contentFs, "new_en_2", "1989-07-30", "new2.en.md", -10)
   499  				writeNewContentFile(t, contentFs, "new_fr_1", "2016-07-30", "new1.fr.md", 10)
   500  			},
   501  			[]fsnotify.Event{
   502  				{Name: filepath.FromSlash("content/new1.en.md"), Op: fsnotify.Create},
   503  				{Name: filepath.FromSlash("content/new2.en.md"), Op: fsnotify.Create},
   504  				{Name: filepath.FromSlash("content/new1.fr.md"), Op: fsnotify.Create},
   505  			},
   506  			func(t *testing.T) {
   507  				assert.Len(enSite.RegularPages, 6)
   508  				assert.Len(enSite.AllPages, 34)
   509  				assert.Len(frSite.RegularPages, 5)
   510  				require.Equal(t, "new_fr_1", frSite.RegularPages[3].title)
   511  				require.Equal(t, "new_en_2", enSite.RegularPages[0].title)
   512  				require.Equal(t, "new_en_1", enSite.RegularPages[1].title)
   513  
   514  				rendered := readDestination(t, fs, "public/en/new1/index.html")
   515  				require.True(t, strings.Contains(rendered, "new_en_1"), rendered)
   516  			},
   517  		},
   518  		{
   519  			func(t *testing.T) {
   520  				p := "sect/doc1.en.md"
   521  				doc1 := readFileFromFs(t, contentFs, p)
   522  				doc1 += "CHANGED"
   523  				writeToFs(t, contentFs, p, doc1)
   524  			},
   525  			[]fsnotify.Event{{Name: filepath.FromSlash("content/sect/doc1.en.md"), Op: fsnotify.Write}},
   526  			func(t *testing.T) {
   527  				assert.Len(enSite.RegularPages, 6)
   528  				doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
   529  				require.True(t, strings.Contains(doc1, "CHANGED"), doc1)
   530  
   531  			},
   532  		},
   533  		// Rename a file
   534  		{
   535  			func(t *testing.T) {
   536  				if err := contentFs.Rename("new1.en.md", "new1renamed.en.md"); err != nil {
   537  					t.Fatalf("Rename failed: %s", err)
   538  				}
   539  			},
   540  			[]fsnotify.Event{
   541  				{Name: filepath.FromSlash("content/new1renamed.en.md"), Op: fsnotify.Rename},
   542  				{Name: filepath.FromSlash("content/new1.en.md"), Op: fsnotify.Rename},
   543  			},
   544  			func(t *testing.T) {
   545  				assert.Len(enSite.RegularPages, 6, "Rename")
   546  				require.Equal(t, "new_en_1", enSite.RegularPages[1].title)
   547  				rendered := readDestination(t, fs, "public/en/new1renamed/index.html")
   548  				require.True(t, strings.Contains(rendered, "new_en_1"), rendered)
   549  			}},
   550  		{
   551  			// Change a template
   552  			func(t *testing.T) {
   553  				template := "layouts/_default/single.html"
   554  				templateContent := readSource(t, fs, template)
   555  				templateContent += "{{ print \"Template Changed\"}}"
   556  				writeSource(t, fs, template, templateContent)
   557  			},
   558  			[]fsnotify.Event{{Name: filepath.FromSlash("layouts/_default/single.html"), Op: fsnotify.Write}},
   559  			func(t *testing.T) {
   560  				assert.Len(enSite.RegularPages, 6)
   561  				assert.Len(enSite.AllPages, 34)
   562  				assert.Len(frSite.RegularPages, 5)
   563  				doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
   564  				require.True(t, strings.Contains(doc1, "Template Changed"), doc1)
   565  			},
   566  		},
   567  		{
   568  			// Change a language file
   569  			func(t *testing.T) {
   570  				languageFile := "i18n/fr.yaml"
   571  				langContent := readSource(t, fs, languageFile)
   572  				langContent = strings.Replace(langContent, "Bonjour", "Salut", 1)
   573  				writeSource(t, fs, languageFile, langContent)
   574  			},
   575  			[]fsnotify.Event{{Name: filepath.FromSlash("i18n/fr.yaml"), Op: fsnotify.Write}},
   576  			func(t *testing.T) {
   577  				assert.Len(enSite.RegularPages, 6)
   578  				assert.Len(enSite.AllPages, 34)
   579  				assert.Len(frSite.RegularPages, 5)
   580  				docEn := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
   581  				require.True(t, strings.Contains(docEn, "Hello"), "No Hello")
   582  				docFr := readDestination(t, fs, "public/fr/sect/doc1/index.html")
   583  				require.True(t, strings.Contains(docFr, "Salut"), "No Salut")
   584  
   585  				homeEn := enSite.getPage(KindHome)
   586  				require.NotNil(t, homeEn)
   587  				assert.Len(homeEn.Translations(), 3)
   588  				require.Equal(t, "fr", homeEn.Translations()[0].Lang())
   589  
   590  			},
   591  		},
   592  		// Change a shortcode
   593  		{
   594  			func(t *testing.T) {
   595  				writeSource(t, fs, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}")
   596  			},
   597  			[]fsnotify.Event{
   598  				{Name: filepath.FromSlash("layouts/shortcodes/shortcode.html"), Op: fsnotify.Write},
   599  			},
   600  			func(t *testing.T) {
   601  				assert.Len(enSite.RegularPages, 6)
   602  				assert.Len(enSite.AllPages, 34)
   603  				assert.Len(frSite.RegularPages, 5)
   604  				b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Modified Shortcode: Salut")
   605  				b.AssertFileContent("public/en/sect/doc1-slug/index.html", "Single", "Modified Shortcode: Hello")
   606  			},
   607  		},
   608  	} {
   609  
   610  		if this.preFunc != nil {
   611  			this.preFunc(t)
   612  		}
   613  
   614  		err := b.H.Build(BuildCfg{}, this.events...)
   615  
   616  		if err != nil {
   617  			t.Fatalf("[%d] Failed to rebuild sites: %s", i, err)
   618  		}
   619  
   620  		this.assertFunc(t)
   621  	}
   622  
   623  	// Check that the drafts etc. are not built/processed/rendered.
   624  	assertShouldNotBuild(t, b.H)
   625  
   626  }
   627  
   628  func assertShouldNotBuild(t *testing.T, sites *HugoSites) {
   629  	s := sites.Sites[0]
   630  
   631  	for _, p := range s.rawAllPages {
   632  		// No HTML when not processed
   633  		require.Equal(t, p.shouldBuild(), bytes.Contains(p.workContent, []byte("</")), p.BaseFileName()+": "+string(p.workContent))
   634  		require.Equal(t, p.shouldBuild(), p.content() != "", p.BaseFileName())
   635  
   636  		require.Equal(t, p.shouldBuild(), p.content() != "", p.BaseFileName())
   637  
   638  	}
   639  }
   640  
   641  func TestAddNewLanguage(t *testing.T) {
   642  	t.Parallel()
   643  	assert := require.New(t)
   644  
   645  	b := newMultiSiteTestDefaultBuilder(t)
   646  	b.CreateSites().Build(BuildCfg{})
   647  
   648  	fs := b.Fs
   649  
   650  	newConfig := multiSiteTOMLConfigTemplate + `
   651  
   652  [Languages.sv]
   653  weight = 15
   654  title = "Svenska"
   655  `
   656  
   657  	writeNewContentFile(t, fs.Source, "Swedish Contentfile", "2016-01-01", "content/sect/doc1.sv.md", 10)
   658  	// replace the config
   659  	b.WithNewConfig(newConfig)
   660  
   661  	sites := b.H
   662  
   663  	// Watching does not work with in-memory fs, so we trigger a reload manually
   664  	assert.NoError(sites.Cfg.(*langs.Language).Cfg.(*viper.Viper).ReadInConfig())
   665  	err := b.H.Build(BuildCfg{CreateSitesFromConfig: true})
   666  
   667  	if err != nil {
   668  		t.Fatalf("Failed to rebuild sites: %s", err)
   669  	}
   670  
   671  	require.Len(t, sites.Sites, 5, fmt.Sprintf("Len %d", len(sites.Sites)))
   672  
   673  	// The Swedish site should be put in the middle (language weight=15)
   674  	enSite := sites.Sites[0]
   675  	svSite := sites.Sites[1]
   676  	frSite := sites.Sites[2]
   677  	require.True(t, enSite.Language.Lang == "en", enSite.Language.Lang)
   678  	require.True(t, svSite.Language.Lang == "sv", svSite.Language.Lang)
   679  	require.True(t, frSite.Language.Lang == "fr", frSite.Language.Lang)
   680  
   681  	homeEn := enSite.getPage(KindHome)
   682  	require.NotNil(t, homeEn)
   683  	require.Len(t, homeEn.Translations(), 4)
   684  	require.Equal(t, "sv", homeEn.Translations()[0].Lang())
   685  
   686  	require.Len(t, enSite.RegularPages, 5)
   687  	require.Len(t, frSite.RegularPages, 4)
   688  
   689  	// Veriy Swedish site
   690  	require.Len(t, svSite.RegularPages, 1)
   691  	svPage := svSite.RegularPages[0]
   692  
   693  	require.Equal(t, "Swedish Contentfile", svPage.title)
   694  	require.Equal(t, "sv", svPage.Lang())
   695  	require.Len(t, svPage.Translations(), 2)
   696  	require.Len(t, svPage.AllTranslations(), 3)
   697  	require.Equal(t, "en", svPage.Translations()[0].Lang())
   698  
   699  	// Regular pages have no children
   700  	require.Len(t, svPage.Pages, 0)
   701  	require.Len(t, svPage.data["Pages"], 0)
   702  
   703  }
   704  
   705  func TestChangeDefaultLanguage(t *testing.T) {
   706  	t.Parallel()
   707  
   708  	assert := require.New(t)
   709  
   710  	b := newMultiSiteTestBuilder(t, "", "", map[string]interface{}{
   711  		"DefaultContentLanguage":         "fr",
   712  		"DefaultContentLanguageInSubdir": false,
   713  	})
   714  	b.CreateSites().Build(BuildCfg{})
   715  
   716  	b.AssertFileContent("public/sect/doc1/index.html", "Single", "Bonjour")
   717  	b.AssertFileContent("public/en/sect/doc2/index.html", "Single", "Hello")
   718  
   719  	// Switch language
   720  	b.WithNewConfigData(map[string]interface{}{
   721  		"DefaultContentLanguage":         "en",
   722  		"DefaultContentLanguageInSubdir": false,
   723  	})
   724  
   725  	// Watching does not work with in-memory fs, so we trigger a reload manually
   726  	// This does not look pretty, so we should think of something else.
   727  	assert.NoError(b.H.Cfg.(*langs.Language).Cfg.(*viper.Viper).ReadInConfig())
   728  	err := b.H.Build(BuildCfg{CreateSitesFromConfig: true})
   729  	if err != nil {
   730  		t.Fatalf("Failed to rebuild sites: %s", err)
   731  	}
   732  
   733  	// Default language is now en, so that should now be the "root" language
   734  	b.AssertFileContent("public/fr/sect/doc1/index.html", "Single", "Bonjour")
   735  	b.AssertFileContent("public/sect/doc2/index.html", "Single", "Hello")
   736  }
   737  
   738  // https://github.com/gohugoio/hugo/issues/4706
   739  func TestContentStressTest(t *testing.T) {
   740  	b := newTestSitesBuilder(t)
   741  
   742  	numPages := 500
   743  
   744  	contentTempl := `
   745  ---
   746  %s
   747  title: %q
   748  weight: %d
   749  multioutput: %t
   750  ---
   751  
   752  # Header
   753  
   754  CONTENT
   755  
   756  The End.
   757  `
   758  
   759  	contentTempl = strings.Replace(contentTempl, "CONTENT", strings.Repeat(`
   760  	
   761  ## Another header
   762  
   763  Some text. Some more text.
   764  
   765  `, 100), -1)
   766  
   767  	var content []string
   768  	defaultOutputs := `outputs: ["html", "json", "rss" ]`
   769  
   770  	for i := 1; i <= numPages; i++ {
   771  		outputs := defaultOutputs
   772  		multioutput := true
   773  		if i%3 == 0 {
   774  			outputs = `outputs: ["json"]`
   775  			multioutput = false
   776  		}
   777  		section := "s1"
   778  		if i%10 == 0 {
   779  			section = "s2"
   780  		}
   781  		content = append(content, []string{fmt.Sprintf("%s/page%d.md", section, i), fmt.Sprintf(contentTempl, outputs, fmt.Sprintf("Title %d", i), i, multioutput)}...)
   782  	}
   783  
   784  	content = append(content, []string{"_index.md", fmt.Sprintf(contentTempl, defaultOutputs, fmt.Sprintf("Home %d", 0), 0, true)}...)
   785  	content = append(content, []string{"s1/_index.md", fmt.Sprintf(contentTempl, defaultOutputs, fmt.Sprintf("S %d", 1), 1, true)}...)
   786  	content = append(content, []string{"s2/_index.md", fmt.Sprintf(contentTempl, defaultOutputs, fmt.Sprintf("S %d", 2), 2, true)}...)
   787  
   788  	b.WithSimpleConfigFile()
   789  	b.WithTemplates("layouts/_default/single.html", `Single: {{ .Content }}`)
   790  	b.WithTemplates("layouts/_default/myview.html", `View: {{ len .Content }}`)
   791  	b.WithTemplates("layouts/_default/single.json", `Single JSON: {{ .Content }}`)
   792  	b.WithTemplates("layouts/_default/list.html", `
   793  Page: {{ .Paginator.PageNumber }}
   794  P: {{ path.Join .Path }}
   795  List: {{ len .Paginator.Pages }}|List Content: {{ len .Content }}
   796  {{ $shuffled :=  where .Site.RegularPages "Params.multioutput" true | shuffle }}
   797  {{ $first5 := $shuffled | first 5 }}
   798  L1: {{ len .Site.RegularPages }} L2: {{ len $first5 }}
   799  {{ range $i, $e := $first5 }}
   800  Render {{ $i }}: {{ .Render "myview" }}
   801  {{ end }}
   802  END
   803  `)
   804  
   805  	b.WithContent(content...)
   806  
   807  	b.CreateSites().Build(BuildCfg{})
   808  
   809  	contentMatchers := []string{"<h2 id=\"another-header\">Another header</h2>", "<h2 id=\"another-header-99\">Another header</h2>", "<p>The End.</p>"}
   810  
   811  	for i := 1; i <= numPages; i++ {
   812  		if i%3 != 0 {
   813  			section := "s1"
   814  			if i%10 == 0 {
   815  				section = "s2"
   816  			}
   817  			checkContent(b, fmt.Sprintf("public/%s/page%d/index.html", section, i), 8343, contentMatchers...)
   818  		}
   819  	}
   820  
   821  	for i := 1; i <= numPages; i++ {
   822  		section := "s1"
   823  		if i%10 == 0 {
   824  			section = "s2"
   825  		}
   826  		checkContent(b, fmt.Sprintf("public/%s/page%d/index.json", section, i), 8348, contentMatchers...)
   827  	}
   828  
   829  	checkContent(b, "public/s1/index.html", 184, "P: s1/_index.md\nList: 10|List Content: 8335\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8335\n\nRender 1: View: 8335\n\nRender 2: View: 8335\n\nRender 3: View: 8335\n\nRender 4: View: 8335\n\nEND\n")
   830  	checkContent(b, "public/s2/index.html", 184, "P: s2/_index.md\nList: 10|List Content: 8335", "Render 4: View: 8335\n\nEND")
   831  	checkContent(b, "public/index.html", 181, "P: _index.md\nList: 10|List Content: 8335", "4: View: 8335\n\nEND")
   832  
   833  	// Chek paginated pages
   834  	for i := 2; i <= 9; i++ {
   835  		checkContent(b, fmt.Sprintf("public/page/%d/index.html", i), 181, fmt.Sprintf("Page: %d", i), "Content: 8335\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8335", "Render 4: View: 8335\n\nEND")
   836  	}
   837  }
   838  
   839  func checkContent(s *sitesBuilder, filename string, length int, matches ...string) {
   840  	content := readDestination(s.T, s.Fs, filename)
   841  	for _, match := range matches {
   842  		if !strings.Contains(content, match) {
   843  			s.Fatalf("No match for %q in content for %s\n%q", match, filename, content)
   844  		}
   845  	}
   846  	if len(content) != length {
   847  		s.Fatalf("got %d expected %d", len(content), length)
   848  	}
   849  }
   850  
   851  func TestTableOfContentsInShortcodes(t *testing.T) {
   852  	t.Parallel()
   853  
   854  	b := newMultiSiteTestDefaultBuilder(t)
   855  
   856  	b.WithTemplatesAdded("layouts/shortcodes/toc.html", tocShortcode)
   857  	b.WithContent("post/simple.en.md", tocPageSimple)
   858  	b.WithContent("post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings)
   859  
   860  	b.CreateSites().Build(BuildCfg{})
   861  
   862  	b.AssertFileContent("public/en/post/simple/index.html", tocPageSimpleExpected)
   863  	b.AssertFileContent("public/en/post/withSCInHeading/index.html", tocPageWithShortcodesInHeadingsExpected)
   864  }
   865  
   866  var tocShortcode = `
   867  {{ .Page.TableOfContents }}
   868  `
   869  
   870  func TestSelfReferencedContentInShortcode(t *testing.T) {
   871  	t.Parallel()
   872  
   873  	b := newMultiSiteTestDefaultBuilder(t)
   874  
   875  	var (
   876  		shortcode = `{{- .Page.Content -}}{{- .Page.Summary -}}{{- .Page.Plain -}}{{- .Page.PlainWords -}}{{- .Page.WordCount -}}{{- .Page.ReadingTime -}}`
   877  
   878  		page = `---
   879  title: sctest
   880  ---
   881  Empty:{{< mycontent >}}:
   882  `
   883  	)
   884  
   885  	b.WithTemplatesAdded("layouts/shortcodes/mycontent.html", shortcode)
   886  	b.WithContent("post/simple.en.md", page)
   887  
   888  	b.CreateSites().Build(BuildCfg{})
   889  
   890  	b.AssertFileContent("public/en/post/simple/index.html", "Empty:[]00:")
   891  }
   892  
   893  var tocPageSimple = `---
   894  title: tocTest
   895  publishdate: "2000-01-01"
   896  ---
   897  {{< toc >}}
   898  # Heading 1 {#1}
   899  Some text.
   900  ## Subheading 1.1 {#1-1}
   901  Some more text.
   902  # Heading 2 {#2}
   903  Even more text.
   904  ## Subheading 2.1 {#2-1}
   905  Lorem ipsum...
   906  `
   907  
   908  var tocPageSimpleExpected = `<nav id="TableOfContents">
   909  <ul>
   910  <li><a href="#1">Heading 1</a>
   911  <ul>
   912  <li><a href="#1-1">Subheading 1.1</a></li>
   913  </ul></li>
   914  <li><a href="#2">Heading 2</a>
   915  <ul>
   916  <li><a href="#2-1">Subheading 2.1</a></li>
   917  </ul></li>
   918  </ul>
   919  </nav>`
   920  
   921  var tocPageWithShortcodesInHeadings = `---
   922  title: tocTest
   923  publishdate: "2000-01-01"
   924  ---
   925  
   926  {{< toc >}}
   927  
   928  # Heading 1 {#1}
   929  
   930  Some text.
   931  
   932  ## Subheading 1.1 {{< shortcode >}} {#1-1}
   933  
   934  Some more text.
   935  
   936  # Heading 2 {{% shortcode %}} {#2}
   937  
   938  Even more text.
   939  
   940  ## Subheading 2.1 {#2-1}
   941  
   942  Lorem ipsum...
   943  `
   944  
   945  var tocPageWithShortcodesInHeadingsExpected = `<nav id="TableOfContents">
   946  <ul>
   947  <li><a href="#1">Heading 1</a>
   948  <ul>
   949  <li><a href="#1-1">Subheading 1.1 Shortcode: Hello</a></li>
   950  </ul></li>
   951  <li><a href="#2">Heading 2 Shortcode: Hello</a>
   952  <ul>
   953  <li><a href="#2-1">Subheading 2.1</a></li>
   954  </ul></li>
   955  </ul>
   956  </nav>`
   957  
   958  var multiSiteTOMLConfigTemplate = `
   959  baseURL = "http://example.com/blog"
   960  rssURI = "index.xml"
   961  
   962  paginate = 1
   963  disablePathToLower = true
   964  defaultContentLanguage = "{{ .DefaultContentLanguage }}"
   965  defaultContentLanguageInSubdir = {{ .DefaultContentLanguageInSubdir }}
   966  
   967  [permalinks]
   968  other = "/somewhere/else/:filename"
   969  
   970  [blackfriday]
   971  angledQuotes = true
   972  
   973  [Taxonomies]
   974  tag = "tags"
   975  
   976  [Languages]
   977  [Languages.en]
   978  weight = 10
   979  title = "In English"
   980  languageName = "English"
   981  [Languages.en.blackfriday]
   982  angledQuotes = false
   983  [[Languages.en.menu.main]]
   984  url    = "/"
   985  name   = "Home"
   986  weight = 0
   987  
   988  [Languages.fr]
   989  weight = 20
   990  title = "Le Français"
   991  languageName = "Français"
   992  [Languages.fr.Taxonomies]
   993  plaque = "plaques"
   994  
   995  [Languages.nn]
   996  weight = 30
   997  title = "På nynorsk"
   998  languageName = "Nynorsk"
   999  paginatePath = "side"
  1000  [Languages.nn.Taxonomies]
  1001  lag = "lag"
  1002  [[Languages.nn.menu.main]]
  1003  url    = "/"
  1004  name   = "Heim"
  1005  weight = 1
  1006  
  1007  [Languages.nb]
  1008  weight = 40
  1009  title = "På bokmål"
  1010  languageName = "Bokmål"
  1011  paginatePath = "side"
  1012  [Languages.nb.Taxonomies]
  1013  lag = "lag"
  1014  `
  1015  
  1016  var multiSiteYAMLConfigTemplate = `
  1017  baseURL: "http://example.com/blog"
  1018  rssURI: "index.xml"
  1019  
  1020  disablePathToLower: true
  1021  paginate: 1
  1022  defaultContentLanguage: "{{ .DefaultContentLanguage }}"
  1023  defaultContentLanguageInSubdir: {{ .DefaultContentLanguageInSubdir }}
  1024  
  1025  permalinks:
  1026      other: "/somewhere/else/:filename"
  1027  
  1028  blackfriday:
  1029      angledQuotes: true
  1030  
  1031  Taxonomies:
  1032      tag: "tags"
  1033  
  1034  Languages:
  1035      en:
  1036          weight: 10
  1037          title: "In English"
  1038          languageName: "English"
  1039          blackfriday:
  1040              angledQuotes: false
  1041          menu:
  1042              main:
  1043                  - url: "/"
  1044                    name: "Home"
  1045                    weight: 0
  1046      fr:
  1047          weight: 20
  1048          title: "Le Français"
  1049          languageName: "Français"
  1050          Taxonomies:
  1051              plaque: "plaques"
  1052      nn:
  1053          weight: 30
  1054          title: "På nynorsk"
  1055          languageName: "Nynorsk"
  1056          paginatePath: "side"
  1057          Taxonomies:
  1058              lag: "lag"
  1059          menu:
  1060              main:
  1061                  - url: "/"
  1062                    name: "Heim"
  1063                    weight: 1
  1064      nb:
  1065          weight: 40
  1066          title: "På bokmål"
  1067          languageName: "Bokmål"
  1068          paginatePath: "side"
  1069          Taxonomies:
  1070              lag: "lag"
  1071  
  1072  `
  1073  
  1074  // TODO(bep) clean move
  1075  var multiSiteJSONConfigTemplate = `
  1076  {
  1077    "baseURL": "http://example.com/blog",
  1078    "rssURI": "index.xml",
  1079    "paginate": 1,
  1080    "disablePathToLower": true,
  1081    "defaultContentLanguage": "{{ .DefaultContentLanguage }}",
  1082    "defaultContentLanguageInSubdir": true,
  1083    "permalinks": {
  1084      "other": "/somewhere/else/:filename"
  1085    },
  1086    "blackfriday": {
  1087      "angledQuotes": true
  1088    },
  1089    "Taxonomies": {
  1090      "tag": "tags"
  1091    },
  1092    "Languages": {
  1093      "en": {
  1094        "weight": 10,
  1095        "title": "In English",
  1096        "languageName": "English",
  1097        "blackfriday": {
  1098          "angledQuotes": false
  1099        },
  1100  	  "menu": {
  1101          "main": [
  1102  			{
  1103  			"url": "/",
  1104  			"name": "Home",
  1105  			"weight": 0
  1106  			}
  1107  		]
  1108        }
  1109      },
  1110      "fr": {
  1111        "weight": 20,
  1112        "title": "Le Français",
  1113        "languageName": "Français",
  1114        "Taxonomies": {
  1115          "plaque": "plaques"
  1116        }
  1117      },
  1118      "nn": {
  1119        "weight": 30,
  1120        "title": "På nynorsk",
  1121        "paginatePath": "side",
  1122        "languageName": "Nynorsk",
  1123        "Taxonomies": {
  1124          "lag": "lag"
  1125        },
  1126  	  "menu": {
  1127          "main": [
  1128  			{
  1129          	"url": "/",
  1130  			"name": "Heim",
  1131  			"weight": 1
  1132  			}
  1133        	]
  1134        }
  1135      },
  1136      "nb": {
  1137        "weight": 40,
  1138        "title": "På bokmål",
  1139        "paginatePath": "side",
  1140        "languageName": "Bokmål",
  1141        "Taxonomies": {
  1142          "lag": "lag"
  1143        }
  1144      }
  1145    }
  1146  }
  1147  `
  1148  
  1149  func writeSource(t testing.TB, fs *hugofs.Fs, filename, content string) {
  1150  	writeToFs(t, fs.Source, filename, content)
  1151  }
  1152  
  1153  func writeToFs(t testing.TB, fs afero.Fs, filename, content string) {
  1154  	if err := afero.WriteFile(fs, filepath.FromSlash(filename), []byte(content), 0755); err != nil {
  1155  		t.Fatalf("Failed to write file: %s", err)
  1156  	}
  1157  }
  1158  
  1159  func readDestination(t testing.TB, fs *hugofs.Fs, filename string) string {
  1160  	return readFileFromFs(t, fs.Destination, filename)
  1161  }
  1162  
  1163  func destinationExists(fs *hugofs.Fs, filename string) bool {
  1164  	b, err := helpers.Exists(filename, fs.Destination)
  1165  	if err != nil {
  1166  		panic(err)
  1167  	}
  1168  	return b
  1169  }
  1170  
  1171  func readSource(t *testing.T, fs *hugofs.Fs, filename string) string {
  1172  	return readFileFromFs(t, fs.Source, filename)
  1173  }
  1174  
  1175  func readFileFromFs(t testing.TB, fs afero.Fs, filename string) string {
  1176  	filename = filepath.Clean(filename)
  1177  	b, err := afero.ReadFile(fs, filename)
  1178  	if err != nil {
  1179  		// Print some debug info
  1180  		root := strings.Split(filename, helpers.FilePathSeparator)[0]
  1181  		helpers.PrintFs(fs, root, os.Stdout)
  1182  		Fatalf(t, "Failed to read file: %s", err)
  1183  	}
  1184  	return string(b)
  1185  }
  1186  
  1187  const testPageTemplate = `---
  1188  title: "%s"
  1189  publishdate: "%s"
  1190  weight: %d
  1191  ---
  1192  # Doc %s
  1193  `
  1194  
  1195  func newTestPage(title, date string, weight int) string {
  1196  	return fmt.Sprintf(testPageTemplate, title, date, weight, title)
  1197  }
  1198  
  1199  func writeNewContentFile(t *testing.T, fs afero.Fs, title, date, filename string, weight int) {
  1200  	content := newTestPage(title, date, weight)
  1201  	writeToFs(t, fs, filename, content)
  1202  }
  1203  
  1204  type multiSiteTestBuilder struct {
  1205  	configData   interface{}
  1206  	config       string
  1207  	configFormat string
  1208  
  1209  	*sitesBuilder
  1210  }
  1211  
  1212  func newMultiSiteTestDefaultBuilder(t testing.TB) *multiSiteTestBuilder {
  1213  	return newMultiSiteTestBuilder(t, "", "", nil)
  1214  }
  1215  
  1216  func (b *multiSiteTestBuilder) WithNewConfig(config string) *multiSiteTestBuilder {
  1217  	b.WithConfigTemplate(b.configData, b.configFormat, config)
  1218  	return b
  1219  }
  1220  
  1221  func (b *multiSiteTestBuilder) WithNewConfigData(data interface{}) *multiSiteTestBuilder {
  1222  	b.WithConfigTemplate(data, b.configFormat, b.config)
  1223  	return b
  1224  }
  1225  
  1226  func newMultiSiteTestBuilder(t testing.TB, configFormat, config string, configData interface{}) *multiSiteTestBuilder {
  1227  	if configData == nil {
  1228  		configData = map[string]interface{}{
  1229  			"DefaultContentLanguage":         "fr",
  1230  			"DefaultContentLanguageInSubdir": true,
  1231  		}
  1232  	}
  1233  
  1234  	if config == "" {
  1235  		config = multiSiteTOMLConfigTemplate
  1236  	}
  1237  
  1238  	if configFormat == "" {
  1239  		configFormat = "toml"
  1240  	}
  1241  
  1242  	b := newTestSitesBuilder(t).WithConfigTemplate(configData, configFormat, config)
  1243  	b.WithContent("root.en.md", `---
  1244  title: root
  1245  weight: 10000
  1246  slug: root
  1247  publishdate: "2000-01-01"
  1248  ---
  1249  # root
  1250  `,
  1251  		"sect/doc1.en.md", `---
  1252  title: doc1
  1253  weight: 1
  1254  slug: doc1-slug
  1255  tags:
  1256   - tag1
  1257  publishdate: "2000-01-01"
  1258  ---
  1259  # doc1
  1260  *some "content"*
  1261  
  1262  {{< shortcode >}}
  1263  
  1264  {{< lingo >}}
  1265  
  1266  NOTE: slug should be used as URL
  1267  `,
  1268  		"sect/doc1.fr.md", `---
  1269  title: doc1
  1270  weight: 1
  1271  plaques:
  1272   - frtag1
  1273   - frtag2
  1274  publishdate: "2000-01-04"
  1275  ---
  1276  # doc1
  1277  *quelque "contenu"*
  1278  
  1279  {{< shortcode >}}
  1280  
  1281  {{< lingo >}}
  1282  
  1283  NOTE: should be in the 'en' Page's 'Translations' field.
  1284  NOTE: date is after "doc3"
  1285  `,
  1286  		"sect/doc2.en.md", `---
  1287  title: doc2
  1288  weight: 2
  1289  publishdate: "2000-01-02"
  1290  ---
  1291  # doc2
  1292  *some content*
  1293  NOTE: without slug, "doc2" should be used, without ".en" as URL
  1294  `,
  1295  		"sect/doc3.en.md", `---
  1296  title: doc3
  1297  weight: 3
  1298  publishdate: "2000-01-03"
  1299  aliases: [/en/al/alias1,/al/alias2/]
  1300  tags:
  1301   - tag2
  1302   - tag1
  1303  url: /superbob
  1304  ---
  1305  # doc3
  1306  *some content*
  1307  NOTE: third 'en' doc, should trigger pagination on home page.
  1308  `,
  1309  		"sect/doc4.md", `---
  1310  title: doc4
  1311  weight: 4
  1312  plaques:
  1313   - frtag1
  1314  publishdate: "2000-01-05"
  1315  ---
  1316  # doc4
  1317  *du contenu francophone*
  1318  NOTE: should use the defaultContentLanguage and mark this doc as 'fr'.
  1319  NOTE: doesn't have any corresponding translation in 'en'
  1320  `,
  1321  		"other/doc5.fr.md", `---
  1322  title: doc5
  1323  weight: 5
  1324  publishdate: "2000-01-06"
  1325  ---
  1326  # doc5
  1327  *autre contenu francophone*
  1328  NOTE: should use the "permalinks" configuration with :filename
  1329  `,
  1330  		// Add some for the stats
  1331  		"stats/expired.fr.md", `---
  1332  title: expired
  1333  publishdate: "2000-01-06"
  1334  expiryDate: "2001-01-06"
  1335  ---
  1336  # Expired
  1337  `,
  1338  		"stats/future.fr.md", `---
  1339  title: future
  1340  weight: 6
  1341  publishdate: "2100-01-06"
  1342  ---
  1343  # Future
  1344  `,
  1345  		"stats/expired.en.md", `---
  1346  title: expired
  1347  weight: 7
  1348  publishdate: "2000-01-06"
  1349  expiryDate: "2001-01-06"
  1350  ---
  1351  # Expired
  1352  `,
  1353  		"stats/future.en.md", `---
  1354  title: future
  1355  weight: 6
  1356  publishdate: "2100-01-06"
  1357  ---
  1358  # Future
  1359  `,
  1360  		"stats/draft.en.md", `---
  1361  title: expired
  1362  publishdate: "2000-01-06"
  1363  draft: true
  1364  ---
  1365  # Draft
  1366  `,
  1367  		"stats/tax.nn.md", `---
  1368  title: Tax NN
  1369  weight: 8
  1370  publishdate: "2000-01-06"
  1371  weight: 1001
  1372  lag:
  1373  - Sogndal
  1374  ---
  1375  # Tax NN
  1376  `,
  1377  		"stats/tax.nb.md", `---
  1378  title: Tax NB
  1379  weight: 8
  1380  publishdate: "2000-01-06"
  1381  weight: 1002
  1382  lag:
  1383  - Sogndal
  1384  ---
  1385  # Tax NB
  1386  `,
  1387  		// Bundle
  1388  		"bundles/b1/index.en.md", `---
  1389  title: Bundle EN
  1390  publishdate: "2000-01-06"
  1391  weight: 2001
  1392  ---
  1393  # Bundle Content EN
  1394  `,
  1395  		"bundles/b1/index.md", `---
  1396  title: Bundle Default
  1397  publishdate: "2000-01-06"
  1398  weight: 2002
  1399  ---
  1400  # Bundle Content Default
  1401  `,
  1402  		"bundles/b1/logo.png", `
  1403  PNG Data
  1404  `)
  1405  
  1406  	return &multiSiteTestBuilder{sitesBuilder: b, configFormat: configFormat, config: config, configData: configData}
  1407  }