github.com/neohugo/neohugo@v0.123.8/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/neohugo/neohugo/config"
    24  	"github.com/neohugo/neohugo/config/allconfig"
    25  	"github.com/neohugo/neohugo/config/testconfig"
    26  
    27  	"github.com/neohugo/neohugo/deps"
    28  
    29  	"github.com/neohugo/neohugo/hugolib"
    30  
    31  	"github.com/neohugo/neohugo/hugofs"
    32  
    33  	qt "github.com/frankban/quicktest"
    34  	"github.com/neohugo/neohugo/create"
    35  	"github.com/neohugo/neohugo/helpers"
    36  	"github.com/spf13/afero"
    37  )
    38  
    39  // TODO(bep) clean this up. Export the test site builder in Hugolib or something.
    40  func TestNewContentFromFile(t *testing.T) {
    41  	cases := []struct {
    42  		name     string
    43  		kind     string
    44  		path     string
    45  		expected any
    46  	}{
    47  		{"Post", "post", "post/sample-1.md", []string{`title = "Post Arch title"`, `test = "test1"`, "date = \"2015-01-12T19:20:04-07:00\""}},
    48  		{"Post org-mode", "post", "post/org-1.org", []string{`#+title: ORG-1`}},
    49  		{"Post, unknown content filetype", "post", "post/sample-1.pdoc", false},
    50  		{"Empty date", "emptydate", "post/sample-ed.md", []string{`title = "Empty Date Arch title"`, `test = "test1"`}},
    51  		{"Archetype file not found", "stump", "stump/sample-2.md", []string{`title: "Sample 2"`}}, // no archetype file
    52  		{"No archetype", "", "sample-3.md", []string{`title: "Sample 3"`}},                        // no archetype
    53  		{"Empty archetype", "product", "product/sample-4.md", []string{`title = "SAMPLE-4"`}},     // empty archetype front matter
    54  		{"Filenames", "filenames", "content/mypage/index.md", []string{"title = \"INDEX\"\n+++\n\n\nContentBaseName: mypage"}},
    55  		{"Branch Name", "name", "content/tags/tag-a/_index.md", []string{"+++\ntitle = 'Tag A'\n+++"}},
    56  
    57  		{"Lang 1", "lang", "post/lang-1.md", []string{`Site Lang: en|Name: Lang 1|i18n: Hugo Rocks!`}},
    58  		{"Lang 2", "lang", "post/lang-2.en.md", []string{`Site Lang: en|Name: Lang 2|i18n: Hugo Rocks!`}},
    59  		{"Lang nn file", "lang", "content/post/lang-3.nn.md", []string{`Site Lang: nn|Name: Lang 3|i18n: Hugo Rokkar!`}},
    60  		{"Lang nn dir", "lang", "content_nn/post/lang-4.md", []string{`Site Lang: nn|Name: Lang 4|i18n: Hugo Rokkar!`}},
    61  		{"Lang en in nn dir", "lang", "content_nn/post/lang-5.en.md", []string{`Site Lang: en|Name: Lang 5|i18n: Hugo Rocks!`}},
    62  		{"Lang en default", "lang", "post/my-bundle/index.md", []string{`Site Lang: en|Name: My Bundle|i18n: Hugo Rocks!`}},
    63  		{"Lang en file", "lang", "post/my-bundle/index.en.md", []string{`Site Lang: en|Name: My Bundle|i18n: Hugo Rocks!`}},
    64  		{"Lang nn bundle", "lang", "content/post/my-bundle/index.nn.md", []string{`Site Lang: nn|Name: My Bundle|i18n: Hugo Rokkar!`}},
    65  		{"Site", "site", "content/mypage/index.md", []string{"RegularPages .Site: 10", "RegularPages site: 10"}},
    66  		{"Shortcodes", "shortcodes", "shortcodes/go.md", []string{
    67  			`title = "GO"`,
    68  			"{{< myshortcode >}}",
    69  			"{{% myshortcode %}}",
    70  			"{{</* comment */>}}\n{{%/* comment */%}}",
    71  		}}, // shortcodes
    72  	}
    73  
    74  	c := qt.New(t)
    75  
    76  	for i, cas := range cases {
    77  		cas := cas
    78  
    79  		c.Run(cas.name, func(c *qt.C) {
    80  			c.Parallel()
    81  
    82  			mm := afero.NewMemMapFs()
    83  			c.Assert(initFs(mm), qt.IsNil)
    84  			cfg, fs := newTestCfg(c, mm)
    85  			conf := testconfig.GetTestConfigs(fs.Source, cfg)
    86  			h, err := hugolib.NewHugoSites(deps.DepsCfg{Configs: conf, Fs: fs})
    87  			c.Assert(err, qt.IsNil)
    88  			err = create.NewContent(h, cas.kind, cas.path, false)
    89  
    90  			if b, ok := cas.expected.(bool); ok && !b {
    91  				if !b {
    92  					c.Assert(err, qt.Not(qt.IsNil))
    93  				}
    94  				return
    95  			}
    96  
    97  			c.Assert(err, qt.IsNil)
    98  
    99  			fname := filepath.FromSlash(cas.path)
   100  			if !strings.HasPrefix(fname, "content") {
   101  				fname = filepath.Join("content", fname)
   102  			}
   103  
   104  			content := readFileFromFs(c, fs.Source, fname)
   105  
   106  			for _, v := range cas.expected.([]string) {
   107  				found := strings.Contains(content, v)
   108  				if !found {
   109  					c.Fatalf("[%d] %q missing from output:\n%q", i, v, content)
   110  				}
   111  			}
   112  		})
   113  
   114  	}
   115  }
   116  
   117  func TestNewContentFromDirSiteFunction(t *testing.T) {
   118  	mm := afero.NewMemMapFs()
   119  	c := qt.New(t)
   120  
   121  	archetypeDir := filepath.Join("archetypes", "my-bundle")
   122  	defaultArchetypeDir := filepath.Join("archetypes", "default")
   123  	c.Assert(mm.MkdirAll(archetypeDir, 0o755), qt.IsNil)
   124  	c.Assert(mm.MkdirAll(defaultArchetypeDir, 0o755), qt.IsNil)
   125  
   126  	contentFile := `
   127  File: %s
   128  site RegularPages: {{ len site.RegularPages  }} 	
   129  
   130  `
   131  
   132  	c.Assert(afero.WriteFile(mm, filepath.Join(archetypeDir, "index.md"), []byte(fmt.Sprintf(contentFile, "index.md")), 0o755), qt.IsNil)
   133  	c.Assert(afero.WriteFile(mm, filepath.Join(defaultArchetypeDir, "index.md"), []byte("default archetype index.md"), 0o755), qt.IsNil)
   134  
   135  	c.Assert(initFs(mm), qt.IsNil)
   136  	cfg, fs := newTestCfg(c, mm)
   137  
   138  	conf := testconfig.GetTestConfigs(fs.Source, cfg)
   139  	h, err := hugolib.NewHugoSites(deps.DepsCfg{Configs: conf, Fs: fs})
   140  	c.Assert(err, qt.IsNil)
   141  	c.Assert(len(h.Sites), qt.Equals, 2)
   142  
   143  	c.Assert(create.NewContent(h, "my-bundle", "post/my-post", false), qt.IsNil)
   144  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post/index.md")), `site RegularPages: 10`)
   145  
   146  	// Default bundle archetype
   147  	c.Assert(create.NewContent(h, "", "post/my-post2", false), qt.IsNil)
   148  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/my-post2/index.md")), `default archetype index.md`)
   149  
   150  	// Regular file with bundle kind.
   151  	c.Assert(create.NewContent(h, "my-bundle", "post/foo.md", false), qt.IsNil)
   152  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "post/foo.md")), `draft: true`)
   153  
   154  	// Regular files should fall back to the default archetype (we have no regular file archetype).
   155  	c.Assert(create.NewContent(h, "my-bundle", "mypage.md", false), qt.IsNil)
   156  	cContains(c, readFileFromFs(t, fs.Source, filepath.Join("content", "mypage.md")), `draft: true`)
   157  }
   158  
   159  func initFs(fs afero.Fs) error {
   160  	perm := os.FileMode(0o755)
   161  	var err error
   162  
   163  	// create directories
   164  	dirs := []string{
   165  		"archetypes",
   166  		"content",
   167  		filepath.Join("themes", "sample", "archetypes"),
   168  	}
   169  	for _, dir := range dirs {
   170  		err = fs.Mkdir(dir, perm)
   171  		if err != nil && !os.IsExist(err) {
   172  			return err
   173  		}
   174  	}
   175  
   176  	// create some dummy content
   177  	for i := 1; i <= 10; i++ {
   178  		filename := filepath.Join("content", fmt.Sprintf("page%d.md", i))
   179  		//nolint
   180  		afero.WriteFile(fs, filename, []byte(`---
   181  title: Test
   182  ---
   183  `), 0o666)
   184  	}
   185  
   186  	// create archetype files
   187  	for _, v := range []struct {
   188  		path    string
   189  		content string
   190  	}{
   191  		{
   192  			path:    filepath.Join("archetypes", "post.md"),
   193  			content: "+++\ndate = \"2015-01-12T19:20:04-07:00\"\ntitle = \"Post Arch title\"\ntest = \"test1\"\n+++\n",
   194  		},
   195  		{
   196  			path:    filepath.Join("archetypes", "post.org"),
   197  			content: "#+title: {{ .BaseFileName  | upper }}",
   198  		},
   199  		{
   200  			path: filepath.Join("archetypes", "name.md"),
   201  			content: `+++
   202  title = '{{ replace .Name "-" " " | title }}'
   203  +++`,
   204  		},
   205  		{
   206  			path: filepath.Join("archetypes", "product.md"),
   207  			content: `+++
   208  title = "{{ .BaseFileName  | upper }}"
   209  +++`,
   210  		},
   211  		{
   212  			path: filepath.Join("archetypes", "filenames.md"),
   213  			content: `...
   214  title = "{{ .BaseFileName  | upper }}"
   215  +++
   216  
   217  
   218  ContentBaseName: {{ .File.ContentBaseName }}
   219  
   220  `,
   221  		},
   222  		{
   223  			path: filepath.Join("archetypes", "site.md"),
   224  			content: `...
   225  title = "{{ .BaseFileName  | upper }}"
   226  +++
   227  
   228  Len RegularPages .Site: {{ len .Site.RegularPages }}
   229  Len RegularPages site: {{ len site.RegularPages }}
   230  
   231  
   232  `,
   233  		},
   234  		{
   235  			path:    filepath.Join("archetypes", "emptydate.md"),
   236  			content: "+++\ndate =\"\"\ntitle = \"Empty Date Arch title\"\ntest = \"test1\"\n+++\n",
   237  		},
   238  		{
   239  			path:    filepath.Join("archetypes", "lang.md"),
   240  			content: `Site Lang: {{ site.Language.Lang  }}|Name: {{ replace .Name "-" " " | title }}|i18n: {{ T "hugo" }}`,
   241  		},
   242  		// #3623x
   243  		{
   244  			path: filepath.Join("archetypes", "shortcodes.md"),
   245  			content: `+++
   246  title = "{{ .BaseFileName  | upper }}"
   247  +++
   248  
   249  {{< myshortcode >}}
   250  
   251  Some text.
   252  
   253  {{% myshortcode %}}
   254  {{</* comment */>}}
   255  {{%/* comment */%}}
   256  
   257  
   258  `,
   259  		},
   260  	} {
   261  		f, err := fs.Create(v.path)
   262  		if err != nil {
   263  			return err
   264  		}
   265  		defer f.Close()
   266  
   267  		_, err = f.Write([]byte(v.content))
   268  		if err != nil {
   269  			return err
   270  		}
   271  	}
   272  
   273  	return nil
   274  }
   275  
   276  func cContains(c *qt.C, v any, matches ...string) {
   277  	for _, m := range matches {
   278  		c.Assert(v, qt.Contains, m)
   279  	}
   280  }
   281  
   282  // TODO(bep) extract common testing package with this and some others
   283  func readFileFromFs(t testing.TB, fs afero.Fs, filename string) string {
   284  	t.Helper()
   285  	filename = filepath.FromSlash(filename)
   286  	b, err := afero.ReadFile(fs, filename)
   287  	if err != nil {
   288  		// Print some debug info
   289  		root := strings.Split(filename, helpers.FilePathSeparator)[0]
   290  		err = afero.Walk(fs, root, func(path string, info os.FileInfo, err error) error {
   291  			if info != nil && !info.IsDir() {
   292  				fmt.Println("    ", path)
   293  			}
   294  
   295  			return nil
   296  		})
   297  		if err != nil {
   298  			t.Fatalf("Failed to read file: %s", err)
   299  		}
   300  	}
   301  	return string(b)
   302  }
   303  
   304  func newTestCfg(c *qt.C, mm afero.Fs) (config.Provider, *hugofs.Fs) {
   305  	cfg := `
   306  
   307  theme = "mytheme"
   308  [languages]
   309  [languages.en]
   310  weight = 1
   311  languageName = "English"
   312  [languages.nn]
   313  weight = 2
   314  languageName = "Nynorsk"
   315  
   316  [module]
   317  [[module.mounts]]
   318    source = 'archetypes'
   319    target = 'archetypes'
   320  [[module.mounts]]
   321    source = 'content'
   322    target = 'content'
   323    lang = 'en'
   324  [[module.mounts]]
   325    source = 'content_nn'
   326    target = 'content'
   327    lang = 'nn'
   328  `
   329  	if mm == nil {
   330  		mm = afero.NewMemMapFs()
   331  	}
   332  
   333  	c.Assert(mm.MkdirAll(filepath.FromSlash("content_nn"), 0o777), qt.IsNil)
   334  
   335  	c.Assert(mm.MkdirAll(filepath.FromSlash("themes/mytheme"), 0o777), qt.IsNil)
   336  
   337  	c.Assert(afero.WriteFile(mm, filepath.Join("i18n", "en.toml"), []byte(`[hugo]
   338  other = "Hugo Rocks!"`), 0o755), qt.IsNil)
   339  	c.Assert(afero.WriteFile(mm, filepath.Join("i18n", "nn.toml"), []byte(`[hugo]
   340  other = "Hugo Rokkar!"`), 0o755), qt.IsNil)
   341  
   342  	c.Assert(afero.WriteFile(mm, "config.toml", []byte(cfg), 0o755), qt.IsNil)
   343  
   344  	res, err := allconfig.LoadConfig(allconfig.ConfigSourceDescriptor{Fs: mm, Filename: "config.toml"})
   345  	c.Assert(err, qt.IsNil)
   346  
   347  	return res.LoadingInfo.Cfg, hugofs.NewFrom(mm, res.LoadingInfo.BaseConfig)
   348  }