import { test, expect } from '@playwright/test'; import { switchTab, waitForHtmx, collectJsErrors } from './helpers'; /** * User Journey E2E Tests * * Tests authenticated user flows. Auth is handled by auth.setup.ts + storageState. */ test.describe('User Journey E2E', () => { test('should browse homepage without JS errors', async ({ page }) => { const jsErrors = collectJsErrors(page); await page.goto('/web'); // Main content should be visible await expect(page.locator('#main-content')).toBeVisible(); await expect(page.locator('header h1')).toContainText('Ohm Stream'); // At least one tab visible await expect(page.locator('.tab').first()).toBeVisible(); // Authenticated user info should be visible await expect(page.locator('#userInfo')).toBeVisible(); expect(jsErrors).toHaveLength(0); }); test('should search for anime', async ({ page }) => { // Mock the anime search API to return deterministic HTML await page.route('/api/anime/search?**', async (route) => { await route.fulfill({ status: 200, contentType: 'text/html', body: `

Naruto Shippuden

Anime-Sama

Boruto: Naruto Next Generations

Neko-Sama

`, }); }); await page.goto('/web'); await switchTab(page, 'Anime'); await page.locator('#tab-anime').waitFor({ state: 'visible', timeout: 5000 }); await page.fill('#animeSearchInput', 'Naruto'); // Click search button to trigger submit await page.click('#tab-anime button[type="submit"]'); // Wait for results to appear await page.locator('#animeSearchResults .sr-card').first().waitFor({ state: 'visible', timeout: 10000 }); // Results container should be visible and contain mocked data await expect(page.locator('#animeSearchResults')).toBeVisible(); await expect(page.locator('#animeSearchResults')).toContainText('Naruto Shippuden'); }); test('should update settings', async ({ page }) => { await page.goto('/web'); await switchTab(page, 'Paramètres'); // Wait for settings form loaded via HTMX await page.locator('#default_lang').waitFor({ state: 'visible', timeout: 15000 }); await page.selectOption('#default_lang', 'vf'); const [response] = await Promise.all([ page.waitForResponse( (resp) => resp.url().includes('/api/settings') && resp.request().method() === 'PATCH' ), page.locator('button:has-text("Enregistrer les preferences")').click(), ]); expect(response.status()).toBe(200); // Verify the setting was updated in the UI await expect(page.locator('#default_lang')).toHaveValue('vf'); }); test('should logout successfully', async ({ page }) => { await page.goto('/web'); const [response] = await Promise.all([ page.waitForResponse((resp) => resp.url().includes('/api/auth/logout')), page.locator('#userInfo button:has-text("Déconnexion")').click(), ]); expect(response.status()).toBeLessThan(400); // Should redirect to login await page.waitForURL('**/login**', { timeout: 10000 }); // The auth token must be cleared from localStorage const token = await page.evaluate(() => localStorage.getItem('auth_token')); expect(token).toBeNull(); }); });