...

Source file src/html/template/template_test.go

Documentation: html/template

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

View as plain text