UNPKG

sw-test-env

Version:

A sandboxed ServiceWorker environment for testing

83 lines 7.97 kB
{ "name": "sw-test-env", "description": "A sandboxed ServiceWorker environment for testing", "version": "3.0.0", "author": "Alexander Pope <alex@pope-industries.com>", "contributors": [ "Ola Christian Gundelsby <ola.christian.gundelsby@nrk.no>" ], "repository": "https://github.com/popeindustries/sw-test-env.git", "license": "MIT", "keywords": [ "mock", "pseudo", "sandbox", "service worker", "ServiceWorker", "test", "testing", "worker" ], "type": "module", "main": "sw-test-env.js", "files": [ "bin", "*.d.ts", "*.js", "README.MD" ], "engines": { "node": ">=16" }, "dependencies": { "esbuild": "~0.14.27", "fake-indexeddb": "^3.1.7", "form-data": "^4.0.0", "mime-types": "^2.1.35", "node-fetch": "^3.2.3" }, "devDependencies": { "@types/chai": "^4.3.0", "@types/mime-types": "^2.1.1", "@types/mocha": "^9.1.0", "@types/node": "^17.0.23", "@typescript-eslint/eslint-plugin": "^5.16.0", "@typescript-eslint/parser": "^5.16.0", "chai": "^4.3.6", "dvlp": "^14.2.0", "eslint": "^8.11.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.0.0", "glob": "^7.2.0", "husky": "^7.0.4", "lint-staged": "^12.3.7", "mocha": "^9.2.2", "prettier": "^2.6.1", "typescript": "4.6.3" }, "prettier": { "arrowParens": "always", "printWidth": 120, "singleQuote": true, "trailingComma": "all" }, "lint-staged": { "*.js": [ "eslint" ], "*.{js,json,md,html}": [ "prettier --write" ] }, "scripts": { "build": "node ./scripts/build.js", "clean": "rm -f ./test/*.js && rm -rf docs", "format": "prettier --write './{src,test}/**/*'", "lint": "pnpm run lint:src && pnpm run lint:types", "lint:src": "eslint './{src,test}/**/*.js'", "lint:types": "tsc --noEmit --skipLibCheck", "preinstall": "npx only-allow pnpm", "test": "pnpm run build && mocha test/*.js --reporter spec --bail" }, "readme": "[![NPM Version](https://img.shields.io/npm/v/sw-test-env.svg?style=flat)](https://npmjs.org/package/sw-test-env)\n[![Build Status](https://img.shields.io/travis/popeindustries/sw-test-env.svg?style=flat)](https://github.com/popeindustries/sw-test-env/actions)\n\n# ServiceWorker Test Environment\n\nA sandboxed `ServiceWorker` context for testing your `ServiceWorker` code on the command line.\n\nTesting 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:\n\n```js\nimport assert from 'assert';\nimport { connect } from 'sw-test-env';\n\n// Equivalent to opening a browser window and accessing window.navigator.serviceWorker\nconst sw = connect('http://localhost:3000', 'path/to/webroot');\n\nasync function test() {\n // Load and execute sw.js in a sandboxed ServiceWorker context\n const registration = await sw.register('sw.js');\n // Trigger the 'install' event\n await sw.trigger('install');\n // Inspect the cache contents by reading from the installing service worker's internal scope\n const cache = await sw.__serviceWorker__.self.caches.open('v1');\n const requests = await cache.keys();\n const urls = requests.map((request) => request.url);\n assert.ok(urls.includes('assets/index.js'));\n}\n```\n\n## Features\n\n- load and execute `ServiceWorker` script files in a sandboxed context\n- inspect the properties of the `ServiceWorker` scope (`clients`, `caches`, `registration`, variables, etc)\n- manually trigger events on `ServiceWorker` (`install`, `activate`, `fetch`, `error`, etc)\n- connect multiple clients\n- register multiple, scoped `ServiceWorker` instances\n- `postMessage` between clients and registered `ServiceWorker` instances\n- use `indexedDB`\n- TODO: register for notifications and push messages to connected clients\n\n## Caveats\n\n- limited `Response` streaming and body conversion (uses the primitives from [node-fetch](https://github.com/bitinn/node-fetch))\n- `fetch` calls will be executed, so a request mocking tool like [nock](https://github.com/node-nock/nock) is recommended\n- `importScripts()` in service worker files not supported (use `import` statements instead)\n- requires at least version 16 of Node\n- not yet possible to cache based on `VARY` header\n- not tested against spec test suite or specific browser behaviour\n\n## API\n\n#### **`connect(url: string, webroot: string): Promise<MockServiceWorkerContainer>`**\n\nCreate 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](#mockserviceworkercontainer) below for additional behaviour.\n\nMultiple connections to same/different origins are supported, with access to `MockServiceWorker` instances determined by `scope`.\n\n**Note**: the `webroot` argument is used to resolve the path for registering the `MockServiceWorker`.\n\n#### **`destroy(): Promise<void>`**\n\nDestroy 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).\n\n#### **`Headers, MessageChannel, Request, Response`**\n\nClasses for creating instances of `Headers`, `MessageChannel`, `Request`, and `Response` to be used when interacting with the `MockServiceWorker` context.\n\n### MockServiceWorkerContainer\n\nIn addition to the behaviour documented [here](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer), a `MockServiceWorkerContainer` instance returned by `connect()` has the following additions:\n\n#### **`register(scriptURL: String, options: { scope: string }): Promise<MockServiceWorkerRegistration>`**\n\nLoad and execute `scriptURL` in a `MockServiceWorker` context. `scriptURL` may be a relative or absolute filepath.\n\n**`options`** include:\n\n- **`scope: String`** the `MockServiceWorker` registration scope (defaults to `./`). Multiple `MockServiceWorker` instances can be registered on the same origin with different scopes.\n\n#### **`ready: Promise<void>`**\n\nForce registered script to `install` and `activate`:\n\n```js\nconst registration = await sw.register('sw.js');\nawait sw.ready;\nassert.equal(sw.controller.state, 'activated');\n```\n\n#### **`trigger(eventType: 'install' | 'activate'): Promise<void>`**\n\n#### **`trigger(eventType: 'fetch', options: FetchEventInit): Promise<Response>`**\n\n#### **`trigger(eventType: 'error' | 'unhandledrejection', error: Error): Promise<void>`**\n\nManually trigger an event in the `MockServiceWorker` scope:\n\n```js\nconst registration = await sw.register('sw.js');\nawait sw.ready;\nconst response = await sw.trigger('fetch', { request: '/assets/index.js' });\nassert.equal(response.url, 'http://localhost:3333/assets/index.js');\n```\n\n#### **`__serviceWorker__: MockServiceWorker`**\n\nAccess the registered `MockServiceWorker`, including it's internal `self` scope:\n\n```js\nconst registration = await sw.register('sw.js');\nawait sw.ready;\nconst cache = sw.__serviceWorker__.self.caches.open('v1');\nconst requests = await cache.keys();\nconst urls = requests.map((request) => request.url);\nassert.ok(urls.includes('assets/index.js'));\n```\n\n## Inspiration & Thanks\n\nSpecial thanks goes to Pinterest ([service-worker-mock](https://github.com/pinterest/service-workers/tree/master/packages/service-worker-mock)) and Nolan Lawson ([pseudo-worker](https://github.com/nolanlawson/pseudo-worker)) for their ideas (some of which were borrowed here) and inspiring work.\n" }