github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/create/content_test.go (about)

     1  // Copyright 2016 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 create_test
    15  
    16  import (
    17  	"fmt"
    18  	"os"
    19  	"path/filepath"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/gohugoio/hugo/config"
    24  
    25  	"github.com/gohugoio/hugo/deps"
    26  
    27  	"github.com/gohugoio/hugo/hugolib"
    28  
    29  	"github.com/gohugoio/hugo/hugofs"
    30  
    31  	qt "github.com/frankban/quicktest"
    32  	"github.com/gohugoio/hugo/create"
    33  	"github.com/gohugoio/hugo/helpers"
    34  	"github.com/spf13/afero"
    35  )
    36  
    37  // TODO(bep) clean this up. Export the test site builder in Hugolib or something.
    38  func TestNewContentFromFile(t *testing.T) {
    39  	cases := []struct {
    40  		name     string
    41  		kind     string
    42  		path     string
    43  		expected any
    44  	}{
    45  		{"Post", "post", "post/sample-1.md", []string{`title = "Post Arch title"`, `test = "test1"`, "date = \"2015-01-12T19:20:04-07:00\""}},
    46  		{"Post org-mode", "post", "post/org-1.org", []string{`#+title: ORG-1`}},
    47  		{"Post, unknown content filetype", "post", "post/sample-1.pdoc", false},
    48  		{"Empty date", "emptydate", "post/sample-ed.md", []string{`title = "Empty Date Arch title"`, `test = "test1"`}},
    49  		{"Archetype file not found", "stump", "stump/sample-2.md", []string{`title: "Sample 2"`}}, // no archetype file
    50  		{"No archetype", "", "sample-3.md", []string{`title: "Sample 3"`}},                        // no archetype
    51  		{"Empty archetype", "product", "product/sample-4.md", []string{`title = "SAMPLE-4"`}},     // empty archetype front matter
    52  		{"Filenames", "filenames", "content/mypage/index.md", []string{"title = \"INDEX\"\n+++\n\n\nContentBaseName: mypage"}},
    53  		{"Branch Name", "name", "content/tags/tag-a/_index.md", []string{"+++\ntitle = 'Tag A'\n+++"}},
    54  
    55  		{"Lang 1", "lang", "post/lang-1.md", []string{`Site Lang: en|Name: Lang 1|i18n: Hugo Rocks!`}},
    56  		{"Lang 2", "lang", "post/lang-2.en.md", []string{`Site Lang: en|Name: Lang 2|i18n: Hugo Rocks!`}},
    57  		{"Lang nn file", "lang", "content/post/lang-3.nn.md", []string{`Site Lang: nn|Name: Lang 3|i18n: Hugo Rokkar!`}},
    58  		{"Lang nn dir", "lang", "content_nn/post/lang-4.md", []string{`Site Lang: nn|Name: Lang 4|i18n: Hugo Rokkar!`}},
    59  		{"Lang en in nn dir", "lang", "content_nn/post/lang-5.en.md", []string{`Site Lang: en|Name: Lang 5|i18n: Hugo Rocks!`}},
    60  		{"Lang en default", "lang", "post/my-bundle/index.md", []string{`Site Lang: en|Name: My Bundle|i18n: Hugo Rocks!`}},
    61  		{"Lang en file", "lang", "post/my-bundle/index.en.md", []string{`Site Lang: en|Name: My Bundle|i18n: Hugo Rocks!`}},
    62  		{"Lang nn bundle", "lang", "content/post/my-bundle/index.nn.md", []string{`Site Lang: nn|Name: My Bundle|i18n: Hugo Rokkar!`}},
    63  		{"Site", "site", "content/mypage/index.md", []string{"RegularPages .Site: 10", "RegularPages site: 10"}},
    64  		{"Shortcodes", "shortcodes", "shortcodes/go.md", []string{
    65  			`title = "GO"`,
    66  			"{{< myshortcode >}}",
    67  			"{{% myshortcode %}}",
    68  			"{{</* comment */>}}\n{{%/* comment */%}}",
    69  		}}, // shortcodes
    70  	}
    71  
    72  	c := qt.New(t)
    73  
    74  	for i, cas := range cases {
    75  		cas := cas
    76  
    77  		c.Run(cas.name, func(c *qt.C) {
    78  			c.Parallel()
    79  
    80  			mm := afero.NewMemMapFs()
    81  			c.Assert(initFs(mm), qt.IsNil)
    82  			cfg, fs := newTestCfg(c, mm)
    83  			h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
    84  			c.Assert(err, qt.IsNil)
    85  			err = create.NewContent(h, cas.kind, cas.path, false)
    86  
    87  			if b, ok := cas.expected.(bool); ok && !b {
    88  				if !b {
    89  					c.Assert(err, qt.Not(qt.IsNil))
    90  				}
    91  				return
    92  			}
    93  
    94  			c.Assert(err, qt.IsNil)
    95  
    96  			fname := filepath.FromSlash(cas.path)
    97  			if !strings.HasPrefix(fname, "content") {
    98  				fname = filepath.Join("content", fname)
    99  			}
   100  
   101  			content := readFileFromFs(c, fs.Source, fname)
   102  
   103  			for _, v := range cas.expected.([]string) {
   104  				found := strings.Contains(content, v)
   105  				if !found {
   106  					c.Fatalf("[%d] %q missing from output:\n%q", i, v, content)
   107  				}
   108  			}
   109  		})
   110  
   111  	}
   112  }
   113  
   114  func TestNewContentFromDir(t *testing.T) {
   115  	mm := afero.NewMemMapFs()
   116  	c := qt.New(t)
   117  
   118  	archetypeDir := filepath.Join("archetypes", "my-bundle")
   119  	c.Assert(mm.MkdirAll(archetypeDir, 0o755), qt.IsNil)
   120  
   121  	archetypeThemeDir := filepath.Join("themes", "mytheme", "archetypes", "my-theme-bundle")
   122  	c.Assert(mm.MkdirAll(archetypeThemeDir, 0o755), qt.IsNil)
   123  
   124  	contentFile := `
   125  File: %s
   126  Site Lang: {{ .Site.Language.Lang  }} 	
   127  Name: {{ replace .Name "-" " " | title }}
   128  i18n: {{ T "hugo" }}
   129  `
   130  
   131  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil)
   132  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.nn.md"), []byte(fmt.Sprintf(contentFile, "index.nn.md")), 0o755), qt.IsNil)
   133  
   134  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "pages", "bio.md"), []byte(fmt.Sprintf(contentFile, "bio.md")), 0o755), qt.IsNil)
   135  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "resources", "hugo1.json"), []byte(`hugo1: {{ printf "no template handling in here" }}`), 0o755), qt.IsNil)
   136  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "resources", "hugo2.xml"), []byte(`hugo2: {{ printf "no template handling in here" }}`), 0o755), qt.IsNil)
   137  
   138  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeThemeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil)
   139  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeThemeDir, "resources", "hugo1.json"), []byte(`hugo1: {{ printf "no template handling in here" }}`), 0o755), qt.IsNil)
   140  
   141  	c.Assert(initFs(mm), qt.IsNil)
   142  	cfg, fs := newTestCfg(c, mm)
   143  
   144  	h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
   145  	c.Assert(err, qt.IsNil)
   146  	c.Assert(len(h.Sites), qt.Equals, 2)
   147  
   148  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
   149  
   150  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
   151  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo2.xml")), `hugo2: {{ printf "no template handling in here" }}`)
   152  
   153  	// Content files should get the correct site context.
   154  	// TODO(bep) archetype check i18n
   155  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.md")), `File: index.md`, `Site Lang: en`, `Name: My Post`, `i18n: Hugo Rocks!`)
   156  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.nn.md")), `File: index.nn.md`, `Site Lang: nn`, `Name: My Post`, `i18n: Hugo Rokkar!`)
   157  
   158  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/pages/bio.md")), `File: bio.md`, `Site Lang: en`, `Name: Bio`)
   159  
   160  	c.Assert(create.NewContent(h, "my-theme-bundle", "post/my-theme-post", false), qt.IsNil)
   161  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/index.md")), `File: index.md`, `Site Lang: en`, `Name: My Theme Post`, `i18n: Hugo Rocks!`)
   162  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
   163  }
   164  
   165  func TestNewContentFromDirSiteFunction(t *testing.T) {
   166  	mm := afero.NewMemMapFs()
   167  	c := qt.New(t)
   168  
   169  	archetypeDir := filepath.Join("archetypes", "my-bundle")
   170  	defaultArchetypeDir := filepath.Join("archetypes", "default")
   171  	c.Assert(mm.MkdirAll(archetypeDir, 0o755), qt.IsNil)
   172  	c.Assert(mm.MkdirAll(defaultArchetypeDir, 0o755), qt.IsNil)
   173  
   174  	contentFile := `
   175  File: %s
   176  site RegularPages: {{ len site.RegularPages  }} 	
   177  
   178  `
   179  
   180  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil)
   181  	c.Assert(afero.WriteFile(mm, filepath.Join(defaultArchetypeDir, "index.md"), []byte("default archetype index.md"), 0o755), qt.IsNil)
   182  
   183  	c.Assert(initFs(mm), qt.IsNil)
   184  	cfg, fs := newTestCfg(c, mm)
   185  
   186  	h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
   187  	c.Assert(err, qt.IsNil)
   188  	c.Assert(len(h.Sites), qt.Equals, 2)
   189  
   190  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
   191  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.md")), `site RegularPages: 10`)
   192  
   193  	// Default bundle archetype
   194  	c.Assert(create.NewContent(h, "", "post/my-post2", false), qt.IsNil)
   195  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post2/index.md")), `default archetype index.md`)
   196  
   197  	// Regular file with bundle kind.
   198  	c.Assert(create.NewContent(h, "my-bundle", "post/foo.md", false), qt.IsNil)
   199  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/foo.md")), `draft: true`)
   200  
   201  	// Regular files should fall back to the default archetype (we have no regular file archetype).
   202  	c.Assert(create.NewContent(h, "my-bundle", "mypage.md", false), qt.IsNil)
   203  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "mypage.md")), `draft: true`)
   204  
   205  }
   206  
   207  func TestNewContentFromDirNoSite(t *testing.T) {
   208  	mm := afero.NewMemMapFs()
   209  	c := qt.New(t)
   210  
   211  	archetypeDir := filepath.Join("archetypes", "my-bundle")
   212  	c.Assert(mm.MkdirAll(archetypeDir, 0o755), qt.IsNil)
   213  
   214  	archetypeThemeDir := filepath.Join("themes", "mytheme", "archetypes", "my-theme-bundle")
   215  	c.Assert(mm.MkdirAll(archetypeThemeDir, 0o755), qt.IsNil)
   216  
   217  	contentFile := `
   218  File: %s
   219  Name: {{ replace .Name "-" " " | title }}
   220  i18n: {{ T "hugo" }}
   221  `
   222  
   223  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil)
   224  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.nn.md"), []byte(fmt.Sprintf(contentFile, "index.nn.md")), 0o755), qt.IsNil)
   225  
   226  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "pages", "bio.md"), []byte(fmt.Sprintf(contentFile, "bio.md")), 0o755), qt.IsNil)
   227  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "resources", "hugo1.json"), []byte(`hugo1: {{ printf "no template handling in here" }}`), 0o755), qt.IsNil)
   228  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "resources", "hugo2.xml"), []byte(`hugo2: {{ printf "no template handling in here" }}`), 0o755), qt.IsNil)
   229  
   230  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeThemeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil)
   231  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeThemeDir, "resources", "hugo1.json"), []byte(`hugo1: {{ printf "no template handling in here" }}`), 0o755), qt.IsNil)
   232  
   233  	c.Assert(initFs(mm), qt.IsNil)
   234  	cfg, fs := newTestCfg(c, mm)
   235  
   236  	h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
   237  	c.Assert(err, qt.IsNil)
   238  	c.Assert(len(h.Sites), qt.Equals, 2)
   239  
   240  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
   241  
   242  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
   243  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/resources/hugo2.xml")), `hugo2: {{ printf "no template handling in here" }}`)
   244  
   245  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.md")), `File: index.md`, `Name: My Post`, `i18n: Hugo Rocks!`)
   246  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.nn.md")), `File: index.nn.md`, `Name: My Post`, `i18n: Hugo Rokkar!`)
   247  
   248  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/pages/bio.md")), `File: bio.md`, `Name: Bio`)
   249  
   250  	c.Assert(create.NewContent(h, "my-theme-bundle", "post/my-theme-post", false), qt.IsNil)
   251  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/index.md")), `File: index.md`, `Name: My Theme Post`, `i18n: Hugo Rocks!`)
   252  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-theme-post/resources/hugo1.json")), `hugo1: {{ printf "no template handling in here" }}`)
   253  }
   254  
   255  func TestNewContentForce(t *testing.T) {
   256  	mm := afero.NewMemMapFs()
   257  	c := qt.New(t)
   258  
   259  	archetypeDir := filepath.Join("archetypes", "my-bundle")
   260  	c.Assert(mm.MkdirAll(archetypeDir, 0o755), qt.IsNil)
   261  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(""), 0o755), qt.IsNil)
   262  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.nn.md"), []byte(""), 0o755), qt.IsNil)
   263  
   264  	c.Assert(initFs(mm), qt.IsNil)
   265  	cfg, fs := newTestCfg(c, mm)
   266  
   267  	h, err := hugolib.NewHugoSites(deps.DepsCfg{Cfg: cfg, Fs: fs})
   268  	c.Assert(err, qt.IsNil)
   269  	c.Assert(len(h.Sites), qt.Equals, 2)
   270  
   271  	// from file
   272  	c.Assert(create.NewContent(h, "post", "post/my-post.md", false), qt.IsNil)
   273  	c.Assert(create.NewContent(h, "post", "post/my-post.md", false), qt.IsNotNil)
   274  	c.Assert(create.NewContent(h, "post", "post/my-post.md", true), qt.IsNil)
   275  
   276  	// from dir
   277  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
   278  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNotNil)
   279  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", true), qt.IsNil)
   280  }
   281  
   282  func initFs(fs afero.Fs) error {
   283  	perm := os.FileMode(0o755)
   284  	var err error
   285  
   286  	// create directories
   287  	dirs := []string{
   288  		"archetypes",
   289  		"content",
   290  		filepath.Join("themes", "sample", "archetypes"),
   291  	}
   292  	for _, dir := range dirs {
   293  		err = fs.Mkdir(dir, perm)
   294  		if err != nil && !os.IsExist(err) {
   295  			return err
   296  		}
   297  	}
   298  
   299  	// create some dummy content
   300  	for i := 1; i <= 10; i++ {
   301  		filename := filepath.Join("content", fmt.Sprintf("page%d.md", i))
   302  		afero.WriteFile(fs, filename, []byte(`---
   303  title: Test
   304  ---
   305  `), 0666)
   306  	}
   307  
   308  	// create archetype files
   309  	for _, v := range []struct {
   310  		path    string
   311  		content string
   312  	}{
   313  		{
   314  			path:    filepath.Join("archetypes", "post.md"),
   315  			content: "+++\ndate = \"2015-01-12T19:20:04-07:00\"\ntitle = \"Post Arch title\"\ntest = \"test1\"\n+++\n",
   316  		},
   317  		{
   318  			path:    filepath.Join("archetypes", "post.org"),
   319  			content: "#+title: {{ .BaseFileName  | upper }}",
   320  		},
   321  		{
   322  			path: filepath.Join("archetypes", "name.md"),
   323  			content: `+++
   324  title = '{{ replace .Name "-" " " | title }}'
   325  +++`,
   326  		},
   327  		{
   328  			path: filepath.Join("archetypes", "product.md"),
   329  			content: `+++
   330  title = "{{ .BaseFileName  | upper }}"
   331  +++`,
   332  		},
   333  		{
   334  			path: filepath.Join("archetypes", "filenames.md"),
   335  			content: `...
   336  title = "{{ .BaseFileName  | upper }}"
   337  +++
   338  
   339  
   340  ContentBaseName: {{ .File.ContentBaseName }}
   341  
   342  `,
   343  		},
   344  		{
   345  			path: filepath.Join("archetypes", "site.md"),
   346  			content: `...
   347  title = "{{ .BaseFileName  | upper }}"
   348  +++
   349  
   350  Len RegularPages .Site: {{ len .Site.RegularPages }}
   351  Len RegularPages site: {{ len site.RegularPages }}
   352  
   353  
   354  `,
   355  		},
   356  		{
   357  			path:    filepath.Join("archetypes", "emptydate.md"),
   358  			content: "+++\ndate =\"\"\ntitle = \"Empty Date Arch title\"\ntest = \"test1\"\n+++\n",
   359  		},
   360  		{
   361  			path:    filepath.Join("archetypes", "lang.md"),
   362  			content: `Site Lang: {{ site.Language.Lang  }}|Name: {{ replace .Name "-" " " | title }}|i18n: {{ T "hugo" }}`,
   363  		},
   364  		// #3623x
   365  		{
   366  			path: filepath.Join("archetypes", "shortcodes.md"),
   367  			content: `+++
   368  title = "{{ .BaseFileName  | upper }}"
   369  +++
   370  
   371  {{< myshortcode >}}
   372  
   373  Some text.
   374  
   375  {{% myshortcode %}}
   376  {{</* comment */>}}
   377  {{%/* comment */%}}
   378  
   379  
   380  `,
   381  		},
   382  	} {
   383  		f, err := fs.Create(v.path)
   384  		if err != nil {
   385  			return err
   386  		}
   387  		defer f.Close()
   388  
   389  		_, err = f.Write([]byte(v.content))
   390  		if err != nil {
   391  			return err
   392  		}
   393  	}
   394  
   395  	return nil
   396  }
   397  
   398  func cContains(c *qt.C, v any, matches ...string) {
   399  	for _, m := range matches {
   400  		c.Assert(v, qt.Contains, m)
   401  	}
   402  }
   403  
   404  // TODO(bep) extract common testing package with this and some others
   405  func readFileFromFs(t testing.TB, fs afero.Fs, filename string) string {
   406  	t.Helper()
   407  	filename = filepath.FromSlash(filename)
   408  	b, err := afero.ReadFile(fs, filename)
   409  	if err != nil {
   410  		// Print some debug info
   411  		root := strings.Split(filename, helpers.FilePathSeparator)[0]
   412  		afero.Walk(fs, root, func(path string, info os.FileInfo, err error) error {
   413  			if info != nil && !info.IsDir() {
   414  				fmt.Println("    ", path)
   415  			}
   416  
   417  			return nil
   418  		})
   419  		t.Fatalf("Failed to read file: %s", err)
   420  	}
   421  	return string(b)
   422  }
   423  
   424  func newTestCfg(c *qt.C, mm afero.Fs) (config.Provider, *hugofs.Fs) {
   425  	cfg := `
   426  
   427  theme = "mytheme"
   428  [languages]
   429  [languages.en]
   430  weight = 1
   431  languageName = "English"
   432  [languages.nn]
   433  weight = 2
   434  languageName = "Nynorsk"
   435  
   436  [module]
   437  [[module.mounts]]
   438    source = 'archetypes'
   439    target = 'archetypes'
   440  [[module.mounts]]
   441    source = 'content'
   442    target = 'content'
   443    lang = 'en'
   444  [[module.mounts]]
   445    source = 'content_nn'
   446    target = 'content'
   447    lang = 'nn'
   448  `
   449  	if mm == nil {
   450  		mm = afero.NewMemMapFs()
   451  	}
   452  
   453  	mm.MkdirAll(filepath.FromSlash("content_nn"), 0o777)
   454  
   455  	mm.MkdirAll(filepath.FromSlash("themes/mytheme"), 0o777)
   456  
   457  	c.Assert(afero.WriteFile(mm, filepath.Join("i18n", "en.toml"), []byte(`[hugo]
   458  other = "Hugo Rocks!"`), 0o755), qt.IsNil)
   459  	c.Assert(afero.WriteFile(mm, filepath.Join("i18n", "nn.toml"), []byte(`[hugo]
   460  other = "Hugo Rokkar!"`), 0o755), qt.IsNil)
   461  
   462  	c.Assert(afero.WriteFile(mm, "config.toml", []byte(cfg), 0o755), qt.IsNil)
   463  
   464  	v, _, err := hugolib.LoadConfig(hugolib.ConfigSourceDescriptor{Fs: mm, Filename: "config.toml"})
   465  	c.Assert(err, qt.IsNil)
   466  
   467  	return v, hugofs.NewFrom(mm, v)
   468  }