Home | History | Annotate | Download | only in 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 	. "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, "&lt;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, "&lt;foo>")
     64 	c.mustNotParse(c.root, `{{define "X"}}bar{{end}}`)
     65 	c.mustExecute(c.root, nil, "&lt;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