import { test } from '@playwright/test'; /** * Diagnostic Test - Full Stack Compatibility Check * * Ce test vérifie l'intégration Frontend-Backend après le refactoring de l'authentification. * Il capture toutes les erreurs réseau, console, CORS et vérifie le stockage des tokens. */ // Configuration const FRONTEND_URL = process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000'; const TEST_EMAIL = process.env.TEST_EMAIL || 'user@example.com'; const TEST_PASSWORD = process.env.TEST_PASSWORD || 'password123'; // Collecteurs d'erreurs interface DiagnosticReport { networkErrors: Array<{ url: string; status: number; method: string; error: string; }>; consoleErrors: Array<{ type: string; message: string; stack?: string; }>; corsErrors: Array<{ url: string; reason: string; }>; localStorage: Record; navigationSuccess: boolean; finalUrl: string; formVisible: boolean; errorMessage?: string; } test.describe('Full Stack Compatibility Diagnostic', () => { let report: DiagnosticReport; test.beforeEach(() => { report = { networkErrors: [], consoleErrors: [], corsErrors: [], localStorage: {}, navigationSuccess: false, finalUrl: '', formVisible: false, }; }); test('Login Flow - Complete Diagnostic', async ({ page }) => { // Setup: Écouter les erreurs console AVANT toute navigation const consoleMessages: Array<{ type: string; text: string }> = []; page.on('console', (msg) => { const type = msg.type(); const text = msg.text(); consoleMessages.push({ type, text }); if (type === 'error' || type === 'warning') { report.consoleErrors.push({ type, message: text, stack: msg.location()?.url, }); console.log(`🔴 [CONSOLE ${type.toUpperCase()}] ${text}`); } }); // Setup: Écouter les erreurs de page (uncaught exceptions) page.on('pageerror', (error) => { report.consoleErrors.push({ type: 'pageerror', message: error.message, stack: error.stack, }); console.log(`🔴 [PAGE ERROR] ${error.message}`); }); // Setup: Écouter les requêtes réseau échouées page.on('response', (response) => { const status = response.status(); const url = response.url(); // Capturer les erreurs 4xx et 5xx if (status >= 400) { report.networkErrors.push({ url, status, method: response.request().method(), error: `HTTP ${status}`, }); // Détecter les erreurs CORS potentielles if (status === 0 || url.includes('localhost:8080')) { const headers = response.headers(); if (!headers['access-control-allow-origin']) { report.corsErrors.push({ url, reason: 'Missing CORS headers', }); } } } }); // Setup: Écouter les requêtes échouées (network errors) page.on('requestfailed', (request) => { const failure = request.failure(); if (failure) { report.networkErrors.push({ url: request.url(), status: 0, method: request.method(), error: failure.errorText || 'Network error', }); // Détecter les erreurs CORS if (failure.errorText?.includes('CORS') || failure.errorText?.includes('Access-Control')) { report.corsErrors.push({ url: request.url(), reason: failure.errorText, }); } } }); // Étape 1: Aller sur la page de login console.log('🔍 [DIAGNOSTIC] Navigation vers /login...'); try { await page.goto(`${FRONTEND_URL}/login`, { waitUntil: 'domcontentloaded', timeout: 30000 }); } catch (error) { console.error('❌ [DIAGNOSTIC] Erreur lors de la navigation:', error); report.finalUrl = page.url(); return; } // Attendre que la page soit chargée await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => { console.warn('⚠️ [DIAGNOSTIC] Timeout sur networkidle, continuation...'); }); // Prendre une capture d'écran pour debug await page.screenshot({ path: 'e2e/diagnostic-login-page.png', fullPage: true }); // Attendre que le formulaire soit chargé (plusieurs stratégies) try { // Essayer d'attendre un élément de formulaire await page.waitForSelector('form, input[type="email"], input[type="password"]', { timeout: 10000 }); } catch { console.warn('⚠️ [DIAGNOSTIC] Timeout en attendant le formulaire'); } // Attendre un peu pour que React hydrate await page.waitForTimeout(3000); // Vérifier que le formulaire est visible avec plusieurs sélecteurs possibles const emailInput = page.locator('input[type="email"], input[name="email"], input[placeholder*="email" i]').first(); const passwordInput = page.locator('input[type="password"], input[name="password"]').first(); const submitButton = page.locator('button[type="submit"], button:has-text("connecter"), button:has-text("login"), button:has-text("Se connecter")').first(); // Vérifier aussi avec des sélecteurs plus génériques const allInputs = await page.locator('input').count(); const allButtons = await page.locator('button').count(); console.log('📄 [DIAGNOSTIC] Nombre d\'inputs sur la page:', allInputs); console.log('📄 [DIAGNOSTIC] Nombre de boutons sur la page:', allButtons); const emailVisible = await emailInput.isVisible().catch(() => false); const passwordVisible = await passwordInput.isVisible().catch(() => false); const submitVisible = await submitButton.isVisible().catch(() => false); report.formVisible = emailVisible && passwordVisible; // Logger le contenu de la page pour debug const pageContent = await page.content(); const hasForm = pageContent.includes('form') || pageContent.includes('email') || pageContent.includes('password'); console.log('📄 [DIAGNOSTIC] Page title:', await page.title()); console.log('📄 [DIAGNOSTIC] URL actuelle:', page.url()); console.log('📄 [DIAGNOSTIC] Email input visible:', emailVisible); console.log('📄 [DIAGNOSTIC] Password input visible:', passwordVisible); console.log('📄 [DIAGNOSTIC] Submit button visible:', submitVisible); console.log('📄 [DIAGNOSTIC] Page contient "form":', hasForm); if (!report.formVisible) { console.error('❌ [DIAGNOSTIC] Le formulaire de login n\'est pas visible'); // Logger le HTML pour debug const bodyText = await page.locator('body').textContent(); console.log('📄 [DIAGNOSTIC] Contenu de la page (premiers 500 chars):', bodyText?.substring(0, 500)); // Logger toutes les erreurs console capturées if (consoleMessages.length > 0) { console.log('\n🔴 [DIAGNOSTIC] Messages console capturés:'); consoleMessages.forEach((msg) => { console.log(` [${msg.type}] ${msg.text}`); }); } // Vérifier s'il y a des scripts qui ont échoué à charger const failedResources = await page.evaluate(() => { const resources: Array<{ url: string; error: string }> = []; const scripts = document.querySelectorAll('script[src]'); scripts.forEach((script) => { const src = script.getAttribute('src'); if (src && !(script as any).loaded) { resources.push({ url: src, error: 'Script not loaded' }); } }); return resources; }); if (failedResources.length > 0) { console.log('🔴 [DIAGNOSTIC] Scripts non chargés:', failedResources); } // Sauvegarder le rapport même en cas d'échec await page.evaluate((report) => { (window as any).__diagnosticReport = report; }, report); return; } console.log('✅ [DIAGNOSTIC] Formulaire de login visible'); // Étape 2: Remplir le formulaire console.log('🔍 [DIAGNOSTIC] Remplissage du formulaire...'); await emailInput.fill(TEST_EMAIL); await passwordInput.fill(TEST_PASSWORD); // Vérifier si checkbox "remember me" existe const rememberMeCheckbox = page.locator('input[type="checkbox"][id*="remember"]'); if (await rememberMeCheckbox.count() > 0) { await rememberMeCheckbox.check(); } // Étape 3: Cliquer sur le bouton de connexion console.log('🔍 [DIAGNOSTIC] Clic sur le bouton de connexion...'); // Attendre la navigation ou un message d'erreur const navigationPromise = page.waitForURL( (url) => url.pathname === '/dashboard' || url.pathname === '/', { timeout: 10000 } ).catch(() => null); const errorMessagePromise = page .waitForSelector('.bg-red-100, [role="alert"], .text-red-700', { timeout: 5000 }) .catch(() => null); await submitButton.click(); // Attendre soit la navigation, soit un message d'erreur const navigationResult = await navigationPromise; const errorElement = await errorMessagePromise; if (navigationResult) { report.navigationSuccess = true; report.finalUrl = page.url(); console.log('✅ [DIAGNOSTIC] Navigation réussie vers:', report.finalUrl); } else if (errorElement) { report.errorMessage = await errorElement.textContent() || 'Erreur inconnue'; console.log('❌ [DIAGNOSTIC] Message d\'erreur détecté:', report.errorMessage); } else { // Attendre un peu plus pour voir si quelque chose se passe await page.waitForTimeout(2000); report.finalUrl = page.url(); console.log('⚠️ [DIAGNOSTIC] Pas de navigation ni d\'erreur visible. URL actuelle:', report.finalUrl); } // Étape 4: Vérifier le localStorage console.log('🔍 [DIAGNOSTIC] Vérification du localStorage...'); const localStorageItems = await page.evaluate(() => { const items: Record = {}; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key) { items[key] = localStorage.getItem(key) || ''; } } return items; }); report.localStorage = localStorageItems; // Vérifier spécifiquement les tokens const hasAccessToken = 'access_token' in localStorageItems || 'veza_access_token' in localStorageItems || localStorageItems['access_token'] !== undefined || localStorageItems['veza_access_token'] !== undefined; console.log('📦 [DIAGNOSTIC] LocalStorage:', Object.keys(localStorageItems)); console.log(hasAccessToken ? '✅ [DIAGNOSTIC] Token d\'accès présent' : '❌ [DIAGNOSTIC] Token d\'accès absent'); // Générer le rapport console.log('\n📊 [DIAGNOSTIC] === RAPPORT DE DIAGNOSTIC ==='); console.log('Erreurs réseau:', report.networkErrors.length); console.log('Erreurs console:', report.consoleErrors.length); console.log('Erreurs CORS:', report.corsErrors.length); console.log('Navigation réussie:', report.navigationSuccess); console.log('Token présent:', hasAccessToken); // Afficher les détails des erreurs if (report.networkErrors.length > 0) { console.log('\n🔴 Erreurs réseau:'); report.networkErrors.forEach((err) => { console.log(` - ${err.method} ${err.url}: ${err.error}`); }); } if (report.consoleErrors.length > 0) { console.log('\n🔴 Erreurs console:'); report.consoleErrors.forEach((err) => { console.log(` - [${err.type}] ${err.message}`); }); } if (report.corsErrors.length > 0) { console.log('\n🟠 Erreurs CORS:'); report.corsErrors.forEach((err) => { console.log(` - ${err.url}: ${err.reason}`); }); } // Sauvegarder le rapport pour l'analyse await page.evaluate((report) => { (window as any).__diagnosticReport = report; }, report); }); test.afterEach(async ({ page }) => { // Récupérer le rapport depuis la page si disponible const savedReport = await page.evaluate(() => { return (window as any).__diagnosticReport; }).catch(() => null); if (savedReport) { report = savedReport; } }); });