diff --git a/internal/http/handlers/pricing_test.go b/internal/http/handlers/pricing_test.go new file mode 100644 index 0000000..6e44428 --- /dev/null +++ b/internal/http/handlers/pricing_test.go @@ -0,0 +1,53 @@ +package handlers_test + +import ( + "html/template" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/softsrv/rowbot/internal/http/handlers" + "github.com/softsrv/rowbot/web" +) + +func newPricingRealTemplateRenderer(t *testing.T) *handlers.TemplateRenderer { + t.Helper() + base, err := template.ParseFS(web.FS, "templates/base.html") + if err != nil { + t.Fatalf("parse base template: %v", err) + } + if _, err := base.ParseFS(web.FS, "templates/partials/*.html"); err != nil { + t.Fatalf("parse partials: %v", err) + } + return handlers.NewTemplateRenderer(base, web.FS, "templates") +} + +func TestPricingPageRendersPublicPricingContent(t *testing.T) { + renderer := newPricingRealTemplateRenderer(t) + rr := httptest.NewRecorder() + + renderer.Page(rr, http.StatusOK, "pricing.html", nil) + + if rr.Code != http.StatusOK { + t.Fatalf("status = %d, want 200", rr.Code) + } + body := rr.Body.String() + for _, want := range []string{ + "Free", + "Cozy Community", + "Large Community", + "up to 10", + "up to 50", + "up to 250", + "$5", + "$10", + "js-get-started", + `id="get-started-modal"`, + `href="/pricing"`, + } { + if !strings.Contains(body, want) { + t.Fatalf("rendered pricing page missing %q in:\n%s", want, body) + } + } +} diff --git a/internal/http/router.go b/internal/http/router.go index f63d295..d890a38 100644 --- a/internal/http/router.go +++ b/internal/http/router.go @@ -111,6 +111,9 @@ func NewRouter(ctx context.Context, cfg RouterConfig) http.Handler { mux.HandleFunc("GET /privacy", func(w http.ResponseWriter, r *http.Request) { cfg.Renderer.Page(w, http.StatusOK, "privacy.html", nil) }) + mux.HandleFunc("GET /pricing", func(w http.ResponseWriter, r *http.Request) { + cfg.Renderer.Page(w, http.StatusOK, "pricing.html", nil) + }) // {$} anchors this to the exact path "/" — without it, a pattern ending in // "/" is a subtree match in net/http's ServeMux and catches every diff --git a/internal/http/router_test.go b/internal/http/router_test.go index 943ee39..2916697 100644 --- a/internal/http/router_test.go +++ b/internal/http/router_test.go @@ -32,6 +32,9 @@ var routerTestTemplateFS = fstest.MapFS{ "templates/privacy.html": &fstest.MapFile{Data: []byte( `{{define "content"}}privacy{{end}}`, )}, + "templates/pricing.html": &fstest.MapFile{Data: []byte( + `{{define "content"}}pricing{{end}}`, + )}, } func newRouterTestRenderer(t *testing.T) *handlers.TemplateRenderer { @@ -179,14 +182,14 @@ func TestDashboardServerChannelDeleteRouteIsRegistered(t *testing.T) { } } -func TestTermsAndPrivacyPagesArePublic(t *testing.T) { +func TestPublicInformationalPagesArePublic(t *testing.T) { t.Parallel() router := httpapp.NewRouter(context.Background(), httpapp.RouterConfig{ Renderer: newRouterTestRenderer(t), JWTSecret: routerTestJWTSecret, }) - for _, path := range []string{"/terms", "/privacy"} { + for _, path := range []string{"/terms", "/privacy", "/pricing"} { req := httptest.NewRequest(http.MethodGet, path, nil) rr := httptest.NewRecorder() router.ServeHTTP(rr, req) diff --git a/web/templates/base.html b/web/templates/base.html index 74d2fa2..5f5fdf4 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -24,6 +24,7 @@
+ Choose the RowBot tier that fits how many reporting users your server needs. + Every tier keeps Concept2 results flowing straight to Discord. +
+Free
+No cost for small teams and trial servers.
+ +Includes up to 10 reporting users per server.
+$5 per month
+For growing clubs with a steady group of athletes.
+ +Includes up to 50 reporting users per server.
+$10 per month
+For larger servers that need room for more reporting athletes.
+ +Includes up to 250 reporting users per server.
+