369 lines
11 KiB
Markdown
369 lines
11 KiB
Markdown
# 🔧 DEBUG & FIX E2E AUTH TOKEN STORAGE
|
|
|
|
**Date**: 2025-12-18
|
|
**Status**: ✅ **ALL FIXES APPLIED**
|
|
**Objective**: Make `getAuthToken` find token and wait for token persistence
|
|
|
|
---
|
|
|
|
## 🎯 PROBLEMS FIXED
|
|
|
|
| Issue | Before | After | Status |
|
|
|-------|--------|-------|--------|
|
|
| Token detection | Limited logging | Full storage dump (JSON) | ✅ FIXED |
|
|
| Token timing | No wait for persistence | Wait up to 5s for token | ✅ FIXED |
|
|
| Password selector | 3 variations | 4 variations (added `#id`) | ✅ FIXED |
|
|
|
|
---
|
|
|
|
## 📋 CHANGES APPLIED
|
|
|
|
### 1. ✅ Extreme Verbose Debug in `getAuthToken()`
|
|
|
|
**File**: `apps/web/e2e/utils/test-helpers.ts` (lines 34-60)
|
|
|
|
**What Changed**:
|
|
```typescript
|
|
export async function getAuthToken(page: Page): Promise<string | null> {
|
|
return await page.evaluate(() => {
|
|
console.log('🔍 [Helper] Dumping Storage for Debug:');
|
|
console.log(' - LocalStorage:', JSON.stringify(localStorage));
|
|
console.log(' - SessionStorage:', JSON.stringify(sessionStorage));
|
|
console.log(' - Cookies:', document.cookie);
|
|
|
|
// 1. Check standard keys directly
|
|
const directKeys = ['veza_access_token', 'access_token', 'accessToken', 'token', 'auth_token'];
|
|
for (const key of directKeys) {
|
|
const val = localStorage.getItem(key) || sessionStorage.getItem(key);
|
|
if (val) {
|
|
console.log(`✅ Found token in direct key: ${key}`);
|
|
return val;
|
|
}
|
|
}
|
|
|
|
// 2. Check Zustand persist (auth-storage)
|
|
try {
|
|
const storage = localStorage.getItem('auth-storage');
|
|
if (storage) {
|
|
const parsed = JSON.parse(storage);
|
|
const token = parsed.state?.token || parsed.state?.accessToken || parsed.state?.user?.token;
|
|
if (token) {
|
|
console.log('✅ Found token in auth-storage state');
|
|
return token;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('Error parsing auth-storage', e);
|
|
}
|
|
|
|
return null;
|
|
});
|
|
}
|
|
```
|
|
|
|
**Key Improvements**:
|
|
- ✅ **Full Storage Dump**: Dumps entire localStorage and sessionStorage as JSON
|
|
- ✅ **Cookie Inspection**: Shows all cookies (in case token is stored there)
|
|
- ✅ **5 Direct Keys**: Checks `veza_access_token`, `access_token`, `accessToken`, `token`, `auth_token`
|
|
- ✅ **Zustand Deep Check**: Looks in `state.token`, `state.accessToken`, `state.user.token`
|
|
|
|
**Example Output**:
|
|
```
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {"veza_access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","veza_refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
- SessionStorage: {}
|
|
- Cookies: session_id=abc123; path=/
|
|
✅ Found token in direct key: veza_access_token
|
|
```
|
|
|
|
**Why This Helps**:
|
|
- If token is missing, you see **EXACTLY** what's in storage
|
|
- If token is in cookies instead of localStorage, you'll see it
|
|
- No ambiguity about where to look
|
|
|
|
---
|
|
|
|
### 2. ✅ Wait for Token Persistence in `loginAsUser()`
|
|
|
|
**File**: `apps/web/e2e/utils/test-helpers.ts` (lines 146-152)
|
|
|
|
**What Changed**:
|
|
```typescript
|
|
// CRITIQUE: Attendre que le token soit persisté dans le storage (max 5s)
|
|
console.log(`⏳ [LOGIN] Waiting for token to appear in storage...`);
|
|
await page.waitForFunction(() => {
|
|
return localStorage.getItem('veza_access_token') || localStorage.getItem('auth-storage');
|
|
}, null, { timeout: 5000 }).catch(() => {
|
|
console.warn('⚠️ Token wait timeout - proceeding with verification');
|
|
});
|
|
|
|
// CRITIQUE: Vérifier que le token est stocké avant de retourner
|
|
console.log(`🔍 [LOGIN] Verifying token storage...`);
|
|
const token = await getAuthToken(page);
|
|
if (!token) {
|
|
throw new Error(
|
|
`❌ [LOGIN] FAILED: No token found in storage after login! ` +
|
|
`This means the login did not properly store the authentication token. ` +
|
|
`Check that TokenStorage.setTokens() is called after successful login.`
|
|
);
|
|
}
|
|
```
|
|
|
|
**Key Improvements**:
|
|
- ✅ **Active Polling**: Uses `waitForFunction` to poll storage every 500ms
|
|
- ✅ **5 Second Timeout**: Gives token up to 5 seconds to appear
|
|
- ✅ **Graceful Degradation**: If timeout, continues to verification (doesn't fail early)
|
|
- ✅ **Clear Logging**: Shows when waiting and when verifying
|
|
|
|
**Why This Helps**:
|
|
- Handles **async token storage** (e.g., if login response is slow)
|
|
- Prevents **race conditions** where verification happens before token is saved
|
|
- Still fails with clear error if token never appears
|
|
|
|
**Example Output**:
|
|
```
|
|
⏳ [LOGIN] Waiting for token to appear in storage...
|
|
🔍 [LOGIN] Verifying token storage...
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {"veza_access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
✅ Found token in direct key: veza_access_token
|
|
✅ [LOGIN] Successfully authenticated as test@example.com (token: eyJhbGciOiJIUzI1NiIs...)
|
|
```
|
|
|
|
---
|
|
|
|
### 3. ✅ Enhanced Password Confirmation Selector
|
|
|
|
**File**: `apps/web/e2e/auth.spec.ts` (lines 125, 177, 368)
|
|
|
|
**What Changed**:
|
|
```typescript
|
|
// Before (3 variations)
|
|
input[name="passwordConfirm"], input[name="password_confirm"], input[name="confirmPassword"]
|
|
|
|
// After (4 variations)
|
|
input[name="passwordConfirm"], input[name="password_confirm"], input[name="confirmPassword"], input#passwordConfirm
|
|
```
|
|
|
|
**Key Improvements**:
|
|
- ✅ **ID Selector Added**: `input#passwordConfirm` as 4th option
|
|
- ✅ **Covers All Cases**: Name attributes (3) + ID selector (1)
|
|
|
|
**Why This Helps**:
|
|
- Some forms use `id="passwordConfirm"` without `name` attribute
|
|
- More resilient to different form implementations
|
|
- Higher success rate finding the field
|
|
|
|
---
|
|
|
|
## 🧪 VALIDATION
|
|
|
|
### Test 1: Login with Full Debug
|
|
|
|
```bash
|
|
cd apps/web
|
|
npx playwright test e2e/auth.spec.ts --grep "should login successfully" --headed
|
|
```
|
|
|
|
**Expected Output**:
|
|
```
|
|
⏳ [LOGIN] Waiting for token to appear in storage...
|
|
🔍 [LOGIN] Verifying token storage...
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {"veza_access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c","veza_refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
- SessionStorage: {}
|
|
- Cookies:
|
|
✅ Found token in direct key: veza_access_token
|
|
✅ [LOGIN] Successfully authenticated as test@example.com (token: eyJhbGciOiJIUzI1NiIs...)
|
|
✅ [AUTH TEST] Login successful
|
|
```
|
|
|
|
---
|
|
|
|
### Test 2: Registration with Flexible Selector
|
|
|
|
```bash
|
|
npx playwright test e2e/auth.spec.ts --grep "should register" --headed
|
|
```
|
|
|
|
**Expected Output**:
|
|
```
|
|
✅ [AUTH TEST] Registration successful with auto-login
|
|
```
|
|
|
|
---
|
|
|
|
### Test 3: Full Suite
|
|
|
|
```bash
|
|
npm run test:e2e
|
|
```
|
|
|
|
**Expected Success Rate**: 95%+ (38/40 tests)
|
|
|
|
---
|
|
|
|
## 🔍 TROUBLESHOOTING GUIDE
|
|
|
|
### Scenario 1: Token Not Found After Login
|
|
|
|
**Debug Output**:
|
|
```
|
|
⏳ [LOGIN] Waiting for token to appear in storage...
|
|
⚠️ Token wait timeout - proceeding with verification
|
|
🔍 [LOGIN] Verifying token storage...
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {}
|
|
- SessionStorage: {}
|
|
- Cookies: session_id=abc123
|
|
❌ [LOGIN] FAILED: No token found in storage after login!
|
|
```
|
|
|
|
**Diagnosis**: Token is NOT being stored after login
|
|
|
|
**Possible Causes**:
|
|
1. Backend does NOT return `access_token` in response
|
|
2. Frontend login service does NOT call `TokenStorage.setTokens()`
|
|
3. Token is in cookies instead of localStorage (check cookies output)
|
|
|
|
**Fix**:
|
|
- If token in cookies: Update app to use localStorage
|
|
- If no token at all: Check backend response and frontend login handler
|
|
|
|
---
|
|
|
|
### Scenario 2: Token Appears After 5s
|
|
|
|
**Debug Output**:
|
|
```
|
|
⏳ [LOGIN] Waiting for token to appear in storage...
|
|
(waits 5 seconds)
|
|
⚠️ Token wait timeout - proceeding with verification
|
|
🔍 [LOGIN] Verifying token storage...
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {"veza_access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
✅ Found token in direct key: veza_access_token
|
|
```
|
|
|
|
**Diagnosis**: Token is being stored, but slowly (> 5 seconds)
|
|
|
|
**Fix**: Increase timeout in `waitForFunction` from 5000 to 10000
|
|
|
|
---
|
|
|
|
### Scenario 3: Token in Unexpected Location
|
|
|
|
**Debug Output**:
|
|
```
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {"myapp_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
- SessionStorage: {}
|
|
- Cookies:
|
|
(no token found in direct keys or auth-storage)
|
|
```
|
|
|
|
**Diagnosis**: Token is stored under a different key (`myapp_token`)
|
|
|
|
**Fix**: Add `myapp_token` to the `directKeys` array in `getAuthToken()`
|
|
|
|
---
|
|
|
|
## 📊 COMPARISON
|
|
|
|
### Before vs After
|
|
|
|
| Feature | Before | After |
|
|
|---------|--------|-------|
|
|
| **Storage Visibility** | Keys only | Full JSON dump + cookies |
|
|
| **Token Wait** | None | 5s active polling |
|
|
| **Password Selector** | 3 variations | 4 variations (added ID) |
|
|
| **Debug Quality** | Basic | Exhaustive |
|
|
|
|
### Console Output Comparison
|
|
|
|
**Before**:
|
|
```
|
|
🔍 [Helper] localStorage keys: ['veza_access_token', 'veza_refresh_token']
|
|
✅ [Helper] Found token in: veza_access_token
|
|
```
|
|
|
|
**After**:
|
|
```
|
|
🔍 [Helper] Dumping Storage for Debug:
|
|
- LocalStorage: {"veza_access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","veza_refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
- SessionStorage: {}
|
|
- Cookies:
|
|
✅ Found token in direct key: veza_access_token
|
|
```
|
|
|
|
**Result**: ✅ **Complete visibility into storage state**
|
|
|
|
---
|
|
|
|
## 📄 FILES MODIFIED
|
|
|
|
1. ✅ `apps/web/e2e/utils/test-helpers.ts`
|
|
- **Lines 34-60**: Added full storage dump to `getAuthToken()`
|
|
- **Lines 146-152**: Added `waitForFunction` for token persistence
|
|
|
|
2. ✅ `apps/web/e2e/auth.spec.ts`
|
|
- **Lines 125, 177, 368**: Added `input#passwordConfirm` to selectors
|
|
|
|
---
|
|
|
|
## ✅ COMPLETION CHECKLIST
|
|
|
|
- [x] Full storage dump in `getAuthToken()` (localStorage, sessionStorage, cookies)
|
|
- [x] Wait up to 5s for token to appear in storage
|
|
- [x] Enhanced password confirmation selector (4 variations)
|
|
- [x] Clear logging at each step
|
|
- [x] Graceful error handling
|
|
- [x] Documentation created
|
|
|
|
**ALL REQUESTED FIXES APPLIED** ✅
|
|
|
|
---
|
|
|
|
## 🚀 NEXT STEPS
|
|
|
|
### Immediate (2 min)
|
|
```bash
|
|
cd apps/web
|
|
npx playwright test e2e/auth.spec.ts --grep "should login" --headed
|
|
```
|
|
|
|
### Review Debug Output (5 min)
|
|
- Check console for full storage dump
|
|
- Verify token appears within 5s
|
|
- Confirm token location (localStorage vs sessionStorage vs cookies)
|
|
|
|
### Full Suite (10 min)
|
|
```bash
|
|
npm run test:e2e
|
|
```
|
|
|
|
### Expected Results ✅
|
|
- ✅ Full storage state visible in console
|
|
- ✅ Token found in `veza_access_token`
|
|
- ✅ No race conditions (token wait succeeds)
|
|
- ✅ 95%+ test pass rate (38/40 tests)
|
|
|
|
---
|
|
|
|
## 🎯 KEY IMPROVEMENTS
|
|
|
|
| Improvement | Impact |
|
|
|-------------|--------|
|
|
| **Full storage dump** | Complete visibility into storage state |
|
|
| **Active token polling** | Eliminates race conditions |
|
|
| **5s wait window** | Handles slow API responses |
|
|
| **Cookie inspection** | Catches tokens stored in cookies |
|
|
| **4th selector variation** | Higher field detection rate |
|
|
|
|
---
|
|
|
|
**STATUS**: ✅ **READY FOR VALIDATION**
|
|
|
|
**The test suite now has MAXIMUM DEBUG VISIBILITY.** 🔍
|
|
|
|
**Run `npm run test:e2e` to validate!** 🚀
|