blob: 90c5a73ba7a3afa5bda0bf56702bcc32dcb46624 [file] [log] [blame]
Russ Cox604146c2016-10-19 10:27:05 -04001// 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
5package template_test
Nodir Turakulovcf59c1f2015-10-20 09:02:28 -07006
7import (
8 "bytes"
Russ Cox604146c2016-10-19 10:27:05 -04009 . "html/template"
10 "strings"
Nodir Turakulovcf59c1f2015-10-20 09:02:28 -070011 "testing"
12)
13
14func TestTemplateClone(t *testing.T) {
15 // https://golang.org/issue/12996
16 orig := New("name")
17 clone, err := orig.Clone()
18 if err != nil {
19 t.Fatal(err)
20 }
21 if len(clone.Templates()) != len(orig.Templates()) {
Martin Möhrmannfdd01792016-02-24 11:55:20 +010022 t.Fatalf("Invalid length of t.Clone().Templates()")
Nodir Turakulovcf59c1f2015-10-20 09:02:28 -070023 }
24
25 const want = "stuff"
26 parsed := Must(clone.Parse(want))
27 var buf bytes.Buffer
28 err = parsed.Execute(&buf, nil)
29 if err != nil {
30 t.Fatal(err)
31 }
32 if got := buf.String(); got != want {
33 t.Fatalf("got %q; want %q", got, want)
34 }
35}
Russ Cox604146c2016-10-19 10:27:05 -040036
37func TestRedefineNonEmptyAfterExecution(t *testing.T) {
38 c := newTestCase(t)
39 c.mustParse(c.root, `foo`)
40 c.mustExecute(c.root, nil, "foo")
41 c.mustNotParse(c.root, `bar`)
42}
43
44func TestRedefineEmptyAfterExecution(t *testing.T) {
45 c := newTestCase(t)
46 c.mustParse(c.root, ``)
47 c.mustExecute(c.root, nil, "")
48 c.mustNotParse(c.root, `foo`)
49 c.mustExecute(c.root, nil, "")
50}
51
52func TestRedefineAfterNonExecution(t *testing.T) {
53 c := newTestCase(t)
54 c.mustParse(c.root, `{{if .}}<{{template "X"}}>{{end}}{{define "X"}}foo{{end}}`)
55 c.mustExecute(c.root, 0, "")
56 c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
57 c.mustExecute(c.root, 1, "&lt;foo>")
58}
59
60func TestRedefineAfterNamedExecution(t *testing.T) {
61 c := newTestCase(t)
62 c.mustParse(c.root, `<{{template "X" .}}>{{define "X"}}foo{{end}}`)
63 c.mustExecute(c.root, nil, "&lt;foo>")
64 c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
65 c.mustExecute(c.root, nil, "&lt;foo>")
66}
67
68func TestRedefineNestedByNameAfterExecution(t *testing.T) {
69 c := newTestCase(t)
70 c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
71 c.mustExecute(c.lookup("X"), nil, "foo")
72 c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
73 c.mustExecute(c.lookup("X"), nil, "foo")
74}
75
76func TestRedefineNestedByTemplateAfterExecution(t *testing.T) {
77 c := newTestCase(t)
78 c.mustParse(c.root, `{{define "X"}}foo{{end}}`)
79 c.mustExecute(c.lookup("X"), nil, "foo")
80 c.mustNotParse(c.lookup("X"), `bar`)
81 c.mustExecute(c.lookup("X"), nil, "foo")
82}
83
84func TestRedefineSafety(t *testing.T) {
85 c := newTestCase(t)
86 c.mustParse(c.root, `<html><a href="{{template "X"}}">{{define "X"}}{{end}}`)
87 c.mustExecute(c.root, nil, `<html><a href="">`)
88 // Note: Every version of Go prior to Go 1.8 accepted the redefinition of "X"
89 // on the next line, but luckily kept it from being used in the outer template.
90 // Now we reject it, which makes clearer that we're not going to use it.
91 c.mustNotParse(c.root, `{{define "X"}}" bar="baz{{end}}`)
92 c.mustExecute(c.root, nil, `<html><a href="">`)
93}
94
95func TestRedefineTopUse(t *testing.T) {
96 c := newTestCase(t)
97 c.mustParse(c.root, `{{template "X"}}{{.}}{{define "X"}}{{end}}`)
98 c.mustExecute(c.root, 42, `42`)
99 c.mustNotParse(c.root, `{{define "X"}}<script>{{end}}`)
100 c.mustExecute(c.root, 42, `42`)
101}
102
103func TestRedefineOtherParsers(t *testing.T) {
104 c := newTestCase(t)
105 c.mustParse(c.root, ``)
106 c.mustExecute(c.root, nil, ``)
107 if _, err := c.root.ParseFiles("no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
108 t.Errorf("ParseFiles: %v\nwanted error about already having Executed", err)
109 }
110 if _, err := c.root.ParseGlob("*.no.template"); err == nil || !strings.Contains(err.Error(), "Execute") {
111 t.Errorf("ParseGlob: %v\nwanted error about already having Executed", err)
112 }
113 if _, err := c.root.AddParseTree("t1", c.root.Tree); err == nil || !strings.Contains(err.Error(), "Execute") {
114 t.Errorf("AddParseTree: %v\nwanted error about already having Executed", err)
115 }
116}
117
118type testCase struct {
119 t *testing.T
120 root *Template
121}
122
123func newTestCase(t *testing.T) *testCase {
124 return &testCase{
125 t: t,
126 root: New("root"),
127 }
128}
129
130func (c *testCase) lookup(name string) *Template {
131 return c.root.Lookup(name)
132}
133
134func (c *testCase) mustParse(t *Template, text string) {
135 _, err := t.Parse(text)
136 if err != nil {
137 c.t.Fatalf("parse: %v", err)
138 }
139}
140
141func (c *testCase) mustNotParse(t *Template, text string) {
142 _, err := t.Parse(text)
143 if err == nil {
144 c.t.Fatalf("parse: unexpected success")
145 }
146}
147
148func (c *testCase) mustExecute(t *Template, val interface{}, want string) {
149 var buf bytes.Buffer
150 err := t.Execute(&buf, val)
151 if err != nil {
152 c.t.Fatalf("execute: %v", err)
153 }
154 if buf.String() != want {
155 c.t.Fatalf("template output:\n%s\nwant:\n%s", buf.String(), want)
156 }
157}