// walkthrough: error-contract (cross-cutting) // covers: bullet 4 — forced 5xx response triggers the error toast, no UI // bleed (no orphan partial, no half-swap). // // Uses rod's request hijacking to inject a 500 response for a specific // known POST without modifying the app. Verifies that // internal/embeds/static/error-handler.js fires its showErrorToast funnel. package operatorwalkthroughs_test import ( "strings" "testing" "time" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" ) func TestErrorContractWalkthrough(t *testing.T) { base := baseURL(t) browser := newBrowser(t) page := browser.MustPage() loginAsOperator(t, page, base, "/operator/entitlement-sets") t.Logf("hijacking POST /partials/operator/entitlement-sets → 500") router := page.HijackRequests() defer router.MustStop() router.MustAdd("*/partials/operator/entitlement-sets", func(ctx *rod.Hijack) { if string(ctx.Request.Method()) != "POST" { ctx.ContinueRequest(&proto.FetchContinueRequest{}) return } ctx.Response.SetHeader("Content-Type", "text/html") ctx.Response.Payload().ResponseCode = 500 ctx.Response.SetBody("internal server error") }) go router.Run() t.Logf("submit valid entitlement set form → hijacked 500 → expect error toast") page.MustElement(`#setName`).MustInput("e2e-error-contract") page.MustElement(`#createEntitlementSetForm button[type=submit]`).MustClick() // error-handler.js calls showErrorToast() for 5xx; #errorToast.show // should appear. page.Timeout(10 * time.Second).MustElement(`#errorToast.show`) // No UI bleed: the form should still be intact (not half-swapped). if _, err := page.Element(`#createEntitlementSetForm`); err != nil { t.Errorf("UI bleed: createEntitlementSetForm vanished after 5xx (should be preserved)") } errText := strings.TrimSpace(page.MustElement(`#errorToast`).MustText()) if errText == "" { t.Errorf("error toast text is empty") } }