FeaturesDevelopersDocsChangelog
hello@givelink.ai
GGiveLink
For NonprofitsPricingCompareBlogAbout
Join the Waitlist
← All runbooks

E2E Signup Testing

How to run the Playwright signup test suite locally and in CI, what each scenario covers, and how to debug preview failures. CI blocks merges when these fail.

Last reviewed April 19, 2026

E2E Signup Testing

Purpose

The signup E2E suite validates the end-to-end user journey from landing on the sign-up page through completing onboarding and reaching the dashboard. It runs against a real Vercel preview deployment using real Clerk test-mode auth and a real database, so it exercises the full stack — Next.js routing, Clerk auth, Brandfetch enrichment, and Stripe Connect onboarding — exactly as a new nonprofit admin would experience it.

The suite exists because the signup flow involves complex multi-step state (Clerk session, DB onboarding flags, Stripe account readiness) that is difficult to cover with unit tests alone. A green run here means a real user can sign up without hitting a dead end.

CI impact: These tests run on every Vercel preview deployment and block merges if they fail (see Running in CI).

What's covered

The suite contains 6 scenarios:

FileScenarioWhat it validates
happy-path.spec.tsHappy path — nonprofit domainFull signup with a nonprofit email domain; Brandfetch enrichment runs; org is created; user reaches /home.
generic-email.spec.tsGeneric email (manual entry path)Signup with a public-email domain (gmail.com); Brandfetch is skipped; org name must be entered manually.
already-signed-in.spec.tsAlready-signed-in guardAfter completing signup, navigating to /sign-up must redirect to /home.
resume.spec.tsMid-flow resumeSign up, complete onboarding step 1, close context. Re-sign-in, assert we land at onboarding step 2 (not step 1 or /home).
stripe-connect-resume.spec.tsStripe Connect resumeSign up, reach the Stripe Connect step, close context. Re-sign-in, click "Resume Stripe setup", assert the redirect is a Stripe account link URL.
demo-redirect.spec.tsDemo hostname redirectA request to demo.givelink.ai/sign-up must redirect to app.givelink.ai/sign-up. Tests proxy.ts DEMO classification via Host-header override.
impersonation-roundtrip.spec.tsImpersonation round-tripFull Clerk actor-token impersonation cycle: seed fixture, impersonate, return to admin. Validates admin_impersonations DB row and admin_audit_log entries. (Spec D §12.1)

Note: impersonation-roundtrip.spec.ts is included in the signup suite because it requires the same real-Clerk, real-DB environment as the signup scenarios. Other impersonation invariants stay in Vitest (Spec §12.6).

Running locally

The suite requires a running dev server (or a preview URL). The dev server auto-picks a port in the 3006–3199 range based on the worktree; check the terminal output for the assigned port.

# From the repo root — start the dev server first
pnpm dev
 
# In a second terminal — run the full signup suite
E2E_BASE_URL=http://localhost:<PORT> TEST_TEARDOWN_SECRET=<secret> pnpm test:e2e:signup

TEST_TEARDOWN_SECRET must match the value in your .env.local. It is required — the suite calls teardown endpoints after each test and will throw if the secret is missing.

To run a single scenario during debugging:

E2E_BASE_URL=http://localhost:<PORT> TEST_TEARDOWN_SECRET=<secret> \
  pnpm exec playwright test tests/e2e/signup/happy-path.spec.ts

To open the Playwright trace viewer after a local failure:

pnpm exec playwright show-trace test-results/<run-id>/trace.zip

Running in CI

The workflow file is .github/workflows/e2e-signup.yml. It triggers on every successful Vercel Preview deployment:

on:
  deployment_status:
 
jobs:
  e2e:
    if: |
      github.event.deployment_status.state == 'success' &&
      github.event.deployment_status.environment == 'Preview'

The job sets E2E_BASE_URL to the Vercel preview URL from the deployment event, installs Chromium, and runs pnpm test:e2e:signup. No local build or web server is needed — the suite hits the live preview deploy.

On failure, the job uploads the Playwright report as a GitHub Actions artifact named playwright-report with a 7-day retention. Download it and open index.html to see the HTML report, or use the trace files with playwright show-trace.

