From 41b259fae50ae847d7b365e9031b397eecd4b3cf Mon Sep 17 00:00:00 2001 From: "Claude Opus 4.8" Date: Wed, 8 Jul 2026 22:24:29 +0200 Subject: [PATCH] Harden the offline e2e for slower CI timing (#38) The offline pack passed locally but flaked in CI. Make its timing-sensitive steps robust without changing the feature: - Before going offline, wait until the service worker not only controls the page but has actually populated Cache Storage (app-shell precache complete), so the offline reload is guaranteed to be servable from cache. - After coming back online, wait for the reloaded tab to reconnect (so it has pushed its local state) before checking a second client converges. - Raise the service-worker-ready and convergence timeouts, and the IndexedDB flush wait, for headroom on slower runners. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY --- apps/web/e2e/offline.spec.ts | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/apps/web/e2e/offline.spec.ts b/apps/web/e2e/offline.spec.ts index e1859c7..a79931f 100644 --- a/apps/web/e2e/offline.spec.ts +++ b/apps/web/e2e/offline.spec.ts @@ -25,11 +25,23 @@ test('offline: edit, reload while offline, then reconnect converges (#38)', asyn await expect(page.locator('.editor-connection')).toHaveAttribute('data-status', 'connected', { timeout: 15000, }); - // The service worker must control the page before an offline reload can be - // served from the cached app shell. - await page.waitForFunction(() => navigator.serviceWorker?.controller != null, null, { - timeout: 20000, - }); + // The service worker must control the page AND have finished precaching the + // app shell before an offline reload can be served from cache. Checking the + // Cache Storage is populated avoids a race where the SW controls the page but + // its install (precache) has not completed yet. + await page.waitForFunction( + async () => { + if (!navigator.serviceWorker?.controller) return false; + const names = await caches.keys(); + for (const name of names) { + const cache = await caches.open(name); + if ((await cache.keys()).length > 0) return true; + } + return false; + }, + null, + { timeout: 30000 }, + ); const editor = page.locator('.ProseMirror'); await editor.click(); @@ -58,7 +70,7 @@ test('offline: edit, reload while offline, then reconnect converges (#38)', asyn expect(apiResult).toBe('network-error'); // Give y-indexeddb a moment to flush the offline edit before the reload. - await page.waitForTimeout(1000); + await page.waitForTimeout(1500); // Reload the tab while offline: the app shell loads from the service worker, // the page resolves from the offline metadata cache, and its content comes @@ -68,13 +80,18 @@ test('offline: edit, reload while offline, then reconnect converges (#38)', asyn await expect(reloaded).toContainText('offline extra', { timeout: 20000 }); await expect(reloaded).toContainText('online base'); - // Back online: a second participant converges on the merged content. + // Back online: the reloaded tab reconnects and pushes its local state to the + // server; wait for that before checking a second participant converges. await owner.setOffline(false); + await expect(page.locator('.editor-connection')).toHaveAttribute('data-status', 'connected', { + timeout: 30000, + }); + const admin = await contextForUser(browser, BASE_URL, 'fixture-admin'); const adminPage = await admin.newPage(); await adminPage.goto(`/p/${pond.slug}/${slug}`); await expect(adminPage.locator('.ProseMirror')).toContainText('offline extra', { - timeout: 25000, + timeout: 30000, }); await owner.close();