| // Copyright 2020 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 present |
| |
| import ( |
| "bytes" |
| "fmt" |
| "html/template" |
| "os" |
| "os/exec" |
| "path/filepath" |
| "runtime" |
| "testing" |
| ) |
| |
| func TestTestdata(t *testing.T) { |
| tmpl := template.Must(Template().Parse(testTmpl)) |
| filesP, err := filepath.Glob("testdata/*.p") |
| if err != nil { |
| t.Fatal(err) |
| } |
| filesMD, err := filepath.Glob("testdata/*.md") |
| if err != nil { |
| t.Fatal(err) |
| } |
| files := append(filesP, filesMD...) |
| for _, file := range files { |
| file := file |
| name := filepath.Base(file) |
| if name == "README" { |
| continue |
| } |
| t.Run(name, func(t *testing.T) { |
| data, err := os.ReadFile(file) |
| if err != nil { |
| t.Fatalf("%s: %v", file, err) |
| } |
| marker := []byte("\n---\n") |
| i := bytes.Index(data, marker) |
| if i < 0 { |
| t.Fatalf("%s: cannot find --- marker in input", file) |
| } |
| input, html := data[:i+1], data[i+len(marker):] |
| doc, err := Parse(bytes.NewReader(input), name, 0) |
| if err != nil { |
| t.Fatalf("%s: %v", file, err) |
| } |
| var buf bytes.Buffer |
| if err := doc.Render(&buf, tmpl); err != nil { |
| t.Fatalf("%s: %v", file, err) |
| } |
| if !bytes.Equal(buf.Bytes(), html) { |
| diffText, err := diff("present-test-", "want", html, "have", buf.Bytes()) |
| if err != nil { |
| t.Fatalf("%s: diff: %v", file, err) |
| } |
| t.Errorf("%s: incorrect html:\n%s", file, diffText) |
| } |
| }) |
| } |
| } |
| |
| func diff(prefix string, name1 string, b1 []byte, name2 string, b2 []byte) ([]byte, error) { |
| f1, err := writeTempFile(prefix, b1) |
| if err != nil { |
| return nil, err |
| } |
| defer os.Remove(f1) |
| |
| f2, err := writeTempFile(prefix, b2) |
| if err != nil { |
| return nil, err |
| } |
| defer os.Remove(f2) |
| |
| cmd := "diff" |
| if runtime.GOOS == "plan9" { |
| cmd = "/bin/ape/diff" |
| } |
| |
| data, err := exec.Command(cmd, "-u", f1, f2).Output() |
| if len(data) > 0 { |
| // diff exits with a non-zero status when the files don't match. |
| // Ignore that failure as long as we get output. |
| err = nil |
| } else if exit, ok := err.(*exec.ExitError); ok && len(exit.Stderr) > 0 { |
| err = fmt.Errorf("%w\nstderr:\n%s)", err, exit.Stderr) |
| } |
| |
| data = bytes.Replace(data, []byte(f1), []byte(name1), -1) |
| data = bytes.Replace(data, []byte(f2), []byte(name2), -1) |
| |
| return data, err |
| } |
| |
| func writeTempFile(prefix string, data []byte) (string, error) { |
| file, err := os.CreateTemp("", prefix) |
| if err != nil { |
| return "", err |
| } |
| _, err = file.Write(data) |
| if err1 := file.Close(); err == nil { |
| err = err1 |
| } |
| if err != nil { |
| os.Remove(file.Name()) |
| return "", err |
| } |
| return file.Name(), nil |
| } |
| |
| var testTmpl = ` |
| {{define "root" -}} |
| <h1>{{.Title}}</h1> |
| {{with .Subtitle}}<h2>{{.}}</h2> |
| {{end -}} |
| {{range .Authors}}<author> |
| {{range .Elem}}{{elem $.Template .}}{{end}}</author> |
| {{end -}} |
| {{range .Sections}}<section>{{elem $.Template .}}</section> |
| {{end -}} |
| {{end}} |
| |
| {{define "newline"}}{{/* No automatic line break. Paragraphs are free-form. */}} |
| {{end}} |
| |
| {{define "section"}} |
| {{if .Title}}<h2 id="TOC_{{.FormattedNumber}}">{{.Title}}</h2> |
| {{end -}} |
| {{range .Elem}}{{elem $.Template .}}{{end}} |
| {{- end}} |
| |
| {{define "list" -}} |
| <ul> |
| {{range .Bullet -}} |
| <li>{{style .}}</li> |
| {{end -}} |
| </ul> |
| {{end}} |
| |
| {{define "text" -}} |
| {{if .Pre -}} |
| <pre>{{range .Lines}}{{.}}{{end}}</pre> |
| {{else -}} |
| <p>{{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}{{end}}{{style $l}}{{end}}</p> |
| {{end -}} |
| {{end}} |
| |
| {{define "code" -}} |
| {{if .Play -}} |
| <div class="playground">{{.Text}}</div> |
| {{else -}} |
| <div class="code">{{.Text}}</div> |
| {{end -}} |
| {{end}} |
| |
| {{define "image" -}} |
| <img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}} alt=""> |
| {{end}} |
| |
| {{define "caption" -}} |
| <figcaption>{{style .Text}}</figcaption> |
| {{end}} |
| |
| {{define "iframe" -}} |
| <iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe> |
| {{end}} |
| |
| {{define "link" -}} |
| <p class="link"><a href="{{.URL}}">{{style .Label}}</a></p> |
| {{end}} |
| |
| {{define "html" -}}{{.HTML}}{{end}} |
| ` |