import { devices, expect, test } from '@playwright/test'; const MOBILE_VIEWPORT = devices['iPhone 14 Pro Max']; test.describe('Mobile map native experience', () => { const { defaultBrowserType: _bt, ...mobileContext } = MOBILE_VIEWPORT; test.describe('timezone-based startup region', () => { test('America/New_York → america view', async ({ browser }) => { const context = await browser.newContext({ ...mobileContext, timezoneId: 'America/New_York', locale: 'en-US', }); const page = await context.newPage(); await page.addInitScript(() => { (window as any).__testResolvedLocation = true; }); await page.goto('/'); await page.waitForTimeout(3000); const region = await page.evaluate(() => { const select = document.getElementById('regionSelect') as HTMLSelectElement | null; return select?.value ?? null; }); expect(region).toBe('america'); await context.close(); }); test('Europe/London → eu view', async ({ browser }) => { const context = await browser.newContext({ ...mobileContext, timezoneId: 'Europe/London', locale: 'en-GB', }); const page = await context.newPage(); await page.goto('/'); await page.waitForTimeout(3000); const region = await page.evaluate(() => { const select = document.getElementById('regionSelect') as HTMLSelectElement | null; return select?.value ?? null; }); expect(region).toBe('eu'); await context.close(); }); test('Asia/Tokyo → asia view', async ({ browser }) => { const context = await browser.newContext({ ...mobileContext, timezoneId: 'Asia/Tokyo', locale: 'ja-JP', }); const page = await context.newPage(); await page.goto('/'); await page.waitForTimeout(3000); const region = await page.evaluate(() => { const select = document.getElementById('regionSelect') as HTMLSelectElement | null; return select?.value ?? null; }); expect(region).toBe('asia'); await context.close(); }); }); test.describe('URL restore', () => { test.use(mobileContext); test('lat/lon override view center', async ({ page }) => { await page.goto('/?view=eu&lat=48.86&lon=2.35&zoom=5'); await page.waitForTimeout(3000); const url = page.url(); const params = new URL(url).searchParams; const lat = params.get('lat'); const lon = params.get('lon'); if (lat && lon) { expect(parseFloat(lat)).toBeCloseTo(48.86, 0); expect(parseFloat(lon)).toBeCloseTo(2.35, 0); } else { const region = await page.evaluate(() => { const select = document.getElementById('regionSelect') as HTMLSelectElement | null; return select?.value ?? null; }); expect(region).not.toBe('eu'); } }); test('zero-degree coordinates center at equator/prime meridian', async ({ page }) => { await page.goto('/?lat=0&lon=0&zoom=4'); await page.waitForTimeout(3000); const url = page.url(); const params = new URL(url).searchParams; const lat = params.get('lat'); const lon = params.get('lon'); expect(lat).not.toBeNull(); expect(lon).not.toBeNull(); if (lat && lon) { expect(Math.abs(parseFloat(lat))).toBeLessThan(5); expect(Math.abs(parseFloat(lon))).toBeLessThan(5); } }); }); test.describe('touch interactions', () => { test.use(mobileContext); test('single-finger pan does not scroll page', async ({ page }) => { await page.goto('/'); await page.waitForTimeout(3000); const mapEl = page.locator('#mapContainer'); await expect(mapEl).toBeVisible({ timeout: 10000 }); const scrollBefore = await page.evaluate(() => window.scrollY); const box = await mapEl.boundingBox(); if (box) { const startX = box.x + box.width / 2; const startY = box.y + box.height / 2; await page.touchscreen.tap(startX, startY); await page.mouse.move(startX, startY); await page.touchscreen.tap(startX, startY + 50); } const scrollAfter = await page.evaluate(() => window.scrollY); expect(scrollAfter).toBe(scrollBefore); }); }); test.describe('geolocation startup centering', () => { test('centers map on granted geolocation coords', async ({ browser }) => { const context = await browser.newContext({ ...mobileContext, geolocation: { latitude: 48.8566, longitude: 2.3522 }, permissions: ['geolocation'], }); const page = await context.newPage(); await page.goto('/'); await page.waitForFunction( () => { const select = document.getElementById('regionSelect') as HTMLSelectElement | null; return select?.value === 'eu'; }, { timeout: 10000 }, ); await context.close(); }); }); test.describe('mobile map viewport', () => { test('map starts expanded and occupies most of viewport', async ({ browser }) => { const context = await browser.newContext({ ...mobileContext, locale: 'en-US', }); const page = await context.newPage(); await page.goto('/'); const mapSection = page.locator('#mapSection'); await expect(mapSection).toBeVisible({ timeout: 10000 }); await expect(mapSection).not.toHaveClass(/collapsed/); const ratio = await page.evaluate(() => { const el = document.getElementById('mapSection'); return (el?.getBoundingClientRect().height ?? 0) / window.innerHeight; }); expect(ratio).toBeGreaterThanOrEqual(0.7); await context.close(); }); }); test.describe('breakpoint consistency at 768px', () => { test('JS and CSS agree at exactly 768px', async ({ browser }) => { const context = await browser.newContext({ viewport: { width: 768, height: 1024 }, locale: 'en-US', }); const page = await context.newPage(); await page.goto('/'); await page.waitForTimeout(2000); const result = await page.evaluate(() => { const jsMobile = window.innerWidth <= 768; const el = document.createElement('div'); el.style.display = 'none'; document.body.appendChild(el); const cssMobile = window.matchMedia('(max-width: 768px)').matches; el.remove(); return { jsMobile, cssMobile }; }); expect(result.jsMobile).toBe(result.cssMobile); await context.close(); }); }); });