github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/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  
   461  // Issue 7528
   462  func TestPartialWithZeroedArgs(t *testing.T) {
   463  	b := newTestSitesBuilder(t)
   464  	b.WithTemplatesAdded("index.html",
   465  		` 
   466  X{{ partial "retval" dict }}X
   467  X{{ partial "retval" slice }}X
   468  X{{ partial "retval" "" }}X
   469  X{{ partial "retval" false }}X
   470  X{{ partial "retval" 0 }}X
   471  {{ define "partials/retval" }}
   472    {{ return 123 }}
   473  {{ end }}`)
   474  
   475  	b.WithContentAdded("p.md", ``)
   476  	b.Build(BuildCfg{})
   477  	b.AssertFileContent("public/index.html",
   478  		`
   479  X123X
   480  X123X
   481  X123X
   482  X123X
   483  X123X
   484  `)
   485  }
   486  
   487  func TestPartialCached(t *testing.T) {
   488  	b := newTestSitesBuilder(t)
   489  
   490  	b.WithTemplatesAdded(
   491  		"index.html", `
   492  {{ $key1 := (dict "a" "av" ) }}
   493  {{ $key2 := (dict "a" "av2" ) }}
   494  Partial cached1: {{ partialCached "p1" "input1" $key1 }}
   495  Partial cached2: {{ partialCached "p1" "input2" $key1 }}
   496  Partial cached3: {{ partialCached "p1" "input3" $key2 }}
   497  `,
   498  
   499  		"partials/p1.html", `partial: {{ . }}`,
   500  	)
   501  
   502  	b.Build(BuildCfg{})
   503  
   504  	b.AssertFileContent("public/index.html", `
   505   Partial cached1: partial: input1
   506   Partial cached2: partial: input1
   507   Partial cached3: partial: input3
   508  `)
   509  }
   510  
   511  // https://github.com/gohugoio/hugo/issues/6615
   512  func TestTemplateTruth(t *testing.T) {
   513  	b := newTestSitesBuilder(t)
   514  	b.WithTemplatesAdded("index.html", `
   515  {{ $p := index site.RegularPages 0 }}
   516  {{ $zero := $p.ExpiryDate }}
   517  {{ $notZero := time.Now }}
   518  
   519  if: Zero: {{ if $zero }}FAIL{{ else }}OK{{ end }}
   520  if: Not Zero: {{ if $notZero }}OK{{ else }}Fail{{ end }}
   521  not: Zero: {{ if not $zero }}OK{{ else }}FAIL{{ end }}
   522  not: Not Zero: {{ if not $notZero }}FAIL{{ else }}OK{{ end }}
   523  
   524  with: Zero {{ with $zero }}FAIL{{ else }}OK{{ end }}
   525  
   526  `)
   527  
   528  	b.Build(BuildCfg{})
   529  
   530  	b.AssertFileContent("public/index.html", `
   531  if: Zero: OK
   532  if: Not Zero: OK
   533  not: Zero: OK
   534  not: Not Zero: OK
   535  with: Zero OK
   536  `)
   537  }
   538  
   539  func TestTemplateDependencies(t *testing.T) {
   540  	b := newTestSitesBuilder(t).Running()
   541  
   542  	b.WithTemplates("index.html", `
   543  {{ $p := site.GetPage "p1" }}
   544  {{ partial "p1.html"  $p }}
   545  {{ partialCached "p2.html" "foo" }}
   546  {{ partials.Include "p3.html" "data" }}
   547  {{ partials.IncludeCached "p4.html" "foo" }}
   548  {{ $p := partial "p5" }}
   549  {{ partial "sub/p6.html" }}
   550  {{ partial "P7.html" }}
   551  {{ template "_default/foo.html" }}
   552  Partial nested: {{ partial "p10" }}
   553  
   554  `,
   555  		"partials/p1.html", `ps: {{ .Render "li" }}`,
   556  		"partials/p2.html", `p2`,
   557  		"partials/p3.html", `p3`,
   558  		"partials/p4.html", `p4`,
   559  		"partials/p5.html", `p5`,
   560  		"partials/sub/p6.html", `p6`,
   561  		"partials/P7.html", `p7`,
   562  		"partials/p8.html", `p8 {{ partial "p9.html" }}`,
   563  		"partials/p9.html", `p9`,
   564  		"partials/p10.html", `p10 {{ partial "p11.html" }}`,
   565  		"partials/p11.html", `p11`,
   566  		"_default/foo.html", `foo`,
   567  		"_default/li.html", `li {{ partial "p8.html" }}`,
   568  	)
   569  
   570  	b.WithContent("p1.md", `---
   571  title: P1
   572  ---
   573  
   574  
   575  `)
   576  
   577  	b.Build(BuildCfg{})
   578  
   579  	s := b.H.Sites[0]
   580  
   581  	templ, found := s.lookupTemplate("index.html")
   582  	b.Assert(found, qt.Equals, true)
   583  
   584  	idset := make(map[identity.Identity]bool)
   585  	collectIdentities(idset, templ.(tpl.Info))
   586  	b.Assert(idset, qt.HasLen, 11)
   587  }
   588  
   589  func TestTemplateGoIssues(t *testing.T) {
   590  	b := newTestSitesBuilder(t)
   591  
   592  	b.WithTemplatesAdded(
   593  		"index.html", `
   594  {{ $title := "a & b" }}
   595  <script type="application/ld+json">{"@type":"WebPage","headline":"{{$title}}"}</script>
   596  
   597  {{/* Action/commands newlines, from Go 1.16, see https://github.com/golang/go/issues/29770 */}}
   598  {{ $norway := dict
   599  	"country" "Norway"
   600  	"population" "5 millions"
   601  	"language" "Norwegian"
   602  	"language_code" "nb"
   603  	"weather" "freezing cold"
   604  	"capitol" "Oslo"
   605  	"largest_city" "Oslo"
   606  	"currency"  "Norwegian krone"
   607  	"dialing_code" "+47"
   608  }}
   609  
   610  Population in Norway is {{
   611  	  $norway.population
   612  	| lower
   613  	| upper
   614  }}
   615  
   616  `,
   617  	)
   618  
   619  	b.Build(BuildCfg{})
   620  
   621  	b.AssertFileContent("public/index.html", `
   622  <script type="application/ld+json">{"@type":"WebPage","headline":"a \u0026 b"}</script>
   623  Population in Norway is 5 MILLIONS
   624  
   625  `)
   626  }
   627  
   628  func collectIdentities(set map[identity.Identity]bool, provider identity.Provider) {
   629  	if ids, ok := provider.(identity.IdentitiesProvider); ok {
   630  		for _, id := range ids.GetIdentities() {
   631  			collectIdentities(set, id)
   632  		}
   633  	} else {
   634  		set[provider.GetIdentity()] = true
   635  	}
   636  }
   637  
   638  func ident(level int) string {
   639  	return strings.Repeat(" ", level)
   640  }
   641  
   642  func TestPartialInline(t *testing.T) {
   643  	b := newTestSitesBuilder(t)
   644  
   645  	b.WithContent("p1.md", "")
   646  
   647  	b.WithTemplates(
   648  		"index.html", `
   649  
   650  {{ $p1 := partial "p1" . }}
   651  {{ $p2 := partial "p2" . }}
   652  
   653  P1: {{ $p1 }}
   654  P2: {{ $p2 }}
   655  
   656  {{ define "partials/p1" }}Inline: p1{{ end }}
   657  
   658  {{ define "partials/p2" }}
   659  {{ $value := 32 }}
   660  {{ return $value }}
   661  {{ end }}
   662  
   663  
   664  `,
   665  	)
   666  
   667  	b.CreateSites().Build(BuildCfg{})
   668  
   669  	b.AssertFileContent("public/index.html",
   670  		`
   671  P1: Inline: p1
   672  P2: 32`,
   673  	)
   674  }
   675  
   676  func TestPartialInlineBase(t *testing.T) {
   677  	b := newTestSitesBuilder(t)
   678  
   679  	b.WithContent("p1.md", "")
   680  
   681  	b.WithTemplates(
   682  		"baseof.html", `{{ $p3 := partial "p3" . }}P3: {{ $p3 }}
   683  {{ block "main" . }}{{ end }}{{ define "partials/p3" }}Inline: p3{{ end }}`,
   684  		"index.html", `
   685  {{ define "main" }}
   686  
   687  {{ $p1 := partial "p1" . }}
   688  {{ $p2 := partial "p2" . }}
   689  
   690  P1: {{ $p1 }}
   691  P2: {{ $p2 }}
   692  
   693  {{ end }}
   694  
   695  
   696  {{ define "partials/p1" }}Inline: p1{{ end }}
   697  
   698  {{ define "partials/p2" }}
   699  {{ $value := 32 }}
   700  {{ return $value }}
   701  {{ end }}
   702  
   703  
   704  `,
   705  	)
   706  
   707  	b.CreateSites().Build(BuildCfg{})
   708  
   709  	b.AssertFileContent("public/index.html",
   710  		`
   711  P1: Inline: p1
   712  P2: 32
   713  P3: Inline: p3
   714  `,
   715  	)
   716  }
   717  
   718  // https://github.com/gohugoio/hugo/issues/7478
   719  func TestBaseWithAndWithoutDefine(t *testing.T) {
   720  	b := newTestSitesBuilder(t)
   721  
   722  	b.WithContent("p1.md", "---\ntitle: P\n---\nContent")
   723  
   724  	b.WithTemplates(
   725  		"_default/baseof.html", `
   726  ::Header Start:{{ block "header" . }}{{ end }}:Header End:
   727  ::{{ block "main" . }}Main{{ end }}::
   728  `, "index.html", `
   729  {{ define "header" }}
   730  Home Header
   731  {{ end }}
   732  {{ define "main" }}
   733  This is home main
   734  {{ end }}
   735  `,
   736  
   737  		"_default/single.html", `
   738  {{ define "main" }}
   739  This is single main
   740  {{ end }}
   741  `,
   742  	)
   743  
   744  	b.CreateSites().Build(BuildCfg{})
   745  
   746  	b.AssertFileContent("public/index.html", `
   747  Home Header
   748  This is home main
   749  `,
   750  	)
   751  
   752  	b.AssertFileContent("public/p1/index.html", `
   753   ::Header Start::Header End:
   754  This is single main
   755  `,
   756  	)
   757  }
   758  
   759  // Issue 9393.
   760  func TestApplyWithNamespace(t *testing.T) {
   761  	b := newTestSitesBuilder(t)
   762  
   763  	b.WithTemplates(
   764  		"index.html", `
   765  {{ $b := slice " a " "     b "   "       c" }}		
   766  {{ $a := apply $b "strings.Trim" "." " " }}
   767  a: {{ $a }}
   768  `,
   769  	).WithContent("p1.md", "")
   770  
   771  	b.Build(BuildCfg{})
   772  
   773  	b.AssertFileContent("public/index.html", `a: [a b c]`)
   774  }