Move sub-surfaces into their sections: billing views get a pill row, org types a header button. Replace inline IdP handoff copy with an SVG icon and tooltip, add help icons to dense form rows, and delete the registry-driven sidebar nav plumbing. Update specs and tests.
166 lines
5.7 KiB
Go
166 lines
5.7 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"html/template"
|
|
"io/fs"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/config"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/embeds"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/web"
|
|
)
|
|
|
|
// TestOperatorTemplateRendersBothBranches parses operator.html and renders
|
|
// both dispatch branches (curated landing surface and the MPA BodyTemplate
|
|
// dispatch) to catch Go-template syntax errors that would otherwise only
|
|
// surface at request time. The legacy tabstrip branch was retired in slice
|
|
// 8 of operator-mpa-conversion §5.
|
|
func TestOperatorTemplateRendersBothBranches(t *testing.T) {
|
|
sub, err := fs.Sub(embeds.Templates, "templates")
|
|
if err != nil {
|
|
t.Fatalf("fs.Sub: %v", err)
|
|
}
|
|
partialsSub, err := fs.Sub(embeds.Templates, "templates/partials")
|
|
if err != nil {
|
|
t.Fatalf("fs.Sub partials: %v", err)
|
|
}
|
|
// operator.html references the `renderBody` and `routeURL` template
|
|
// funcs and includes partials like operator_lookup_result.html via
|
|
// `{{ template "..." . }}`. Mirror NewOperatorPartialsHandler: load
|
|
// partials first, then the shell.
|
|
tmpl := template.New("operator").Funcs(template.FuncMap{
|
|
"renderBody": func(string, any) (template.HTML, error) { return "", nil },
|
|
"routeURL": web.RouteURL,
|
|
"fieldErr": func(_, _, _, _ string, _, _ any) string { return "" },
|
|
"stripeEntityURL": func(string, string) string { return "" },
|
|
"deploymentName": config.DeploymentName,
|
|
})
|
|
tmpl, err = tmpl.ParseFS(partialsSub, "operator_*.html")
|
|
if err != nil {
|
|
t.Fatalf("ParseFS partials: %v", err)
|
|
}
|
|
tmpl, err = tmpl.ParseFS(sub, "operator.html")
|
|
if err != nil {
|
|
t.Fatalf("ParseFS operator.html: %v", err)
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
data OperatorPageData
|
|
want string
|
|
}{
|
|
{
|
|
name: "landing",
|
|
data: OperatorPageData{
|
|
CSRFToken: "csrf",
|
|
IAPosition: "runtime:landing",
|
|
},
|
|
want: "Find a person or organization",
|
|
},
|
|
{
|
|
// BodyTemplate dispatch branch must short-circuit the landing
|
|
// surface; the stub renderBody returns "" so the assertion is
|
|
// that the shell still renders (no landing markup leaks in).
|
|
name: "body-template-dispatch",
|
|
data: OperatorPageData{
|
|
CSRFToken: "csrf",
|
|
IAPosition: "runtime:organizations",
|
|
ActiveCapability: "organizations",
|
|
BodyTemplate: "operator_organizations.html",
|
|
BodyData: nil,
|
|
},
|
|
want: `id="operator-main"`,
|
|
},
|
|
{
|
|
// Dedicated Integrations section: the sidebar renders a labeled
|
|
// "Integrations" home link to /operator/integrations. Provider
|
|
// surfaces are reached from that home's provider cards, not from
|
|
// sidebar children (maintainer 2026-08-23).
|
|
name: "integration-section-home",
|
|
data: OperatorPageData{
|
|
CSRFToken: "csrf",
|
|
ActiveCapability: "integrations",
|
|
},
|
|
want: `href="/operator/integrations">Integrations</a>`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := tmpl.ExecuteTemplate(&buf, "operator.html", tc.data); err != nil {
|
|
t.Fatalf("ExecuteTemplate: %v", err)
|
|
}
|
|
out := buf.String()
|
|
if !strings.Contains(out, tc.want) {
|
|
t.Errorf("rendered output missing %q", tc.want)
|
|
}
|
|
if !strings.Contains(out, `hx-boost="true"`) {
|
|
t.Errorf("rendered output missing hx-boost on body")
|
|
}
|
|
if !strings.Contains(out, `id="operator-main"`) {
|
|
t.Errorf("rendered output missing main#operator-main swap target")
|
|
}
|
|
// Slice 8 guard: legacy tabstrip markup must not reappear.
|
|
if strings.Contains(out, `id="operatorTabs"`) {
|
|
t.Errorf("legacy tabstrip leaked back into operator.html — re-check §5 retirement")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestOperatorIntegrationsTemplate renders the Integrations landing body
|
|
// partial directly (the operator.html smoke test stubs renderBody, so the body
|
|
// markup is not exercised there). Covers a populated provider row and the
|
|
// empty state.
|
|
func TestOperatorIntegrationsTemplate(t *testing.T) {
|
|
partialsSub, err := fs.Sub(embeds.Templates, "templates/partials")
|
|
if err != nil {
|
|
t.Fatalf("fs.Sub partials: %v", err)
|
|
}
|
|
tmpl := template.New("integrations").Funcs(template.FuncMap{
|
|
"renderBody": func(string, any) (template.HTML, error) { return "", nil },
|
|
"routeURL": web.RouteURL,
|
|
"fieldErr": func(_, _, _, _ string, _, _ any) string { return "" },
|
|
"stripeEntityURL": func(string, string) string { return "" },
|
|
})
|
|
tmpl, err = tmpl.ParseFS(partialsSub, "operator_*.html")
|
|
if err != nil {
|
|
t.Fatalf("ParseFS partials: %v", err)
|
|
}
|
|
|
|
t.Run("populated", func(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
data := IntegrationsData{Integrations: []IntegrationRow{{
|
|
Slug: "example",
|
|
DisplayName: "Example",
|
|
Kind: "provisioning",
|
|
Status: "active",
|
|
SurfacePath: "/operator/example-sites",
|
|
Operations: []string{"create", "delete", "set_status"},
|
|
ResourceKeys: []ResourceKeyOption{{ResourceKey: "example_sites", DisplayName: "Example sites"}},
|
|
}}}
|
|
if err := tmpl.ExecuteTemplate(&buf, "operator_integrations.html", data); err != nil {
|
|
t.Fatalf("ExecuteTemplate: %v", err)
|
|
}
|
|
out := buf.String()
|
|
for _, want := range []string{"Example", "example_sites", "Example sites", "set_status", `href="/operator/example-sites"`, "Active"} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("integrations landing missing %q", want)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("empty", func(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := tmpl.ExecuteTemplate(&buf, "operator_integrations.html", IntegrationsData{}); err != nil {
|
|
t.Fatalf("ExecuteTemplate empty: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), "No integrations registered") {
|
|
t.Errorf("empty state not rendered")
|
|
}
|
|
})
|
|
}
|