From 7c560090625021865f8d4509a01f8e52cc664163 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Aug 2026 11:27:44 +0000 Subject: [PATCH] Fix cached embed navigation offline Co-authored-by: scaryrawr <661373+scaryrawr@users.noreply.github.com> --- apps/page/playwright/offline.e2e.js | 14 ++++++++++ apps/page/src/service-worker.test.ts | 38 ++++++++++++++++++++++++++++ apps/page/src/service-worker.ts | 10 +++++++- apps/page/take-screenshots.mjs | 2 +- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/apps/page/playwright/offline.e2e.js b/apps/page/playwright/offline.e2e.js index 9f271d6b..abefda22 100644 --- a/apps/page/playwright/offline.e2e.js +++ b/apps/page/playwright/offline.e2e.js @@ -57,3 +57,17 @@ test('loads the home page offline from the bare project path', async ({ context, expect(requestedOutsideProjectPath).not.toContain('/index.js'); expect(requestedOutsideProjectPath).not.toContain('/manifest.json'); }); + +test('loads and runs the embed page offline', async ({ context, page }) => { + await page.goto('/sl/', { waitUntil: 'networkidle' }); + await waitForServiceWorkerControl(page); + + await context.setOffline(true); + await page.goto('/sl/embed.html?trainType=d51&smoke=true', { waitUntil: 'domcontentloaded' }); + + await expect(page.getByRole('heading', { name: 'SL Steam Locomotive Animation' })).toBeAttached(); + const terminal = page.getByRole('img', { name: 'Animated ASCII art train moving across terminal screen' }); + await expect(terminal).toBeVisible(); + await expect.poll(() => terminal.textContent()).toMatch(/[^\s\u00a0]/); + await expect(terminal).not.toContainText('Failed to load animation'); +}); diff --git a/apps/page/src/service-worker.test.ts b/apps/page/src/service-worker.test.ts index edaf31c0..fd07e8aa 100644 --- a/apps/page/src/service-worker.test.ts +++ b/apps/page/src/service-worker.test.ts @@ -149,4 +149,42 @@ describe('service worker regressions', () => { expect(response.headers.get('Location')).toBe('https://example.com/sl/'); expect(cacheOpenCalls).toBe(0); }); + + test('serves the cached embed shell when the extensionless route returns 404', async () => { + openCache = async () => + ({ + match: async (request: RequestInfo | URL) => { + expect(request).toBe('/sl/embed.html'); + return new Response('SL embed', { + headers: { + 'Content-Type': 'text/html' + } + }); + } + }) as unknown as Cache; + fetchImpl = (async () => new Response('Not found', { status: 404 })) as unknown as typeof fetch; + + const fetchHandler = listeners.get('fetch'); + expect(fetchHandler).toBeDefined(); + + let responsePromise: Promise | undefined; + fetchHandler?.({ + request: { + destination: 'document', + method: 'GET', + mode: 'navigate', + url: 'https://example.com/sl/embed?trainType=d51' + }, + respondWith: (promise: Promise) => { + responsePromise = promise; + } + }); + + expect(responsePromise).toBeDefined(); + const response = await responsePromise; + + expect(response.status).toBe(200); + expect(await response.text()).toContain('SL embed'); + expect(cacheOpenCalls).toBe(1); + }); }); diff --git a/apps/page/src/service-worker.ts b/apps/page/src/service-worker.ts index 013caab1..8ef7254c 100644 --- a/apps/page/src/service-worker.ts +++ b/apps/page/src/service-worker.ts @@ -179,7 +179,15 @@ serviceWorker.addEventListener('fetch', (event: FetchEventLike) => { fetch(request) .then((response) => { if (!response.ok) { - return response; + const navigationCachePath = getNavigationCachePath(BASE_PATH, url.pathname); + if (!navigationCachePath) { + return response; + } + + return caches + .open(CACHE_NAME) + .then((cache) => cache.match(navigationCachePath)) + .then((cachedResponse) => cachedResponse || response); } // Clone the response before caching diff --git a/apps/page/take-screenshots.mjs b/apps/page/take-screenshots.mjs index 4945fe5b..81071d9a 100644 --- a/apps/page/take-screenshots.mjs +++ b/apps/page/take-screenshots.mjs @@ -52,7 +52,7 @@ console.log('Offline screenshot saved.'); // Also take offline embed screenshot const embedPage = await context.newPage(); -await embedPage.goto(`${baseURL}/sl/embed?train=D1&loop=true`, { waitUntil: 'domcontentloaded' }); +await embedPage.goto(`${baseURL}/sl/embed.html?train=D1&loop=true`, { waitUntil: 'domcontentloaded' }); await embedPage.waitForTimeout(2000); await embedPage.screenshot({ path: path.join(__dirname, 'offline-embed.png'), fullPage: false }); console.log('Offline embed screenshot saved.');