sw-test-env
v3.0.0
Published
A sandboxed ServiceWorker environment for testing
Downloads
1,448
Maintainers
Readme
ServiceWorker Test Environment
A sandboxed ServiceWorker
context for testing your ServiceWorker
code on the command line.
Testing code written to run in a ServiceWorker
is hard, and generally requires a browser environment and lots of ceremony to work. sw-test-env
is the magic ingredient for easy unit/integration testing of ServiceWorker
code. Just load your script, and poke, prod, inspect, and manipulate the ServiceWorker
context:
import assert from 'assert';
import { connect } from 'sw-test-env';
// Equivalent to opening a browser window and accessing window.navigator.serviceWorker
const sw = connect('http://localhost:3000', 'path/to/webroot');
async function test() {
// Load and execute sw.js in a sandboxed ServiceWorker context
const registration = await sw.register('sw.js');
// Trigger the 'install' event
await sw.trigger('install');
// Inspect the cache contents by reading from the installing service worker's internal scope
const cache = await sw.__serviceWorker__.self.caches.open('v1');
const requests = await cache.keys();
const urls = requests.map((request) => request.url);
assert.ok(urls.includes('assets/index.js'));
}
Features
- load and execute
ServiceWorker
script files in a sandboxed context - inspect the properties of the
ServiceWorker
scope (clients
,caches
,registration
, variables, etc) - manually trigger events on
ServiceWorker
(install
,activate
,fetch
,error
, etc) - connect multiple clients
- register multiple, scoped
ServiceWorker
instances postMessage
between clients and registeredServiceWorker
instances- use
indexedDB
- TODO: register for notifications and push messages to connected clients
Caveats
- limited
Response
streaming and body conversion (uses the primitives from node-fetch) fetch
calls will be executed, so a request mocking tool like nock is recommendedimportScripts()
in service worker files not supported (useimport
statements instead)- requires at least version 16 of Node
- not yet possible to cache based on
VARY
header - not tested against spec test suite or specific browser behaviour
API
connect(url: string, webroot: string): Promise<MockServiceWorkerContainer>
Create a new MockServiceWorkerContainer
instance at url
(default is http://localhost:3333/
) with webroot
(default is current working directory). This is equivalent to opening a browser at url
and accessing the window.navigator.serviceworker
object. See MockServiceWorkerContainer below for additional behaviour.
Multiple connections to same/different origins are supported, with access to MockServiceWorker
instances determined by scope
.
Note: the webroot
argument is used to resolve the path for registering the MockServiceWorker
.
destroy(): Promise<void>
Destroy all active MockServiceWorkerContainer
instances and their registered MockServiceWorker
instances. Should generally be called after each test (for example, in afterEach()
when using Mocha/Jest/etc).
Headers, MessageChannel, Request, Response
Classes for creating instances of Headers
, MessageChannel
, Request
, and Response
to be used when interacting with the MockServiceWorker
context.
MockServiceWorkerContainer
In addition to the behaviour documented here, a MockServiceWorkerContainer
instance returned by connect()
has the following additions:
register(scriptURL: String, options: { scope: string }): Promise<MockServiceWorkerRegistration>
Load and execute scriptURL
in a MockServiceWorker
context. scriptURL
may be a relative or absolute filepath.
options
include:
scope: String
theMockServiceWorker
registration scope (defaults to./
). MultipleMockServiceWorker
instances can be registered on the same origin with different scopes.
ready: Promise<void>
Force registered script to install
and activate
:
const registration = await sw.register('sw.js');
await sw.ready;
assert.equal(sw.controller.state, 'activated');
trigger(eventType: 'install' | 'activate'): Promise<void>
trigger(eventType: 'fetch', options: FetchEventInit): Promise<Response>
trigger(eventType: 'error' | 'unhandledrejection', error: Error): Promise<void>
Manually trigger an event in the MockServiceWorker
scope:
const registration = await sw.register('sw.js');
await sw.ready;
const response = await sw.trigger('fetch', { request: '/assets/index.js' });
assert.equal(response.url, 'http://localhost:3333/assets/index.js');
__serviceWorker__: MockServiceWorker
Access the registered MockServiceWorker
, including it's internal self
scope:
const registration = await sw.register('sw.js');
await sw.ready;
const cache = sw.__serviceWorker__.self.caches.open('v1');
const requests = await cache.keys();
const urls = requests.map((request) => request.url);
assert.ok(urls.includes('assets/index.js'));
Inspiration & Thanks
Special thanks goes to Pinterest (service-worker-mock) and Nolan Lawson (pseudo-worker) for their ideas (some of which were borrowed here) and inspiring work.