package templates import ( "context" "strings" "testing" "bookhoard/internal/handlers" ) // The rendering-ownership partials must render standalone (they are // fetched by htmx.ajax at runtime) and must never leak templ literal // expressions into their markup. func TestRenderingPartialsRender(t *testing.T) { var b strings.Builder col := CollectionData{ID: "c1", Name: "Test Shelf", IsSmart: false} books := []handlers.BookInfo{{MediaItemID: "m1", Title: "A Book", Author: "An Author"}} if err := CollectionBooksGrid(col, books).Render(context.Background(), &b); err != nil { t.Fatalf("CollectionBooksGrid: %v", err) } out := b.String() for _, want := range []string{"A Book", `data-media-id="m1"`, "Remove from Collection"} { if !strings.Contains(out, want) { t.Errorf("books grid missing %q", want) } } // Empty collection renders the empty state, not a literal loop. b.Reset() if err := CollectionBooksGrid(col, nil).Render(context.Background(), &b); err != nil { t.Fatalf("CollectionBooksGrid empty: %v", err) } if !strings.Contains(b.String(), "No books in this collection yet.") { t.Error("books grid empty state missing") } b.Reset() user := User{ID: "u1", Timezone: "UTC"} conflicts := []handlers.ConflictDetailResponse{{ID: "cf1", MediaItemTitle: "War and Peace", ConflictType: "progress"}} if err := ConflictsRefreshFragment(user, conflicts, 3, 1).Render(context.Background(), &b); err != nil { t.Fatalf("ConflictsRefreshFragment: %v", err) } out = b.String() for _, want := range []string{`id="conflicts-list"`, `id="stats-bar"`, "hx-swap-oob", "Resolve"} { if !strings.Contains(out, want) { t.Errorf("conflicts fragment missing %q", want) } } }