<< All versions

Skill v1.0.0

currentAutomated scan100/100
samhennessy/hlive/hlive-testing
──Details
PublishedOctober 25, 2026 at 04:57 PM
Content Hashsha256:e6971a48f42ddab1...
Git SHAbc5f9205c90c
──Files
Files (1 file, 4.6 KB)
SKILL.md4.6 KBactive
SKILL.md · 165 lines · 4.6 KB

version: "1.0.0" name: hlive-testing description: >- Write browser tests for HLive pages using the hlivetest package (github.com/SamHennessy/hlive/hlivetest) and Playwright. Use when testing HLive UI behavior end-to-end — spinning up a test server, driving a real browser (Click/ClickAndWait, TextContent, GetAttribute), and asserting with hlivetest.Diff. Use when an HLive task mentions tests, Playwright, or verifying interactive behavior.


HLive Testing

HLive's behavior lives in the browser round-trip, so it's tested by driving a real browser with Playwright via the hlivetest package. Tests boot an HLive server, open a browser page against it, interact, and assert on the resulting DOM.

Setup

Tests need Playwright browsers installed:

shell
make install-test
# or: go run github.com/playwright-community/playwright-go/cmd/playwright install --with-deps

Because these are heavy browser tests, guard them so go test -short skips them (the harness below does this).

The harness pattern

HLive's own suite (hlivetest/pages/) uses a small per-package harness. Copy it into your test package:

go
package pages_test
import (
"testing"
l "github.com/SamHennessy/hlive"
"github.com/SamHennessy/hlive/hlivetest"
"github.com/playwright-community/playwright-go"
)
type harness struct {
server *hlivetest.Server
pwpage playwright.Page
teardown func()
}
func setup(t *testing.T, pageFn func() *l.Page) harness {
t.Helper()
if testing.Short() {
t.Skip("skipping test in short mode.")
}
h := harness{
server: hlivetest.NewServer(pageFn),
pwpage: hlivetest.NewBrowserPage(),
}
h.teardown = func() {
if err := h.pwpage.Close(); err != nil {
t.Error(err)
}
}
if _, err := h.pwpage.Goto(h.server.HTTPServer.URL); err != nil {
t.Fatal("goto page:", err)
}
return h
}

NewServer takes a page factory (func() *l.Page) — the same kind of function you hand to l.NewPageServer. In a structured app (see the hlive-project skill) that's an exported factory from your page package, so a test imports it and passes it straight in: setup(t, page.Home).

Give the elements you select stable ids. With hhtml use the Id(...) attribute:

go
import (
l "github.com/SamHennessy/hlive"
. "github.com/SamHennessy/hlive/hhtml"
)
func Counter() *l.Page {
count := l.Box(0)
page := l.NewPage()
page.DOM().Body().Add(
Button(Id("btn"), l.On("click", func(_ context.Context, _ l.Event) {
count.Lock(func(v int) int { return v + 1 })
}), "+"),
Div(Id("count"), count),
)
return page
}

Writing a test

go
func TestClick_OneClick(t *testing.T) {
t.Parallel()
h := setup(t, pages.Click()) // pages.Click() returns the page factory
defer h.teardown()
hlivetest.Diff(t, "0", hlivetest.TextContent(t, h.pwpage, "#count"))
hlivetest.ClickAndWait(t, h.pwpage, "#btn") // click, then wait for the diff
hlivetest.Diff(t, "1", hlivetest.TextContent(t, h.pwpage, "#count"))
}

Use ClickAndWait before an assertion (it waits for HLive's diff to apply). Use plain Click for intermediate clicks where you'll assert only at the end:

go
for i := 0; i < 9; i++ {
hlivetest.Click(t, h.pwpage, "#btn")
}
hlivetest.ClickAndWait(t, h.pwpage, "#btn")
hlivetest.Diff(t, "10", hlivetest.TextContent(t, h.pwpage, "#count"))

hlivetest helpers

All take (t, pwpage, ...) unless noted:

  • Click(selector) — click without waiting for the diff.
  • ClickAndWait(selector) — click, then block until the diff is applied.
  • TextContent(selector) string — element text.
  • GetAttribute(selector, attribute) string — an attribute value.
  • GetID(selector) string — the element's HLive id.
  • Title(pwpage) string — page title.
  • Diff(want, got any) — assert equal, reporting a diff on failure (t.Error).
  • DiffFatal(want, got any) — same but t.Fatal.
  • FatalOnErr(err) — fail fast on an error.
  • NewServer(pageFn) *Server / NewBrowserPage() playwright.Page — fixtures.

For interactions hlivetest doesn't wrap (typing, hover, selects), use the playwright.Page directly (h.pwpage.Fill(...), .Hover(...)), then assert with hlivetest.Diff.

Running

shell
go test ./... # full run (needs browsers)
go test -short ./... # skips the browser tests

Reference

hlivetest/hlivetest.go, hlivetest/server.go, hlivetest/browser.go, and the examples hlivetest/pages/harness_test.go, hlivetest/pages/click_test.go, hlivetest/pages/click.go. Larger suites live under systemtests/.

All versions