package html import ( "strings" "testing" ) func TestResponsive_SingleVariant(t *testing.T) { ctx := NewContext() r := NewResponsive(). Variant("desktop", NewLayout("HLCRF"). H(Raw("header")).L(Raw("nav")).C(Raw("main")).R(Raw("aside")).F(Raw("footer"))) got := r.Render(ctx) if !strings.Contains(got, `data-variant="desktop"`) { t.Errorf("responsive should contain data-variant, got:\n%s", got) } if !strings.Contains(got, `data-block="H-0"`) { t.Errorf("responsive should contain layout content, got:\n%s", got) } } func TestResponsive_MultiVariant(t *testing.T) { ctx := NewContext() r := NewResponsive(). Variant("desktop", NewLayout("HLCRF").H(Raw("h")).L(Raw("l")).C(Raw("c")).R(Raw("r")).F(Raw("f"))). Variant("tablet", NewLayout("HCF").H(Raw("h")).C(Raw("c")).F(Raw("f"))). Variant("mobile", NewLayout("C").C(Raw("c"))) got := r.Render(ctx) for _, v := range []string{"desktop", "tablet", "mobile"} { if !strings.Contains(got, `data-variant="`+v+`"`) { t.Errorf("responsive missing variant %q in:\n%s", v, got) } } } func TestResponsive_VariantOrder(t *testing.T) { ctx := NewContext() r := NewResponsive(). Variant("desktop", NewLayout("HLCRF").C(Raw("d"))). Variant("mobile", NewLayout("C").C(Raw("m"))) got := r.Render(ctx) di := strings.Index(got, `data-variant="desktop"`) mi := strings.Index(got, `data-variant="mobile"`) if di < 0 || mi < 0 { t.Fatalf("missing variants in:\n%s", got) } if di >= mi { t.Errorf("desktop should appear before mobile (insertion order), desktop=%d mobile=%d", di, mi) } } func TestResponsive_NestedPaths(t *testing.T) { ctx := NewContext() inner := NewLayout("HCF").H(Raw("ih")).C(Raw("ic")).F(Raw("if")) r := NewResponsive(). Variant("desktop", NewLayout("HLCRF").C(inner)) got := r.Render(ctx) if !strings.Contains(got, `data-block="C-0-H-0"`) { t.Errorf("nested layout in responsive variant missing C-0-H-0 in:\n%s", got) } if !strings.Contains(got, `data-block="C-0-C-0"`) { t.Errorf("nested layout in responsive variant missing C-0-C-0 in:\n%s", got) } } func TestResponsive_VariantsIndependent(t *testing.T) { ctx := NewContext() r := NewResponsive(). Variant("a", NewLayout("HLCRF").C(Raw("content-a"))). Variant("b", NewLayout("HCF").C(Raw("content-b"))) got := r.Render(ctx) count := strings.Count(got, `data-block="C-0"`) if count != 2 { t.Errorf("expected 2 independent C-0 blocks, got %d in:\n%s", count, got) } } func TestResponsive_ImplementsNode(t *testing.T) { var _ Node = NewResponsive() } func TestResponsive_RenderNilReceiver(t *testing.T) { var r *Responsive got := r.Render(NewContext()) if got != "" { t.Fatalf("nil Responsive should render empty string, got %q", got) } } func TestResponsive_BuilderNilReceiver(t *testing.T) { var r *Responsive if got := r.Variant("desktop", NewLayout("C")); got != nil { t.Fatalf("nil Responsive.Variant() should return nil, got %v", got) } } func TestResponsive_RenderNilContext(t *testing.T) { r := NewResponsive(). Variant("desktop", NewLayout("C").C(Raw("main"))) got := r.Render(nil) if !strings.Contains(got, `data-variant="desktop"`) { t.Fatalf("Responsive.Render(nil) should still render the variant wrapper, got:\n%s", got) } if !strings.Contains(got, `data-block="C-0"`) { t.Fatalf("Responsive.Render(nil) should still render the layout block, got:\n%s", got) } } func TestResponsive_NilLayoutVariant(t *testing.T) { ctx := NewContext() r := NewResponsive(). Variant("desktop", nil). Variant("mobile", NewLayout("C").C(Raw("m"))) got := r.Render(ctx) if !strings.Contains(got, `data-variant="desktop"`) { t.Fatalf("nil layout variant should still render its wrapper, got:\n%s", got) } if strings.Contains(got, "") { t.Fatalf("nil layout variant should not render placeholder text, got:\n%s", got) } if !strings.Contains(got, `data-variant="mobile"`) { t.Fatalf("responsive should still render subsequent variants, got:\n%s", got) } } func TestResponsive_Attributes(t *testing.T) { ctx := NewContext() r := Attr(NewResponsive(). Variant("desktop", NewLayout("C").C(Raw("main"))). Variant("mobile", NewLayout("C").C(Raw("main"))), "aria-label", "Responsive content", ) r = Attr(r, "class", "responsive-shell") got := r.Render(ctx) if count := strings.Count(got, `aria-label="Responsive content"`); count != 2 { t.Fatalf("responsive attrs should apply to each wrapper, got %d in:\n%s", count, got) } if count := strings.Count(got, `class="responsive-shell"`); count != 2 { t.Fatalf("responsive class should apply to each wrapper, got %d in:\n%s", count, got) } if !strings.Contains(got, `aria-label="Responsive content" class="responsive-shell" data-variant="desktop"`) { t.Fatalf("responsive wrapper attrs should be sorted and preserved, got:\n%s", got) } } func TestVariantSelector(t *testing.T) { tests := []struct { name string variant string want string }{ {name: "plain", variant: "desktop", want: `[data-variant="desktop"]`}, {name: "escaped", variant: `desk"top\` + "\n" + `line`, want: `[data-variant="desk\"top\\\a line"]`}, {name: "control char", variant: "tab\tname", want: `[data-variant="tab\9 name"]`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := VariantSelector(tt.variant) if got != tt.want { t.Fatalf("VariantSelector(%q) = %q, want %q", tt.variant, got, tt.want) } }) } } func TestScopeVariant(t *testing.T) { tests := []struct { name string variant string selector string want string }{ {name: "scope", variant: "desktop", selector: ".nav", want: `[data-variant="desktop"] .nav`}, {name: "empty selector", variant: "mobile", selector: "", want: `[data-variant="mobile"]`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ScopeVariant(tt.variant, tt.selector) if got != tt.want { t.Fatalf("ScopeVariant(%q, %q) = %q, want %q", tt.variant, tt.selector, got, tt.want) } }) } } func TestScopeVariant_MultipleSelectors(t *testing.T) { got := ScopeVariant("desktop", ".nav, .sidebar") want := `[data-variant="desktop"] .nav, [data-variant="desktop"] .sidebar` if got != want { t.Fatalf("ScopeVariant with selector list = %q, want %q", got, want) } } func TestScopeVariant_IgnoresEmptySelectorSegments(t *testing.T) { got := ScopeVariant("desktop", ".nav, , .sidebar,") want := `[data-variant="desktop"] .nav, [data-variant="desktop"] .sidebar` if got != want { t.Fatalf("ScopeVariant should skip empty selector segments = %q, want %q", got, want) } }