Source file
src/html/template/template_test.go
1
2
3
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
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 strings.Builder
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, "<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, "<foo>")
66 c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
67 c.mustExecute(c.root, nil, "<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
91
92
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
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 any, want string) {
210 var buf strings.Builder
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