From 19cad829459c27033b973451a6a899b4b2320a31 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 16 Feb 2026 23:43:05 +0000 Subject: [PATCH] feat: add render pipeline and validation tests Co-Authored-By: Claude Opus 4.6 --- render.go | 9 +++++ render_test.go | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 render.go create mode 100644 render_test.go diff --git a/render.go b/render.go new file mode 100644 index 0000000..3d3a7e3 --- /dev/null +++ b/render.go @@ -0,0 +1,9 @@ +package html + +// Render is a convenience function that renders a node tree to HTML. +func Render(node Node, ctx *Context) string { + if ctx == nil { + ctx = NewContext() + } + return node.Render(ctx) +} diff --git a/render_test.go b/render_test.go new file mode 100644 index 0000000..083ee1c --- /dev/null +++ b/render_test.go @@ -0,0 +1,97 @@ +package html + +import ( + "strings" + "testing" + + i18n "forge.lthn.ai/core/go-i18n" +) + +func TestRender_FullPage(t *testing.T) { + svc, _ := i18n.New() + i18n.SetDefault(svc) + ctx := NewContext() + + page := NewLayout("HCF"). + H(El("h1", Text("Dashboard"))). + C( + El("div", + El("p", Text("Welcome")), + Each([]string{"Home", "Settings", "Profile"}, func(item string) Node { + return El("a", Raw(item)) + }), + ), + ). + F(El("small", Text("Footer"))) + + got := page.Render(ctx) + + // Contains semantic elements + for _, want := range []string{"" + if strings.Count(got, open) != strings.Count(got, close) { + t.Errorf("unbalanced <%s> tags in:\n%s", tag, got) + } + } +} + +func TestRender_EntitlementGating(t *testing.T) { + svc, _ := i18n.New() + i18n.SetDefault(svc) + ctx := NewContext() + ctx.Entitlements = func(f string) bool { return f == "admin" } + + page := NewLayout("HCF"). + H(Raw("header")). + C( + Raw("public"), + Entitled("admin", Raw(" admin-panel")), + Entitled("premium", Raw(" premium-content")), + ). + F(Raw("footer")) + + got := page.Render(ctx) + + if !strings.Contains(got, "public") { + t.Errorf("entitlement gating should render public content, got:\n%s", got) + } + if !strings.Contains(got, "admin-panel") { + t.Errorf("entitlement gating should render admin-panel for admin, got:\n%s", got) + } + if strings.Contains(got, "premium-content") { + t.Errorf("entitlement gating should NOT render premium-content, got:\n%s", got) + } +} + +func TestRender_XSSPrevention(t *testing.T) { + svc, _ := i18n.New() + i18n.SetDefault(svc) + ctx := NewContext() + + page := NewLayout("C"). + C(Text("")) + + got := page.Render(ctx) + + if strings.Contains(got, "