//go:build js && wasm package wasmruntime import ( "testing" "kjol/vdom" ) // An built with document.createElement() is NOT an SVG element — it is an // HTMLUnknownElement that happens to be spelled "svg". It has no geometry and it // draws nothing, while still looking perfectly correct in the DOM inspector. // // This only bit on NAVIGATION: a server-rendered page's icons come from the HTML // parser, which handles as foreign content correctly, and hydration merely // adopts those nodes. Every icon the CLIENT created afterwards was inert. func TestSVGIsCreatedInTheSVGNamespace(t *testing.T) { root := document.Call("createElement", "div") icon := vdom.El("svg", vdom.Attr("viewBox", "0 0 24 24"), vdom.El("path", vdom.Attr("d", "M4 4h16")), ) patchChildren(root, nil, one(icon)) svg := root.Get("childNodes").Index(0) if got := svg.Get("namespaceURI").String(); got != svgNamespace { t.Fatalf(" namespace = %q, want %q — it will render nothing", got, svgNamespace) } // The subtree inherits it: a in the HTML namespace draws nothing either. path := svg.Get("childNodes").Index(0) if got := path.Get("namespaceURI").String(); got != svgNamespace { t.Errorf(" namespace = %q, want %q", got, svgNamespace) } } // Ordinary HTML must NOT end up in the SVG namespace, including siblings that follow // an icon. func TestHTMLStaysInTheHTMLNamespace(t *testing.T) { root := document.Call("createElement", "div") tree := vdom.El("div", vdom.El("svg", vdom.El("path")), vdom.El("span", vdom.Text("after the icon")), ) patchChildren(root, nil, one(tree)) div := root.Get("childNodes").Index(0) span := div.Get("childNodes").Index(1) if got := span.Get("namespaceURI").String(); got == svgNamespace { t.Errorf(" after an leaked into the SVG namespace") } } // The navigation case, end to end: page A's tree is replaced by page B's, and the // icon page B creates mid-diff must still be a real SVG. This is the one that broke. func TestIconCreatedDuringNavigationIsRealSVG(t *testing.T) { root := document.Call("createElement", "div") pageA := vdom.El("div", vdom.El("p", vdom.Text("no icons here"))) patchChildren(root, nil, one(pageA)) // Navigate: same tag at the same index, so the diff reuses the
and creates // the icon *underneath* it rather than from a fresh mount. pageB := vdom.El("div", vdom.El("svg", vdom.Attr("viewBox", "0 0 24 24"), vdom.El("path"))) patchChildren(root, one(pageA), one(pageB)) svg := root.Get("childNodes").Index(0).Get("childNodes").Index(0) if got := svg.Get("tag").String(); got != "svg" { t.Fatalf("expected an , got <%s>", got) } if got := svg.Get("namespaceURI").String(); got != svgNamespace { t.Errorf("icon created during navigation has namespace %q, want %q — this is the bug", got, svgNamespace) } } // A switches back to HTML for its contents. func TestForeignObjectReturnsToHTML(t *testing.T) { root := document.Call("createElement", "div") tree := vdom.El("svg", vdom.El("foreignObject", vdom.El("div", vdom.Text("html again")))) patchChildren(root, nil, one(tree)) fo := root.Get("childNodes").Index(0).Get("childNodes").Index(0) inner := fo.Get("childNodes").Index(0) if got := inner.Get("namespaceURI").String(); got == svgNamespace { t.Error("content inside should be HTML, not SVG") } }