import { test, expect } from '@chromatic-com/playwright'; import { loginViaAPI, CONFIG, navigateTo } from './helpers'; // ============================================================================ // CHAT — Messagerie temps réel (/chat) // ============================================================================ test.describe('CHAT — Messagerie', () => { test.beforeEach(async ({ page }) => { await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password); }); test('01. Page /chat se charge @critical', async ({ page }) => { await navigateTo(page, '/chat'); const body = await page.textContent('body') || ''; expect(body).not.toMatch(/500|error|crash/i); expect(body.length).toBeGreaterThan(100); }); test('02. Sidebar avec liste des conversations (Channels)', async ({ page }) => { await navigateTo(page, '/chat'); const channelsHeading = page.getByText('Channels', { exact: true }); await expect(channelsHeading).toBeVisible(); const sidebar = page.locator('[class*="w-80"]'); await expect(sidebar.first()).toBeVisible(); }); test('03. Champ de saisie de message visible', async ({ page }) => { await navigateTo(page, '/chat'); const msgInput = page.getByLabel('Type a message') .or(page.getByPlaceholder(/broadcast message|écrire dans/i)) .or(page.locator('input[type="text"][aria-label="Type a message"]')); await expect(msgInput.first()).toBeVisible(); }); test('04. Boutons attach/emoji/send présents', async ({ page }) => { await navigateTo(page, '/chat'); await expect(page.getByLabel('Attach file')).toBeVisible(); await expect(page.getByLabel(/add emoji|close emoji/i)).toBeVisible(); await expect(page.getByLabel('Send message')).toBeVisible(); }); test('05. WebSocket status indicator visible', async ({ page }) => { await navigateTo(page, '/chat'); const statusDot = page.locator('[class*="rounded-full"][class*="bg-success"], [class*="rounded-full"][class*="bg-destructive"]'); await expect(statusDot.first()).toBeVisible(); }); }); // ============================================================================ // NOTIFICATIONS — Centre de notifications // ============================================================================ test.describe('NOTIFICATIONS — Centre de notifications', () => { test.beforeEach(async ({ page }) => { await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password); }); test('06. Bouton notifications (bell) visible dans le header @critical', async ({ page }) => { await navigateTo(page, '/dashboard'); const notifBtn = page.getByRole('button', { name: 'Notifications' }); await expect(notifBtn).toBeVisible({ timeout: 10_000 }); }); test('07. Page /notifications se charge', async ({ page }) => { await navigateTo(page, '/notifications'); const body = await page.textContent('body') || ''; expect(body).not.toMatch(/500|error|crash/i); const heading = page.getByRole('heading', { name: /notifications/i }); await expect(heading.first()).toBeVisible(); }); test('08. Bouton "Mark All as Read" présent si notifications non lues', async ({ page }) => { await navigateTo(page, '/notifications'); // This button only appears when there are unread notifications — skip if none const markAllBtn = page.getByRole('button', { name: /mark all as read|marking/i }); const visible = await markAllBtn.isVisible().catch(() => false); test.skip(!visible, 'No unread notifications — Mark All button not expected'); await expect(markAllBtn).toBeVisible(); }); test('09. Préférences de notifications accessibles via settings', async ({ page }) => { await navigateTo(page, '/settings'); const notifTab = page.getByRole('tab', { name: /notification/i }); await expect(notifTab.first()).toBeVisible(); await notifTab.first().click(); await page.waitForTimeout(500); const emailNotifCheckbox = page.locator('#email_notifications'); await expect(emailNotifCheckbox).toBeVisible(); const pushNotifCheckbox = page.locator('#push_notifications'); await expect(pushNotifCheckbox).toBeVisible(); }); }); // ============================================================================ // SETTINGS — Paramètres utilisateur (/settings) // ============================================================================ test.describe('SETTINGS — Paramètres', () => { test.beforeEach(async ({ page }) => { await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password); }); test('10. Page /settings se charge avec les tabs @critical', async ({ page }) => { await navigateTo(page, '/settings'); const body = await page.textContent('body') || ''; expect(body).not.toMatch(/500|Internal Server Error|crash|TypeError/i); const heading = page.getByRole('heading', { name: /system config|settings|paramètres/i }); await expect(heading).toBeVisible(); const tabPatterns: [string, RegExp][] = [ ['Account', /account|compte/i], ['Preferences', /pr[ée]f[ée]rences|preferences/i], ['Notifications', /notification/i], ['Privacy', /confidentialit[ée]|privacy/i], ['Playback', /playback|lecture/i], ]; for (const [, pattern] of tabPatterns) { const tab = page.getByRole('tab', { name: pattern }).first(); await expect(tab).toBeVisible(); } }); test('11. Tab Account — password change form present', async ({ page }) => { await navigateTo(page, '/settings'); const changePasswordTitle = page.getByText('Change Password', { exact: true }); await expect(changePasswordTitle.first()).toBeVisible(); await expect(page.locator('#current-password')).toBeVisible(); await expect(page.locator('#new-password')).toBeVisible(); await expect(page.locator('#confirm-password')).toBeVisible(); }); test('12. Tab Account — 2FA section present', async ({ page }) => { await navigateTo(page, '/settings'); const twoFactorTitle = page.getByText('Two-Factor Authentication (2FA)'); await expect(twoFactorTitle).toBeVisible(); const statusText = page.getByText(/2FA is (enabled|not enabled)/); await expect(statusText.first()).toBeVisible(); }); test('13. Tab Account — data export button (GDPR)', async ({ page }) => { await navigateTo(page, '/settings'); const exportTitle = page.getByText('Data Export', { exact: true }); await expect(exportTitle.first()).toBeVisible(); const exportBtn = page.getByRole('button', { name: /export my data/i }); await expect(exportBtn).toBeVisible(); }); test('14. Tab Account — delete account button with warning', async ({ page }) => { await navigateTo(page, '/settings'); const deleteTitle = page.getByText('Delete Account').first(); await expect(deleteTitle).toBeVisible(); const warningText = page.getByText(/this action cannot be undone/i); await expect(warningText.first()).toBeVisible(); const deleteBtn = page.getByRole('button', { name: /delete account/i }); await expect(deleteBtn).toBeVisible(); }); test('15. Tab Preferences — theme radio group', async ({ page }) => { await navigateTo(page, '/settings'); const prefsTab = page.getByRole('tab', { name: /pr[ée]f[ée]rences|preferences/i }).first(); await expect(prefsTab).toBeVisible(); await prefsTab.click(); await page.waitForTimeout(500); await expect(page.locator('#theme-light')).toBeVisible(); await expect(page.locator('#theme-dark')).toBeVisible(); await expect(page.locator('#theme-auto')).toBeVisible(); }); test('16. Tab Preferences — language selector', async ({ page }) => { await navigateTo(page, '/settings'); const prefsTab = page.getByRole('tab', { name: /pr[ée]f[ée]rences|preferences/i }).first(); await expect(prefsTab).toBeVisible(); await prefsTab.click(); await page.waitForTimeout(500); // Language selector is a custom Select component (not native