package templates import ( "context" "strings" "testing" ) // The docs layout is a standalone documentation site: it must NOT embed the // app chrome (its sidebar assumed a signed-in user) and must never contain a // login form — the old layout posted credentials via htmx that was never // loaded on this page, degrading to a native GET that put the username and // password into the URL. These render tests pin that fix. func TestDocsLayoutRendersStandalone(t *testing.T) { nav := Navigation{ Sections: []NavSection{ { Title: "Getting Started", Icon: "book-open", Items: []NavItem{{Title: "Overview", URL: "/docs"}}, }, { Title: "User Guide", Icon: "book", Collapsed: true, Items: []NavItem{{Title: "Dashboard", URL: "/docs/user/dashboard"}}, Groups: []NavGroup{ { Title: "Devices", Items: []NavItem{{Title: "Kobo Setup", URL: "/docs/user/devices/kobo-setup"}}, Collapsed: true, }, }, }, }, } doc := Document{ Title: "Test Doc", Content: "

Hello docs

", TOC: []TOCItem{{Level: 2, Title: "Section", Anchor: "section"}}, Breadcrumb: []BreadcrumbItem{ {Title: "Docs", URL: "/docs"}, {Title: "User Guide"}, }, SourceFile: "user/test.md", } var b strings.Builder if err := DocsLayout(nav, doc, User{Theme: "tokyo-night"}, "/docs/user/test").Render(context.Background(), &b); err != nil { t.Fatalf("DocsLayout: %v", err) } out := b.String() for _, banned := range []string{ "app-sidebar", // app chrome `hx-post="/api/auth/login"`, // no inline login form (the URL-leak bug) `name="password"`, // credentials never render on docs pages "prose-invert", // theme-aware prose, not hardcoded dark "highlight-dark.min.css", // highlighting is server-side now "cdn.tailwindcss.com", // no CDN dependencies "lunr", // dead client-side search is gone } { if strings.Contains(out, banned) { t.Errorf("docs layout must not contain %q", banned) } } for _, want := range []string{ `href="/docs"`, // sidebar + logo links `data-collapsed="true"`, // collapsed section state `data-collapsed="false"`, // expanded section state "Open App", // topbar action "theme-select", // theme picker "docs-search-results", // search dropdown target "Hello docs", // content rendered "page-docs", } { if !strings.Contains(out, want) { t.Errorf("docs layout missing %q", want) } } // Anonymous visitor: a sign-in LINK, never a form. b.Reset() if err := DocsLayout(nav, doc, User{Theme: "tokyo-night"}, "/docs/user/test").Render(context.Background(), &b); err != nil { t.Fatalf("DocsLayout anonymous: %v", err) } if !strings.Contains(b.String(), `href="/login"`) { t.Error("anonymous docs layout should link to /login") } } func TestDocsTopbarSignedIn(t *testing.T) { var b strings.Builder if err := DocsTopbar(User{ID: "u1", Username: "reader", Theme: "tokyo-night"}).Render(context.Background(), &b); err != nil { t.Fatalf("DocsTopbar: %v", err) } out := b.String() if strings.Contains(out, `href="/login"`) { t.Error("signed-in topbar must not offer /login") } if !strings.Contains(out, "reader") { t.Error("signed-in topbar should surface the username") } } func TestDocsSearchResultsPartial(t *testing.T) { var b strings.Builder results := []SearchResultItem{ {Title: "Kobo Setup", Category: "User Guide", URL: "/docs/user/devices/kobo-setup"}, } if err := DocsSearchResults(results).Render(context.Background(), &b); err != nil { t.Fatalf("DocsSearchResults: %v", err) } out := b.String() for _, want := range []string{"Kobo Setup", "User Guide", `/docs/user/devices/kobo-setup`} { if !strings.Contains(out, want) { t.Errorf("search results missing %q", want) } } // Empty state renders, not blank markup. b.Reset() if err := DocsSearchResults(nil).Render(context.Background(), &b); err != nil { t.Fatalf("DocsSearchResults empty: %v", err) } if !strings.Contains(b.String(), "No matching pages.") { t.Error("search results empty state missing") } } func TestDocsNotFoundRenders(t *testing.T) { var b strings.Builder if err := DocsNotFound("Document Not Found", "no such page").Render(context.Background(), &b); err != nil { t.Fatalf("DocsNotFound: %v", err) } out := b.String() for _, want := range []string{"Document Not Found", "no such page", `href="/docs"`} { if !strings.Contains(out, want) { t.Errorf("404 page missing %q", want) } } if strings.Contains(out, "cdn.tailwindcss.com") { t.Error("404 page must not pull the tailwind CDN") } }