E2E tests fail because:
- OAuth is the PRIMARY auth method — GitHub button shown first
- Email/password is HIDDEN — requires clicking "Other sign-in options"
- Tests need to expand "Other sign-in options" before filling email form
┌─────────────────────────────────────┐
│ Welcome back / Create account │
├─────────────────────────────────────┤
│ [ Continue with GitHub ] ← PRIMARY│
│ │
│ ▶ Other sign-in options │
│ │
│ ─────── COLLAPSED ─────── │
│ │ [ Continue with Google ] │ │
│ │ [ Continue with Twitter ] │ │
│ │ [Sign In with Ethereum] │ │
│ │ │ │
│ │ ─── or ─── │ │
│ │ │ │
│ │ [Email input] │ │
│ │ [Password input] │ │
│ │ [Submit button] │ │
│ ─────────────────────────────────── │
└─────────────────────────────────────┘
The test-helpers.ts already handles this flow:
// Click "Other sign-in options" to expand
await dialog.locator('button').filter({ hasText: /other sign/i }).first().click();BUT the issue is:
- Email verification required in production mode —
requireEmailVerification: !isNonProd - APP_ENV detection — If
APP_ENVis not set or invalid, falls back to'local' - Current .dev.vars has
APP_ENV=developmentwhich is NOT a valid value
'local'← what tests should use'develop''staging''production''test'← IDEAL for E2E
1. Add APP_ENV=test support in auth.server.tsx:
// In getAuthOptions():
const isNonProd = isNonProduction(env);
const isTestEnv = isTest(env); // Add this
emailAndPassword: {
enabled: true,
// In test mode, skip email verification entirely
requireEmailVerification: !isNonProd && !isTestEnv,
...
},2. Create E2E test user seeding:
// scripts/e2e/seed-test-user.ts
import { drizzle } from 'drizzle-orm/...';
import { users } from '~/lib/.server/db/betterAuthSchema';
async function seedTestUser() {
const db = drizzle(...);
// Create or update test user with verified email
await db.insert(users).values({
id: 'e2e-test-user-id',
email: 'test-e2e@example.com',
name: 'E2E Test User',
emailVerified: true, // Pre-verified!
// ... hash password
}).onConflictDoUpdate({
target: users.email,
set: { emailVerified: true }
});
}3. Update .dev.vars for E2E:
APP_ENV=test # Not 'development'!Save auth state after first login:
// e2e/setup/auth.setup.ts
import { test as setup } from '@playwright/test';
setup('authenticate', async ({ page }) => {
await page.goto('/');
// ... login flow
await page.context().storageState({ path: '.auth/user.json' });
});Then reuse in tests:
// playwright.config.ts
export default defineConfig({
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'e2e',
use: { storageState: '.auth/user.json' },
dependencies: ['setup'],
},
],
});Add a test-only endpoint that bypasses OAuth:
// routes/api.test.login.ts (only enabled when APP_ENV=test)
export async function action({ request, context }: ActionFunctionArgs) {
if (!isTest(context.cloudflare.env)) {
throw new Response('Not found', { status: 404 });
}
const { email, password } = await request.json();
// Directly create session without OAuth/email verification
const session = await createTestSession(email, password);
return json({ token: session.token });
}| Fix | Effort | Reliability | Recommendation |
|---|---|---|---|
| APP_ENV=test + pre-verified user | Low | High | ✅ DO THIS |
| Playwright storageState | Medium | High | ✅ Also do |
| Test-only login endpoint | Medium | High | Optional |
| Full OAuth automation | High | Low | ❌ Don't bother |
- Billing/Credits — No E2E tests at all
- Settings/Profile — No E2E tests at all
- Partner Features — No E2E tests at all
- Admin Features — No E2E tests at all
- User views credit balance
- User purchases credits
- User adds payment method
- User signs out
- User creates/edits files in editor
- User runs terminal commands
-
Fix APP_ENV — Change
.dev.varsfromdevelopmenttolocalortest -
Seed test user — Create pre-verified user in database:
INSERT INTO users (id, email, name, email_verified, created_at) VALUES ('e2e-test', 'test-e2e@example.com', 'E2E Test', true, NOW()) ON CONFLICT (email) DO UPDATE SET email_verified = true;
-
Update test-helpers.ts — Add retry logic for auth flow
-
Add Playwright auth setup — Persist session for faster tests
From stress-edge-cases test:
- When network disconnects during streaming and reconnects, auth state is LOST
- User gets redirected to OAuth login
- This is a session persistence bug, not auth flow bug
Fix: Better session recovery / reconnection handling in ConnectionStateMachine.ts