github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/hugolib/template_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 hugolib
    15  
    16  import (
    17  	"fmt"
    18  	"path/filepath"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/gohugoio/hugo/config"
    23  	"github.com/gohugoio/hugo/identity"
    24  
    25  	qt "github.com/frankban/quicktest"
    26  	"github.com/gohugoio/hugo/deps"
    27  	"github.com/gohugoio/hugo/hugofs"
    28  	"github.com/gohugoio/hugo/tpl"
    29  )
    30  
    31  func TestTemplateLookupOrder(t *testing.T) {
    32  	var (
    33  		fs  *hugofs.Fs
    34  		cfg config.Provider
    35  		th  testHelper
    36  	)
    37  
    38  	// Variants base templates:
    39  	//   1. <current-path>/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
    40  	//   2. <current-path>/baseof.<suffix>
    41  	//   3. _default/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
    42  	//   4. _default/baseof.<suffix>
    43  	for _, this := range []struct {
    44  		name   string
    45  		setup  func(t *testing.T)
    46  		assert func(t *testing.T)
    47  	}{
    48  		{
    49  			"Variant 1",
    50  			func(t *testing.T) {
    51  				writeSource(t, fs, filepath.Join("layouts", "section", "sect1-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
    52  				writeSource(t, fs, filepath.Join("layouts", "section", "sect1.html"), `{{define "main"}}sect{{ end }}`)
    53  			},
    54  			func(t *testing.T) {
    55  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base: sect")
    56  			},
    57  		},
    58  		{
    59  			"Variant 2",
    60  			func(t *testing.T) {
    61  				writeSource(t, fs, filepath.Join("layouts", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
    62  				writeSource(t, fs, filepath.Join("layouts", "index.html"), `{{define "main"}}index{{ end }}`)
    63  			},
    64  			func(t *testing.T) {
    65  				th.assertFileContent(filepath.Join("public", "index.html"), "Base: index")
    66  			},
    67  		},
    68  		{
    69  			"Variant 3",
    70  			func(t *testing.T) {
    71  				writeSource(t, fs, filepath.Join("layouts", "_default", "list-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
    72  				writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
    73  			},
    74  			func(t *testing.T) {
    75  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base: list")
    76  			},
    77  		},
    78  		{
    79  			"Variant 4",
    80  			func(t *testing.T) {
    81  				writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
    82  				writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
    83  			},
    84  			func(t *testing.T) {
    85  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base: list")
    86  			},
    87  		},
    88  		{
    89  			"Variant 1, theme, use site base",
    90  			func(t *testing.T) {
    91  				cfg.Set("theme", "mytheme")
    92  				writeSource(t, fs, filepath.Join("layouts", "section", "sect1-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
    93  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
    94  				writeSource(t, fs, filepath.Join("layouts", "section", "sect1.html"), `{{define "main"}}sect{{ end }}`)
    95  			},
    96  			func(t *testing.T) {
    97  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base: sect")
    98  			},
    99  		},
   100  		{
   101  			"Variant 1, theme, use theme base",
   102  			func(t *testing.T) {
   103  				cfg.Set("theme", "mytheme")
   104  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect1-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
   105  				writeSource(t, fs, filepath.Join("layouts", "section", "sect1.html"), `{{define "main"}}sect{{ end }}`)
   106  			},
   107  			func(t *testing.T) {
   108  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base Theme: sect")
   109  			},
   110  		},
   111  		{
   112  			"Variant 4, theme, use site base",
   113  			func(t *testing.T) {
   114  				cfg.Set("theme", "mytheme")
   115  				writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
   116  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
   117  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
   118  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "index.html"), `{{define "main"}}index{{ end }}`)
   119  			},
   120  			func(t *testing.T) {
   121  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base: list")
   122  				th.assertFileContent(filepath.Join("public", "index.html"), "Base: index") // Issue #3505
   123  			},
   124  		},
   125  		{
   126  			"Variant 4, theme, use themes base",
   127  			func(t *testing.T) {
   128  				cfg.Set("theme", "mytheme")
   129  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
   130  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
   131  			},
   132  			func(t *testing.T) {
   133  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base Theme: list")
   134  			},
   135  		},
   136  		{
   137  			// Issue #3116
   138  			"Test section list and single template selection",
   139  			func(t *testing.T) {
   140  				cfg.Set("theme", "mytheme")
   141  
   142  				writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
   143  
   144  				// Both single and list template in /SECTION/
   145  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "sect1", "list.html"), `sect list`)
   146  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `default list`)
   147  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "sect1", "single.html"), `sect single`)
   148  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "single.html"), `default single`)
   149  
   150  				// sect2 with list template in /section
   151  				writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect2.html"), `sect2 list`)
   152  			},
   153  			func(t *testing.T) {
   154  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "sect list")
   155  				th.assertFileContent(filepath.Join("public", "sect1", "page1", "index.html"), "sect single")
   156  				th.assertFileContent(filepath.Join("public", "sect2", "index.html"), "sect2 list")
   157  			},
   158  		},
   159  		{
   160  			// Issue #2995
   161  			"Test section list and single template selection with base template",
   162  			func(t *testing.T) {
   163  				writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base Default: {{block "main" .}}block{{end}}`)
   164  				writeSource(t, fs, filepath.Join("layouts", "sect1", "baseof.html"), `Base Sect1: {{block "main" .}}block{{end}}`)
   165  				writeSource(t, fs, filepath.Join("layouts", "section", "sect2-baseof.html"), `Base Sect2: {{block "main" .}}block{{end}}`)
   166  
   167  				// Both single and list + base template in /SECTION/
   168  				writeSource(t, fs, filepath.Join("layouts", "sect1", "list.html"), `{{define "main"}}sect1 list{{ end }}`)
   169  				writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}default list{{ end }}`)
   170  				writeSource(t, fs, filepath.Join("layouts", "sect1", "single.html"), `{{define "main"}}sect single{{ end }}`)
   171  				writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{define "main"}}default single{{ end }}`)
   172  
   173  				// sect2 with list template in /section
   174  				writeSource(t, fs, filepath.Join("layouts", "section", "sect2.html"), `{{define "main"}}sect2 list{{ end }}`)
   175  			},
   176  			func(t *testing.T) {
   177  				th.assertFileContent(filepath.Join("public", "sect1", "index.html"), "Base Sect1", "sect1 list")
   178  				th.assertFileContent(filepath.Join("public", "sect1", "page1", "index.html"), "Base Sect1", "sect single")
   179  				th.assertFileContent(filepath.Join("public", "sect2", "index.html"), "Base Sect2", "sect2 list")
   180  
   181  				// Note that this will get the default base template and not the one in /sect2 -- because there are no
   182  				// single template defined in /sect2.
   183  				th.assertFileContent(filepath.Join("public", "sect2", "page2", "index.html"), "Base Default", "default single")
   184  			},
   185  		},
   186  	} {
   187  
   188  		this := this
   189  		t.Run(this.name, func(t *testing.T) {
   190  			// TODO(bep) there are some function vars need to pull down here to enable => t.Parallel()
   191  			cfg, fs = newTestCfg()
   192  			th = newTestHelper(cfg, fs, t)
   193  
   194  			for i := 1; i <= 3; i++ {
   195  				writeSource(t, fs, filepath.Join("content", fmt.Sprintf("sect%d", i), fmt.Sprintf("page%d.md", i)), `---
   196  title: Template test
   197  ---
   198  Some content
   199  `)
   200  			}
   201  
   202  			this.setup(t)
   203  
   204  			buildSingleSite(t, deps.DepsCfg{Fs: fs, Cfg: cfg}, BuildCfg{})
   205  			// helpers.PrintFs(s.BaseFs.Layouts.Fs, "", os.Stdout)
   206  			this.assert(t)
   207  		})
   208  
   209  	}
   210  }
   211  
   212  // https://github.com/gohugoio/hugo/issues/4895
   213  func TestTemplateBOM(t *testing.T) {
   214  	b := newTestSitesBuilder(t).WithSimpleConfigFile()
   215  	bom := "\ufeff"
   216  
   217  	b.WithTemplatesAdded(
   218  		"_default/baseof.html", bom+`
   219  		Base: {{ block "main" . }}base main{{ end }}`,
   220  		"_default/single.html", bom+`{{ define "main" }}Hi!?{{ end }}`)
   221  
   222  	b.WithContent("page.md", `---
   223  title: "Page"
   224  ---
   225  
   226  Page Content
   227  `)
   228  
   229  	b.CreateSites().Build(BuildCfg{})
   230  
   231  	b.AssertFileContent("public/page/index.html", "Base: Hi!?")
   232  }
   233  
   234  func TestTemplateManyBaseTemplates(t *testing.T) {
   235  	t.Parallel()
   236  	b := newTestSitesBuilder(t).WithSimpleConfigFile()
   237  
   238  	numPages := 100 // To get some parallelism
   239  
   240  	pageTemplate := `---
   241  title: "Page %d"
   242  layout: "layout%d"
   243  ---
   244  
   245  Content.
   246  `
   247  
   248  	singleTemplate := `
   249  {{ define "main" }}%d{{ end }}
   250  `
   251  	baseTemplate := `
   252  Base %d: {{ block "main" . }}FOO{{ end }}
   253  `
   254  
   255  	for i := 0; i < numPages; i++ {
   256  		id := i + 1
   257  		b.WithContent(fmt.Sprintf("page%d.md", id), fmt.Sprintf(pageTemplate, id, id))
   258  		b.WithTemplates(fmt.Sprintf("_default/layout%d.html", id), fmt.Sprintf(singleTemplate, id))
   259  		b.WithTemplates(fmt.Sprintf("_default/layout%d-baseof.html", id), fmt.Sprintf(baseTemplate, id))
   260  	}
   261  
   262  	b.Build(BuildCfg{})
   263  	for i := 0; i < numPages; i++ {
   264  		id := i + 1
   265  		b.AssertFileContent(fmt.Sprintf("public/page%d/index.html", id), fmt.Sprintf(`Base %d: %d`, id, id))
   266  	}
   267  }
   268  
   269  // https://github.com/gohugoio/hugo/issues/6790
   270  func TestTemplateNoBasePlease(t *testing.T) {
   271  	t.Parallel()
   272  	b := newTestSitesBuilder(t).WithSimpleConfigFile()
   273  
   274  	b.WithTemplates("_default/list.html", `
   275  	{{ define "main" }}
   276  	  Bonjour
   277  	{{ end }}
   278  
   279  	{{ printf "list" }}
   280  
   281  
   282  	`)
   283  
   284  	b.WithTemplates(
   285  		"_default/single.html", `
   286  {{ printf "single" }}
   287  {{ define "main" }}
   288    Bonjour
   289  {{ end }}
   290  
   291  
   292  `)
   293  
   294  	b.WithContent("blog/p1.md", `---
   295  title: The Page
   296  ---
   297  `)
   298  
   299  	b.Build(BuildCfg{})
   300  
   301  	b.AssertFileContent("public/blog/p1/index.html", `single`)
   302  	b.AssertFileContent("public/blog/index.html", `list`)
   303  }
   304  
   305  // https://github.com/gohugoio/hugo/issues/6816
   306  func TestTemplateBaseWithComment(t *testing.T) {
   307  	t.Parallel()
   308  	b := newTestSitesBuilder(t).WithSimpleConfigFile()
   309  	b.WithTemplatesAdded(
   310  		"baseof.html", `Base: {{ block "main" . }}{{ end }}`,
   311  		"index.html", `
   312  	{{/*  A comment */}}
   313  	{{ define "main" }}
   314  	  Bonjour
   315  	{{ end }}
   316  
   317  
   318  	`)
   319  
   320  	b.Build(BuildCfg{})
   321  	b.AssertFileContent("public/index.html", `Base:
   322  Bonjour`)
   323  }
   324  
   325  func TestTemplateLookupSite(t *testing.T) {
   326  	t.Run("basic", func(t *testing.T) {
   327  		t.Parallel()
   328  		b := newTestSitesBuilder(t).WithSimpleConfigFile()
   329  		b.WithTemplates(
   330  			"_default/single.html", `Single: {{ .Title }}`,
   331  			"_default/list.html", `List: {{ .Title }}`,
   332  		)
   333  
   334  		createContent := func(title string) string {
   335  			return fmt.Sprintf(`---
   336  title: %s
   337  ---`, title)
   338  		}
   339  
   340  		b.WithContent(
   341  			"_index.md", createContent("Home Sweet Home"),
   342  			"p1.md", createContent("P1"))
   343  
   344  		b.CreateSites().Build(BuildCfg{})
   345  		b.AssertFileContent("public/index.html", `List: Home Sweet Home`)
   346  		b.AssertFileContent("public/p1/index.html", `Single: P1`)
   347  	})
   348  
   349  	t.Run("baseof", func(t *testing.T) {
   350  		t.Parallel()
   351  		b := newTestSitesBuilder(t).WithDefaultMultiSiteConfig()
   352  
   353  		b.WithTemplatesAdded(
   354  			"index.html", `{{ define "main" }}Main Home En{{ end }}`,
   355  			"index.fr.html", `{{ define "main" }}Main Home Fr{{ end }}`,
   356  			"baseof.html", `Baseof en: {{ block "main" . }}main block{{ end }}`,
   357  			"baseof.fr.html", `Baseof fr: {{ block "main" . }}main block{{ end }}`,
   358  			"mysection/baseof.html", `Baseof mysection: {{ block "main" .  }}mysection block{{ end }}`,
   359  			"_default/single.html", `{{ define "main" }}Main Default Single{{ end }}`,
   360  			"_default/list.html", `{{ define "main" }}Main Default List{{ end }}`,
   361  		)
   362  
   363  		b.WithContent("mysection/p1.md", `---
   364  title: My Page
   365  ---
   366  
   367  `)
   368  
   369  		b.CreateSites().Build(BuildCfg{})
   370  
   371  		b.AssertFileContent("public/en/index.html", `Baseof en: Main Home En`)
   372  		b.AssertFileContent("public/fr/index.html", `Baseof fr: Main Home Fr`)
   373  		b.AssertFileContent("public/en/mysection/index.html", `Baseof mysection: Main Default List`)
   374  		b.AssertFileContent("public/en/mysection/p1/index.html", `Baseof mysection: Main Default Single`)
   375  	})
   376  }
   377  
   378  func TestTemplateFuncs(t *testing.T) {
   379  	b := newTestSitesBuilder(t).WithDefaultMultiSiteConfig()
   380  
   381  	homeTpl := `Site: {{ site.Language.Lang }} / {{ .Site.Language.Lang }} / {{ site.BaseURL }}
   382  Sites: {{ site.Sites.First.Home.Language.Lang }}
   383  Hugo: {{ hugo.Generator }}
   384  `
   385  
   386  	b.WithTemplatesAdded(
   387  		"index.html", homeTpl,
   388  		"index.fr.html", homeTpl,
   389  	)
   390  
   391  	b.CreateSites().Build(BuildCfg{})
   392  
   393  	b.AssertFileContent("public/en/index.html",
   394  		"Site: en / en / http://example.com/blog",
   395  		"Sites: en",
   396  		"Hugo: <meta name=\"generator\" content=\"Hugo")
   397  	b.AssertFileContent("public/fr/index.html",
   398  		"Site: fr / fr / http://example.com/blog",
   399  		"Sites: en",
   400  		"Hugo: <meta name=\"generator\" content=\"Hugo",
   401  	)
   402  }
   403  
   404  func TestPartialWithReturn(t *testing.T) {
   405  	c := qt.New(t)
   406  
   407  	newBuilder := func(t testing.TB) *sitesBuilder {
   408  		b := newTestSitesBuilder(t).WithSimpleConfigFile()
   409  		b.WithTemplatesAdded(
   410  			"partials/add42.tpl", `
   411  		{{ $v := add . 42 }}
   412  		{{ return $v }}
   413  		`,
   414  			"partials/dollarContext.tpl", `
   415  {{ $v := add $ 42 }}
   416  {{ return $v }}
   417  `,
   418  			"partials/dict.tpl", `
   419  {{ $v := add $.adder 42 }}
   420  {{ return $v }}
   421  `,
   422  			"partials/complex.tpl", `
   423  {{ return add . 42 }}
   424  `, "partials/hello.tpl", `
   425  		{{ $v := printf "hello %s" . }}
   426  		{{ return $v }}
   427  		`,
   428  		)
   429  
   430  		return b
   431  	}
   432  
   433  	c.Run("Return", func(c *qt.C) {
   434  		b := newBuilder(c)
   435  
   436  		b.WithTemplatesAdded(
   437  			"index.html", `
   438  Test Partials With Return Values:
   439  
   440  add42: 50: {{ partial "add42.tpl" 8 }}
   441  hello world: {{ partial "hello.tpl" "world" }}
   442  dollarContext: 60: {{ partial "dollarContext.tpl" 18 }}
   443  adder: 70: {{ partial "dict.tpl" (dict "adder" 28) }}
   444  complex: 80: {{ partial "complex.tpl" 38 }}
   445  `,
   446  		)
   447  
   448  		b.CreateSites().Build(BuildCfg{})
   449  
   450  		b.AssertFileContent("public/index.html", `
   451  add42: 50: 50
   452  hello world: hello world
   453  dollarContext: 60: 60
   454  adder: 70: 70
   455  complex: 80: 80
   456  `,
   457  		)
   458  	})
   459  
   460  	c.Run("Zero argument", func(c *qt.C) {
   461  		b := newBuilder(c)
   462  
   463  		b.WithTemplatesAdded(
   464  			"index.html", `
   465  Test Partials With Return Values:
   466  
   467  add42: fail: {{ partial "add42.tpl" 0 }}
   468  
   469  `,
   470  		)
   471  
   472  		e := b.CreateSites().BuildE(BuildCfg{})
   473  		b.Assert(e, qt.Not(qt.IsNil))
   474  	})
   475  }
   476  
   477  func TestPartialCached(t *testing.T) {
   478  	b := newTestSitesBuilder(t)
   479  
   480  	b.WithTemplatesAdded(
   481  		"index.html", `
   482  {{ $key1 := (dict "a" "av" ) }}
   483  {{ $key2 := (dict "a" "av2" ) }}
   484  Partial cached1: {{ partialCached "p1" "input1" $key1 }}
   485  Partial cached2: {{ partialCached "p1" "input2" $key1 }}
   486  Partial cached3: {{ partialCached "p1" "input3" $key2 }}
   487  `,
   488  
   489  		"partials/p1.html", `partial: {{ . }}`,
   490  	)
   491  
   492  	b.Build(BuildCfg{})
   493  
   494  	b.AssertFileContent("public/index.html", `
   495   Partial cached1: partial: input1
   496   Partial cached2: partial: input1
   497   Partial cached3: partial: input3
   498  `)
   499  }
   500  
   501  // https://github.com/gohugoio/hugo/issues/6615
   502  func TestTemplateTruth(t *testing.T) {
   503  	b := newTestSitesBuilder(t)
   504  	b.WithTemplatesAdded("index.html", `
   505  {{ $p := index site.RegularPages 0 }}
   506  {{ $zero := $p.ExpiryDate }}
   507  {{ $notZero := time.Now }}
   508  
   509  if: Zero: {{ if $zero }}FAIL{{ else }}OK{{ end }}
   510  if: Not Zero: {{ if $notZero }}OK{{ else }}Fail{{ end }}
   511  not: Zero: {{ if not $zero }}OK{{ else }}FAIL{{ end }}
   512  not: Not Zero: {{ if not $notZero }}FAIL{{ else }}OK{{ end }}
   513  
   514  with: Zero {{ with $zero }}FAIL{{ else }}OK{{ end }}
   515  
   516  `)
   517  
   518  	b.Build(BuildCfg{})
   519  
   520  	b.AssertFileContent("public/index.html", `
   521  if: Zero: OK
   522  if: Not Zero: OK
   523  not: Zero: OK
   524  not: Not Zero: OK
   525  with: Zero OK
   526  `)
   527  }
   528  
   529  func TestTemplateDependencies(t *testing.T) {
   530  	b := newTestSitesBuilder(t).Running()
   531  
   532  	b.WithTemplates("index.html", `
   533  {{ $p := site.GetPage "p1" }}
   534  {{ partial "p1.html"  $p }}
   535  {{ partialCached "p2.html" "foo" }}
   536  {{ partials.Include "p3.html" "data" }}
   537  {{ partials.IncludeCached "p4.html" "foo" }}
   538  {{ $p := partial "p5" }}
   539  {{ partial "sub/p6.html" }}
   540  {{ partial "P7.html" }}
   541  {{ template "_default/foo.html" }}
   542  Partial nested: {{ partial "p10" }}
   543  
   544  `,
   545  		"partials/p1.html", `ps: {{ .Render "li" }}`,
   546  		"partials/p2.html", `p2`,
   547  		"partials/p3.html", `p3`,
   548  		"partials/p4.html", `p4`,
   549  		"partials/p5.html", `p5`,
   550  		"partials/sub/p6.html", `p6`,
   551  		"partials/P7.html", `p7`,
   552  		"partials/p8.html", `p8 {{ partial "p9.html" }}`,
   553  		"partials/p9.html", `p9`,
   554  		"partials/p10.html", `p10 {{ partial "p11.html" }}`,
   555  		"partials/p11.html", `p11`,
   556  		"_default/foo.html", `foo`,
   557  		"_default/li.html", `li {{ partial "p8.html" }}`,
   558  	)
   559  
   560  	b.WithContent("p1.md", `---
   561  title: P1
   562  ---
   563  
   564  
   565  `)
   566  
   567  	b.Build(BuildCfg{})
   568  
   569  	s := b.H.Sites[0]
   570  
   571  	templ, found := s.lookupTemplate("index.html")
   572  	b.Assert(found, qt.Equals, true)
   573  
   574  	idset := make(map[identity.Identity]bool)
   575  	collectIdentities(idset, templ.(tpl.Info))
   576  	b.Assert(idset, qt.HasLen, 11)
   577  }
   578  
   579  func TestTemplateGoIssues(t *testing.T) {
   580  	b := newTestSitesBuilder(t)
   581  
   582  	b.WithTemplatesAdded(
   583  		"index.html", `
   584  {{ $title := "a & b" }}
   585  <script type="application/ld+json">{"@type":"WebPage","headline":"{{$title}}"}</script>
   586  
   587  {{/* Action/commands newlines, from Go 1.16, see https://github.com/golang/go/issues/29770 */}}
   588  {{ $norway := dict
   589  	"country" "Norway"
   590  	"population" "5 millions"
   591  	"language" "Norwegian"
   592  	"language_code" "nb"
   593  	"weather" "freezing cold"
   594  	"capitol" "Oslo"
   595  	"largest_city" "Oslo"
   596  	"currency"  "Norwegian krone"
   597  	"dialing_code" "+47"
   598  }}
   599  
   600  Population in Norway is {{
   601  	  $norway.population
   602  	| lower
   603  	| upper
   604  }}
   605  
   606  `,
   607  	)
   608  
   609  	b.Build(BuildCfg{})
   610  
   611  	b.AssertFileContent("public/index.html", `
   612  <script type="application/ld+json">{"@type":"WebPage","headline":"a \u0026 b"}</script>
   613  Population in Norway is 5 MILLIONS
   614  
   615  `)
   616  }
   617  
   618  func collectIdentities(set map[identity.Identity]bool, provider identity.Provider) {
   619  	if ids, ok := provider.(identity.IdentitiesProvider); ok {
   620  		for _, id := range ids.GetIdentities() {
   621  			collectIdentities(set, id)
   622  		}
   623  	} else {
   624  		set[provider.GetIdentity()] = true
   625  	}
   626  }
   627  
   628  func ident(level int) string {
   629  	return strings.Repeat(" ", level)
   630  }
   631  
   632  func TestPartialInline(t *testing.T) {
   633  	b := newTestSitesBuilder(t)
   634  
   635  	b.WithContent("p1.md", "")
   636  
   637  	b.WithTemplates(
   638  		"index.html", `
   639  
   640  {{ $p1 := partial "p1" . }}
   641  {{ $p2 := partial "p2" . }}
   642  
   643  P1: {{ $p1 }}
   644  P2: {{ $p2 }}
   645  
   646  {{ define "partials/p1" }}Inline: p1{{ end }}
   647  
   648  {{ define "partials/p2" }}
   649  {{ $value := 32 }}
   650  {{ return $value }}
   651  {{ end }}
   652  
   653  
   654  `,
   655  	)
   656  
   657  	b.CreateSites().Build(BuildCfg{})
   658  
   659  	b.AssertFileContent("public/index.html",
   660  		`
   661  P1: Inline: p1
   662  P2: 32`,
   663  	)
   664  }
   665  
   666  func TestPartialInlineBase(t *testing.T) {
   667  	b := newTestSitesBuilder(t)
   668  
   669  	b.WithContent("p1.md", "")
   670  
   671  	b.WithTemplates(
   672  		"baseof.html", `{{ $p3 := partial "p3" . }}P3: {{ $p3 }}
   673  {{ block "main" . }}{{ end }}{{ define "partials/p3" }}Inline: p3{{ end }}`,
   674  		"index.html", `
   675  {{ define "main" }}
   676  
   677  {{ $p1 := partial "p1" . }}
   678  {{ $p2 := partial "p2" . }}
   679  
   680  P1: {{ $p1 }}
   681  P2: {{ $p2 }}
   682  
   683  {{ end }}
   684  
   685  
   686  {{ define "partials/p1" }}Inline: p1{{ end }}
   687  
   688  {{ define "partials/p2" }}
   689  {{ $value := 32 }}
   690  {{ return $value }}
   691  {{ end }}
   692  
   693  
   694  `,
   695  	)
   696  
   697  	b.CreateSites().Build(BuildCfg{})
   698  
   699  	b.AssertFileContent("public/index.html",
   700  		`
   701  P1: Inline: p1
   702  P2: 32
   703  P3: Inline: p3
   704  `,
   705  	)
   706  }
   707  
   708  // https://github.com/gohugoio/hugo/issues/7478
   709  func TestBaseWithAndWithoutDefine(t *testing.T) {
   710  	b := newTestSitesBuilder(t)
   711  
   712  	b.WithContent("p1.md", "---\ntitle: P\n---\nContent")
   713  
   714  	b.WithTemplates(
   715  		"_default/baseof.html", `
   716  ::Header Start:{{ block "header" . }}{{ end }}:Header End:
   717  ::{{ block "main" . }}Main{{ end }}::
   718  `, "index.html", `
   719  {{ define "header" }}
   720  Home Header
   721  {{ end }}
   722  {{ define "main" }}
   723  This is home main
   724  {{ end }}
   725  `,
   726  
   727  		"_default/single.html", `
   728  {{ define "main" }}
   729  This is single main
   730  {{ end }}
   731  `,
   732  	)
   733  
   734  	b.CreateSites().Build(BuildCfg{})
   735  
   736  	b.AssertFileContent("public/index.html", `
   737  Home Header
   738  This is home main
   739  `,
   740  	)
   741  
   742  	b.AssertFileContent("public/p1/index.html", `
   743   ::Header Start::Header End:
   744  This is single main
   745  `,
   746  	)
   747  }