Seed from go-template-repo, renamed to simplexcalc
The template's files at a77fd30, without its history or LICENSE, after script/rename simplexcalc. Model: opus-5-5
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
package render_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"sneak.berlin/go/simplexcalc/internal/database"
|
||||
"sneak.berlin/go/simplexcalc/internal/globals"
|
||||
"sneak.berlin/go/simplexcalc/internal/render"
|
||||
"sneak.berlin/go/simplexcalc/templates"
|
||||
)
|
||||
|
||||
func newRenderer(t *testing.T) *render.Renderer {
|
||||
t.Helper()
|
||||
|
||||
r, err := render.New(nil, render.Params{
|
||||
Globals: &globals.Globals{
|
||||
Appname: "simplexcalc", Version: "test", Buildarch: "amd64",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("compiling templates: %v", err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// TestEveryEmbeddedPageCompiles is the reason New parses everything at
|
||||
// startup: a template that does not compile must be a process that does
|
||||
// not start, and this is what proves the set is complete rather than
|
||||
// just non-empty.
|
||||
func TestEveryEmbeddedPageCompiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newRenderer(t)
|
||||
|
||||
embedded, err := templates.FS.ReadDir(".")
|
||||
if err != nil {
|
||||
t.Fatalf("reading embedded templates: %v", err)
|
||||
}
|
||||
|
||||
want := 0
|
||||
|
||||
for _, e := range embedded {
|
||||
if !e.IsDir() && strings.HasSuffix(e.Name(), ".html") && e.Name() != "base.html" {
|
||||
want++
|
||||
}
|
||||
}
|
||||
|
||||
if want == 0 {
|
||||
t.Fatal("no page templates were embedded, so this test proves nothing")
|
||||
}
|
||||
|
||||
if got := len(r.Names()); got != want {
|
||||
t.Errorf("compiled %d pages (%v), embedded %d", got, r.Names(), want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestIndexRendersEmbeddedContent renders the page the service serves
|
||||
// at /, with real data, and checks that the base document, both
|
||||
// partials and the page body all made it into one response.
|
||||
func TestIndexRendersEmbeddedContent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newRenderer(t)
|
||||
|
||||
data := render.IndexPage{
|
||||
Page: r.NewPage(template.HTML(`<input type="hidden" name="csrf" />`)),
|
||||
WidgetCount: 1,
|
||||
Widgets: []database.Widget{
|
||||
{
|
||||
ID: "abc", Name: "a widget", SizeBytes: 4096,
|
||||
CreatedAt: time.Now().Add(-time.Hour),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := r.Execute(&buf, "index.html", data)
|
||||
if err != nil {
|
||||
t.Fatalf("rendering index: %v", err)
|
||||
}
|
||||
|
||||
out := buf.String()
|
||||
|
||||
for _, want := range []string{
|
||||
"<!doctype html>", // base document
|
||||
`href="/static/css/style.css"`, // base document links the embedded asset
|
||||
"<nav>", // navbar partial
|
||||
"<footer>", // footer partial
|
||||
"a widget", // page data
|
||||
"4.0 KiB", // the bytes template func
|
||||
"ago", // the since template func
|
||||
`name="csrf"`, // the CSRF field was placed
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Errorf("rendered page is missing %q\n---\n%s", want, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestHTMLEscapesUserData: the name comes from a form, and html/template
|
||||
// is only a defence if the data actually goes through it.
|
||||
func TestHTMLEscapesUserData(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newRenderer(t)
|
||||
|
||||
data := render.IndexPage{
|
||||
Page: r.NewPage(""),
|
||||
WidgetCount: 1,
|
||||
Widgets: []database.Widget{
|
||||
{ID: "x", Name: `<script>alert(1)</script>`, CreatedAt: time.Now()},
|
||||
},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := r.Execute(&buf, "index.html", data)
|
||||
if err != nil {
|
||||
t.Fatalf("rendering: %v", err)
|
||||
}
|
||||
|
||||
if strings.Contains(buf.String(), "<script>alert(1)</script>") {
|
||||
t.Error("a widget name was rendered as live markup")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPagesDoNotShareBlocks: index.html and error.html both define
|
||||
// "title" and "content". Parsed into one set, the last one parsed would
|
||||
// define both for everybody, and every page would render as whichever
|
||||
// that was.
|
||||
func TestPagesDoNotShareBlocks(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newRenderer(t)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
err := r.Execute(&buf, "error.html", render.ErrorPage{
|
||||
Page: r.NewPage(""), Status: 404, Message: "gone fishing",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("rendering error page: %v", err)
|
||||
}
|
||||
|
||||
out := buf.String()
|
||||
|
||||
if !strings.Contains(out, "gone fishing") {
|
||||
t.Errorf("error page did not render its own content:\n%s", out)
|
||||
}
|
||||
|
||||
if strings.Contains(out, "widgets (") {
|
||||
t.Error("the error page rendered the index's content block")
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnknownTemplateIsAnError, rather than an empty 200.
|
||||
func TestUnknownTemplateIsAnError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newRenderer(t)
|
||||
|
||||
err := r.Execute(&bytes.Buffer{}, "nope.html", nil)
|
||||
if err == nil {
|
||||
t.Fatal("rendering a template that does not exist must fail")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHTMLWritesStatusAndType checks the response contract, since the
|
||||
// handlers rely on it for every page they serve.
|
||||
func TestHTMLWritesStatusAndType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
r := newRenderer(t)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
err := r.HTML(w, 404, "error.html", render.ErrorPage{
|
||||
Page: r.NewPage(""), Status: 404, Message: "no",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("rendering: %v", err)
|
||||
}
|
||||
|
||||
if w.Code != 404 {
|
||||
t.Errorf("status = %d, want 404", w.Code)
|
||||
}
|
||||
|
||||
if ct := w.Header().Get("Content-Type"); !strings.HasPrefix(ct, "text/html") {
|
||||
t.Errorf("Content-Type = %q", ct)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user