, got %d", count)
}
if count := countText(got, "| "); count != 9 {
t.Errorf("nested Each: expected 9 | , got %d", count)
}
if !containsText(got, "1-b") {
t.Error("nested Each: missing cell content '1-b'")
}
}
func TestEach_WrappedElement_PreservesItemPaths_Good(t *testing.T) {
ctx := NewContext()
node := Each([]string{"a", "b"}, func(item string) Node {
return If(func(*Context) bool { return true }, El("span", Raw(item)))
})
got := NewLayout("C").C(node).Render(ctx)
if !containsText(got, `data-block="C.0.0"`) {
t.Fatalf("wrapped Each element should preserve first item path, got:\n%s", got)
}
if !containsText(got, `data-block="C.0.1"`) {
t.Fatalf("wrapped Each element should preserve second item path, got:\n%s", got)
}
}
func TestEach_WrappedLayout_PreservesBlockPath_Good(t *testing.T) {
ctx := NewContext()
inner := NewLayout("C").C(Raw("item"))
node := Each([]Node{inner}, func(item Node) Node {
return If(func(*Context) bool { return true }, item)
})
got := NewLayout("C").C(node).Render(ctx)
want := `item`
if got != want {
t.Fatalf("wrapped Each layout render = %q, want %q", got, want)
}
}
// --- Layout variant validation ---
func TestLayout_InvalidVariantChars_Bad(t *testing.T) {
ctx := NewContext()
tests := []struct {
name string
variant string
}{
{"all invalid", "XYZ"},
{"lowercase valid", "hlcrf"},
{"numbers", "123"},
{"special chars", "!@#"},
{"mixed valid and invalid", "HXC"},
{"empty string", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
layout := NewLayout(tt.variant).
H(Raw("header")).L(Raw("left")).C(Raw("main")).R(Raw("right")).F(Raw("footer"))
got := layout.Render(ctx)
// Invalid variant chars should silently produce no output for those slots
// This documents the current behaviour: no panic, no error.
if tt.variant == "XYZ" || tt.variant == "hlcrf" || tt.variant == "123" ||
tt.variant == "!@#" || tt.variant == "" {
if got != "" {
t.Errorf("NewLayout(%q) with all invalid chars should produce empty output, got %q", tt.variant, got)
}
}
})
}
}
func TestLayout_VariantError_NoOp_Good(t *testing.T) {
tests := []struct {
name string
variant string
build func(*Layout)
wantRender string
}{
{
name: "valid variant",
variant: "HCF",
build: func(layout *Layout) {
layout.H(Raw("header")).C(Raw("main")).F(Raw("footer"))
},
wantRender: `main`,
},
{
name: "mixed invalid variant",
variant: "HXC",
build: func(layout *Layout) {
layout.H(Raw("header")).C(Raw("main"))
},
wantRender: `main`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
layout := NewLayout(tt.variant)
if tt.build != nil {
tt.build(layout)
}
if layout.VariantError() != nil {
t.Fatalf("VariantError() = %v, want nil", layout.VariantError())
}
got := layout.Render(NewContext())
if got != tt.wantRender {
t.Fatalf("Render() = %q, want %q", got, tt.wantRender)
}
})
}
}
func TestValidateLayoutVariant_NoOp_Good(t *testing.T) {
tests := []struct {
name string
variant string
}{
{name: "valid", variant: "HCF"},
{name: "invalid", variant: "HXC"},
{name: "empty", variant: ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateLayoutVariant(tt.variant)
if err != nil {
t.Fatalf("ValidateLayoutVariant(%q) = %v, want nil", tt.variant, err)
}
})
}
}
func TestLayout_InvalidVariantMixedValidInvalid_Bad(t *testing.T) {
ctx := NewContext()
// "HXC" — H and C are valid, X is not. Only H and C should render.
layout := NewLayout("HXC").
H(Raw("header")).C(Raw("main"))
got := layout.Render(ctx)
if !containsText(got, "header") {
t.Errorf("HXC variant should render H slot, got:\n%s", got)
}
if !containsText(got, "main") {
t.Errorf("HXC variant should render C slot, got:\n%s", got)
}
// Should only have 2 semantic elements
if count := countText(got, "data-block="); count != 2 {
t.Errorf("HXC variant should produce 2 blocks, got %d in:\n%s", count, got)
}
}
func TestLayout_DuplicateVariantChars_Ugly(t *testing.T) {
ctx := NewContext()
// "CCC" — C appears three times. Should render C slot content three times.
layout := NewLayout("CCC").C(Raw("content"))
got := layout.Render(ctx)
count := countText(got, "content")
if count != 3 {
t.Errorf("CCC variant should render C slot 3 times, got %d occurrences in:\n%s", count, got)
}
}
func TestLayout_DuplicateVariantChars_UniqueBlockIDs_Good(t *testing.T) {
ctx := NewContext()
layout := NewLayout("CCC").C(Raw("content"))
got := layout.Render(ctx)
for _, want := range []string{`data-block="C"`, `data-block="C.1"`, `data-block="C.2"`} {
if !containsText(got, want) {
t.Fatalf("CCC variant should assign unique block ID %q, got:\n%s", want, got)
}
}
}
func TestLayout_EmptySlots_Ugly(t *testing.T) {
ctx := NewContext()
// Variant includes all slots but none are populated — should produce empty output.
layout := NewLayout("HLCRF")
got := layout.Render(ctx)
if got != "" {
t.Errorf("layout with no slot content should produce empty output, got %q", got)
}
}
func TestLayout_NestedThroughIf_Ugly(t *testing.T) {
ctx := NewContext()
inner := NewLayout("C").C(Raw("wrapped"))
outer := NewLayout("C").C(If(func(*Context) bool { return true }, inner))
got := outer.Render(ctx)
if !containsText(got, `data-block="C.0"`) {
t.Fatalf("nested layout inside If should inherit block path, got:\n%s", got)
}
}
func TestLayout_NestedThroughSwitch_Ugly(t *testing.T) {
ctx := NewContext()
inner := NewLayout("C").C(Raw("wrapped"))
outer := NewLayout("C").C(Switch(func(*Context) string { return "match" }, map[string]Node{
"match": inner,
"miss": Raw("ignored"),
}))
got := outer.Render(ctx)
if !containsText(got, `data-block="C.0"`) {
t.Fatalf("nested layout inside Switch should inherit block path, got:\n%s", got)
}
}
// --- Render convenience function edge cases ---
func TestRender_NilContext_Ugly(t *testing.T) {
node := Raw("test")
got := Render(node, nil)
if got != "test" {
t.Errorf("Render with nil context = %q, want %q", got, "test")
}
}
func TestImprint_NilContext_Ugly(t *testing.T) {
svc, _ := i18n.New()
i18n.SetDefault(svc)
node := NewLayout("C").C(El("p", Text("Building project")))
imp := Imprint(node, nil)
if imp.TokenCount == 0 {
t.Error("Imprint with nil context should still produce tokens")
}
}
func TestCompareVariants_NilContext_Ugly(t *testing.T) {
svc, _ := i18n.New()
i18n.SetDefault(svc)
r := NewResponsive().
Variant("a", NewLayout("C").C(Text("Building project"))).
Variant("b", NewLayout("C").C(Text("Building project")))
scores := CompareVariants(r, nil)
if _, ok := scores["a:b"]; !ok {
t.Error("CompareVariants with nil context should still produce scores")
}
}
func TestCompareVariants_SingleVariant_Ugly(t *testing.T) {
svc, _ := i18n.New()
i18n.SetDefault(svc)
r := NewResponsive().
Variant("only", NewLayout("C").C(Text("Building project")))
scores := CompareVariants(r, NewContext())
if len(scores) != 0 {
t.Errorf("CompareVariants with single variant should produce no pairs, got %d", len(scores))
}
}
// --- escapeHTML / escapeAttr edge cases ---
func TestEscapeAttr_AllSpecialChars_Ugly(t *testing.T) {
ctx := NewContext()
node := Attr(El("div"), "data-val", `&<>"'`)
got := node.Render(ctx)
if containsText(got, `"&<>"'"`) {
t.Error("attribute value with special chars must be fully escaped")
}
if !containsText(got, "&<>"'") {
t.Errorf("expected all special chars escaped in attribute, got: %s", got)
}
}
func TestElNode_EmptyTag_Ugly(t *testing.T) {
ctx := NewContext()
node := El("", Raw("content"))
got := node.Render(ctx)
// Empty tag is weird but should not panic
if !containsText(got, "content") {
t.Errorf("El with empty tag should still render children, got %q", got)
}
}
func TestSwitchNode_NoMatch_Ugly(t *testing.T) {
ctx := NewContext()
cases := map[string]Node{
"a": Raw("alpha"),
"b": Raw("beta"),
}
node := Switch(func(*Context) string { return "c" }, cases)
got := node.Render(ctx)
if got != "" {
t.Errorf("Switch with no matching case should produce empty string, got %q", got)
}
}
func TestEntitled_NilContext_Ugly(t *testing.T) {
node := Entitled("premium", Raw("content"))
got := node.Render(nil)
if got != "" {
t.Errorf("Entitled with nil context should produce empty string, got %q", got)
}
}
|