Issue #33 asked how UI/UX tests should be run, including raw-HTML and PNG snapshots. This adds the first raw-HTML-snapshot layer from the proposal posted on that issue: an integration test that exercises server/ui's real routing and templating path end-to-end (real templates, real Store types, the actual response middleware) and diffs the output against a golden file, so unintentional markup changes show up as a reviewable diff. Starts with a single case (the logged-out home page) to establish the pattern; extending to authenticated pages via testdb.SeedOAuthSession is noted in the test's doc comment. No golden file is committed yet - it needs to be generated once, with TEST_DATABASE_URL set, by running the test with its -update flag, then reviewed and committed; until then the case skips rather than failing, matching this repo's skip-gracefully convention for missing test infrastructure.
129 lines
4.1 KiB
Go
129 lines
4.1 KiB
Go
package ui_test
|
|
|
|
import (
|
|
"flag"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"ruben/inventory2/domains/accounts"
|
|
"ruben/inventory2/domains/authentication"
|
|
etsy_platform "ruben/inventory2/domains/platforms/etsy"
|
|
"ruben/inventory2/domains/raw_events"
|
|
"ruben/inventory2/domains/reports"
|
|
"ruben/inventory2/internal/testdb"
|
|
"ruben/inventory2/server/auth"
|
|
"ruben/inventory2/server/response"
|
|
"ruben/inventory2/server/ui"
|
|
)
|
|
|
|
// update regenerates the golden files under testdata/golden from the
|
|
// current rendered output, instead of comparing against them. Review the
|
|
// diff like any other golden file before committing it, e.g.:
|
|
//
|
|
// TEST_DATABASE_URL=... go test ./server/ui/... -run TestServeTemplate_Golden -update
|
|
var update = flag.Bool("update", false, "update golden files in testdata/golden instead of comparing against them")
|
|
|
|
// TestServeTemplate_Golden is a raw-HTML snapshot/regression test: it
|
|
// exercises server/ui's real routing + templating path end-to-end (real
|
|
// templates, real Store types backed by a real Postgres connection, the
|
|
// same response.HandleResponses/HandleErrors middleware production uses -
|
|
// nothing about the render path is mocked) and diffs the rendered HTML
|
|
// against a committed golden file, so an unintentional template/markup
|
|
// change shows up as a reviewable diff instead of shipping silently.
|
|
//
|
|
// This is the "raw HTML snapshot" layer described in issue #33's UI-testing
|
|
// proposal. A browser-screenshot layer (e.g. chromedp, for catching CSS/
|
|
// layout regressions raw HTML can't) and Penpot-design-fidelity comparisons
|
|
// are separate, not-yet-implemented layers of that same proposal.
|
|
//
|
|
// Only covers logged-out pages for now (no seeded oauth session/cookie);
|
|
// extend the tests table below using testdb.SeedOAuthSession plus an
|
|
// "access_token" cookie on the request to cover authenticated pages.
|
|
//
|
|
// Like other integration tests in this repo, it's skipped (not failed) if
|
|
// TEST_DATABASE_URL is unset. If a golden file hasn't been generated yet,
|
|
// the individual case is also skipped (not failed) rather than breaking
|
|
// the suite - run with -update once, review the generated file, and commit
|
|
// it to turn that case into a real regression check.
|
|
func TestServeTemplate_Golden(t *testing.T) {
|
|
pool := testdb.Pool(t)
|
|
logger := testdb.Logger()
|
|
|
|
accts := accounts.NewStore(logger, pool)
|
|
rawEvents := raw_events.NewStore(logger, pool)
|
|
reps := reports.NewStore(logger, pool, accts)
|
|
etsy := etsy_platform.NewPlatform(
|
|
logger,
|
|
func(acctID int64) string { return "" },
|
|
"", "",
|
|
pool,
|
|
)
|
|
|
|
authr := authentication.NewDev(pool, logger)
|
|
authM := auth.NewService(logger, authr, accts)
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(
|
|
authM.Identify,
|
|
response.HandleResponses,
|
|
response.HandleErrors,
|
|
)
|
|
|
|
ui.Routes(
|
|
logger,
|
|
router.Group("/ui"),
|
|
"/ui",
|
|
rawEvents,
|
|
accts,
|
|
reps,
|
|
etsy,
|
|
authM.Authenticate(),
|
|
true,
|
|
)
|
|
|
|
tests := []struct {
|
|
name string
|
|
path string
|
|
wantStatus int
|
|
}{
|
|
{name: "home-logged-out", path: "/ui", wantStatus: http.StatusOK},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
|
|
rec := httptest.NewRecorder()
|
|
router.ServeHTTP(rec, req)
|
|
|
|
require.Equal(t, tt.wantStatus, rec.Code, "unexpected status for %s", tt.path)
|
|
|
|
got := rec.Body.Bytes()
|
|
golden := filepath.Join("testdata", "golden", tt.name+".html")
|
|
|
|
if *update {
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(golden), 0o755))
|
|
require.NoError(t, os.WriteFile(golden, got, 0o644))
|
|
return
|
|
}
|
|
|
|
want, err := os.ReadFile(golden)
|
|
if os.IsNotExist(err) {
|
|
t.Skipf(
|
|
"golden file %s does not exist yet; generate it once via `go test ./server/ui/... -run TestServeTemplate_Golden -update` (requires TEST_DATABASE_URL), then review and commit it",
|
|
golden,
|
|
)
|
|
}
|
|
require.NoError(t, err, "reading golden file %s", golden)
|
|
|
|
require.Equal(t, string(want), string(got), "rendered output for %s no longer matches %s; if this change is intentional, regenerate with -update", tt.path, golden)
|
|
})
|
|
}
|
|
}
|