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 . "html/template" 10 "strings" 11 "testing" 12 ) 13 14 func 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()) { 22 t.Fatalf("Invalid length of t.Clone().Templates()") 23 } 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 } 36 37 func 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 44 func 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 52 func 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, "<foo>") 58 } 59 60 func 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, "<foo>") 64 c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`) 65 c.mustExecute(c.root, nil, "<foo>") 66 } 67 68 func 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 76 func 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 84 func 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 95 func 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 103 func 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 118 type testCase struct { 119 t *testing.T 120 root *Template 121 } 122 123 func newTestCase(t *testing.T) *testCase { 124 return &testCase{ 125 t: t, 126 root: New("root"), 127 } 128 } 129 130 func (c *testCase) lookup(name string) *Template { 131 return c.root.Lookup(name) 132 } 133 134 func (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 141 func (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 148 func (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 } 158