| | |
| | |
| | |
| |
|
| | package template |
| |
|
| | import ( |
| | "bytes" |
| | "fmt" |
| | "strings" |
| | "testing" |
| | ) |
| |
|
| | func TestTypedContent(t *testing.T) { |
| | data := []any{ |
| | `<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/,`), |
| | } |
| |
|
| | |
| | |
| | tests := []struct { |
| | |
| | input string |
| | want []string |
| | }{ |
| | { |
| | `<style>{{.}} { color: blue }</style>`, |
| | []string{ |
| | `ZgotmplZ`, |
| | |
| | `a[href =~ "//example.com"]#foo`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | }, |
| | }, |
| | { |
| | `<div style="{{.}}">`, |
| | []string{ |
| | `ZgotmplZ`, |
| | |
| | `a[href =~ "//example.com"]#foo`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | }, |
| | }, |
| | { |
| | `{{.}}`, |
| | []string{ |
| | `<b> "foo%" O'Reilly &bar;`, |
| | `a[href =~ "//example.com"]#foo`, |
| | |
| | `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`, |
| | |
| | ` dir="ltr"`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | `ZgotmplZ`, |
| | }, |
| | }, |
| | { |
| | `<a title={{.}}>`, |
| | []string{ |
| | `<b> "foo%" O'Reilly &bar;`, |
| | `a[href =~ "//example.com"]#foo`, |
| | |
| | `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`, |
| | |
| | `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`, |
| | |
| | `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\""`, |
| | |
| | `c && alert("Hello, World!");`, |
| | |
| | `"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\""`, |
| | |
| | `c && alert("Hello, World!");`, |
| | |
| | `"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);`, |
| | |
| | `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);`, |
| | |
| | `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\""`, |
| | |
| | `c && alert("Hello, World!");`, |
| | |
| | `"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 type="text/template">{{.}}</script>`, |
| | []string{ |
| | `<b> "foo%" O'Reilly &bar;`, |
| | `a[href =~ "//example.com"]#foo`, |
| | |
| | `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);`, |
| | |
| | `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`, |
| | |
| | `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`, |
| | |
| | `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`, |
| | |
| | `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={{.}}>`, |
| | []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="{{.}} 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 strings.Builder |
| | 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 |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | 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(strings.Builder) |
| | 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()) |
| | } |
| | } |
| |
|
| | |
| | func TestEscapingNilNonemptyInterfaces(t *testing.T) { |
| | tmpl := Must(New("x").Parse("{{.E}}")) |
| |
|
| | got := new(bytes.Buffer) |
| | testData := struct{ E error }{} |
| | tmpl.Execute(got, testData) |
| |
|
| | |
| | want := new(bytes.Buffer) |
| | data := struct{ E any }{} |
| | tmpl.Execute(want, data) |
| |
|
| | if !bytes.Equal(want.Bytes(), got.Bytes()) { |
| | t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes())) |
| | } |
| | } |
| |
|