Secrets required in the repo:

  • TEST_TEARDOWN_SECRET — authorizes the teardown endpoints. Set in GitHub repo → Settings → Secrets → Actions.
  • ALLOW_TEST_TEARDOWN=true — set as an Actions environment variable (not a secret) for the preview environment. The teardown endpoints refuse all requests when this is unset.

Test accounts

Clerk test-mode automatically verifies email addresses whose local-part ends with +clerk_test (literal). GiveLink's uniqueClerkTestEmail() helper in tests/e2e/signup/helpers.ts generates unique addresses for each run:

// Pattern: givelink_<timestamp>-<nonce>+clerk_test@<domain>
// Example: givelink_1713499200000-abc12345+clerk_test@hopefoundation-test.org
export function uniqueClerkTestEmail(domain = "hopefoundation-test.org"): string {
  const nonce = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
  return `givelink_${nonce}+clerk_test@${domain}`;
}

The timestamp + random nonce ensures no two parallel test runs collide. Always use this helper — don't hardcode test emails, as Clerk enforces uniqueness and reusing an email across runs causes sign-up failures.

The impersonation-roundtrip.spec.ts scenario uses the seedImpersonationFixture API (see Teardown) to create both an admin user and a target user. These are also +clerk_test accounts.

Teardown

Every test that creates a Clerk user or org is responsible for cleaning up after itself — whether the test passes or fails. The suite calls three /api/test-admin/admin/ endpoints, all triple-gated:

  1. Triple gate on every endpoint:
    • ALLOW_TEST_TEARDOWN env var must be "true" (absent in production).
    • The request must carry the x-test-secret header matching TEST_TEARDOWN_SECRET.
    • The app must not be running with VERCEL_ENV=production.

The four test endpoints are:

EndpointMethodPurpose
/api/test-admin/admin/teardownPOSTDelete a user by email — Clerk + DB cascade. Used by teardownUser() in helpers.ts.
/api/test-admin/admin/seed-impersonation-fixturePOSTCreate admin + target Clerk users, seed DB rows. Returns ImpersonationFixture.
/api/test-admin/admin/impersonation-stateGETReturn admin_impersonations and admin_audit_log rows for a target user ID. Used to assert state after impersonation.
/api/test-admin/admin/teardown-fixturePOSTDelete the admin user, target user, and org created by seed-impersonation-fixture.

The helpers.ts functions for each:

teardownUser(request, email)            // → POST /teardown
seedImpersonationFixture(request)       // → POST /seed-impersonation-fixture
getImpersonationState(request, userId)  // → GET  /impersonation-state?targetUserId=...
teardownImpersonationFixture(request, fx) // → POST /teardown-fixture

callTestApi (internal) handles 429 (Clerk rate-limit) with a 2 s backoff and 409 (seed collision, near-impossible with nonces) with a 50 ms retry. All other errors fail immediately.

Debugging failures

Download the Playwright report from GitHub Actions

  1. Go to the failed workflow run in GitHub → Actions.
  2. Scroll to Artifacts at the bottom → download playwright-report.zip.
  3. Unzip and open index.html in a browser.
  4. Click a failing test → Traces tab → open the trace viewer.

Common failure classes

SymptomLikely causeFix
[timeout] waiting for URL to match /home/Brandfetch is down or rate-limiting. The enrichment step spins indefinitely when Brandfetch returns nothing.Retry once. If persistent, check Brandfetch status page.
Teardown failed: 401TEST_TEARDOWN_SECRET mismatch or ALLOW_TEST_TEARDOWN not set on the preview environment.Verify the Vercel preview env vars and GitHub secret match.
Teardown failed: 429 after fixture seedClerk rate-limiting test-mode account creation. The 2 s backoff in callTestApi handles one retry. Persistent 429 means too many parallel runs.Reduce parallelism or wait 60 s.
expect(url).toMatch(/stripe.com/) failsStripe returned an error creating the account link. Usually a test-mode credential issue.Check STRIPE_SECRET_KEY on the preview environment — must be a test-mode key.
Already signed in test flakyClerk session cookie persisted across tests. The scenario isolates context per test — confirm storageState is not leaking.Check playwright.config.ts for shared auth state.
demo-redirect.spec.ts fails in prod-like envproxy.ts DEMO classification depends on the Host header override. Some preview configurations strip custom headers.Confirm the preview URL is not mapped to demo.givelink.ai.

