import { test, expect } from '@playwright/test' /** * Tests complets pour les Settings UX (Story 11-2) * * Ce fichier teste toutes les fonctionnalités implémentées: * - General Settings: Notifications (Email), Privacy (Analytics) * - Profile Settings: Language, Font Size, Show Recent Notes * - Appearance Settings: Theme Persistence (Light/Dark/Auto) * - SettingsSearch: Functional search with filtering * * Prérequis: * - Être connecté avec un compte utilisateur * - Avoir accès aux pages de settings * - Base de données avec les nouveaux champs (emailNotifications, anonymousAnalytics) */ test.describe('Settings UX - Story 11-2', () => { // Variables pour stocker les credentials let email: string let password: string test.beforeAll(async ({ browser }) => { // Les credentials seront fournis par l'utilisateur console.log('Credentials nécessaires pour exécuter les tests') console.log('Veuillez fournir email et password') }) test.beforeEach(async ({ page }) => { // Se connecter avant chaque test await page.goto('http://localhost:3000/login') await page.fill('input[name="email"]', email) await page.fill('input[name="password"]', password) await page.click('button[type="submit"]') // Attendre la connexion et vérifier qu'on est sur la page d'accueil await page.waitForURL('**/main**') await expect(page).toHaveURL(/\/main/) }) /** * Tests pour General Settings */ test.describe('General Settings', () => { test('devrait afficher la page General Settings', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Vérifier le titre de la page const title = await page.textContent('h1') expect(title).toContain('General') // Vérifier que les sections sont présentes await expect(page.locator('#language')).toBeVisible() await expect(page.locator('#notifications')).toBeVisible() await expect(page.locator('#privacy')).toBeVisible() }) test('devrait avoir le toggle Email Notifications', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Scroll vers la section notifications await page.locator('#notifications').scrollIntoViewIfNeeded() // Vérifier que le toggle est présent const emailToggle = page.getByRole('switch', { name: /email notifications/i }) await expect(emailToggle).toBeVisible() }) test('devrait pouvoir activer/désactiver Email Notifications', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Scroll vers la section notifications await page.locator('#notifications').scrollIntoViewIfNeeded() // Récupérer l'état initial du toggle const emailToggle = page.getByRole('switch', { name: /email notifications/i }) const initialState = await emailToggle.getAttribute('aria-checked') const initialEnabled = initialState === 'true' // Cliquer sur le toggle await emailToggle.click() // Attendre un peu pour l'opération asynchrone await page.waitForTimeout(500) // Vérifier que l'état a changé const newState = await emailToggle.getAttribute('aria-checked') const newEnabled = newState === 'true' expect(newEnabled).toBe(!initialEnabled) // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) }) test('devrait avoir le toggle Anonymous Analytics', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Scroll vers la section privacy await page.locator('#privacy').scrollIntoViewIfNeeded() // Vérifier que le toggle est présent const analyticsToggle = page.getByRole('switch', { name: /anonymous analytics/i }) await expect(analyticsToggle).toBeVisible() }) test('devrait pouvoir activer/désactiver Anonymous Analytics', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Scroll vers la section privacy await page.locator('#privacy').scrollIntoViewIfNeeded() // Récupérer l'état initial du toggle const analyticsToggle = page.getByRole('switch', { name: /anonymous analytics/i }) const initialState = await analyticsToggle.getAttribute('aria-checked') const initialEnabled = initialState === 'true' // Cliquer sur le toggle await analyticsToggle.click() // Attendre un peu pour l'opération asynchrone await page.waitForTimeout(500) // Vérifier que l'état a changé const newState = await analyticsToggle.getAttribute('aria-checked') const newEnabled = newState === 'true' expect(newEnabled).toBe(!initialEnabled) // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) }) test('devrait avoir le composant SettingsSearch', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Vérifier que la barre de recherche est présente const searchInput = page.getByPlaceholder(/search/i) await expect(searchInput).toBeVisible() // Vérifier l'icône de recherche await expect(page.locator('svg').filter({ hasText: '' }).first()).toBeVisible() }) test('devrait filtrer les sections avec SettingsSearch', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') // Cliquer sur la barre de recherche const searchInput = page.getByPlaceholder(/search/i) await searchInput.click() // Taper "notification" await searchInput.fill('notification') // Vérifier que la section notifications est visible await expect(page.locator('#notifications')).toBeVisible() // Vérifier que les autres sections ne sont plus visibles (ou sont filtrées) // Note: Cela dépend de l'implémentation exacte du filtrage }) test('devrait effacer la recherche avec le bouton X', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') const searchInput = page.getByPlaceholder(/search/i) await searchInput.fill('test') // Vérifier que le bouton X apparaît const clearButton = page.getByRole('button', { name: /clear search/i }) await expect(clearButton).toBeVisible() // Cliquer sur le bouton X await clearButton.click() // Vérifier que la recherche est vide await expect(searchInput).toHaveValue('') }) test('devrait effacer la recherche avec la touche Escape', async ({ page }) => { await page.goto('http://localhost:3000/settings/general') const searchInput = page.getByPlaceholder(/search/i) await searchInput.fill('test') // Appuyer sur Escape await page.keyboard.press('Escape') // Vérifier que la recherche est vide await expect(searchInput).toHaveValue('') }) }) /** * Tests pour Profile Settings */ test.describe('Profile Settings', () => { test('devrait afficher la page Profile Settings', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Vérifier le titre de la page const title = await page.textContent('h1') expect(title).toContain('Profile') // Vérifier les sections await expect(page.getByText(/display name/i)).toBeVisible() await expect(page.getByText(/email/i)).toBeVisible() await expect(page.getByText(/language preferences/i)).toBeVisible() await expect(page.getByText(/display settings/i)).toBeVisible() await expect(page.getByText(/change password/i)).toBeVisible() }) test('devrait avoir le sélecteur de langue', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Scroll vers la section language await page.getByText(/language preferences/i).scrollIntoViewIfNeeded() // Vérifier que le sélecteur est présent const languageSelect = page.locator('#language') await expect(languageSelect).toBeVisible() }) test('devrait pouvoir changer la langue', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Scroll vers la section language await page.getByText(/language preferences/i).scrollIntoViewIfNeeded() // Cliquer sur le sélecteur const languageSelect = page.locator('#language') await languageSelect.click() // Sélectionner une langue (ex: français) await page.getByRole('option', { name: /français/i }).click() // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) }) test('devrait avoir le sélecteur de taille de police', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Scroll vers la section display settings await page.getByText(/display settings/i).scrollIntoViewIfNeeded() // Vérifier que le sélecteur est présent const fontSizeSelect = page.locator('#fontSize') await expect(fontSizeSelect).toBeVisible() }) test('devrait pouvoir changer la taille de police', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Scroll vers la section display settings await page.getByText(/display settings/i).scrollIntoViewIfNeeded() // Cliquer sur le sélecteur const fontSizeSelect = page.locator('#fontSize') await fontSizeSelect.click() // Sélectionner une taille (ex: large) await page.getByRole('option', { name: /large/i }).click() // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) // Vérifier que la taille de police a changé (vérifier la variable CSS) const rootFontSize = await page.evaluate(() => { return getComputedStyle(document.documentElement).getPropertyValue('--user-font-size') }) expect(rootFontSize).toBeTruthy() }) test('devrait avoir le toggle Show Recent Notes', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Scroll vers la section display settings await page.getByText(/display settings/i).scrollIntoViewIfNeeded() // Vérifier que le toggle est présent const recentNotesToggle = page.getByRole('switch', { name: /show recent notes/i }) await expect(recentNotesToggle).toBeVisible() }) test('devrait pouvoir activer/désactiver Show Recent Notes', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Scroll vers la section display settings await page.getByText(/display settings/i).scrollIntoViewIfNeeded() // Récupérer l'état initial du toggle const recentNotesToggle = page.getByRole('switch', { name: /show recent notes/i }) const initialState = await recentNotesToggle.getAttribute('aria-checked') const initialEnabled = initialState === 'true' // Cliquer sur le toggle await recentNotesToggle.click() // Attendre un peu pour l'opération asynchrone await page.waitForTimeout(500) // Vérifier que l'état a changé const newState = await recentNotesToggle.getAttribute('aria-checked') const newEnabled = newState === 'true' expect(newEnabled).toBe(!initialEnabled) // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) }) test('devrait pouvoir changer le nom d\'affichage', async ({ page }) => { await page.goto('http://localhost:3000/settings/profile') // Remplir le champ nom const nameInput = page.getByLabel(/display name/i) const newName = 'Test User ' + Date.now() await nameInput.fill(newName) // Soumettre le formulaire await page.getByRole('button', { name: /save/i }).click() // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) // Recharger la page et vérifier que le nom a été sauvegardé await page.reload() await expect(nameInput).toHaveValue(newName) }) }) /** * Tests pour Appearance Settings */ test.describe('Appearance Settings', () => { test('devrait afficher la page Appearance Settings', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') // Vérifier le titre de la page const title = await page.textContent('h1') expect(title).toContain('Appearance') // Vérifier les sections await expect(page.getByText(/theme/i)).toBeVisible() await expect(page.getByText(/typography/i)).toBeVisible() }) test('devrait avoir le sélecteur de thème', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') // Vérifier que le sélecteur est présent const themeSelect = page.locator('#theme') await expect(themeSelect).toBeVisible() }) test('devrait pouvoir changer le thème pour Light', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') // Cliquer sur le sélecteur de thème const themeSelect = page.locator('#theme') await themeSelect.click() // Sélectionner "Light" await page.getByRole('option', { name: /light/i }).click() // Vérifier que le thème est appliqué immédiatement await expect(page.locator('html')).toHaveClass(/light/) await expect(page.locator('html')).not.toHaveClass(/dark/) // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) // Vérifier que le thème est sauvegardé dans localStorage const localStorageTheme = await page.evaluate(() => { return localStorage.getItem('theme') }) expect(localStorageTheme).toBe('light') }) test('devrait pouvoir changer le thème pour Dark', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') // Cliquer sur le sélecteur de thème const themeSelect = page.locator('#theme') await themeSelect.click() // Sélectionner "Dark" await page.getByRole('option', { name: /dark/i }).click() // Vérifier que le thème est appliqué immédiatement await expect(page.locator('html')).toHaveClass(/dark/) await expect(page.locator('html')).not.toHaveClass(/light/) // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) // Vérifier que le thème est sauvegardé dans localStorage const localStorageTheme = await page.evaluate(() => { return localStorage.getItem('theme') }) expect(localStorageTheme).toBe('dark') }) test('devrait pouvoir changer le thème pour Auto', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') // Cliquer sur le sélecteur de thème const themeSelect = page.locator('#theme') await themeSelect.click() // Sélectionner "Auto" await page.getByRole('option', { name: /auto/i }).click() // Vérifier qu'un toast de succès apparaît await expect(page.getByText(/success/i)).toBeVisible({ timeout: 3000 }) // Vérifier que le thème est sauvegardé dans localStorage const localStorageTheme = await page.evaluate(() => { return localStorage.getItem('theme') }) expect(localStorageTheme).toBe('auto') }) test('devrait charger le thème depuis localStorage', async ({ page }) => { // Définir le thème dans localStorage avant de charger la page await page.goto('about:blank') await page.evaluate(() => { localStorage.setItem('theme', 'dark') }) // Aller sur la page Appearance Settings await page.goto('http://localhost:3000/settings/appearance') // Attendre que la page charge await page.waitForLoadState('networkidle') // Vérifier que le thème est chargé et appliqué const themeSelect = page.locator('#theme') await expect(themeSelect).toHaveValue('dark') // Vérifier que le thème est appliqué au DOM await expect(page.locator('html')).toHaveClass(/dark/) }) test('devrait persister le thème après rechargement de page', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') // Changer le thème pour dark const themeSelect = page.locator('#theme') await themeSelect.click() await page.getByRole('option', { name: /dark/i }).click() // Attendre que le thème soit appliqué await expect(page.locator('html')).toHaveClass(/dark/) // Recharger la page await page.reload() await page.waitForLoadState('networkidle') // Vérifier que le thème est toujours appliqué await expect(page.locator('html')).toHaveClass(/dark/) await expect(themeSelect).toHaveValue('dark') }) }) /** * Tests d'intégration cross-pages */ test.describe('Integration Tests', () => { test('devrait naviguer entre les pages de settings', async ({ page }) => { // Commencer sur General Settings await page.goto('http://localhost:3000/settings/general') // Cliquer sur Appearance dans la navigation await page.getByRole('link', { name: /appearance/i }).click() await expect(page).toHaveURL(/\/settings\/appearance/) // Cliquer sur Profile dans la navigation await page.getByRole('link', { name: /profile/i }).click() await expect(page).toHaveURL(/\/settings\/profile/) // Cliquer sur General dans la navigation await page.getByRole('link', { name: /general/i }).click() await expect(page).toHaveURL(/\/settings\/general/) }) test('devrait persister les settings entre les pages', async ({ page }) => { // Changer le thème sur Appearance Settings await page.goto('http://localhost:3000/settings/appearance') const themeSelect = page.locator('#theme') await themeSelect.click() await page.getByRole('option', { name: /dark/i }).click() await expect(page.locator('html')).toHaveClass(/dark/) // Aller sur General Settings et vérifier que le thème est toujours appliqué await page.goto('http://localhost:3000/settings/general') await expect(page.locator('html')).toHaveClass(/dark/) // Aller sur Profile Settings et vérifier que le thème est toujours appliqué await page.goto('http://localhost:3000/settings/profile') await expect(page.locator('html')).toHaveClass(/dark/) }) }) /** * Tests de responsive design */ test.describe('Responsive Design', () => { test('devrait fonctionner sur mobile', async ({ page }) => { // Simuler un viewport mobile await page.setViewportSize({ width: 375, height: 667 }) await page.goto('http://localhost:3000/settings/general') // Vérifier que la page est utilisable await expect(page.locator('h1')).toBeVisible() await expect(page.getByPlaceholder(/search/i)).toBeVisible() }) test('devrait fonctionner sur tablet', async ({ page }) => { // Simuler un viewport tablet await page.setViewportSize({ width: 768, height: 1024 }) await page.goto('http://localhost:3000/settings/general') // Vérifier que la page est utilisable await expect(page.locator('h1')).toBeVisible() await expect(page.getByPlaceholder(/search/i)).toBeVisible() }) test('devrait fonctionner sur desktop', async ({ page }) => { // Simuler un viewport desktop await page.setViewportSize({ width: 1280, height: 800 }) await page.goto('http://localhost:3000/settings/general') // Vérifier que la page est utilisable await expect(page.locator('h1')).toBeVisible() await expect(page.getByPlaceholder(/search/i)).toBeVisible() }) }) /** * Tests de performance */ test.describe('Performance Tests', () => { test('devrait charger rapidement General Settings', async ({ page }) => { const startTime = Date.now() await page.goto('http://localhost:3000/settings/general') await page.waitForLoadState('networkidle') const loadTime = Date.now() - startTime // La page devrait charger en moins de 2 secondes expect(loadTime).toBeLessThan(2000) }) test('devrait appliquer le thème rapidement', async ({ page }) => { await page.goto('http://localhost:3000/settings/appearance') const themeSelect = page.locator('#theme') const startTime = Date.now() await themeSelect.click() await page.getByRole('option', { name: /dark/i }).click() await page.waitForSelector('html.dark', { timeout: 1000 }) const applyTime = Date.now() - startTime // Le thème devrait être appliqué en moins de 500ms expect(applyTime).toBeLessThan(500) }) }) })