121 lines
3.8 KiB
Go
121 lines
3.8 KiB
Go
package app
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"kjol/vdom"
|
|
)
|
|
|
|
// The landing page's whole claim is that its two panes are ONE function: the live
|
|
// component on the left, and the HTML string the server sends on the right. If they
|
|
// could drift, the page would be a lie told in the most embarrassing possible place.
|
|
//
|
|
// So: render it, click the button the way the browser would, render again, and check
|
|
// that BOTH panes moved. A pane rendered from a stale copy of the tree — or from a
|
|
// second, hand-written one — fails here.
|
|
func TestLandingPanesShareOneTree(t *testing.T) {
|
|
page := HomePage(Deps{Path: func() string { return "/" }})
|
|
|
|
html := vdom.RenderHTML(page())
|
|
if !strings.Contains(html, "clicked 0 times") {
|
|
t.Fatalf("the live pane did not render its initial state:\n%s", html)
|
|
}
|
|
// The right-hand pane is the ESCAPED HTML of the same tree, so the markup it shows
|
|
// appears in the page's own markup double-escaped: <div ...
|
|
if !strings.Contains(html, "<div class=") {
|
|
t.Fatal("the right-hand pane is not showing rendered HTML at all")
|
|
}
|
|
|
|
clickButton(t, page(), "Click me")
|
|
|
|
html = vdom.RenderHTML(page())
|
|
if strings.Count(html, "clicked 1 times") < 2 {
|
|
t.Errorf("after one click, %d panes say \"clicked 1 times\" — both should:\n%s",
|
|
strings.Count(html, "clicked 1 times"), html)
|
|
}
|
|
}
|
|
|
|
// The byte count under the right-hand pane is the length of the string actually shown,
|
|
// not a number typed in by hand — so it has to move when the markup does.
|
|
func TestLandingByteCountIsReal(t *testing.T) {
|
|
page := HomePage(Deps{Path: func() string { return "/" }})
|
|
|
|
before := byteCountLabel(t, vdom.RenderHTML(page()))
|
|
clickButton(t, page(), "Click me")
|
|
// "clicked 0 times" -> "clicked 1 times" is the same length, so click into double
|
|
// digits, where the markup genuinely grows by one byte.
|
|
for i := 0; i < 10; i++ {
|
|
clickButton(t, page(), "Click me")
|
|
}
|
|
after := byteCountLabel(t, vdom.RenderHTML(page()))
|
|
|
|
if before == after {
|
|
t.Errorf("the markup grew by a digit but the byte count did not move (%s) — it is not measuring the string", before)
|
|
}
|
|
}
|
|
|
|
// byteCountLabel pulls the "N bytes of HTML" caption out of the rendered page.
|
|
func byteCountLabel(t *testing.T, html string) string {
|
|
t.Helper()
|
|
i := strings.Index(html, " bytes of HTML")
|
|
if i < 0 {
|
|
t.Fatal("no byte-count caption on the landing page")
|
|
}
|
|
start := strings.LastIndexByte(html[:i], '>') + 1
|
|
return html[start : i+len(" bytes of HTML")]
|
|
}
|
|
|
|
// clickButton finds a button by its label and fires its click handler.
|
|
func clickButton(t *testing.T, n *vdom.VNode, label string) {
|
|
t.Helper()
|
|
if !findAndClickButton(n, label) {
|
|
t.Fatalf("no clickable button labelled %q on the page", label)
|
|
}
|
|
}
|
|
|
|
func findAndClickButton(n *vdom.VNode, label string) bool {
|
|
if n == nil {
|
|
return false
|
|
}
|
|
if n.Tag == "button" && strings.Contains(textOf(n), label) {
|
|
if h := n.Events[vdom.EVENT_CLICK]; h != nil {
|
|
h(clickEvent{})
|
|
return true
|
|
}
|
|
}
|
|
for _, c := range n.Children {
|
|
if findAndClickButton(c, label) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func textOf(n *vdom.VNode) string {
|
|
if n.Tag == "" {
|
|
return n.Text
|
|
}
|
|
var b strings.Builder
|
|
for _, c := range n.Children {
|
|
b.WriteString(textOf(c))
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// clickEvent is a vdom.Event with no DOM behind it — enough to invoke a handler.
|
|
type clickEvent struct{}
|
|
|
|
func (clickEvent) PreventDefault() {}
|
|
func (clickEvent) StopPropagation() {}
|
|
func (clickEvent) Value() string { return "" }
|
|
func (clickEvent) Checked() bool { return false }
|
|
func (clickEvent) Key() string { return "" }
|
|
func (clickEvent) ClientX() int { return 0 }
|
|
func (clickEvent) ClientY() int { return 0 }
|
|
func (clickEvent) Target() any { return nil }
|
|
func (clickEvent) SetData(_, _ string) {}
|
|
func (clickEvent) GetData(string) string { return "" }
|
|
|
|
var _ vdom.Event = clickEvent{}
|