Trace viewer key actions

  • Network tab — find the failing API call (Brandfetch, Clerk, Stripe) and its response.
  • Console tab — Next.js server errors surface here.
  • Actions tab — replay the test step-by-step to see where it diverged.

Adding a new scenario

  1. Create a new .spec.ts file in tests/e2e/signup/.
  2. Import uniqueClerkTestEmail, teardownUser, and waitForUrlMatching from ./helpers.
  3. Use test.afterEach to call teardownUser(request, email) — even on failure (Playwright always runs afterEach).
  4. Add a comment at the top of the file citing which part of docs/signup-flow.md it exercises (see existing specs for the format).
  5. Run locally with pnpm test:e2e:signup to confirm it passes before pushing.
  6. The CI workflow picks up new files automatically — no workflow config changes needed.

If the new scenario requires an admin user or impersonation fixtures, use seedImpersonationFixture and teardownImpersonationFixture rather than creating accounts manually — they handle Clerk rate-limit retries and ensure clean teardown.

Troubleshooting

TEST_TEARDOWN_SECRET is required for E2E teardown

The TEST_TEARDOWN_SECRET env var is not set. Add it to your .env.local (for local runs) or to the GitHub repo secrets (for CI). The value must match TEARDOWN_SECRET in the app's Vercel env.

pnpm test:e2e:signup exits immediately with no tests run

The Playwright config (playwright.config.ts) requires E2E_BASE_URL to be set when not in local-server mode. Set the variable to your dev server URL.

Teardown failed: 403 — endpoint reachable but rejected

ALLOW_TEST_TEARDOWN is not set to "true" on the target environment. For preview environments, add this as an environment variable in Vercel project settings (not a secret). For local runs, add it to .env.local.

CI run never triggers

The workflow fires on deployment_status events from Vercel. If no run appears after a preview deploy, check:

  1. The Vercel GitHub integration is connected (repo settings → Integrations).
  2. The deployment status event is firing — check the GitHub repo → Actions → search for "E2E Signup Tests" in the workflow list.
  3. The deployment's environment is exactly "Preview" (case-sensitive).

Teardown leaves orphaned Clerk users after a crash

If the dev server crashed mid-test, teardown may not have run. Find the orphaned accounts in the Clerk dashboard (filter by email pattern givelink_*+clerk_test@) and delete them manually. In CI, orphaned accounts from a failed run clean up automatically on the next run because each email is unique — they don't interfere with future tests.

References

  • Test files: tests/e2e/signup/
  • Helpers: tests/e2e/signup/helpers.ts
  • CI workflow: .github/workflows/e2e-signup.yml
  • Playwright config: playwright.config.ts
  • Teardown endpoints: src/app/api/test-admin/admin/
  • Spec: docs/superpowers/specs/2026-04-18-spec-d-e2e-and-hard-deletes.md §12
GiveLink

Generosity, elevated.

Product

FeaturesFor NonprofitsPricingChangelogBlogDocsDevelopersRoadmap

Compare

GiveLink vs ZeffyGiveLink vs GivebutterGiveLink vs GoFundMe ProGiveLink vs Fundraise UpGiveLink vs DonorboxGiveLink vs Bloomerang

Company

About

Legal

PrivacyTermsAcceptable UseCookiesFee DisclosureTrademark

Our standing commitments

  • 1.No hidden fees. Any amount the donor is asked to cover is shown with a full breakdown — platform fee and processing fee itemized — before they submit.
  • 2.Every dollar you intended reaches your nonprofit. The fees you see on this page are the fees you pay — no tips, no surprise markup.
  • 3.We never call our fee a “tip” or a “contribution.” It's a fee. We name it. We show the number.

© 2026 GiveLink. All rights reserved.