Skip to content

Commit 92ab37d

Browse files
Copilotscaryrawr
andauthored
Fix cached embed navigation offline (#113)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: scaryrawr <661373+scaryrawr@users.noreply.github.com>
1 parent 3a35bb8 commit 92ab37d

4 files changed

Lines changed: 62 additions & 2 deletions

File tree

apps/page/playwright/offline.e2e.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,17 @@ test('loads the home page offline from the bare project path', async ({ context,
5757
expect(requestedOutsideProjectPath).not.toContain('/index.js');
5858
expect(requestedOutsideProjectPath).not.toContain('/manifest.json');
5959
});
60+
61+
test('loads and runs the embed page offline', async ({ context, page }) => {
62+
await page.goto('/sl/', { waitUntil: 'networkidle' });
63+
await waitForServiceWorkerControl(page);
64+
65+
await context.setOffline(true);
66+
await page.goto('/sl/embed.html?trainType=d51&smoke=true', { waitUntil: 'domcontentloaded' });
67+
68+
await expect(page.getByRole('heading', { name: 'SL Steam Locomotive Animation' })).toBeAttached();
69+
const terminal = page.getByRole('img', { name: 'Animated ASCII art train moving across terminal screen' });
70+
await expect(terminal).toBeVisible();
71+
await expect.poll(() => terminal.textContent()).toMatch(/[^\s\u00a0]/);
72+
await expect(terminal).not.toContainText('Failed to load animation');
73+
});

apps/page/src/service-worker.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,42 @@ describe('service worker regressions', () => {
149149
expect(response.headers.get('Location')).toBe('https://example.com/sl/');
150150
expect(cacheOpenCalls).toBe(0);
151151
});
152+
153+
test('serves the cached embed shell when the extensionless route returns 404', async () => {
154+
openCache = async () =>
155+
({
156+
match: async (request: RequestInfo | URL) => {
157+
expect(request).toBe('/sl/embed.html');
158+
return new Response('<!doctype html><title>SL embed</title>', {
159+
headers: {
160+
'Content-Type': 'text/html'
161+
}
162+
});
163+
}
164+
}) as unknown as Cache;
165+
fetchImpl = (async () => new Response('Not found', { status: 404 })) as unknown as typeof fetch;
166+
167+
const fetchHandler = listeners.get('fetch');
168+
expect(fetchHandler).toBeDefined();
169+
170+
let responsePromise: Promise<Response> | undefined;
171+
fetchHandler?.({
172+
request: {
173+
destination: 'document',
174+
method: 'GET',
175+
mode: 'navigate',
176+
url: 'https://example.com/sl/embed?trainType=d51'
177+
},
178+
respondWith: (promise: Promise<Response>) => {
179+
responsePromise = promise;
180+
}
181+
});
182+
183+
expect(responsePromise).toBeDefined();
184+
const response = await responsePromise;
185+
186+
expect(response.status).toBe(200);
187+
expect(await response.text()).toContain('SL embed');
188+
expect(cacheOpenCalls).toBe(1);
189+
});
152190
});

apps/page/src/service-worker.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,15 @@ serviceWorker.addEventListener('fetch', (event: FetchEventLike) => {
179179
fetch(request)
180180
.then((response) => {
181181
if (!response.ok) {
182-
return response;
182+
const navigationCachePath = getNavigationCachePath(BASE_PATH, url.pathname);
183+
if (!navigationCachePath) {
184+
return response;
185+
}
186+
187+
return caches
188+
.open(CACHE_NAME)
189+
.then((cache) => cache.match(navigationCachePath))
190+
.then((cachedResponse) => cachedResponse || response);
183191
}
184192

185193
// Clone the response before caching

apps/page/take-screenshots.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ console.log('Offline screenshot saved.');
5252

5353
// Also take offline embed screenshot
5454
const embedPage = await context.newPage();
55-
await embedPage.goto(`${baseURL}/sl/embed?train=D1&loop=true`, { waitUntil: 'domcontentloaded' });
55+
await embedPage.goto(`${baseURL}/sl/embed.html?train=D1&loop=true`, { waitUntil: 'domcontentloaded' });
5656
await embedPage.waitForTimeout(2000);
5757
await embedPage.screenshot({ path: path.join(__dirname, 'offline-embed.png'), fullPage: false });
5858
console.log('Offline embed screenshot saved.');

0 commit comments

Comments
 (0)