Test service workers with Playwright (`context.serviceWorkers()` + `waitForEvent('serviceworker')`) and unit tests via `service-worker-mock`. Covers the MV3 service-worker lifecycle (~30s suspend), cache strategies (cache-first, network-first, stale-while-revalidate), and `evaluate()` continuity across worker restart. Use when a site registers a service worker and its caching / offline behavior is uncovered, or users report stale content surviving a deploy; for the install / add-to-homescreen flow use pwa-install-flow-tests, and to design (not test) the caching policy use sw-cache-strategy-author.
79
99%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Deep reference for the service-worker-tests SKILL.md. Consult for the
deeper recipes past the persistent-context setup and offline worked
example in the main skill: version-bump cache invalidation,
service-worker-mock unit tests, and push-notification subscription
tests.
Assert a v2 worker deletes the v1 caches when it activates:
test('SW v2 deletes v1 caches on activate', async ({ context, page }) => {
await page.goto('https://localhost:3000');
let sw = context.serviceWorkers()[0]
?? await context.waitForEvent('serviceworker');
const v1Caches = await sw.evaluate(() => caches.keys());
expect(v1Caches).toContain('app-v1');
// Trigger SW update (deploy v2 to test server)
await page.evaluate(() => navigator.serviceWorker.getRegistration().then(r => r?.update()));
// Wait for activation
await page.waitForFunction(() =>
navigator.serviceWorker.controller?.scriptURL.includes('v2')
);
const v2Caches = await sw.evaluate(() => caches.keys());
expect(v2Caches).toContain('app-v2');
expect(v2Caches).not.toContain('app-v1');
});service-worker-mockFor Jest/Vitest unit tests that don't need a browser:
npm install --save-dev service-worker-mockimport makeServiceWorkerEnv from 'service-worker-mock';
beforeEach(() => {
Object.assign(global, makeServiceWorkerEnv());
jest.resetModules();
});
test('install event opens cache and pre-caches assets', async () => {
await import('../src/sw.js');
await self.trigger('install');
expect(self.snapshot().caches['app-v1']).toBeDefined();
expect(self.snapshot().caches['app-v1']['/index.html']).toBeDefined();
});test('push subscription created on registration', async ({ page, context }) => {
await context.grantPermissions(['notifications']);
await page.goto('https://localhost:3000');
const subscription = await page.evaluate(async () => {
const reg = await navigator.serviceWorker.ready;
return reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: '<base64-vapid-key>',
});
});
expect(subscription).toBeDefined();
});Pair with push-notification-test-author for downstream send/receive
assertions.
launchPersistentContext,
context.serviceWorkers(), evaluate(), MV3 lifecycle behavior.