Russ Cox | 604146c | 2016-10-19 10:27:05 -0400 | [diff] [blame] | 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 |
Nodir Turakulov | cf59c1f | 2015-10-20 09:02:28 -0700 | [diff] [blame] | 6 | |
| 7 | import ( |
| 8 | "bytes" |
Russ Cox | 604146c | 2016-10-19 10:27:05 -0400 | [diff] [blame] | 9 | . "html/template" |
| 10 | "strings" |
Nodir Turakulov | cf59c1f | 2015-10-20 09:02:28 -0700 | [diff] [blame] | 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()) { |
Martin Möhrmann | fdd0179 | 2016-02-24 11:55:20 +0100 | [diff] [blame] | 22 | t.Fatalf("Invalid length of t.Clone().Templates()") |
Nodir Turakulov | cf59c1f | 2015-10-20 09:02:28 -0700 | [diff] [blame] | 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 | } |
Russ Cox | 604146c | 2016-10-19 10:27:05 -0400 | [diff] [blame] | 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 | } |