Files
felhom.eu/hub/internal/web/customer_tabs_test.go
T
admin 9f29bf34c0 hub: fix .data-table btn contrast + customer page hash tabs (v0.47.0 part 1-2)
- style.css: .data-table td a -> :not(.btn) so <a class=btn> keeps the .btn
  palette (View/Download buttons were blue-on-blue invisible)
- customer_unified.html: 8 client-side hash tabs (#tab=...) + sticky summary
  strip; all sections preserved in DOM, hiding is a JS-added body class only
  (no-JS = everything visible); Events tab gets a red error-count badge
- new render tests: TestTemplates_CustomerTabs (+_NoReports)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vvz1NCu22p8dGkRCpeX9re
2026-07-11 21:13:23 +02:00

118 lines
4.2 KiB
Go

package web
// Group A (hub v0.47.0 UI reorganization) — the customer page's client-side hash tabs.
// Graceful degradation is load-bearing: panels are hidden ONLY by a JS-added body class,
// so the no-JS render (what these tests see) must still contain EVERY section's markup.
import (
"net/http/httptest"
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
)
// A controller-report body exercising every Overview/Applications section.
const tabsTestReportJSON = `{
"health": {"status": "ok", "warnings": ["disk filling"]},
"system": {"hostname": "felhom-box", "os": "Debian 12", "kernel": "6.8", "cpu_model": "N100", "cpu_cores": 4},
"storage": [{"mount": "/", "percent": 41.0, "used_gb": 12.3, "total_gb": 30.0}],
"containers": {"running": 3, "total": 4, "list": [{"name": "vaultwarden", "state": "running", "cpu_percent": 1.2, "memory_mb": 90}]},
"backup": {"enabled": true, "snapshot_count": 7, "repo_size_mb": 1200, "integrity_ok": true},
"geo_restriction": {"enabled": true, "allowed_countries": ["HU"]}
}`
func renderCustomerPage(t *testing.T, s *Server, customerID string) string {
t.Helper()
rr := httptest.NewRecorder()
s.handleCustomerUnified(rr, httptest.NewRequest("GET", "/customers/"+customerID, nil), customerID)
if rr.Code != 200 {
t.Fatalf("customer page status = %d", rr.Code)
}
return rr.Body.String()
}
func TestTemplates_CustomerTabs(t *testing.T) {
s, st := newTestServer(t)
if err := st.SaveCustomerConfig(&store.CustomerConfig{
CustomerID: "acme", CustomerName: "Acme", Domain: "acme.hu",
RetrievalPassword: "pw", APIKey: "k", Status: "active",
}); err != nil {
t.Fatal(err)
}
if err := st.SaveReport("acme", []byte(tabsTestReportJSON)); err != nil {
t.Fatal(err)
}
// One error event → the Events tab label carries the red count badge.
if _, err := st.SaveEvent("acme", "app_crash_loop", "error", "vaultwarden restarting", "{}", "controller"); err != nil {
t.Fatal(err)
}
html := renderCustomerPage(t, s, "acme")
// The tab nav renders all 8 tabs (hash links — plain anchors without JS).
for _, tab := range []string{
"overview", "applications", "setup", "settings", "backup", "events", "notifications", "host",
} {
if !strings.Contains(html, `href="#tab=`+tab+`"`) {
t.Errorf("tab nav missing tab %q", tab)
}
}
// No-JS completeness: every section's markup is still in the body (hiding is done by a
// JS-added body class — the server render carries everything).
for _, want := range []string{
"Customer Info", "Health", "System", "Storage", "Backup",
"Containers (3/4)", "vaultwarden",
"Credentials", "Setup Command", "YAML Preview",
"Controller Update", "Geo-korlátozás",
"Events", "Notifications", "Report History",
} {
if !strings.Contains(html, want) {
t.Errorf("no-JS body missing section content %q", want)
}
}
// Sticky summary strip with the always-visible vitals.
if !strings.Contains(html, `class="summary-strip"`) {
t.Error("summary strip missing")
}
// Events tab badge: 1 error event → a red count on the tab label.
if !strings.Contains(html, `<span class="tab-badge">1</span>`) {
t.Error("Events tab badge missing despite 1 error event")
}
// Panels are hidden only via the js-tabs body class — no inline hiding in the markup.
if strings.Contains(html, `tab-panel" data-tab="events" style=`) {
t.Error("tab panel carries inline style — hiding must be class-driven")
}
}
// A customer with a config but no reports keeps the waiting banner ABOVE the tab bar
// (always visible) and shows no Events badge.
func TestTemplates_CustomerTabs_NoReports(t *testing.T) {
s, st := newTestServer(t)
if err := st.SaveCustomerConfig(&store.CustomerConfig{
CustomerID: "fresh", CustomerName: "Fresh", RetrievalPassword: "pw", APIKey: "k", Status: "active",
}); err != nil {
t.Fatal(err)
}
html := renderCustomerPage(t, s, "fresh")
banner := strings.Index(html, "Waiting for First Report")
nav := strings.Index(html, `id="tab-nav"`)
if banner < 0 {
t.Fatal("waiting banner missing")
}
if nav < 0 {
t.Fatal("tab nav missing")
}
if banner > nav {
t.Error("waiting banner renders BELOW the tab bar — must stay above (always visible)")
}
if strings.Contains(html, `class="tab-badge"`) {
t.Error("Events badge rendered with no events")
}
}