Indítópult launcher page + universal app placeholder icon (v0.163.0)
New /launcher page: a grid of large tappable tiles, one per openable deployed app (subdomain presence is the single openability criterion, shared with the Megnyitás button via the extracted Server.subdomainMap helper). Colored tiles (deterministic slug color or .felhom.yml brand_color), white glyph/monogram fallback, target=_blank links for operational apps, greyed unclickable tiles for stopped ones. First sidebar item; / stays the Vezérlőpult. Universal app placeholder: new AppPlaceholderSVG served at /static/app-placeholder.svg, now the default FallbackIcon on app_list_row so a logo-less app shows a placeholder instead of visibility:hidden. Brand mark is never an app placeholder. New Metadata.BrandColor; new funcmap tileColor/initial. 10 new test functions + 4 red-proofs. No agent coupling; MinAgent unchanged.
This commit is contained in:
@@ -75,6 +75,50 @@ func TestAppRow_RestoreLists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Group D (v0.163.0) — the app_list_row now defaults its fallback icon to the generic app
|
||||
// placeholder, so a logo-less app shows a placeholder glyph instead of the old visibility:hidden
|
||||
// dead-end; an infra caller still overrides it with the server glyph.
|
||||
// COMPANION red-proof (REPORT): revert app_row.html's data-fallback to the old
|
||||
// `{{with .FallbackIcon}}...{{end}}` (attribute absent when no caller value) → the default-present
|
||||
// assertion FAILS.
|
||||
func TestAppRow_DefaultPlaceholderFallback(t *testing.T) {
|
||||
// No FallbackIcon → the placeholder is the default.
|
||||
def := renderPartial(t, "app_list_row", map[string]interface{}{"Slug": "no-logo-app", "Name": "Logótlan"})
|
||||
if !strings.Contains(def, `data-fallback="/static/app-placeholder.svg"`) {
|
||||
t.Errorf("a row without FallbackIcon must default to the app placeholder, got:\n%s", def)
|
||||
}
|
||||
// A caller-provided FallbackIcon (infra) still wins.
|
||||
infra := renderPartial(t, "app_list_row",
|
||||
map[string]interface{}{"Slug": "filebrowser", "Name": "FileBrowser", "FallbackIcon": "/static/infra-logo.svg"})
|
||||
if !strings.Contains(infra, `data-fallback="/static/infra-logo.svg"`) {
|
||||
t.Errorf("an explicit FallbackIcon must be preserved, got:\n%s", infra)
|
||||
}
|
||||
if strings.Contains(infra, "/static/app-placeholder.svg") {
|
||||
t.Error("an infra row must not also carry the app placeholder")
|
||||
}
|
||||
}
|
||||
|
||||
// The placeholder asset is served with the SVG content-type + long cache, like the infra logo.
|
||||
func TestAppPlaceholderRoute(t *testing.T) {
|
||||
s := testPageServer(t)
|
||||
rec := getPage(t, s, "/static/app-placeholder.svg")
|
||||
if rec.Code != 200 {
|
||||
t.Fatalf("GET /static/app-placeholder.svg = %d", rec.Code)
|
||||
}
|
||||
if ct := rec.Header().Get("Content-Type"); ct != "image/svg+xml" {
|
||||
t.Errorf("content-type = %q, want image/svg+xml", ct)
|
||||
}
|
||||
if !strings.Contains(rec.Body.String(), "<svg") {
|
||||
t.Error("placeholder body must be an SVG")
|
||||
}
|
||||
}
|
||||
|
||||
// renderPartial renders a single {{define}}'d partial (e.g. app_list_row) in isolation.
|
||||
func renderPartial(t *testing.T, name string, data map[string]interface{}) string {
|
||||
t.Helper()
|
||||
return renderBackupPage(t, name, data)
|
||||
}
|
||||
|
||||
func TestAppRow_Dashboard(t *testing.T) {
|
||||
data := map[string]interface{}{
|
||||
"Page": "dashboard", "Title": "Vezérlőpult",
|
||||
|
||||
Reference in New Issue
Block a user