/** * Critical User Flows E2E Tests * FE-TEST-012: Test login, upload, playlist creation end-to-end * * This test suite covers the most critical user journeys: * 1. User login flow * 2. Track upload flow * 3. Playlist creation flow * * These tests ensure that the core functionality works together seamlessly. */ import { test, expect } from '@playwright/test'; import { TEST_CONFIG, TEST_USERS, forceSubmitForm, fillField, waitForToast, setupErrorCapture, openModal, } from './utils/test-helpers'; import { createMockMP3Buffer } from './fixtures/file-helpers'; test.describe('Critical User Flows - End-to-End', () => { // Reset storage state for these tests to ensure we start unauthenticated test.use({ storageState: { cookies: [], origins: [] } }); let consoleErrors: string[] = []; let networkErrors: Array<{ url: string; status: number; method: string }> = []; test.beforeEach(async ({ page }) => { const errorCapture = setupErrorCapture(page); consoleErrors = errorCapture.consoleErrors; networkErrors = errorCapture.networkErrors; }); /** * CRITICAL FLOW 1: Complete user journey from login to playlist creation * * This test simulates a real user scenario: * 1. User logs in * 2. User uploads a track * 3. User creates a playlist * 4. User adds the uploaded track to the playlist */ test('Complete user journey: Login → Upload → Create Playlist → Add Track', async ({ page }) => { test.setTimeout(180000); // 3 minutes for complete flow console.log('🚀 [CRITICAL FLOW] Starting complete user journey test...'); // ========== STEP 1: LOGIN ========== console.log('📝 [CRITICAL FLOW] Step 1: User login...'); await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); await page.waitForLoadState('domcontentloaded'); // Wait for form to be ready await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => { }); await page.waitForTimeout(500); // Fill login form await fillField( page, 'input[type="email"], input[name="email"]', TEST_USERS.default.email ); await fillField( page, 'input[type="password"], input[name="password"]', TEST_USERS.default.password ); // Submit form and wait for navigation const navigationPromise = page.waitForURL( (url) => url.pathname === '/dashboard' || url.pathname === '/', { timeout: 15000 } ); await forceSubmitForm(page, 'form'); await navigationPromise; // Verify user is authenticated await expect(page).toHaveURL(/\/(dashboard|$)/); await expect(page.locator('nav[role="navigation"], aside[role="navigation"]')).toBeVisible({ timeout: 10000, }); // Wait for auth state to be persisted await page.waitForTimeout(1000); const isAuthenticated = await page.evaluate(() => { try { const authStorage = localStorage.getItem('auth-storage'); if (authStorage) { const parsed = JSON.parse(authStorage); return parsed.state?.isAuthenticated === true; } } catch { return false; } return false; }); expect(isAuthenticated).toBe(true); console.log('✅ [CRITICAL FLOW] Step 1: Login successful'); // ========== STEP 2: UPLOAD TRACK ========== console.log('📤 [CRITICAL FLOW] Step 2: Uploading track...'); // Navigate to library await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`); await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => { console.warn('⚠️ [CRITICAL FLOW] Timeout on networkidle, continuing...'); }); // Open upload modal await openModal(page, /upload/i); // Select and upload file const fileInput = page.locator('input[type="file"][accept*="audio"]').first(); await expect(fileInput).toBeAttached({ timeout: 5000 }); const validMp3Buffer = createMockMP3Buffer(); await fileInput.setInputFiles({ name: 'critical-flow-test.mp3', mimeType: 'audio/mpeg', buffer: validMp3Buffer, }); console.log('✅ [CRITICAL FLOW] File selected'); await page.waitForTimeout(1000); // Check for rejection errors const errorMessage = page.locator('[data-testid="upload-error"], [role="alert"]:has-text("Format")').first(); const hasRejectionError = await errorMessage.isVisible().catch(() => false); if (hasRejectionError) { const errorText = await errorMessage.textContent(); throw new Error(`File was rejected: ${errorText}`); } // Fill metadata await fillField(page, 'input[id="title"], input[name="title"]', 'Critical Flow Test Track'); await fillField(page, 'input[id="artist"], input[name="artist"]', 'Test Artist'); // Submit upload const uploadButton = page.locator('button:has-text("Upload"), button:has-text("Envoyer"), button[type="submit"]').first(); await expect(uploadButton).toBeVisible({ timeout: 5000 }); await uploadButton.click(); // Wait for upload success await waitForToast(page, /success|succès|uploaded|téléchargé/i, { timeout: 60000 }); console.log('✅ [CRITICAL FLOW] Step 2: Track uploaded successfully'); // Close modal if still open const closeButton = page.locator('button[aria-label*="close"], button[aria-label*="fermer"]').first(); if (await closeButton.isVisible().catch(() => false)) { await closeButton.click(); await page.waitForTimeout(500); } // ========== STEP 3: CREATE PLAYLIST ========== console.log('📋 [CRITICAL FLOW] Step 3: Creating playlist...'); // Navigate to playlists await page.goto(`${TEST_CONFIG.FRONTEND_URL}/playlists`, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(500); await page.waitForURL(/\/playlists/, { timeout: 15000 }).catch(() => { }); // Wait for page to load try { await Promise.race([ page.locator('h1:has-text("Playlist"), h1:has-text("Playlists")').first().waitFor({ state: 'visible', timeout: 10000 }), page.locator('[data-testid="playlists-page"]').first().waitFor({ state: 'visible', timeout: 10000 }), ]); } catch { console.warn('⚠️ [CRITICAL FLOW] Page load check timeout, continuing...'); } // Open create playlist modal await openModal(page, /create|créer|nouvelle/i); // Fill playlist form await fillField( page, 'input[id="title"], input[name="title"]', 'Critical Flow Test Playlist' ); await fillField( page, 'textarea[id="description"], textarea[name="description"]', 'Playlist created during critical flow test' ); // Submit playlist creation const createButton = page.locator('button:has-text("Créer"), button:has-text("Create"), button[type="submit"]').first(); await expect(createButton).toBeVisible({ timeout: 5000 }); await createButton.click(); // Wait for success await waitForToast(page, /success|succès|created|créé/i, { timeout: 15000 }); console.log('✅ [CRITICAL FLOW] Step 3: Playlist created successfully'); // ========== STEP 4: VERIFY PLAYLIST EXISTS ========== console.log('🔍 [CRITICAL FLOW] Step 4: Verifying playlist exists...'); // Wait for modal to close await page.waitForTimeout(1000); // Check that playlist appears in the list const playlistTitle = page.locator('text="Critical Flow Test Playlist"').first(); await expect(playlistTitle).toBeVisible({ timeout: 10000 }); console.log('✅ [CRITICAL FLOW] Step 4: Playlist verified in list'); // ========== VERIFY NO ERRORS ========== console.log('🔍 [CRITICAL FLOW] Verifying no errors occurred...'); // Check for console errors if (consoleErrors.length > 0) { console.warn('⚠️ [CRITICAL FLOW] Console errors detected:', consoleErrors); } // Check for network errors (excluding expected ones) const criticalNetworkErrors = networkErrors.filter( (error) => error.status >= 500 || (error.status >= 400 && !error.url.includes('favicon')) ); if (criticalNetworkErrors.length > 0) { console.warn('⚠️ [CRITICAL FLOW] Network errors detected:', criticalNetworkErrors); } console.log('✅ [CRITICAL FLOW] Complete user journey test passed!'); }); /** * CRITICAL FLOW 2: Login and immediate playlist creation * * Tests the scenario where a user logs in and immediately creates a playlist * without uploading anything first. */ test('Login → Create Playlist (no upload)', async ({ page }) => { test.setTimeout(90000); // 90 seconds console.log('🚀 [CRITICAL FLOW] Starting login → playlist creation test...'); // Login await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => { }); await fillField( page, 'input[type="email"], input[name="email"]', TEST_USERS.default.email ); await fillField( page, 'input[type="password"], input[name="password"]', TEST_USERS.default.password ); const navigationPromise = page.waitForURL( (url) => url.pathname === '/dashboard' || url.pathname === '/', { timeout: 15000 } ); await forceSubmitForm(page, 'form'); await navigationPromise; await expect(page).toHaveURL(/\/(dashboard|$)/); console.log('✅ [CRITICAL FLOW] Login successful'); // Navigate to playlists await page.goto(`${TEST_CONFIG.FRONTEND_URL}/playlists`, { waitUntil: 'domcontentloaded' }); await page.waitForTimeout(1000); // Create playlist await openModal(page, /create|créer|nouvelle/i); await fillField( page, 'input[id="title"], input[name="title"]', 'Quick Test Playlist' ); const createButton = page.locator('button:has-text("Créer"), button:has-text("Create"), button[type="submit"]').first(); await expect(createButton).toBeVisible({ timeout: 5000 }); await createButton.click(); await waitForToast(page, /success|succès|created|créé/i, { timeout: 15000 }); console.log('✅ [CRITICAL FLOW] Playlist created successfully'); }); /** * CRITICAL FLOW 3: Login and upload only * * Tests the scenario where a user logs in and uploads a track * without creating a playlist. */ test('Login → Upload Track (no playlist)', async ({ page }) => { test.setTimeout(120000); // 2 minutes console.log('🚀 [CRITICAL FLOW] Starting login → upload test...'); // Login await page.goto(`${TEST_CONFIG.FRONTEND_URL}/login`); await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => { }); await fillField( page, 'input[type="email"], input[name="email"]', TEST_USERS.default.email ); await fillField( page, 'input[type="password"], input[name="password"]', TEST_USERS.default.password ); const navigationPromise = page.waitForURL( (url) => url.pathname === '/dashboard' || url.pathname === '/', { timeout: 15000 } ); await forceSubmitForm(page, 'form'); await navigationPromise; await expect(page).toHaveURL(/\/(dashboard|$)/); console.log('✅ [CRITICAL FLOW] Login successful'); // Navigate to library and upload await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`); await page.waitForLoadState('domcontentloaded'); await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => { }); await openModal(page, /upload/i); const fileInput = page.locator('input[type="file"][accept*="audio"]').first(); await expect(fileInput).toBeAttached({ timeout: 5000 }); const validMp3Buffer = createMockMP3Buffer(); await fileInput.setInputFiles({ name: 'upload-only-test.mp3', mimeType: 'audio/mpeg', buffer: validMp3Buffer, }); await page.waitForTimeout(1000); await fillField(page, 'input[id="title"], input[name="title"]', 'Upload Only Test'); await fillField(page, 'input[id="artist"], input[name="artist"]', 'Test Artist'); const uploadButton = page.locator('button:has-text("Upload"), button:has-text("Envoyer"), button[type="submit"]').first(); await expect(uploadButton).toBeVisible({ timeout: 5000 }); await uploadButton.click(); await waitForToast(page, /success|succès|uploaded|téléchargé/i, { timeout: 60000 }); console.log('✅ [CRITICAL FLOW] Upload successful'); }); });