Files
kjol/go/wasmruntime/hydrate_test.go

81 lines
2.9 KiB
Go

//go:build js && wasm
package wasmruntime
import (
"strings"
"syscall/js"
"testing"
"kjol/vdom"
)
// buildServerDOM fakes what the HTML parser hands us: a real DOM tree, built from
// the SERVER's VNode tree, which hydration then adopts.
func buildServerDOM(server *vdom.VNode) js.Value { return createDOM(server, "") }
// Hydration ADOPTS the server's DOM. If the server's HTML and the client's render
// disagree, the disagreement has to be resolved in the CLIENT's favour — it is the
// one that is running, and it is the only one that can still change.
//
// Left unreconciled, the divergence is permanent and invisible: the DOM keeps the
// server's value, and no later re-render corrects it either, because updateAttrs
// compares one client VNode against the next — which agree with each other and
// disagree with the DOM. The symptom is a page that is right when you navigate to it
// and wrong when you refresh onto it.
func TestHydrationReconcilesAttributes(t *testing.T) {
root := document.Call("createElement", "div")
// What the server rendered (say, from a stale binary).
server := vdom.El("main", vdom.Attr("class", "mx-auto max-w-5xl px-4"))
root.Call("appendChild", buildServerDOM(server))
// What the client actually renders now.
client := vdom.El("main", vdom.Attr("class", "mx-auto max-w-[100rem] px-4"))
hydrateNode(root.Get("firstChild"), client)
got := root.Get("firstChild").Call("getAttribute", "class").String()
if got != "mx-auto max-w-[100rem] px-4" {
t.Errorf("after hydration class = %q, want the client's value — the server's stale class was kept", got)
}
}
// And the ordinary case must still work: matching markup is adopted, not rebuilt.
func TestHydrationAdoptsMatchingMarkup(t *testing.T) {
root := document.Call("createElement", "div")
server := vdom.El("div", vdom.Attr("class", "a"),
vdom.El("span", vdom.Text("hello")),
)
root.Call("appendChild", buildServerDOM(server))
adopted := root.Get("firstChild")
client := vdom.El("div", vdom.Attr("class", "a"),
vdom.El("span", vdom.Text("hello")),
)
hydrateNode(root.Get("firstChild"), client)
// Same node — hydration adopted it rather than replacing it.
if !rt(client).dom.Equal(adopted) {
t.Error("hydration replaced a matching node instead of adopting it")
}
if got := root.Get("innerHTML").String(); !strings.Contains(got, "hello") {
t.Errorf("content lost: %s", got)
}
}
// A text node that disagrees is corrected too (this already worked; pin it).
func TestHydrationCorrectsText(t *testing.T) {
root := document.Call("createElement", "div")
server := vdom.El("p", vdom.Text("stale"))
root.Call("appendChild", buildServerDOM(server))
client := vdom.El("p", vdom.Text("fresh"))
hydrateNode(root.Get("firstChild"), client)
if got := root.Get("innerHTML").String(); !strings.Contains(got, "fresh") {
t.Errorf("stale server text survived hydration: %s", got)
}
}