From 68839020bac0753de4d6ec86dcea2b9f00c064ff Mon Sep 17 00:00:00 2001 From: senke Date: Fri, 26 Dec 2025 22:04:38 +0100 Subject: [PATCH] =?UTF-8?q?[FIX]=20E2E:=20S=C3=A9parer=20tests=20authentif?= =?UTF-8?q?i=C3=A9s/non=20authentifi=C3=A9s=20dans=20mvp-integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Créer groupe 'Unauthenticated tests' avec storageState vide - Créer groupe 'Authenticated tests' pour tests nécessitant auth - Corriger indentation des tests 1.1 à 1.5 - Test 1.6 utilise maintenant l'état authentifié du global-setup - Corrige erreurs où LoginPage redirigeait car déjà authentifié --- apps/web/e2e/.auth/user.json | 14 +- apps/web/e2e/mvp-integration.spec.ts | 250 ++++++++++++++------------- 2 files changed, 142 insertions(+), 122 deletions(-) diff --git a/apps/web/e2e/.auth/user.json b/apps/web/e2e/.auth/user.json index 021636563..bc119cb3d 100644 --- a/apps/web/e2e/.auth/user.json +++ b/apps/web/e2e/.auth/user.json @@ -6,15 +6,23 @@ "localStorage": [ { "name": "veza_access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJuYW1lIjoiZTJldXNlciIsInJvbGUiOiJ1c2VyIiwidG9rZW5fdmVyc2lvbiI6MCwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2Njc2OTUzNSwiaWF0IjoxNzY2NzY4NjM1LCJqdGkiOiJkMmJkYTQ2NC05N2ZiLTQ5NjAtYmU0MC1lNjk3NGM5Y2I5N2UifQ.Hj8-sN9JYmIGqFDe2Dv-hKSvb6DOShOb5GlsHFm-Ejs" + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJuYW1lIjoiZTJldXNlciIsInJvbGUiOiJ1c2VyIiwidG9rZW5fdmVyc2lvbiI6MCwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2Njc4Mzg0OSwiaWF0IjoxNzY2NzgyOTQ5LCJqdGkiOiI0M2I4NmM4MC01MWZmLTQwOGEtODQwOS01YzM1NDFiNjdjMDEifQ.vP2LSYsNfZihCm5pbPXpTOYxYP0Y_HqYv4axgd-LT1U" + }, + { + "name": "i18nextLng", + "value": "en" }, { "name": "veza_refresh_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6IiIsInJvbGUiOiIiLCJ0b2tlbl92ZXJzaW9uIjowLCJpc19yZWZyZXNoIjp0cnVlLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsInRva2VuX2ZhbWlseSI6IjMyZDJiOWI3LTNiYjQtNDVmYS1hMjk1LThiM2M1OGU1NTFhYiIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2OTM2MDYzNSwiaWF0IjoxNzY2NzY4NjM1LCJqdGkiOiI4NzRhNjQxZS0xYWZhLTRlZjctODFiYy04YWM3Nzc4OWQ3ODUifQ.B1yOlNC97XfQzKX63PTliH8Km1pzmhWMmCE4ss6ufn4" + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6IiIsInJvbGUiOiIiLCJ0b2tlbl92ZXJzaW9uIjowLCJpc19yZWZyZXNoIjp0cnVlLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsInRva2VuX2ZhbWlseSI6ImY5NWYzMWY2LWNiMTItNDRiOC1iMWE0LTZlZWVlZjVlZGY3YiIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2OTM3NDk0OSwiaWF0IjoxNzY2NzgyOTQ5LCJqdGkiOiJiY2JlM2ExMS1mOGZjLTQ3YWUtOTdjZS05MjU4MTYzOTExOTgifQ.jXfWPxE1Orn7_BD1akRb84LUozkKUIPIV76xs39aJ2k" + }, + { + "name": "ui-storage", + "value": "{\"state\":{\"theme\":\"system\",\"language\":\"en\",\"sidebarOpen\":true},\"version\":0}" }, { "name": "auth-storage", - "value": "{\"state\":{\"isAuthenticated\":true,\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJuYW1lIjoiZTJldXNlciIsInJvbGUiOiJ1c2VyIiwidG9rZW5fdmVyc2lvbiI6MCwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2Njc2OTUzNSwiaWF0IjoxNzY2NzY4NjM1LCJqdGkiOiJkMmJkYTQ2NC05N2ZiLTQ5NjAtYmU0MC1lNjk3NGM5Y2I5N2UifQ.Hj8-sN9JYmIGqFDe2Dv-hKSvb6DOShOb5GlsHFm-Ejs\",\"refreshToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6IiIsInJvbGUiOiIiLCJ0b2tlbl92ZXJzaW9uIjowLCJpc19yZWZyZXNoIjp0cnVlLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsInRva2VuX2ZhbWlseSI6IjMyZDJiOWI3LTNiYjQtNDVmYS1hMjk1LThiM2M1OGU1NTFhYiIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2OTM2MDYzNSwiaWF0IjoxNzY2NzY4NjM1LCJqdGkiOiI4NzRhNjQxZS0xYWZhLTRlZjctODFiYy04YWM3Nzc4OWQ3ODUifQ.B1yOlNC97XfQzKX63PTliH8Km1pzmhWMmCE4ss6ufn4\"}}" + "value": "{\"state\":{\"isAuthenticated\":true,\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJuYW1lIjoiZTJldXNlciIsInJvbGUiOiJ1c2VyIiwidG9rZW5fdmVyc2lvbiI6MCwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2Njc4Mzg0OSwiaWF0IjoxNzY2NzgyOTQ5LCJqdGkiOiI0M2I4NmM4MC01MWZmLTQwOGEtODQwOS01YzM1NDFiNjdjMDEifQ.vP2LSYsNfZihCm5pbPXpTOYxYP0Y_HqYv4axgd-LT1U\",\"refreshToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkM2U1ZjhmOC02MDcxLTRmZDQtYWVhMi05ZmZkMzU0YmVmZDkiLCJlbWFpbCI6IiIsInJvbGUiOiIiLCJ0b2tlbl92ZXJzaW9uIjowLCJpc19yZWZyZXNoIjp0cnVlLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsInRva2VuX2ZhbWlseSI6ImY5NWYzMWY2LWNiMTItNDRiOC1iMWE0LTZlZWVlZjVlZGY3YiIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2OTM3NDk0OSwiaWF0IjoxNzY2NzgyOTQ5LCJqdGkiOiJiY2JlM2ExMS1mOGZjLTQ3YWUtOTdjZS05MjU4MTYzOTExOTgifQ.jXfWPxE1Orn7_BD1akRb84LUozkKUIPIV76xs39aJ2k\"}}" } ] } diff --git a/apps/web/e2e/mvp-integration.spec.ts b/apps/web/e2e/mvp-integration.spec.ts index e998d0d23..3a462a4a1 100644 --- a/apps/web/e2e/mvp-integration.spec.ts +++ b/apps/web/e2e/mvp-integration.spec.ts @@ -49,136 +49,148 @@ test.describe('MVP Integration Tests - Exhaustifs', () => { let refreshToken: string | null = null; test.describe('1. Authentication Flow', () => { - - test('1.1 - Login page loads correctly', async ({ page }) => { - await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); + // Tests that require unauthenticated state + test.describe('Unauthenticated tests', () => { + // Reset storage state to ensure we start unauthenticated + test.use({ storageState: { cookies: [], origins: [] } }); - // Vérifier que la page charge - await expect(page).toHaveTitle(/login|connexion|veza/i); - - // Vérifier les éléments du formulaire - await expect(page.locator('input[type="email"], input[name="email"]')).toBeVisible(); - await expect(page.locator('input[type="password"]')).toBeVisible(); - await expect(page.locator('button[type="submit"]')).toBeVisible(); - - // Pas d'erreurs console - const errors: string[] = []; - page.on('console', msg => { - if (msg.type() === 'error') errors.push(msg.text()); + test('1.1 - Login page loads correctly', async ({ page }) => { + await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); + + // Vérifier que la page charge + await expect(page).toHaveTitle(/login|connexion|veza/i); + + // Vérifier les éléments du formulaire + await expect(page.locator('input[type="email"], input[name="email"]')).toBeVisible(); + await expect(page.locator('input[type="password"]')).toBeVisible(); + await expect(page.locator('button[type="submit"]')).toBeVisible(); + + // Pas d'erreurs console + const errors: string[] = []; + page.on('console', msg => { + if (msg.type() === 'error') errors.push(msg.text()); + }); + await page.waitForTimeout(1000); + const realErrors = errors.filter(e => + !e.includes('favicon') && + !e.includes('ResizeObserver') && + !e.includes('net::ERR') + ); + expect(realErrors).toHaveLength(0); }); - await page.waitForTimeout(1000); - const realErrors = errors.filter(e => - !e.includes('favicon') && - !e.includes('ResizeObserver') && - !e.includes('net::ERR') - ); - expect(realErrors).toHaveLength(0); - }); - test('1.2 - Register page loads correctly', async ({ page }) => { + test('1.2 - Register page loads correctly', async ({ page }) => { await page.goto(`${TEST_CONFIG.FRONTEND_URL}/register`); - await expect(page.locator('input[type="email"], input[name="email"]')).toBeVisible(); - await expect(page.locator('input[name="username"]')).toBeVisible(); - await expect(page.locator('input[type="password"]')).toBeVisible(); - - // Vérifier lien vers login - const loginLink = page.locator('a[href*="login"], a:has-text("Login"), a:has-text("Connexion")'); - const loginLinkVisible = await loginLink.first().isVisible().catch(() => false); - // Ne pas échouer si le lien n'est pas visible (peut être dans un menu) - }); - - test('1.3 - Can register new user', async ({ page }) => { - await page.goto(`${TEST_CONFIG.FRONTEND_URL}/register`); - - // Remplir le formulaire - await page.fill('input[type="email"], input[name="email"]', TEST_USER.email); - await page.fill('input[name="username"]', TEST_USER.username); - await page.fill('input[type="password"]', TEST_USER.password); - - // Si champ confirmation - const confirmField = page.locator('input[name="password_confirmation"], input[name="confirmPassword"], input[name="passwordConfirm"]'); - if (await confirmField.isVisible().catch(() => false)) { - await confirmField.fill(TEST_USER.password); - } - - // Submit - await page.click('button[type="submit"]'); - - // Attendre redirection ou message succès - await page.waitForURL(/\/(login|dashboard|home)/, { timeout: 15000 }).catch(() => {}); - - // Vérifier pas d'erreur visible - const errorVisible = await page.locator('.error, [role="alert"]').isVisible().catch(() => false); - if (errorVisible) { - const errorText = await page.locator('.error, [role="alert"]').textContent(); - console.log('Registration error:', errorText); - // Ne pas échouer immédiatement - peut être un message d'info - } - - // Vérifier que l'utilisateur est créé (via API si nécessaire) - // Pour l'instant, on considère que si on arrive ici sans erreur, c'est OK - }); - - test('1.4 - Can login with registered user', async ({ page }) => { - await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); - - await page.fill('input[type="email"], input[name="email"]', TEST_USER.email); - await page.fill('input[type="password"]', TEST_USER.password); - await page.click('button[type="submit"]'); - - // Attendre redirection vers dashboard - await page.waitForURL(/\/(dashboard|home|app)/, { timeout: 15000 }); - - // Vérifier que l'utilisateur est connecté - const loggedIn = await page.locator('[data-testid="user-menu"], .user-avatar, .logout-button, nav[role="navigation"]').isVisible().catch(() => false); - - // Vérifier localStorage - const token = await page.evaluate(() => - localStorage.getItem('access_token') || - localStorage.getItem('accessToken') || - localStorage.getItem('veza_access_token') - ); - expect(token || loggedIn).toBeTruthy(); - }); - - test('1.5 - Protected route redirects when not logged in', async ({ page }) => { - // Clear any existing auth - await page.goto(`${TEST_CONFIG.FRONTEND_URL}`); - await page.evaluate(() => { - localStorage.clear(); - sessionStorage.clear(); + await expect(page.locator('input[type="email"], input[name="email"]')).toBeVisible(); + await expect(page.locator('input[name="username"]')).toBeVisible(); + await expect(page.locator('input[type="password"]')).toBeVisible(); + + // Vérifier lien vers login + const loginLink = page.locator('a[href*="login"], a:has-text("Login"), a:has-text("Connexion")'); + const loginLinkVisible = await loginLink.first().isVisible().catch(() => false); + // Ne pas échouer si le lien n'est pas visible (peut être dans un menu) }); - - // Try to access protected route - await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`); - - // Should redirect to login - await page.waitForURL(/\/login/, { timeout: 5000 }).catch(() => {}); - const currentUrl = page.url(); - expect(currentUrl).toContain('login'); - }); - test('1.6 - Can logout', async ({ page }) => { - // Login first - await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); - await page.fill('input[type="email"], input[name="email"]', TEST_USER.email); - await page.fill('input[type="password"]', TEST_USER.password); - await page.click('button[type="submit"]'); - await page.waitForURL(/\/(dashboard|home|app)/, { timeout: 15000 }); + test('1.3 - Can register new user', async ({ page }) => { + await page.goto(`${TEST_CONFIG.FRONTEND_URL}/register`); - // Click logout - const logoutButton = page.locator('button:has-text("Logout"), button:has-text("Déconnexion"), [data-testid="logout"]'); - if (await logoutButton.isVisible().catch(() => false)) { - await logoutButton.click(); + // Remplir le formulaire + await page.fill('input[type="email"], input[name="email"]', TEST_USER.email); + await page.fill('input[name="username"]', TEST_USER.username); + await page.fill('input[type="password"]', TEST_USER.password); + + // Si champ confirmation + const confirmField = page.locator('input[name="password_confirmation"], input[name="confirmPassword"], input[name="passwordConfirm"]'); + if (await confirmField.isVisible().catch(() => false)) { + await confirmField.fill(TEST_USER.password); + } + + // Submit + await page.click('button[type="submit"]'); + + // Attendre redirection ou message succès + await page.waitForURL(/\/(login|dashboard|home)/, { timeout: 15000 }).catch(() => {}); + + // Vérifier pas d'erreur visible + const errorVisible = await page.locator('.error, [role="alert"]').isVisible().catch(() => false); + if (errorVisible) { + const errorText = await page.locator('.error, [role="alert"]').textContent(); + console.log('Registration error:', errorText); + // Ne pas échouer immédiatement - peut être un message d'info + } + + // Vérifier que l'utilisateur est créé (via API si nécessaire) + // Pour l'instant, on considère que si on arrive ici sans erreur, c'est OK + }); + + test('1.4 - Can login with registered user', async ({ page }) => { + await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); + + await page.fill('input[type="email"], input[name="email"]', TEST_USER.email); + await page.fill('input[type="password"]', TEST_USER.password); + await page.click('button[type="submit"]'); + + // Attendre redirection vers dashboard + await page.waitForURL(/\/(dashboard|home|app)/, { timeout: 15000 }); + + // Vérifier que l'utilisateur est connecté + const loggedIn = await page.locator('[data-testid="user-menu"], .user-avatar, .logout-button, nav[role="navigation"]').isVisible().catch(() => false); + + // Vérifier localStorage + const token = await page.evaluate(() => + localStorage.getItem('access_token') || + localStorage.getItem('accessToken') || + localStorage.getItem('veza_access_token') + ); + expect(token || loggedIn).toBeTruthy(); + }); + + test('1.5 - Protected route redirects when not logged in', async ({ page }) => { + // Clear any existing auth + await page.goto(`${TEST_CONFIG.FRONTEND_URL}`); + await page.evaluate(() => { + localStorage.clear(); + sessionStorage.clear(); + }); + + // Try to access protected route + await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`); // Should redirect to login - await page.waitForURL(/\/(login|home|\/)/, { timeout: 5000 }); + await page.waitForURL(/\/login/, { timeout: 5000 }).catch(() => {}); + const currentUrl = page.url(); + expect(currentUrl).toContain('login'); + }); + }); + + // Tests that require authenticated state + test.describe('Authenticated tests', () => { + test('1.6 - Can logout', async ({ page }) => { + // Login first (if not already authenticated from storageState) + await page.goto(`${TEST_CONFIG.FRONTEND_URL}/dashboard`); + const isOnDashboard = page.url().includes('/dashboard'); + if (!isOnDashboard) { + await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); + await page.fill('input[type="email"], input[name="email"]', TEST_USER.email); + await page.fill('input[type="password"]', TEST_USER.password); + await page.click('button[type="submit"]'); + await page.waitForURL(/\/(dashboard|home|app)/, { timeout: 15000 }); + } - // Token should be cleared - const token = await page.evaluate(() => localStorage.getItem('access_token')); - expect(token).toBeFalsy(); - } + // Click logout + const logoutButton = page.locator('button:has-text("Logout"), button:has-text("Déconnexion"), [data-testid="logout"]'); + if (await logoutButton.isVisible().catch(() => false)) { + await logoutButton.click(); + + // Should redirect to login + await page.waitForURL(/\/(login|home|\/)/, { timeout: 5000 }); + + // Token should be cleared + const token = await page.evaluate(() => localStorage.getItem('access_token')); + expect(token).toBeFalsy(); + } + }); }); });