| // Copyright 2011 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package template | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"fmt" | 
 | 	"strings" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | func TestTypedContent(t *testing.T) { | 
 | 	data := []interface{}{ | 
 | 		`<b> "foo%" O'Reilly &bar;`, | 
 | 		CSS(`a[href =~ "//example.com"]#foo`), | 
 | 		HTML(`Hello, <b>World</b> &tc!`), | 
 | 		HTMLAttr(` dir="ltr"`), | 
 | 		JS(`c && alert("Hello, World!");`), | 
 | 		JSStr(`Hello, World & O'Reilly\u0021`), | 
 | 		URL(`greeting=H%69,&addressee=(World)`), | 
 | 		Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`), | 
 | 		URL(`,foo/,`), | 
 | 	} | 
 |  | 
 | 	// For each content sensitive escaper, see how it does on | 
 | 	// each of the typed strings above. | 
 | 	tests := []struct { | 
 | 		// A template containing a single {{.}}. | 
 | 		input string | 
 | 		want  []string | 
 | 	}{ | 
 | 		{ | 
 | 			`<style>{{.}} { color: blue }</style>`, | 
 | 			[]string{ | 
 | 				`ZgotmplZ`, | 
 | 				// Allowed but not escaped. | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<div style="{{.}}">`, | 
 | 			[]string{ | 
 | 				`ZgotmplZ`, | 
 | 				// Allowed and HTML escaped. | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`{{.}}`, | 
 | 			[]string{ | 
 | 				`<b> "foo%" O'Reilly &bar;`, | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				// Not escaped. | 
 | 				`Hello, <b>World</b> &tc!`, | 
 | 				` dir="ltr"`, | 
 | 				`c && alert("Hello, World!");`, | 
 | 				`Hello, World & O'Reilly\u0021`, | 
 | 				`greeting=H%69,&addressee=(World)`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<a{{.}}>`, | 
 | 			[]string{ | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				// Allowed and HTML escaped. | 
 | 				` dir="ltr"`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 				`ZgotmplZ`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<a title={{.}}>`, | 
 | 			[]string{ | 
 | 				`<b> "foo%" O'Reilly &bar;`, | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				// Tags stripped, spaces escaped, entity not re-escaped. | 
 | 				`Hello, World &tc!`, | 
 | 				` dir="ltr"`, | 
 | 				`c && alert("Hello, World!");`, | 
 | 				`Hello, World & O'Reilly\u0021`, | 
 | 				`greeting=H%69,&addressee=(World)`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<a title='{{.}}'>`, | 
 | 			[]string{ | 
 | 				`<b> "foo%" O'Reilly &bar;`, | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				// Tags stripped, entity not re-escaped. | 
 | 				`Hello, World &tc!`, | 
 | 				` dir="ltr"`, | 
 | 				`c && alert("Hello, World!");`, | 
 | 				`Hello, World & O'Reilly\u0021`, | 
 | 				`greeting=H%69,&addressee=(World)`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<textarea>{{.}}</textarea>`, | 
 | 			[]string{ | 
 | 				`<b> "foo%" O'Reilly &bar;`, | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				// Angle brackets escaped to prevent injection of close tags, entity not re-escaped. | 
 | 				`Hello, <b>World</b> &tc!`, | 
 | 				` dir="ltr"`, | 
 | 				`c && alert("Hello, World!");`, | 
 | 				`Hello, World & O'Reilly\u0021`, | 
 | 				`greeting=H%69,&addressee=(World)`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<script>alert({{.}})</script>`, | 
 | 			[]string{ | 
 | 				`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, | 
 | 				`"a[href =~ \"//example.com\"]#foo"`, | 
 | 				`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, | 
 | 				`" dir=\"ltr\""`, | 
 | 				// Not escaped. | 
 | 				`c && alert("Hello, World!");`, | 
 | 				// Escape sequence not over-escaped. | 
 | 				`"Hello, World & O'Reilly\u0021"`, | 
 | 				`"greeting=H%69,\u0026addressee=(World)"`, | 
 | 				`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`, | 
 | 				`",foo/,"`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<button onclick="alert({{.}})">`, | 
 | 			[]string{ | 
 | 				`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, | 
 | 				`"a[href =~ \"//example.com\"]#foo"`, | 
 | 				`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, | 
 | 				`" dir=\"ltr\""`, | 
 | 				// Not JS escaped but HTML escaped. | 
 | 				`c && alert("Hello, World!");`, | 
 | 				// Escape sequence not over-escaped. | 
 | 				`"Hello, World & O'Reilly\u0021"`, | 
 | 				`"greeting=H%69,\u0026addressee=(World)"`, | 
 | 				`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`, | 
 | 				`",foo/,"`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<script>alert("{{.}}")</script>`, | 
 | 			[]string{ | 
 | 				`\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`, | 
 | 				`a[href =~ \u0022\/\/example.com\u0022]#foo`, | 
 | 				`Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`, | 
 | 				` dir=\u0022ltr\u0022`, | 
 | 				`c \u0026\u0026 alert(\u0022Hello, World!\u0022);`, | 
 | 				// Escape sequence not over-escaped. | 
 | 				`Hello, World \u0026 O\u0027Reilly\u0021`, | 
 | 				`greeting=H%69,\u0026addressee=(World)`, | 
 | 				`greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | 
 | 				`,foo\/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<script type="text/javascript">alert("{{.}}")</script>`, | 
 | 			[]string{ | 
 | 				`\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`, | 
 | 				`a[href =~ \u0022\/\/example.com\u0022]#foo`, | 
 | 				`Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`, | 
 | 				` dir=\u0022ltr\u0022`, | 
 | 				`c \u0026\u0026 alert(\u0022Hello, World!\u0022);`, | 
 | 				// Escape sequence not over-escaped. | 
 | 				`Hello, World \u0026 O\u0027Reilly\u0021`, | 
 | 				`greeting=H%69,\u0026addressee=(World)`, | 
 | 				`greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | 
 | 				`,foo\/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<script type="text/javascript">alert({{.}})</script>`, | 
 | 			[]string{ | 
 | 				`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, | 
 | 				`"a[href =~ \"//example.com\"]#foo"`, | 
 | 				`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, | 
 | 				`" dir=\"ltr\""`, | 
 | 				// Not escaped. | 
 | 				`c && alert("Hello, World!");`, | 
 | 				// Escape sequence not over-escaped. | 
 | 				`"Hello, World & O'Reilly\u0021"`, | 
 | 				`"greeting=H%69,\u0026addressee=(World)"`, | 
 | 				`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`, | 
 | 				`",foo/,"`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			// Not treated as JS. The output is same as for <div>{{.}}</div> | 
 | 			`<script type="text/template">{{.}}</script>`, | 
 | 			[]string{ | 
 | 				`<b> "foo%" O'Reilly &bar;`, | 
 | 				`a[href =~ "//example.com"]#foo`, | 
 | 				// Not escaped. | 
 | 				`Hello, <b>World</b> &tc!`, | 
 | 				` dir="ltr"`, | 
 | 				`c && alert("Hello, World!");`, | 
 | 				`Hello, World & O'Reilly\u0021`, | 
 | 				`greeting=H%69,&addressee=(World)`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<button onclick='alert("{{.}}")'>`, | 
 | 			[]string{ | 
 | 				`\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`, | 
 | 				`a[href =~ \u0022\/\/example.com\u0022]#foo`, | 
 | 				`Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`, | 
 | 				` dir=\u0022ltr\u0022`, | 
 | 				`c \u0026\u0026 alert(\u0022Hello, World!\u0022);`, | 
 | 				// Escape sequence not over-escaped. | 
 | 				`Hello, World \u0026 O\u0027Reilly\u0021`, | 
 | 				`greeting=H%69,\u0026addressee=(World)`, | 
 | 				`greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`, | 
 | 				`,foo\/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<a href="?q={{.}}">`, | 
 | 			[]string{ | 
 | 				`%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, | 
 | 				`a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, | 
 | 				`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, | 
 | 				`%20dir%3d%22ltr%22`, | 
 | 				`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, | 
 | 				`Hello%2c%20World%20%26%20O%27Reilly%5cu0021`, | 
 | 				// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done. | 
 | 				`greeting=H%69,&addressee=%28World%29`, | 
 | 				`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<style>body { background: url('?img={{.}}') }</style>`, | 
 | 			[]string{ | 
 | 				`%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`, | 
 | 				`a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`, | 
 | 				`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`, | 
 | 				`%20dir%3d%22ltr%22`, | 
 | 				`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`, | 
 | 				`Hello%2c%20World%20%26%20O%27Reilly%5cu0021`, | 
 | 				// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done. | 
 | 				`greeting=H%69,&addressee=%28World%29`, | 
 | 				`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`, | 
 | 				`,foo/,`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset="{{.}}">`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				// Commas are not esacped | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				// Leading spaces are not percent escapes. | 
 | 				` dir=%22ltr%22`, | 
 | 				// Spaces after commas are not percent escaped. | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				// Metadata is not escaped. | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset={{.}}>`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				// Spaces are HTML escaped not %-escaped | 
 | 				` dir=%22ltr%22`, | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				// Commas are escaped. | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset="{{.}} 2x, https://golang.org/ 500.5w">`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				` dir=%22ltr%22`, | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset="http://godoc.org/ {{.}}, https://golang.org/ 500.5w">`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				` dir=%22ltr%22`, | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset="http://godoc.org/?q={{.}} 2x, https://golang.org/ 500.5w">`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				` dir=%22ltr%22`, | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset="http://godoc.org/ 2x, {{.}} 500.5w">`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				` dir=%22ltr%22`, | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 		{ | 
 | 			`<img srcset="http://godoc.org/ 2x, https://golang.org/ {{.}}">`, | 
 | 			[]string{ | 
 | 				`#ZgotmplZ`, | 
 | 				`#ZgotmplZ`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				` dir=%22ltr%22`, | 
 | 				`#ZgotmplZ, World!%22%29;`, | 
 | 				`Hello,#ZgotmplZ`, | 
 | 				`greeting=H%69%2c&addressee=%28World%29`, | 
 | 				`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`, | 
 | 				`%2cfoo/%2c`, | 
 | 			}, | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for _, test := range tests { | 
 | 		tmpl := Must(New("x").Parse(test.input)) | 
 | 		pre := strings.Index(test.input, "{{.}}") | 
 | 		post := len(test.input) - (pre + 5) | 
 | 		var b bytes.Buffer | 
 | 		for i, x := range data { | 
 | 			b.Reset() | 
 | 			if err := tmpl.Execute(&b, x); err != nil { | 
 | 				t.Errorf("%q with %v: %s", test.input, x, err) | 
 | 				continue | 
 | 			} | 
 | 			if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got { | 
 | 				t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got) | 
 | 				continue | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | // Test that we print using the String method. Was issue 3073. | 
 | type myStringer struct { | 
 | 	v int | 
 | } | 
 |  | 
 | func (s *myStringer) String() string { | 
 | 	return fmt.Sprintf("string=%d", s.v) | 
 | } | 
 |  | 
 | type errorer struct { | 
 | 	v int | 
 | } | 
 |  | 
 | func (s *errorer) Error() string { | 
 | 	return fmt.Sprintf("error=%d", s.v) | 
 | } | 
 |  | 
 | func TestStringer(t *testing.T) { | 
 | 	s := &myStringer{3} | 
 | 	b := new(bytes.Buffer) | 
 | 	tmpl := Must(New("x").Parse("{{.}}")) | 
 | 	if err := tmpl.Execute(b, s); err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	var expect = "string=3" | 
 | 	if b.String() != expect { | 
 | 		t.Errorf("expected %q got %q", expect, b.String()) | 
 | 	} | 
 | 	e := &errorer{7} | 
 | 	b.Reset() | 
 | 	if err := tmpl.Execute(b, e); err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	expect = "error=7" | 
 | 	if b.String() != expect { | 
 | 		t.Errorf("expected %q got %q", expect, b.String()) | 
 | 	} | 
 | } | 
 |  | 
 | // https://golang.org/issue/5982 | 
 | func TestEscapingNilNonemptyInterfaces(t *testing.T) { | 
 | 	tmpl := Must(New("x").Parse("{{.E}}")) | 
 |  | 
 | 	got := new(bytes.Buffer) | 
 | 	testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand | 
 | 	tmpl.Execute(got, testData) | 
 |  | 
 | 	// A non-empty interface should print like an empty interface. | 
 | 	want := new(bytes.Buffer) | 
 | 	data := struct{ E interface{} }{} | 
 | 	tmpl.Execute(want, data) | 
 |  | 
 | 	if !bytes.Equal(want.Bytes(), got.Bytes()) { | 
 | 		t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes())) | 
 | 	} | 
 | } |