import { test, expect } from '@playwright/test'; test.describe('US-100: Touch Navigation & Mobile Accessibility', () => { test('should have 44px minimum touch targets on buttons', async ({ page, context }) => { // Create a mobile context for touch support const mobileContext = await context.browser()?.newContext({ viewport: { width: 375, height: 667 }, hasTouch: true, isMobile: true, }); if (!mobileContext) return; const mobilePage = await mobileContext.newPage(); await mobilePage.goto('/ru-ru/schedule'); await mobilePage.waitForLoadState('networkidle'); const buttons = await mobilePage.locator('button').all(); for (const button of buttons) { const box = await button.boundingBox(); if (box) { expect(box.width).toBeGreaterThanOrEqual(44); expect(box.height).toBeGreaterThanOrEqual(44); } } await mobileContext.close(); }); test('should have proper spacing between touch targets', async ({ page, context }) => { // Create a mobile context const mobileContext = await context.browser()?.newContext({ viewport: { width: 375, height: 667 }, hasTouch: true, isMobile: true, }); if (!mobileContext) return; const mobilePage = await mobileContext.newPage(); await mobilePage.goto('/ru-ru/schedule'); const buttons = await mobilePage.locator('button').all(); // Check if any two adjacent buttons on the same row have sufficient spacing let foundValidSpacing = false; for (let i = 0; i < buttons.length - 1; i++) { const box1 = await buttons[i].boundingBox(); const box2 = await buttons[i + 1].boundingBox(); if (box1 && box2) { // Check if buttons are roughly on the same vertical line (within 10px) const onSameRow = Math.abs(box1.y - box2.y) < 10; if (onSameRow) { // Minimum 8px spacing between targets const horizontalSpacing = box2.x - (box1.x + box1.width); if (horizontalSpacing >= 8) { foundValidSpacing = true; break; } } } } // If no horizontal spacing found, buttons are likely stacked vertically which is fine // Just verify that vertically stacked buttons have minimum size if (buttons.length >= 2) { const box1 = await buttons[0].boundingBox(); const box2 = await buttons[1].boundingBox(); if (box1 && box2) { // Either they have horizontal spacing >= 8px or they're on different rows (which is valid) const onSameRow = Math.abs(box1.y - box2.y) < 10; const horizontalSpacing = box2.x - (box1.x + box1.width); if (onSameRow) { expect(horizontalSpacing).toBeGreaterThanOrEqual(8); } else { // Vertical stacking is valid, just ensure minimum height expect(box1.height).toBeGreaterThanOrEqual(44); expect(box2.height).toBeGreaterThanOrEqual(44); } } } await mobileContext.close(); }); test('should not zoom on input focus', async ({ page, context }) => { // Create a mobile context const mobileContext = await context.browser()?.newContext({ viewport: { width: 375, height: 667 }, hasTouch: true, isMobile: true, }); if (!mobileContext) return; const mobilePage = await mobileContext.newPage(); await mobilePage.goto('/ru-ru/schedule'); // Get initial zoom level const initialZoom = await mobilePage.evaluate(() => window.devicePixelRatio); // Focus an input const input = mobilePage.locator('input').first(); await input.focus(); // Check zoom level hasn't changed const zoomAfterFocus = await mobilePage.evaluate(() => window.devicePixelRatio); expect(zoomAfterFocus).toBe(initialZoom); await mobileContext.close(); }); test('should console have zero errors on mobile', async ({ page, context }) => { // Create a mobile context with touch support const mobileContext = await context.browser()?.newContext({ viewport: { width: 375, height: 667 }, hasTouch: true, isMobile: true, }); if (!mobileContext) return; const mobilePage = await mobileContext.newPage(); const errors: string[] = []; mobilePage.on('console', (msg) => { if (msg.type() === 'error') errors.push(msg.text()); }); await mobilePage.goto('/ru-ru/schedule'); await mobilePage.waitForLoadState('networkidle'); // Simulate touch interactions const buttons = await mobilePage.locator('button').all(); if (buttons.length > 0) { await buttons[0].tap(); } expect(errors).toHaveLength(0); await mobileContext.close(); }); });