hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

template_test.go (6480B)

    1 // Copyright 2016 The Go Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style
    3 // license that can be found in the LICENSE file.
    4 
    5 //go:build go1.13
    6 // +build go1.13
    7 
    8 package template_test
    9 
   10 import (
   11 	"bytes"
   12 	"encoding/json"
   13 	"strings"
   14 	"testing"
   15 
   16 	. "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
   17 	"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse" // https://golang.org/issue/12996
   18 )
   19 
   20 func TestTemplateClone(t *testing.T) {
   21 
   22 	orig := New("name")
   23 	clone, err := orig.Clone()
   24 	if err != nil {
   25 		t.Fatal(err)
   26 	}
   27 	if len(clone.Templates()) != len(orig.Templates()) {
   28 		t.Fatalf("Invalid length of t.Clone().Templates()")
   29 	}
   30 
   31 	const want = "stuff"
   32 	parsed := Must(clone.Parse(want))
   33 	var buf bytes.Buffer
   34 	err = parsed.Execute(&buf, nil)
   35 	if err != nil {
   36 		t.Fatal(err)
   37 	}
   38 	if got := buf.String(); got != want {
   39 		t.Fatalf("got %q; want %q", got, want)
   40 	}
   41 }
   42 
   43 func TestRedefineNonEmptyAfterExecution(t *testing.T) {
   44 	c := newTestCase(t)
   45 	c.mustParse(c.root, `foo`)
   46 	c.mustExecute(c.root, nil, "foo")
   47 	c.mustNotParse(c.root, `bar`)
   48 }
   49 
   50 func TestRedefineEmptyAfterExecution(t *testing.T) {
   51 	c := newTestCase(t)
   52 	c.mustParse(c.root, ``)
   53 	c.mustExecute(c.root, nil, "")
   54 	c.mustNotParse(c.root, `foo`)
   55 	c.mustExecute(c.root, nil, "")
   56 }
   57 
   58 func TestRedefineAfterNonExecution(t *testing.T) {
   59 	c := newTestCase(t)
   60 	c.mustParse(c.root, `{{if .}}<{{template "X"}}>{{end}}{{define "X"}}foo{{end}}`)
   61 	c.mustExecute(c.root, 0, "")
   62 	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
   63 	c.mustExecute(c.root, 1, "&lt;foo>")
   64 }
   65 
   66 func TestRedefineAfterNamedExecution(t *testing.T) {
   67 	c := newTestCase(t)
   68 	c.mustParse(c.root, `<{{template "X" .}}>{{define "X"}}foo{{end}}`)
   69 	c.mustExecute(c.root, nil, "&lt;foo>")
   70 	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
   71 	c.mustExecute(c.root, nil, "&lt;foo>")
   72 }
   73 
   74 func TestRedefineNestedByNameAfterExecution(t *testing.T) {
   75 	c := newTestCase(t)
   76 	c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
   77 	c.mustExecute(c.lookup("X"), nil, "foo")
   78 	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
   79 	c.mustExecute(c.lookup("X"), nil, "foo")
   80 }
   81 
   82 func TestRedefineNestedByTemplateAfterExecution(t *testing.T) {
   83 	c := newTestCase(t)
   84 	c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
   85 	c.mustExecute(c.lookup("X"), nil, "foo")
   86 	c.mustNotParse(c.lookup("X"), `bar`)
   87 	c.mustExecute(c.lookup("X"), nil, "foo")
   88 }
   89 
   90 func TestRedefineSafety(t *testing.T) {
   91 	c := newTestCase(t)
   92 	c.mustParse(c.root, `<html><a href="{{template "X"}}">{{define "X"}}{{end}}`)
   93 	c.mustExecute(c.root, nil, `<html><a href="">`)
   94 	// Note: Every version of Go prior to Go 1.8 accepted the redefinition of "X"
   95 	// on the next line, but luckily kept it from being used in the outer template.
   96 	// Now we reject it, which makes clearer that we're not going to use it.
   97 	c.mustNotParse(c.root, `{{define "X"}}" bar="baz{{end}}`)
   98 	c.mustExecute(c.root, nil, `<html><a href="">`)
   99 }
  100 
  101 func TestRedefineTopUse(t *testing.T) {
  102 	c := newTestCase(t)
  103 	c.mustParse(c.root, `{{template "X"}}{{.}}{{define "X"}}{{end}}`)
  104 	c.mustExecute(c.root, 42, `42`)
  105 	c.mustNotParse(c.root, `{{define "X"}}<script>{{end}}`)
  106 	c.mustExecute(c.root, 42, `42`)
  107 }
  108 
  109 func TestRedefineOtherParsers(t *testing.T) {
  110 	c := newTestCase(t)
  111 	c.mustParse(c.root, ``)
  112 	c.mustExecute(c.root, nil, ``)
  113 	if _, err := c.root.ParseFiles("no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
  114 		t.Errorf("ParseFiles: %v\nwanted error about already having Executed", err)
  115 	}
  116 	if _, err := c.root.ParseGlob("*.no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
  117 		t.Errorf("ParseGlob: %v\nwanted error about already having Executed", err)
  118 	}
  119 	if _, err := c.root.AddParseTree("t1", c.root.Tree); err == nil || !strings.Contains(err.Error(), "Execute") {
  120 		t.Errorf("AddParseTree: %v\nwanted error about already having Executed", err)
  121 	}
  122 }
  123 
  124 func TestNumbers(t *testing.T) {
  125 	c := newTestCase(t)
  126 	c.mustParse(c.root, `{{print 1_2.3_4}} {{print 0x0_1.e_0p+02}}`)
  127 	c.mustExecute(c.root, nil, "12.34 7.5")
  128 }
  129 
  130 func TestStringsInScriptsWithJsonContentTypeAreCorrectlyEscaped(t *testing.T) {
  131 	// See #33671 and #37634 for more context on this.
  132 	tests := []struct{ name, in string }{
  133 		{"empty", ""},
  134 		{"invalid", string(rune(-1))},
  135 		{"null", "\u0000"},
  136 		{"unit separator", "\u001F"},
  137 		{"tab", "\t"},
  138 		{"gt and lt", "<>"},
  139 		{"quotes", `'"`},
  140 		{"ASCII letters", "ASCII letters"},
  141 		{"Unicode", "ʕ⊙ϖ⊙ʔ"},
  142 		{"Pizza", "🍕"},
  143 	}
  144 	const (
  145 		prefix = `<script type="application/ld+json">`
  146 		suffix = `</script>`
  147 		templ  = prefix + `"{{.}}"` + suffix
  148 	)
  149 	tpl := Must(New("JS string is JSON string").Parse(templ))
  150 	for _, tt := range tests {
  151 		t.Run(tt.name, func(t *testing.T) {
  152 			var buf bytes.Buffer
  153 			if err := tpl.Execute(&buf, tt.in); err != nil {
  154 				t.Fatalf("Cannot render template: %v", err)
  155 			}
  156 			trimmed := bytes.TrimSuffix(bytes.TrimPrefix(buf.Bytes(), []byte(prefix)), []byte(suffix))
  157 			var got string
  158 			if err := json.Unmarshal(trimmed, &got); err != nil {
  159 				t.Fatalf("Cannot parse JS string %q as JSON: %v", trimmed[1:len(trimmed)-1], err)
  160 			}
  161 			if got != tt.in {
  162 				t.Errorf("Serialization changed the string value: got %q want %q", got, tt.in)
  163 			}
  164 		})
  165 	}
  166 }
  167 
  168 func TestSkipEscapeComments(t *testing.T) {
  169 	c := newTestCase(t)
  170 	tr := parse.New("root")
  171 	tr.Mode = parse.ParseComments
  172 	newT, err := tr.Parse("{{/* A comment */}}{{ 1 }}{{/* Another comment */}}", "", "", make(map[string]*parse.Tree))
  173 	if err != nil {
  174 		t.Fatalf("Cannot parse template text: %v", err)
  175 	}
  176 	c.root, err = c.root.AddParseTree("root", newT)
  177 	if err != nil {
  178 		t.Fatalf("Cannot add parse tree to template: %v", err)
  179 	}
  180 	c.mustExecute(c.root, nil, "1")
  181 }
  182 
  183 type testCase struct {
  184 	t    *testing.T
  185 	root *Template
  186 }
  187 
  188 func newTestCase(t *testing.T) *testCase {
  189 	return &testCase{
  190 		t:    t,
  191 		root: New("root"),
  192 	}
  193 }
  194 
  195 func (c *testCase) lookup(name string) *Template {
  196 	return c.root.Lookup(name)
  197 }
  198 
  199 func (c *testCase) mustParse(t *Template, text string) {
  200 	_, err := t.Parse(text)
  201 	if err != nil {
  202 		c.t.Fatalf("parse: %v", err)
  203 	}
  204 }
  205 
  206 func (c *testCase) mustNotParse(t *Template, text string) {
  207 	_, err := t.Parse(text)
  208 	if err == nil {
  209 		c.t.Fatalf("parse: unexpected success")
  210 	}
  211 }
  212 
  213 func (c *testCase) mustExecute(t *Template, val any, want string) {
  214 	var buf bytes.Buffer
  215 	err := t.Execute(&buf, val)
  216 	if err != nil {
  217 		c.t.Fatalf("execute: %v", err)
  218 	}
  219 	if buf.String() != want {
  220 		c.t.Fatalf("template output:\n%s\nwant:\n%s", buf.String(), want)
  221 	}
  222 }