Local E2E testing plan
Manual end-to-end testing runbook for validating the MVP locally before release.
Use this runbook to validate THE HCLAB locally before a release. There are no automated E2E tests in the repo yet — execute these scenarios manually (or convert them into Playwright suites later).
Goals
Verify that a real user can:
- Browse all public marketing surfaces
- Sign up, log in, manage an account, and hit gated content correctly
- Submit public forms (contact, newsletter)
- Complete assessments and learning paths
- Enroll in and finish a study task flow
- Use admin CMS and research ops with correct role boundaries
- Upgrade via Stripe (test mode) when configured
- Survive maintenance mode without breaking auth, webhooks, or docs
Out of scope for local MVP E2E: production DNS, real Mailchimp double opt-in delivery, production Stripe live mode, real reCAPTCHA scoring in CI.
Pre-flight setup
Prerequisites
| Requirement | Notes |
|---|---|
| Node ≥ 20.9 (Next 16) | nvm use 20 or 22 |
| PostgreSQL running locally | Docker, Postgres.app, etc. |
| Fresh or reset DB recommended | Avoid stale seed conflicts |
Bootstrap
npm install
cp .env.example .env
# Set DATABASE_URL and AUTH_SECRET at minimum
npx prisma generate
npx prisma db push
npm run db:seed
npm run dev
Open http://localhost:3000. Keep a second terminal on dev server logs — password resets and emails log there when Resend is unset.
Environment tiers
Use three .env profiles (or toggle vars between passes):
Tier A — Core local (no external services)
Enough for ~70% of E2E.
| Variable | Value |
|---|---|
DATABASE_URL |
Local Postgres |
AUTH_SECRET |
Any long random string |
ADMIN_PASSWORD |
e.g. change-me |
NEXT_PUBLIC_SITE_URL |
http://localhost:3000 |
With Tier A: reCAPTCHA auto-passes, newsletter saves to DB only, contact/reset emails log to console, Stripe checkout returns 503 if price IDs missing.
Tier B — Forms & email
Add for contact/password-reset verification:
| Variable | Purpose |
|---|---|
RESEND_API_KEY |
Real outbound email |
EMAIL_FROM |
Verified sender in Resend |
Tier C — Full integration
Add as needed:
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_RECAPTCHA_SITE_KEY + RECAPTCHA_SECRET_KEY |
Real bot protection |
STRIPE_* keys + price IDs |
Premium checkout |
OPENAI_API_KEY |
AI-assisted study tasks |
CRON_SECRET |
Background job endpoints |
MAINTENANCE_MODE + MAINTENANCE_BYPASS_SECRET |
Maintenance testing |
See Environment variables for the full reference.
Test personas (seeded)
| Persona | Password | Role | Use for | |
|---|---|---|---|---|
| Guest | — | — | — | Public pages, gating redirects |
| Admin | lab@thehclab.com |
ADMIN_PASSWORD (default hclab-admin if unset) |
ADMIN | CMS, users, full admin |
| Researcher | researcher@thehclab.com |
RESEARCHER_PASSWORD or admin password |
RESEARCHER | Research ops only |
| Demo participant | pfdf-demo-non-ai-1@thehclab.com |
pfdf-demo |
MEMBER | PFDF/coding flows (if PFDF seed ran) |
| Fresh member | Create via /signup |
Your choice | MEMBER | Clean enroll/progress tests |
Tip: Run npm run db:studio to inspect users, enrollments, assessment results, and newsletter subscribers during testing.
Execution order
Run phases in sequence so dependencies build on each other:
- Phase 0 — Smoke & infrastructure
- Phase 1 — Public marketing
- Phase 2 — Auth & account
- Phase 3 — Content gating
- Phase 4 — Forms
- Phase 5 — Assessments & learning paths
- Phase 6 — Research participation
- Phase 7 — Admin CMS
- Phase 8 — Admin research ops
- Phase 9 — Stripe premium (Tier C)
- Phase 10 — Maintenance & edge cases
Phase 0 — Smoke & infrastructure
| # | Test | Steps | Pass criteria |
|---|---|---|---|
| 0.1 | Build | npm run build |
Compiles, no TS errors |
| 0.2 | Dev server | npm run dev |
Homepage 200 |
| 0.3 | SEO routes | Visit /robots.txt, /sitemap.xml |
200, valid XML |
| 0.4 | Docs | /docs → any doc article |
Renders, sidebar scrolls |
| 0.5 | 404 | /does-not-exist |
Custom not-found page |
Phase 1 — Public marketing (guest)
Navigation & layout
| # | Test | Pass criteria |
|---|---|---|
| 1.1 | Header nav | All links work: About, Contact, Learn dropdown, Research dropdown, Build dropdown |
| 1.2 | Mobile menu | Hamburger opens/closes; links reachable |
| 1.3 | Footer | Footer links resolve (including /contact, /docs) |
| 1.4 | Hero consistency | Wave hero on hub pages; homepage excluded (its own hero) |
Hub pages
Each should load, hero visible, no console errors:
/about,/contact,/learn,/systems,/governance/research,/research/artifacts,/research/search/media,/labs,/resources/assessments,/learning-paths,/studies,/pricing
Content detail pages (seeded slugs)
| Route | Example slug | Check |
|---|---|---|
/research/[slug] |
cognitive-agency |
Sections render, accent color |
/articles/[slug] |
the-capability-gap |
Body, metadata |
/media/[slug] |
cognitive-sovereignty-in-the-age-of-ai |
Episode layout |
/labs/[slug] |
local-70b-apple-silicon |
Lab report |
/studies/[slug] |
human-ai-collaboration-baseline |
Enroll CTA for guest |
Cross-page flows
| # | Flow | Pass criteria |
|---|---|---|
| 1.5 | Home → pillar → article | No broken links |
| 1.6 | Research search | /research/search?q=cognitive returns grouped results (min 2 chars) |
| 1.7 | Studies browse | /studies lists recruiting study with stats if applicable |
Phase 2 — Authentication & account
Signup (/signup)
| # | Test | Pass criteria |
|---|---|---|
| 2.1 | Happy path | Valid name/email/password (≥8) → lands on /account |
| 2.2 | Duplicate email | Shows error, no duplicate user |
| 2.3 | Weak password | Validation message |
Login (/login)
| # | Test | Pass criteria |
|---|---|---|
| 2.4 | Admin login | lab@thehclab.com → /admin or callback |
| 2.5 | Bad password | Generic error, no enumeration |
| 2.6 | Callback URL | Visit gated page as guest → login → returns to original URL |
| 2.7 | Open redirect safety | callbackUrl=//evil.com should not redirect externally |
Password reset
| # | Test | Pass criteria |
|---|---|---|
| 2.8 | Forgot password | /forgot-password → success message always shown |
| 2.9 | Reset link | Copy URL from server console (Tier A) or email (Tier B) |
| 2.10 | Reset form | New password → redirect to /login?reset=1 |
| 2.11 | Expired token | Invalid token shows error |
Account (/account)
| # | Test | Pass criteria |
|---|---|---|
| 2.12 | Profile update | Change name/bio → persists on refresh |
| 2.13 | Bookmarks | Save content from article page → appears on account |
| 2.14 | Plan section | Shows FREE plan for new member |
| 2.15 | Logout | Returns to public site, protected routes redirect |
Phase 3 — Content gating & downloads
Test as guest, free member, and premium member (set plan in Prisma Studio or via Stripe in Phase 9).
| # | Content type | Guest | Free member | Premium |
|---|---|---|---|---|
| 3.1 | Public article | Full read | Full read | Full read |
| 3.2 | Subscriber-gated resource | Gate → signup/login | Unlocked | Unlocked |
| 3.3 | Premium learning path | Gate visible | Upgrade CTA | Unlocked |
| 3.4 | Premium assessment/cert | Gate | Gate or limited | Full access |
| 3.5 | Download /api/dl/[id] |
Redirect to login/signup/pricing as appropriate | Same | Download succeeds |
Pass criteria: Gates show correct CTA (login vs pricing), no content leak in HTML for gated bodies, download count increments in admin when applicable.
See also Access & gating.
Phase 4 — Public forms
Contact (/contact) — Tier A or B
| # | Test | Pass criteria |
|---|---|---|
| 4.1 | Required fields | Empty submit → inline/API validation |
| 4.2 | Invalid email | Rejected |
| 4.3 | Valid submission | Success state; server log or Resend delivery to lab@thehclab.com |
| 4.4 | Category routing | Try 2–3 inquiry categories; all route to same inbox |
| 4.5 | reCAPTCHA notice | Footer notice visible when provider loaded |
Newsletter (homepage + contact footer)
| # | Test | Pass criteria |
|---|---|---|
| 4.6 | Valid email | Success message |
| 4.7 | Duplicate subscribe | Graceful handling (no crash) |
| 4.8 | DB record | Row in NewsletterSubscriber via Studio |
| 4.9 | Invalid email | Validation error |
Phase 5 — Assessments & learning paths
Log in as a fresh member for clean state.
Assessments (/assessments)
| # | Test | Pass criteria |
|---|---|---|
| 5.1 | Catalog | 4 seeded assessments listed |
| 5.2 | Guest runner | Can view intro; submit requires login |
| 5.3 | Complete ai-readiness |
Questions → score/result screen |
| 5.4 | Result persistence | Result visible on /account |
| 5.5 | Retake | Second attempt creates new result |
Learning paths (/learning-paths)
| # | Test | Pass criteria |
|---|---|---|
| 5.6 | Path detail | Items listed for ai-foundations |
| 5.7 | Mark complete | Toggle item → progress % updates |
| 5.8 | Account progress | Progress reflected on account page |
| 5.9 | Certification exam | If configured: pass/fail, attempt limits, certificate on account |
Phase 6 — Research participation
Use study human-ai-collaboration-baseline (seeded, RECRUITING).
Guest & member enrollment
| # | Test | Pass criteria |
|---|---|---|
| 6.1 | Guest on study page | Prompt to sign in |
| 6.2 | Enroll | Consent checkboxes → success → participate link active |
| 6.3 | Duplicate enroll | Error message |
| 6.4 | Non-recruiting study | Change status in admin → public message updates |
Task progression
| # | Task slug | Pass criteria |
|---|---|---|
| 6.5 | baseline-survey |
Submit → marked complete |
| 6.6 | reflection |
Unlocks after survey |
| 6.7 | Branching | High-confidence path may unlock ai-assisted-reflection |
| 6.8 | ai-assisted-reflection |
Requires OPENAI_API_KEY or expect graceful failure |
| 6.9 | Completion | All required tasks → completion banner + code |
Account & withdrawal
| # | Test | Pass criteria |
|---|---|---|
| 6.10 | Research panel on /account#research |
Shows enrolled study |
| 6.11 | Withdraw | Cannot access participate routes afterward |
Coding queue (optional, PFDF seed)
| # | Test | Pass criteria |
|---|---|---|
| 6.12 | /research/code as assigned rater |
Queue loads |
| 6.13 | Complete coding assignment | Status updates in admin |
See also Studies & participation.
Phase 7 — Admin CMS
Log in as admin (lab@thehclab.com).
Access control
| # | Test | Pass criteria |
|---|---|---|
| 7.1 | Guest /admin |
Redirect to login |
| 7.2 | Member /admin |
Redirect to login |
| 7.3 | Researcher /admin |
Lands on /admin/research, not CMS dashboard |
| 7.4 | Researcher /admin/articles |
Blocked/redirected |
| 7.5 | Admin /admin/users |
Accessible (admin only) |
CMS CRUD smoke (pick one content type end-to-end)
| # | Test | Pass criteria |
|---|---|---|
| 7.6 | Create draft article | Saves in admin |
| 7.7 | Publish article | Visible on public site after refresh |
| 7.8 | Edit framework | Changes on /research/[slug] |
| 7.9 | Assessment edit | Question changes reflected in runner |
| 7.10 | Learning path edit | Item order/content updates |
| 7.11 | Resource upload | Gated download works for entitled user |
| 7.12 | Podcast episode | Appears on /media |
| 7.13 | Subscribers list | Shows newsletter signups from Phase 4 |
See Content editor guide for CMS workflows.
Phase 8 — Admin research ops
Log in as researcher for role-scoped tests; admin for destructive ops.
| # | Test | Pass criteria |
|---|---|---|
| 8.1 | Studies list | Seeded studies visible |
| 8.2 | Study settings | Edit title/description → public page updates |
| 8.3 | Tasks CRUD | Add/edit task → appears in participant flow |
| 8.4 | Conditions | Experimental arms configurable |
| 8.5 | Participants | Enrolled test user visible with status |
| 8.6 | Participant detail | Responses recorded from Phase 6 |
| 8.7 | Analytics | Metrics reflect enrollments/completions |
| 8.8 | Artifacts | Publish artifact → visible at /research/artifacts/[slug] |
| 8.9 | Events log | study_enroll, task events present |
| 8.10 | Export | Generate export; download via admin API |
| 8.11 | Cron (optional) | curl -H "Authorization: Bearer $CRON_SECRET" http://localhost:3000/api/cron/research-ops → 200 |
See Research operations.
Phase 9 — Stripe premium (Tier C)
Requires Stripe test keys and price IDs. See Commerce setup.
Setup
# Terminal 1
npm run dev
# Terminal 2 — forward webhooks
stripe listen --forward-to localhost:3000/api/stripe/webhook
# Copy whsec_... into STRIPE_WEBHOOK_SECRET
| # | Test | Pass criteria |
|---|---|---|
| 9.1 | Guest pricing CTA | Redirects to login with callback |
| 9.2 | Subscribe monthly | Stripe Checkout opens (test card 4242...) |
| 9.3 | Webhook | User plan → PREMIUM in session/account |
| 9.4 | Gated content | Previously locked premium content unlocked |
| 9.5 | Path one-time purchase | Checkout → /learning-paths/[slug]?purchased=1 |
| 9.6 | Duplicate purchase | 400 “Already purchased” |
| 9.7 | Cancel checkout | Returns to pricing/path page cleanly |
| 9.8 | Missing price IDs | Button shows error / API 503 |
Phase 10 — Maintenance mode & edge cases
Set MAINTENANCE_MODE=true in .env, restart dev server. See Maintenance mode.
| # | Test | Pass criteria |
|---|---|---|
| 10.1 | Guest / |
Maintenance page |
| 10.2 | /login |
Still accessible |
| 10.3 | /docs |
Still accessible |
| 10.4 | Admin logged in | Full site access |
| 10.5 | Bypass URL | /?bypass=YOUR_SECRET sets cookie → site accessible |
| 10.6 | Stripe webhook route | Still reachable (no maintenance block) |
Edge cases
| # | Test | Pass criteria |
|---|---|---|
| 10.7 | Password reset rate limit | 6th request from same IP in 15 min throttled |
| 10.8 | reCAPTCHA failure | With keys set, low-score token rejected |
| 10.9 | Concurrent sessions | Login on two browsers, both work |
| 10.10 | Long form input | Contact message at max length handled |
Pass/fail log template
Track each run in a spreadsheet or Notion:
| Phase | ID | Description | Tier | Pass/Fail | Notes |
|---|---|---|---|---|---|
| 2 | 2.4 | Admin login | A | ✅ | |
| 6 | 6.8 | AI task | C | ⏭ | Skipped — no OpenAI key |
Release gate (minimum)
All of the following must pass on Tier A:
- Phase 0 (full)
- Phase 1 (full)
- Phase 2 (full)
- Phase 4.1–4.8
- Phase 5.1–5.4
- Phase 6.1–6.6
- Phase 7.1–7.7, 7.13
For production deploy smoke checks, see Post-deploy verification.
Debugging cheatsheet
| Symptom | Likely cause | Fix |
|---|---|---|
| Login fails after seed | Wrong ADMIN_PASSWORD |
Match .env or use default hclab-admin |
| Forms submit but “nothing happens” | Check dev server logs | Resend/recaptcha/console output |
| Premium not unlocking | Webhook not forwarded | Run stripe listen |
| Study tasks locked | Prior task incomplete | Complete in order; check admin participant |
| Admin 403/redirect loop | Wrong role | Use correct seeded account |
| Build fails on Node 16 | Node too old | nvm use 20 |
| Empty learning path items | Seed slug mismatch | Re-seed or fix path items in admin |
Future automation (recommended)
When ready to codify this plan:
- Playwright with three projects:
guest,member,admin - Global setup:
prisma db push+ seed before suite - Storage state files for each persona (login once, reuse cookies)
- Stripe: use
stripe listenin CI or mock webhook POST - Email: assert on console log pattern in dev, Mailpit in CI
- Priority automation order: auth → study enroll + survey → contact/newsletter → admin article publish → Stripe webhook
Suggested first specs: e2e/smoke/public-pages.spec.ts and e2e/auth/login.spec.ts.
Estimated time
| Scope | Time |
|---|---|
| Tier A full manual pass | 4–6 hours |
| Tier B (+ email) | +30 min |
| Tier C (+ Stripe, OpenAI, maintenance) | +2–3 hours |
| Admin deep CRUD all content types | +2 hours |