/** * Phase 2 browser chaos — going offline, coming back, and throttled * connections. The app's upload queue is expected to "park" pending * items on offline and resume on reconnect. */ import { test, expect } from '../../fixtures/test'; test.describe('Browser chaos — network', () => { test('offline → reconnect — page does not crash and bottom nav is responsive', async ({ page, guest, signIn }) => { const g = await guest('Offline1'); await signIn(page, g); await page.goto('/feed'); const errors: Error[] = []; page.on('pageerror', (e) => errors.push(e)); await page.context().setOffline(true); // Tap the bottom nav — should not raise unhandled errors. await page.getByRole('link', { name: 'Konto' }).click().catch(() => {}); await page.waitForTimeout(500); await page.context().setOffline(false); // App still functional after coming back. await page.goto('/feed'); await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible(); expect(errors.filter((e) => !e.message.toLowerCase().includes('fetch'))).toHaveLength(0); }); test('slow 3G simulation — initial nav completes within reasonable bound', async ({ page, guest, signIn }) => { const g = await guest('Slow3g'); // 50ms latency on every request, applied to /api/* only so navigation isn't catastrophic. await page.route('**/api/v1/**', async (route) => { await new Promise((r) => setTimeout(r, 50)); await route.continue(); }); await signIn(page, g); await page.goto('/feed'); await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible({ timeout: 15_000 }); }); test('intermittent API failures during navigation — UI surfaces an error state', async ({ page, guest, signIn }) => { const g = await guest('FlakyApi'); let count = 0; await page.route('**/api/v1/me/context', async (route) => { count++; if (count % 2 === 1) await route.fulfill({ status: 503, body: 'service unavailable' }); else await route.continue(); }); await signIn(page, g); await page.goto('/feed'); // App should still mount even when /me/context bounces — it's not on the critical render path. await expect(page.getByRole('link', { name: 'Galerie' })).toBeVisible({ timeout: 10_000 }); }); test('429 from server is surfaced (no infinite retry storm)', async ({ page, guest, signIn }) => { const g = await guest('Throttled'); let attempts = 0; await page.route('**/api/v1/feed', async (route) => { attempts++; await route.fulfill({ status: 429, headers: { 'retry-after': '60' }, body: JSON.stringify({ error: 'rate_limited', message: 'Zu viele Anfragen.' }), }); }); await signIn(page, g); await page.goto('/feed'); await page.waitForTimeout(3_000); // Sanity: client did not hammer the endpoint > a few times under throttle. expect(attempts).toBeLessThan(15); }); });