import { test, expect } from '@chromatic-com/playwright'; import { loginViaAPI, CONFIG, navigateTo, assertNotBroken, SELECTORS, } from './helpers'; // ============================================================================= // Helper: assert no horizontal scroll on the current page // ============================================================================= async function assertNoHorizontalScroll(page: import('@playwright/test').Page): Promise { const hasHScroll = await page.evaluate(() => { return document.documentElement.scrollWidth > document.documentElement.clientWidth; }); expect(hasHScroll).toBe(false); } // ============================================================================= // RESPONSIVE — Mobile 375x667 // ============================================================================= test.describe('RESPONSIVE — Mobile 375x667 @mobile @feature-responsive', () => { test.use({ viewport: { width: 375, height: 667 } }); test.beforeEach(async ({ page }) => { await loginViaAPI(page, CONFIG.users.listener.email, CONFIG.users.listener.password); }); test('Dashboard — pas de scroll horizontal', async ({ page }) => { await navigateTo(page, '/dashboard'); await assertNotBroken(page); await assertNoHorizontalScroll(page); }); test('Dashboard — sidebar est cachee par defaut', async ({ page }) => { await navigateTo(page, '/dashboard'); const sidebar = page.locator(SELECTORS.sidebar); // On mobile, the sidebar has -translate-x-full (Tailwind) which moves it off-screen. // Playwright's isVisible() may still return true because the element has dimensions. // We check the computed transform or Tailwind classes to confirm it's hidden. const sidebarState = await sidebar.evaluate((el) => { const style = window.getComputedStyle(el); const rect = el.getBoundingClientRect(); return { className: el.className, transform: style.transform, visibility: style.visibility, display: style.display, x: rect.x, width: rect.width, rightEdge: rect.x + rect.width, }; }).catch(() => null); // If sidebar element doesn't exist or has no bounding box, it's effectively hidden — acceptable if (!sidebarState) { expect(sidebarState).toBeNull(); return; } const isOffScreen = sidebarState.rightEdge <= 0 || sidebarState.x < -50; const isCollapsed = sidebarState.width <= 64; const hasHiddenTransform = sidebarState.transform.includes('matrix') && sidebarState.x < -50; const hasHiddenClass = /(-translate-x-full|hidden|invisible)/.test(sidebarState.className); const isNotDisplayed = sidebarState.display === 'none' || sidebarState.visibility === 'hidden'; expect(isOffScreen || isCollapsed || hasHiddenTransform || hasHiddenClass || isNotDisplayed).toBeTruthy(); }); test('Dashboard — menu hamburger ouvre la sidebar en overlay', async ({ page }) => { await navigateTo(page, '/dashboard'); // Wait for the header to be fully rendered await page.locator('[data-testid="app-header"]').waitFor({ state: 'visible', timeout: 10_000 }).catch(() => {}); await page.waitForTimeout(500); // The hamburger button in Header.tsx is the first