/* eslint-disable */ /** * MOBILE/RESPONSIVE TEST — key pages at phone (375px) + tablet (768px) viewports. * Checks for horizontal overflow, unusable controls, layout breakage. * * Run headed: * npx playwright test --headed --project chromium mobile-responsive-test --workers=1 */ import { test } from '@playwright/test'; import { attachObservers, observe, loginAndScope, OBS, BASE_URL } from '../../helpers/audit'; import { RoleName } from '../../helpers/helpers'; const M = 'MobileResponsive'; const ADMIN: RoleName = 'hospital_admin'; type Page = import('@playwright/test').Page; async function login(page: Page) { await page.context().clearCookies(); await loginAndScope(page, ADMIN, M); } const VIEWPORTS = [ { width: 375, height: 812, label: 'iphone' }, { width: 768, height: 1024, label: 'ipad' }, ]; const PAGES = [ { url: '/accounts/login/', label: 'login', auth: false }, { url: '/', label: 'dashboard' }, { url: '/complaints/', label: 'complaint-list' }, { url: '/complaints/public/submit/', label: 'public-form', auth: false }, { url: '/observations/new/', label: 'obs-form', auth: false }, { url: '/observations/track/', label: 'obs-track', auth: false }, { url: '/core/public/submit/', label: 'public-landing', auth: false }, { url: '/analytics/dashboard/', label: 'analytics' }, ]; test.describe('Mobile/Responsive', () => { for (const vp of VIEWPORTS) { test.describe(`${vp.label} (${vp.width}px)`, () => { for (const { url, label, auth = true } of PAGES) { test(`${label} at ${vp.width}px`, async ({ browser }) => { const context = await browser.newContext({ viewport: { width: vp.width, height: vp.height } }); const page = await context.newPage(); attachObservers(page, M, ADMIN); if (auth) await login(page); await page.goto(`${BASE_URL}${url}`); await page.waitForLoadState('domcontentloaded'); await page.waitForTimeout(1500); // Check 1: horizontal overflow (content wider than viewport) const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth); const clientWidth = await page.evaluate(() => document.documentElement.clientWidth); const hasHOverflow = scrollWidth > clientWidth + 5; // 5px tolerance // Check 2: page renders with content const bodyLen = ((await page.textContent('body')) || '').length; // Check 3: key interactive elements visible (at least one button/link/input) const interactiveCount = await page.locator('button:visible, a:visible, input:visible, select:visible').count(); // Check 4: sidebar toggle (if present) const hasSidebarToggle = await page.locator('[onclick*="sidebar"], [class*="hamburger"], [class*="menu-toggle"], button[aria-label*="menu"]').count(); observe(M, `${label}/${vp.label}`, !hasHOverflow && bodyLen > 100 ? 'PASS' : 'WARN', `${vp.width}px: hOverflow=${hasHOverflow} (scroll=${scrollWidth} client=${clientWidth}) body=${bodyLen} interactive=${interactiveCount} sidebarToggle=${hasSidebarToggle > 0}`, { url: page.url() }); await context.close(); }); } }); } }); test.afterAll(async () => { const counts = OBS.reduce>((a, o) => ((a[o.status] = (a[o.status] || 0) + 1), a), {}); console.log('\n=========== MOBILE/RESPONSIVE SUMMARY ==========='); console.log('Total observations:', OBS.length, JSON.stringify(counts)); console.log('=================================================\n'); });