81 lines
2.9 KiB
Go
81 lines
2.9 KiB
Go
//go:build js && wasm
|
|
|
|
package wasmruntime
|
|
|
|
import (
|
|
"strings"
|
|
"syscall/js"
|
|
"testing"
|
|
|
|
"kjol/wasmruntime/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)
|
|
}
|
|
}
|