1
0
html/render_test.go
2025-09-11 20:50:00 +02:00

173 lines
3.8 KiB
Go

package html_test
import (
"bytes"
"code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags"
"errors"
"strings"
"testing"
)
type failingWriter struct {
after int
}
func failWriteAfter(n int) *failingWriter {
return &failingWriter{n}
}
func (w *failingWriter) Write(p []byte) (int, error) {
w.after -= len(p)
if w.after < 0 {
return len(p) + w.after, errors.New("test error")
}
return len(p), nil
}
func TestRender(t *testing.T) {
t.Run("merge render guides", func(t *testing.T) {
foo := html.Inline(html.Verbatim(NewTag("foo")))
foo = foo("<bar><baz></bar>")
var b bytes.Buffer
if err := html.RenderIndent(&b, "\t", 0, foo); err != nil {
t.Fatal(err)
}
if b.String() != "<foo><bar><baz></bar></foo>" {
t.Fatal(b.String())
}
})
t.Run("attribute escaping", func(t *testing.T) {
span := Span(Attr("foo", "bar=\"&\""))
var b bytes.Buffer
if err := html.Render(&b, span); err != nil {
t.Fatal(err)
}
if b.String() != "<span foo=\"bar=&quot;&amp;&quot;\"></span>" {
t.Fatal(b.String())
}
})
t.Run("html escape", func(t *testing.T) {
t.Run("basic escape", func(t *testing.T) {
span := Span("<foo>bar&baz</foo>")
var b bytes.Buffer
if err := html.Render(&b, span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>&lt;foo&gt;bar&amp;baz&lt;/foo&gt;</span>" {
t.Fatal(b.String())
}
})
t.Run("consecutive spaces", func(t *testing.T) {
span := Span("consecutive spaces: \" \"")
var b bytes.Buffer
if err := html.Render(&b, span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>consecutive spaces: \"&nbsp;&nbsp;&nbsp;\"</span>" {
t.Fatal(b.String())
}
})
})
t.Run("write error", func(t *testing.T) {
t.Run("fail immediately", func(t *testing.T) {
div := Div(Span("foo"))
w := failWriteAfter(0)
if err := html.Render(w, div); err == nil || !strings.Contains(err.Error(), "test error") {
t.Fatal()
}
})
t.Run("fail in tag", func(t *testing.T) {
div := Div(Span("foo"))
w := failWriteAfter(6)
if err := html.Render(w, div); err == nil || !strings.Contains(err.Error(), "test error") {
t.Fatal()
}
})
t.Run("partial text children", func(t *testing.T) {
div := Div("foo", Div("bar"), "baz")
w := failWriteAfter(5)
if err := html.RenderIndent(w, "\t", 0, div); err == nil || !strings.Contains(err.Error(), "test error") {
t.Fatal()
}
})
t.Run("text children", func(t *testing.T) {
div := Div("foo", "bar", "baz")
w := failWriteAfter(5)
if err := html.RenderIndent(w, "\t", 0, div); err == nil || !strings.Contains(err.Error(), "test error") {
t.Fatal()
}
})
})
t.Run("indent", func(t *testing.T) {
t.Run("simple tag", func(t *testing.T) {
div := Div(Span("foo"))
var b bytes.Buffer
if err := html.RenderIndent(&b, "\t", 0, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\t<span>foo</span>\n</div>\n" {
t.Fatal(b.String())
}
})
t.Run("empty tag", func(t *testing.T) {
div := Div(Br())
var b bytes.Buffer
if err := html.RenderIndent(&b, "\t", 0, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\t<br>\n</div>\n" {
t.Fatal(b.String())
}
})
t.Run("inline fragment between blocks", func(t *testing.T) {
div := Div("foo bar baz", Div("qux quux"), "corge")
var b bytes.Buffer
if err := html.RenderIndent(&b, "\t", 0, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo bar baz\n\t<div>\n\t\tqux quux\n\t</div>\n\tcorge\n</div>\n" {
t.Fatal(b.String())
}
})
t.Run("block inside inline", func(t *testing.T) {
div := Div(Span("foo bar baz", Div("qux quux"), "corge"))
var b bytes.Buffer
if err := html.RenderIndent(&b, "XYZ", 0, div); err != nil {
t.Fatal(err)
}
if b.String() != "" {
t.Fatal(b.String())
}
})
})
}