2024-09-20 02:13:08 +00:00
|
|
|
package h
|
|
|
|
|
|
|
|
|
|
import (
|
2024-09-26 21:01:41 +00:00
|
|
|
"bytes"
|
2024-09-20 02:13:08 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2024-09-26 21:01:41 +00:00
|
|
|
"golang.org/x/net/html"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
2024-09-20 02:13:08 +00:00
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
2024-09-26 21:01:41 +00:00
|
|
|
// Sort attributes of a node by attribute name
|
|
|
|
|
func sortAttributes(node *html.Node) {
|
|
|
|
|
if node.Type == html.ElementNode && len(node.Attr) > 1 {
|
|
|
|
|
sort.SliceStable(node.Attr, func(i, j int) bool {
|
|
|
|
|
return node.Attr[i].Key < node.Attr[j].Key
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Traverse and sort attributes in the entire HTML tree
|
|
|
|
|
func traverseAndSortAttributes(node *html.Node) {
|
|
|
|
|
sortAttributes(node)
|
|
|
|
|
for child := node.FirstChild; child != nil; child = child.NextSibling {
|
|
|
|
|
traverseAndSortAttributes(child)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse HTML, sort attributes, and render back to a string
|
|
|
|
|
func parseSortAndRenderHTML(input string) string {
|
|
|
|
|
// Parse the HTML string into a node tree
|
|
|
|
|
doc, err := html.Parse(strings.NewReader(input))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Traverse and sort attributes for each node
|
|
|
|
|
traverseAndSortAttributes(doc)
|
|
|
|
|
|
|
|
|
|
// Use a buffer to capture the rendered HTML
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
err = html.Render(&buf, doc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the rendered HTML as a string
|
|
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-20 02:13:08 +00:00
|
|
|
func TestRender(t *testing.T) {
|
2024-09-26 21:01:41 +00:00
|
|
|
t.Parallel()
|
2024-09-20 02:13:08 +00:00
|
|
|
div := Div(
|
|
|
|
|
Id("my-div"),
|
|
|
|
|
Attribute("data-attr-2", "value"),
|
|
|
|
|
Attributes(&AttributeMap{
|
|
|
|
|
"data-attr-3": "value",
|
|
|
|
|
"data-attr-4": "value",
|
|
|
|
|
}),
|
2024-09-23 01:59:44 +00:00
|
|
|
HxBeforeRequest(
|
2024-09-20 02:13:08 +00:00
|
|
|
SetText("before request"),
|
|
|
|
|
),
|
2024-09-23 01:59:44 +00:00
|
|
|
HxAfterRequest(
|
2024-09-20 02:13:08 +00:00
|
|
|
SetText("after request"),
|
|
|
|
|
),
|
|
|
|
|
Children(
|
|
|
|
|
Div(Text("hello, world")),
|
|
|
|
|
),
|
|
|
|
|
Text("hello, child"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
div.attributes["data-attr-1"] = "value"
|
|
|
|
|
|
2024-09-26 21:01:41 +00:00
|
|
|
expectedRaw := `<div data-attr-1="value" id="my-div" data-attr-2="value" data-attr-3="value" data-attr-4="value" hx-on::before-request="this.innerText = 'before request';" hx-on::after-request="this.innerText = 'after request';"><div >hello, world</div>hello, child</div>`
|
|
|
|
|
expected := parseSortAndRenderHTML(expectedRaw)
|
|
|
|
|
result := parseSortAndRenderHTML(Render(div))
|
2024-09-20 02:13:08 +00:00
|
|
|
|
|
|
|
|
assert.Equal(t,
|
2024-09-26 21:01:41 +00:00
|
|
|
expected,
|
2024-09-20 02:13:08 +00:00
|
|
|
result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRawContent(t *testing.T) {
|
2024-09-26 21:01:41 +00:00
|
|
|
t.Parallel()
|
2024-09-20 02:13:08 +00:00
|
|
|
str := "<div>hello, world</div>"
|
|
|
|
|
raw := Raw(str)
|
|
|
|
|
assert.Equal(t, str, Render(raw))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConditional(t *testing.T) {
|
2024-09-26 21:01:41 +00:00
|
|
|
t.Parallel()
|
2024-09-20 02:13:08 +00:00
|
|
|
result := Render(
|
|
|
|
|
Div(
|
2024-09-22 16:25:18 +00:00
|
|
|
Ternary(true, Text("true"), Text("false")),
|
2024-09-20 02:13:08 +00:00
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
assert.Equal(t, "<div >true</div>", result)
|
|
|
|
|
|
|
|
|
|
result = Render(
|
|
|
|
|
Div(
|
|
|
|
|
If(false, Text("true")),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
assert.Equal(t, "<div ></div>", result)
|
|
|
|
|
}
|