veza/apps/web/e2e/diagnostic.spec.ts
2025-12-17 08:07:35 -05:00

346 lines
12 KiB
TypeScript

import { test, expect, type Page } 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 BASE_URL = process.env.VITE_API_URL || 'http://localhost:8080/api/v1';
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<string, string>;
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, context }) => {
// 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 (e) {
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 localStorageData = await context.storageState();
const localStorageItems = await page.evaluate(() => {
const items: Record<string, string> = {};
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;
}
});
});