Tier 3 T3-4 — Browser E2E Test Plan (RealOutcomes R940)
Date: 2026-08-28
Status: Test plan ready for Ryan review. Ryan executes browser tests; Cursor does not run browser tests.
Parent: TIER_3_REALOUTCOMES_T3-3_REPORT.md · TIER_3_REALOUTCOMES_MIGRATION_AUDIT.md (T3-0)
Scope
| In scope (T3-4) |
Out of scope |
| Browser E2E on R940 stack via staging tunnel URL |
T3-5 DNS cutover (realoutcomes.app → R940) |
| Directory, auth, subscriptions, crawl/score UI paths |
Railway / hosted Supabase teardown |
| Integration smoke (Stripe checkout path, SendGrid path, storage infra) |
last1-admin proxy tests (Tier 6) |
| Ryan marks PASS/FAIL in Section 10 |
Real transactions / mass email sends |
Production remains Railway at https://realoutcomes.app until T3-5. All T3-4 tests target the R940 parallel stack only.
1. How to reach R940 (staging tunnel)
Public realoutcomes.app still resolves to Railway (x-railway-edge response header). Use a separate staging hostname on the same Cloudflare Tunnel.
| Item |
Value |
| Tunnel |
lastvet-r940 |
| Tunnel UUID |
4c8550f2-2156-4284-b9f7-60cdc0fdc109 |
| App origin |
http://192.168.1.99:5000 |
| Staging app URL |
https://realoutcomes-app.last1.enterprises |
| Staging Supabase URL (auth E2E) |
https://realoutcomes-db-r940.last1.enterprises → http://192.168.1.98:8000 |
One-time prep (Ryan + Cursor)
| Step |
Owner |
Action |
| 1 |
Ryan |
Cloudflare DNS CNAME: realoutcomes-app.last1.enterprises → 4c8550f2-2156-4284-b9f7-60cdc0fdc109.cfargotunnel.com (proxied) |
| 2 |
Ryan |
Cloudflare DNS CNAME: realoutcomes-db-r940.last1.enterprises → same tunnel target (proxied) — required for browser auth |
| 3 |
Cursor |
Dry-run tunnel ingress: DRY_RUN=1 scripts/realoutcomes/configure-tunnel-staging-app.sh on lastvet-edge |
| 4 |
Cursor |
Dry-run Supabase tunnel: DRY_RUN=1 scripts/realoutcomes/configure-tunnel-staging-db.sh |
| 5 |
Ryan + Cursor |
GoTrue allow-list on VM 117: https://realoutcomes-app.last1.enterprises/auth/callback — script: configure-gotrue-staging-redirects.sh |
| 6 |
Cursor |
After Ryan confirms steps 1–2 + 5: apply both tunnel scripts (DRY_RUN=0), update app env PUBLIC_URL + VITE_SUPABASE_URL to staging HTTPS URLs, rebuild container |
| 7 |
Cursor |
Run scripts/realoutcomes/verify-pf-headers.sh — PF-1/PF-2 must PASS before browser E2E |
| 8 |
Ryan |
If using Google OAuth: add authorized redirect URI in GCP OAuth client for staging callback |
Do not change DNS for realoutcomes.app (T3-5).
Confirm you are on R940 (not Railway)
DevTools → Network → reload /:
| Signal |
R940 (pass) |
Railway (wrong stack) |
| Response headers |
No x-railway-edge |
Has x-railway-edge |
GET /api/auth/user (logged out) |
401 |
401 (same) |
GET /api/directory?limit=1 |
200 with ~1.99M total |
200 (hosted DB) |
| Optional |
ssh realoutcomes-app 'docker logs realoutcomes-app --tail 5' shows your requests |
No new lines on VM 116 |
| Item |
Guidance |
| Accounts |
One of the 3 lifted users from Gate B: ryan@last1.org, rydaunicorn@gmail.com, ryan@last1.enterprises (password reset via R940 GoTrue if needed) |
| Browser |
Chrome or Firefox; incognito profile |
| DevTools |
Network tab; filter api/ and auth/v1/ |
| Stripe |
Use test card only; close checkout before confirming payment |
| SendGrid |
Verify API path only; do not trigger bulk digests to real users |
Write policy: Prefer read-only. Mark WRITE (optional) tests as SKIP unless explicitly needed.
3. Test checklist
Mark each row PASS, FAIL, or SKIP in Section 10.
4.0 Pre-flight
| ID |
Test |
Route / action |
Expected |
| PF-1 |
Staging URL reaches R940 |
https://realoutcomes-app.last1.enterprises/ |
SPA loads; no x-railway-edge |
| PF-2 |
Prod URL still Railway |
https://realoutcomes.app/ |
Loads with x-railway-edge |
| PF-3 |
Directory API on R940 |
DevTools: GET /api/directory?limit=1 |
200; pagination total ~1,990,000 |
| PF-4 |
Supabase Kong reachable (if auth tests) |
Browser can reach https://realoutcomes-db-r940.last1.enterprises/auth/v1/health or login attempt reaches Kong |
No browser network error to LAN IP |
4.1 Landing and navigation
| ID |
Test |
Route |
Expected |
| N-1 |
Home / evaluator |
/ |
Page loads (evaluator or redirect when logged in) |
| N-2 |
About / landing |
/about |
Marketing landing renders |
| N-3 |
Directory nav |
/directory |
Directory UI loads |
| N-4 |
Methodology |
/methodology |
Static content loads |
| N-5 |
Framework |
/framework |
Static content loads |
| N-6 |
Header nav links |
Click primary nav items |
No 404 loop |
4.2 Auth
| ID |
Test |
Route |
Expected |
| A-1 |
Login page |
/login |
Email/password + Google options visible |
| A-2 |
Password login |
Sign in with test account |
Redirect to app; session established |
| A-3 |
Auth callback |
OAuth/magic link via /auth/callback |
Lands in app; no redirect_uri mismatch |
| A-4 |
Session API |
GET /api/auth/user |
200 with user profile |
| A-5 |
Logout |
Sign out (if UI present) or clear session |
GET /api/auth/user → 401 |
SKIP A-2/A-3 if Supabase staging tunnel + GoTrue allow-list not complete.
4.3 Directory and org detail (1.99M IRS rows)
| ID |
Test |
Route |
Expected |
| D-1 |
Directory list |
/directory |
Paginated org list loads |
| D-2 |
Search |
Search known org name or EIN |
Results return from R940 Postgres |
| D-3 |
Filters |
State / classification filters |
Filtered results update |
| D-4 |
Org profile |
/org/:ein (pick org from directory) |
Detail tabs load (scores, flags, connections as applicable) |
| D-5 |
Compare |
/compare-orgs |
Compare UI loads (read-only) |
4.4 Authenticated features
| ID |
Test |
Route |
Expected |
| U-1 |
Saved orgs |
/saved (logged in) |
Saved list loads or empty state |
| U-2 |
Watchlists |
/watchlists |
Watchlist UI loads |
| U-3 |
Alerts |
/alerts |
Alerts page loads |
| U-4 |
Portfolio |
/portfolio |
Portfolio view loads |
| U-5 |
Funder dashboard |
/funder-dashboard |
Dashboard loads (if entitled) |
4.5 Admin-facing (RealOutcomes app)
| ID |
Test |
Route |
Expected |
| ADM-1 |
In-app admin routes |
/admin |
Redirects to unified admin portal (not in-app admin UI) — mark PASS if redirect works |
RealOutcomes product admin is via last1-admin proxy (Tier 6). No in-app admin E2E in T3-4.
4.6 Stripe checkout (verify path only)
| ID |
Test |
Route |
Expected |
| ST-1 |
Subscription page |
/settings/subscription |
Plans render with Stripe price IDs |
| ST-2 |
Checkout start |
Click subscribe → POST /api/stripe/checkout |
Redirect to Stripe Checkout session URL |
| ST-3 |
Do not pay |
Close Stripe tab before confirming |
No charge; return to app safely |
4.7 SendGrid (verify path only)
| ID |
Test |
Action |
Expected |
| SG-1 |
Alert digest infra |
ssh realoutcomes-db 'curl -sS -X POST http://127.0.0.1:8000/functions/v1/send-alert-digests ...' OR inspect last timer log |
Function returns 200; do not send to production mailing list |
| SG-2 |
UI alert prefs |
/alerts preferences (if exposed) |
Saves without 5xx (WRITE optional, use test account only) |
4.8 Firecrawl / website analyzer
| ID |
Test |
Route |
Expected |
| FC-1 |
Website analyzer page |
/website-analyzer |
Form loads |
| FC-2 |
Analyze public site |
Submit well-known nonprofit URL |
Analysis completes or graceful error (Firecrawl quota); POST /api/... not 5xx |
Uses server-side Firecrawl key from BWS; does not require Edge Function from browser.
4.9 Edge Function–backed data (timers on VM 117)
Background timers (systemd on VM 117): process-status-changes, run-validations, process-score-runs, send-alert-digests, scheduled-recrawl.
| ID |
Test |
Action |
Expected |
| EF-1 |
Timers active |
ssh realoutcomes-db 'systemctl list-timers realoutcomes-*' |
5 timers listed |
| EF-2 |
Status changes reflected |
Org with recent status change shows in UI/alerts |
Data consistent with R940 DB (read-only) |
| EF-3 |
Score run queue |
Org profile score section |
Scores load or "pending" state sane |
| EF-4 |
Scheduled recrawl |
Check crawl queue table via UI if exposed |
No user-visible errors |
4.10 Storage upload/download
| ID |
Test |
Route |
Expected |
| STO-1 |
Upload infra |
Portfolio or document upload UI (WRITE optional) |
Signed URL flow succeeds; object in R940 storage bucket |
| STO-2 |
Download |
Download uploaded file |
File retrieves correctly |
| STO-3 |
Empty bucket baseline |
N/A |
Hosted had 0 objects; R940 may have test object only after STO-1 |
5. Failure triage
| Symptom |
Likely cause |
Check |
| OAuth redirect error |
Staging URL not in GoTrue ADDITIONAL_REDIRECT_URLS |
VM 117 .env + configure-gotrue-staging-redirects.sh |
| Auth calls fail in browser |
VITE_SUPABASE_URL still LAN IP |
Rebuild with https://realoutcomes-db-r940.last1.enterprises |
| Directory 500 |
Bad DATABASE_URL (Supavisor format) |
postgres.{tenant}@192.168.1.98:5432 |
| Still seeing Railway |
Wrong URL |
Use staging hostname; verify PF-1 |
| Stripe checkout 500 |
Missing/invalid Stripe keys |
BWS REALOUTCOMES_APP_PROD_STRIPE_* |
6. T3-5 placeholder cleanup reminder
Before T3-5 cutover (not a T3-4 blocker):
- Replace VM 117
.env.example placeholders (your-tenant-id, default postgres password)
- Rotate D-live vendor secrets (Stripe webhook, SendGrid, Firecrawl)
- Regenerate JWT/anon/service keys for production cutover
7. Section 10 — Ryan results worksheet
Tester: Ryan
Date: _
Staging URL used: https://realoutcomes-app.last1.enterprises
Test account: _
Pre-flight
| ID |
Result |
Notes |
| PF-1 |
|
|
| PF-2 |
|
|
| PF-3 |
|
|
| PF-4 |
|
|
Navigation
| ID |
Result |
Notes |
| N-1 |
|
|
| N-2 |
|
|
| N-3 |
|
|
| N-4 |
|
|
| N-5 |
|
|
| N-6 |
|
|
Auth
| ID |
Result |
Notes |
| A-1 |
|
|
| A-2 |
|
|
| A-3 |
|
|
| A-4 |
|
|
| A-5 |
|
|
Directory
| ID |
Result |
Notes |
| D-1 |
|
|
| D-2 |
|
|
| D-3 |
|
|
| D-4 |
|
|
| D-5 |
|
|
Authenticated
| ID |
Result |
Notes |
| U-1 |
|
|
| U-2 |
|
|
| U-3 |
|
|
| U-4 |
|
|
| U-5 |
|
|
Admin
Stripe
| ID |
Result |
Notes |
| ST-1 |
|
|
| ST-2 |
|
|
| ST-3 |
|
|
SendGrid
| ID |
Result |
Notes |
| SG-1 |
|
|
| SG-2 |
|
|
Firecrawl
| ID |
Result |
Notes |
| FC-1 |
|
|
| FC-2 |
|
|
Edge Functions
| ID |
Result |
Notes |
| EF-1 |
|
|
| EF-2 |
|
|
| EF-3 |
|
|
| EF-4 |
|
|
Storage
| ID |
Result |
Notes |
| STO-1 |
|
|
| STO-2 |
|
|
| STO-3 |
|
|
Summary
| Metric |
Count |
| PASS |
|
| FAIL |
|
| SKIP |
|
T3-4 gate verdict: ☐ PASS (zero FAIL on PF-1, PF-2, D-2, A-2, ST-2) · ☐ FAIL (block T3-5 until fixed)
Ryan sign-off: _ Date: _
8. After T3-4
| Phase |
Action |
| T3-4 complete |
Ryan fills Section 10; Cursor writes TIER_3_REALOUTCOMES_T3-4_REPORT.md from results |
| T3-5 |
DNS cutover realoutcomes.app → tunnel (Ryan approval) |
| T3-7 |
Railway + hosted Supabase teardown |
Do not proceed to T3-5 until Ryan marks T3-4 gate PASS.