package html_test import ( "bytes" "code.squareroundforest.org/arpio/html" . "code.squareroundforest.org/arpio/html/tag" "strings" "testing" ) func TestRender(t *testing.T) { t.Run("merge render guides", func(t *testing.T) { foo := html.Inline(html.Verbatim(Define("foo"))) foo = foo("") var b bytes.Buffer if err := html.Write(&b, foo); err != nil { t.Fatal(err) } if b.String() != "\n\t\n" { t.Fatal(b.String()) } }) t.Run("attributes", func(t *testing.T) { t.Run("escaping", func(t *testing.T) { span := Span(Attr("foo", "bar=\"&\"")) var b bytes.Buffer if err := html.WriteRaw(&b, span); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("true form", func(t *testing.T) { span := Span(Attr("foo", true)) var b bytes.Buffer if err := html.WriteRaw(&b, span); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("declaration", func(t *testing.T) { comment := Comment("foo & bar & baz", "qux") var b bytes.Buffer if err := html.WriteRaw(&b, comment); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) }) t.Run("html escape", func(t *testing.T) { t.Run("basic escape", func(t *testing.T) { span := Span("bar&baz") var b bytes.Buffer if err := html.WriteRaw(&b, span); err != nil { t.Fatal(err) } if b.String() != "<foo>bar&baz</foo>" { t.Fatal(b.String()) } }) t.Run("consecutive spaces", func(t *testing.T) { span := Span("consecutive spaces: \" \"") var b bytes.Buffer if err := html.WriteRaw(&b, span); err != nil { t.Fatal(err) } if b.String() != "consecutive spaces: \"   \"" { 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 := &errorWriter{} if err := html.WriteRaw(w, div); err == nil || !strings.Contains(err.Error(), "test write error") { t.Fatal() } }) t.Run("fail in tag", func(t *testing.T) { div := Div(Span("foo")) w := &errorWriter{failAfter: 6} if err := html.WriteRaw(w, div); err == nil || !strings.Contains(err.Error(), "test write error") { t.Fatal() } }) t.Run("partial text children", func(t *testing.T) { div := Div("foo", Div("bar"), "baz") w := &errorWriter{failAfter: 5} if err := html.Write(w, div); err == nil || !strings.Contains(err.Error(), "test write error") { t.Fatal() } }) t.Run("text children", func(t *testing.T) { div := Div("foo", "bar", "baz") w := &errorWriter{failAfter: 5} if err := html.Write(w, div); err == nil || !strings.Contains(err.Error(), "test write 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.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo\n
" { t.Fatal(b.String()) } }) t.Run("empty tag", func(t *testing.T) { div := Div(Br) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\t
\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.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo bar baz\n\t
\n\t\tqux quux\n\t
\n\tcorge\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.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo bar baz\n\t\t
\n\t\t\tqux quux\n\t\t
\n\tcorge
\n
" { t.Fatal(b.String()) } }) t.Run("verbatim", func(t *testing.T) { foo := html.Verbatim(Define("foo")) foo = foo("bar\nbaz\nqux") var b bytes.Buffer if err := html.Write(&b, foo); err != nil { t.Fatal(err) } if b.String() != "\n\tbar\n\tbaz\n\tqux\n" { t.Fatal(b.String()) } }) t.Run("script not indented", func(t *testing.T) { script := Div(Script("foo()\nbar()\nbaz()")) var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "
\n\t\n
" { t.Fatal(b.String()) } }) }) t.Run("simple", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { div := Div("foo") var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo
" { t.Fatal(b.String()) } }) t.Run("unindented number", func(t *testing.T) { div := Div(42) var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
42
" { t.Fatal(b.String()) } }) t.Run("unindented after block", func(t *testing.T) { div := Div(Div, "foo") var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo
" { t.Fatal(b.String()) } }) t.Run("unindented after inline", func(t *testing.T) { div := Div(Span, "foo") var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo
" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { span := Span("foo") var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "foo" { t.Fatal(b.String()) } }) t.Run("inline number", func(t *testing.T) { span := Span(42) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "42" { t.Fatal(b.String()) } }) t.Run("inline after block", func(t *testing.T) { span := Span(Div, "foo") var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\t
\nfoo
" { t.Fatal(b.String()) } }) t.Run("inline after inline", func(t *testing.T) { span := Span(Span, "foo") var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "foo" { t.Fatal(b.String()) } }) t.Run("inline tag after block", func(t *testing.T) { span := Span(Div, Span("foo")) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\t
\nfoo
" { t.Fatal(b.String()) } }) t.Run("inline children", func(t *testing.T) { p := P("foo") var b bytes.Buffer if err := html.Write(&b, p); err != nil { t.Fatal(err) } if b.String() != "

foo

" { t.Fatal(b.String()) } }) t.Run("inline children number", func(t *testing.T) { p := P(42) var b bytes.Buffer if err := html.Write(&b, p); err != nil { t.Fatal(err) } if b.String() != "

42

" { t.Fatal(b.String()) } }) t.Run("inline children after inline children", func(t *testing.T) { p := P(P, "foo") var b bytes.Buffer if err := html.Write(&b, p); err != nil { t.Fatal(err) } if b.String() != "

\n\t

\nfoo

" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { div := Div("foo") var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo\n
" { t.Fatal(b.String()) } }) t.Run("block number", func(t *testing.T) { div := Div(42) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\t42\n
" { t.Fatal(b.String()) } }) t.Run("block after block", func(t *testing.T) { div := Div(Div, 42) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\t
\n\t42\n
" { t.Fatal(b.String()) } }) t.Run("block after inline", func(t *testing.T) { div := Div(Span, 42) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\t42\n
" { t.Fatal(b.String()) } }) }) t.Run("nil child", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { div := Div("foo", nil, "bar") var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foobar
" { t.Fatal(b.String()) } }) t.Run("unindented verbatim", func(t *testing.T) { div := html.Verbatim(Div("foo &", nil, " bar")) var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo & bar
" { t.Fatal(b.String()) } }) t.Run("unindented script", func(t *testing.T) { script := Script("let foo =", nil, " bar && baz") var b bytes.Buffer if err := html.WriteRaw(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { span := Span("foo", nil, "bar") var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "foobar" { t.Fatal(b.String()) } }) t.Run("inline verbatim", func(t *testing.T) { span := html.Verbatim(Span("foo &", nil, " bar")) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\tfoo &\n\t bar\n" { t.Fatal(b.String()) } }) t.Run("inline script", func(t *testing.T) { script := html.Inline(Script("let foo =", nil, " bar && baz")) var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { div := Div("foo", nil, "bar") var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoobar\n
" { t.Fatal(b.String()) } }) t.Run("block verbatim", func(t *testing.T) { div := html.Verbatim(Div("foo &", nil, " bar")) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo &\n\t bar\n
" { t.Fatal(b.String()) } }) t.Run("block script", func(t *testing.T) { script := Script("let foo =", nil, " bar && baz") var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) }) t.Run("empty child", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { div := Div("foo", "", "bar") var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foobar
" { t.Fatal(b.String()) } }) t.Run("unindented verbatim", func(t *testing.T) { div := html.Verbatim(Div("foo &", "", " bar")) var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo & bar
" { t.Fatal(b.String()) } }) t.Run("unindented script", func(t *testing.T) { script := Script("let foo =", "", " bar && baz") var b bytes.Buffer if err := html.WriteRaw(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { span := Span("foo", "", "bar") var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "foobar" { t.Fatal(b.String()) } }) t.Run("inline verbatim", func(t *testing.T) { span := html.Verbatim(Span("foo &", "", " bar")) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\tfoo &\n\t bar\n" { t.Fatal(b.String()) } }) t.Run("inline script", func(t *testing.T) { script := html.Inline(Script("let foo =", "", " bar && baz")) var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { div := Div("foo", "", "bar") var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoobar\n
" { t.Fatal(b.String()) } }) t.Run("block verbatim", func(t *testing.T) { div := html.Verbatim(Div("foo &", "", " bar")) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo &\n\t bar\n
" { t.Fatal(b.String()) } }) t.Run("block script", func(t *testing.T) { script := Script("let foo =", "", " bar && baz") var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) }) t.Run("reader child", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") div := Div(rd) var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo & bar & baz
" { t.Fatal(b.String()) } }) t.Run("unindented verbatim", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") div := html.Verbatim(Div(rd)) var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo & bar & baz
" { t.Fatal(b.String()) } }) t.Run("unindented script", func(t *testing.T) { rd := bytes.NewBufferString("let foo = bar() && baz()") script := Script(rd) var b bytes.Buffer if err := html.WriteRaw(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") span := Span(rd) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "foo & bar & baz" { t.Fatal(b.String()) } }) t.Run("inline verbatim", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") span := html.Verbatim(Span(rd)) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\tfoo & bar & baz\n" { t.Fatal(b.String()) } }) t.Run("inline script", func(t *testing.T) { rd := bytes.NewBufferString("let foo = bar() && baz()") script := html.Inline(Script(rd)) var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("inline after block", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") span := Span(Div, rd) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\t
\nfoo & bar & baz
" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") div := Div(rd) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo & bar & baz\n
" { t.Fatal(b.String()) } }) t.Run("block verbatim", func(t *testing.T) { rd := bytes.NewBufferString("foo & bar & baz") div := html.Verbatim(Div(rd)) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo & bar & baz\n
" { t.Fatal(b.String()) } }) t.Run("block script", func(t *testing.T) { rd := bytes.NewBufferString("let foo = bar() && baz()") script := Script(rd) var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) }) t.Run("void", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { var b bytes.Buffer if err := html.WriteRaw(&b, Br); err != nil { t.Fatal(err) } if b.String() != "
" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { var b bytes.Buffer if err := html.Write(&b, Br); err != nil { t.Fatal(err) } if b.String() != "
" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { var b bytes.Buffer if err := html.Write(&b, Hr); err != nil { t.Fatal(err) } if b.String() != "
" { t.Fatal(b.String()) } }) }) t.Run("no children", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { var b bytes.Buffer if err := html.WriteRaw(&b, Div); err != nil { t.Fatal(err) } if b.String() != "
" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { var b bytes.Buffer if err := html.Write(&b, Span); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { var b bytes.Buffer if err := html.Write(&b, Div); err != nil { t.Fatal(err) } if b.String() != "
" { t.Fatal(b.String()) } }) }) t.Run("verbatim", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { div := html.Verbatim(Div("foo & bar")) var b bytes.Buffer if err := html.WriteRaw(&b, div); err != nil { t.Fatal(err) } if b.String() != "
foo & bar
" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { span := html.Verbatim(Span("foo & bar")) var b bytes.Buffer if err := html.Write(&b, span); err != nil { t.Fatal(err) } if b.String() != "\n\tfoo & bar\n" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { div := html.Verbatim(Div("foo & bar")) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\tfoo & bar\n
" { t.Fatal(b.String()) } }) }) t.Run("preformatted", func(t *testing.T) { t.Run("simple", func(t *testing.T) { pre := Pre("foo\nbar") var b bytes.Buffer if err := html.Write(&b, pre); err != nil { t.Fatal(err) } if b.String() != "
\nfoo\nbar\n
" { t.Fatal(b.String()) } }) t.Run("indented", func(t *testing.T) { div := Div(Pre("foo\nbar")) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } if b.String() != "
\n\t
\nfoo\nbar\n\t
\n
" { t.Fatal(b.String()) } }) t.Run("child elements", func(t *testing.T) { div := Div(Pre("foo", Div("bar\n\t", Span("baz\n"), " "), "\t")) var b bytes.Buffer if err := html.Write(&b, div); err != nil { t.Fatal(err) } const expect = "
\n\t
\nfoo
bar\n\tbaz\n
\t\n\t
\n
" if b.String() != expect { t.Fatal(b.String()) } }) t.Run("escaping", func(t *testing.T) { pre := Pre("foo & ", Span("baz & "), "qux") var b bytes.Buffer if err := html.Write(&b, pre); err != nil { t.Fatal(err) } if b.String() != "
\nfoo & baz & qux\n
" { t.Fatal(b.String()) } }) t.Run("verbatim child element", func(t *testing.T) { div := html.Verbatim(Div) pre := Pre("foo", div("\n\tbar\t\n"), "baz") var b bytes.Buffer if err := html.Write(&b, pre); err != nil { t.Fatal(err) } if b.String() != "
\nfoo
\n\tbar\t\n
baz\n
" { t.Fatal(b.String()) } }) t.Run("verbatim", func(t *testing.T) { pre := html.Verbatim(Pre) pre = pre("
\n foo\n
") var b bytes.Buffer if err := html.Write(&b, pre); err != nil { t.Fatal(err) } if b.String() != "
\n
\n foo\n
\n
" { t.Fatal(b.String()) } }) t.Run("reader child", func(t *testing.T) { pre := Pre(bytes.NewBufferString("foo &\nbar &\nbaz")) var b bytes.Buffer if err := html.Write(&b, pre); err != nil { t.Fatal(err) } if b.String() != "
\nfoo &\nbar &\nbaz\n
" { t.Fatal(b.String()) } }) }) t.Run("script", func(t *testing.T) { t.Run("unindented", func(t *testing.T) { script := Script("let foo = bar && baz") var b bytes.Buffer if err := html.WriteRaw(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("inline", func(t *testing.T) { script := html.Inline(Script("let foo = bar && baz")) var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) t.Run("block", func(t *testing.T) { script := Script("let foo = bar && baz") var b bytes.Buffer if err := html.Write(&b, script); err != nil { t.Fatal(err) } if b.String() != "" { t.Fatal(b.String()) } }) }) t.Run("wrap", func(t *testing.T) { t.Run("simple", func(t *testing.T) { p := P("foo bar baz qux quux corge") var b bytes.Buffer if err := html.WriteIndent(&b, html.Indentation{Indent: "\t", PWidth: 15}, p); err != nil { t.Error(err) } if b.String() != "

foo bar baz\nqux quux corge\n

" { t.Error(b.String()) } }) t.Run("min width", func(t *testing.T) { div := Div(P("foo bar baz qux quux corge")) var b bytes.Buffer if err := html.WriteIndent(&b, html.Indentation{Indent: "\t", PWidth: 21, MinPWidth: 18}, div); err != nil { t.Error(err) } if b.String() != "
\n\t

foo bar baz qux\n\tquux corge

\n
" { t.Error(b.String()) } }) t.Run("min width inline", func(t *testing.T) { span := Span(P("foo bar baz qux quux corge")) var b bytes.Buffer if err := html.WriteIndent(&b, html.Indentation{Indent: "\t", PWidth: 21, MinPWidth: 18}, span); err != nil { t.Error(err) } if b.String() != "\n\t

foo bar baz qux\n\tquux corge

\n
" { t.Error(b.String()) } }) }) }