├── docs ├── _config.yml └── index.md ├── tests ├── fixtures │ ├── define-env.js │ ├── define-env-2.js │ ├── simple-worker.js │ ├── environment-worker.js │ └── import-worker.js ├── .eslintrc.json ├── index.html └── index.js ├── .travis.yml ├── testem.js ├── .eslintrc.json ├── package.json ├── CHANGELOG.md ├── LICENSE ├── RELEASE.md ├── README.md └── index.js /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /tests/fixtures/define-env.js: -------------------------------------------------------------------------------- 1 | self.env = { 2 | from: 'define-env', 3 | }; 4 | -------------------------------------------------------------------------------- /tests/fixtures/define-env-2.js: -------------------------------------------------------------------------------- 1 | self.env = Object.assign(self.env, { 2 | again: 'define-env-2', 3 | }); 4 | -------------------------------------------------------------------------------- /tests/fixtures/simple-worker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = function onmessage(message) { 2 | 3 | 'use strict'; 4 | 5 | postMessage(message.data); 6 | 7 | }; 8 | -------------------------------------------------------------------------------- /tests/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "QUnit": true, 4 | "WorkerBox": true 5 | }, 6 | "rules": { 7 | "func-names": "off", 8 | "prefer-arrow-callback": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/fixtures/environment-worker.js: -------------------------------------------------------------------------------- 1 | self.onmessage = function onmessage(message) { 2 | 3 | 'use strict'; 4 | 5 | postMessage({ 6 | message: message.data, 7 | environment: self.env, 8 | }); 9 | 10 | }; 11 | -------------------------------------------------------------------------------- /tests/fixtures/import-worker.js: -------------------------------------------------------------------------------- 1 | self.importScripts('./define-env.js'); 2 | 3 | self.onmessage = function onmessage(message) { 4 | 5 | 'use strict'; 6 | 7 | postMessage({ 8 | message: message.data, 9 | environment: self.env, 10 | }); 11 | 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "8" 5 | 6 | addons: 7 | chrome: stable 8 | 9 | branches: 10 | only: 11 | - master 12 | 13 | cache: 14 | directories: 15 | - $HOME/.npm 16 | 17 | script: 18 | - npm run lint 19 | - npm run test 20 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test_page: 'tests/index.html', 3 | launch_in_ci: [ 4 | 'Chrome', 5 | ], 6 | launch_in_dev: [ 7 | 'Chrome', 8 | ], 9 | browser_args: { 10 | Chrome: { 11 | mode: 'ci', 12 | args: [ 13 | '--disable-gpu', 14 | '--headless', 15 | '--remote-debugging-port=9222', 16 | ], 17 | }, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "parserOptions": { 4 | "sourceType": "script" 5 | }, 6 | "env":{ 7 | "browser": true 8 | }, 9 | "rules": { 10 | "no-param-reassign": [ "error", { "props": false } ], 11 | "no-restricted-globals": "off", 12 | "padded-blocks": [ "error", "always" ], 13 | "space-before-function-paren": "off", 14 | "strict": [ "error", "function" ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WorkerBox Tests 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker-box", 3 | "version": "1.1.0", 4 | "description": "A toolbox to help you test Web Workers", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "lint": "eslint '**/*.js'", 11 | "test": "testem ci", 12 | "test:dev": "testem" 13 | }, 14 | "author": "Trent Willis ", 15 | "repository": "trentmwillis/worker-box", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "eslint": "^4.6.1", 19 | "eslint-config-airbnb-base": "^12.0.0", 20 | "eslint-plugin-import": "^2.7.0", 21 | "qunitjs": "^2.4.0", 22 | "testem": "^1.18.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 1.1.0 / 2017-11-08 2 | ================== 3 | 4 | * Add unregister function for removing previous definitions 5 | 6 | 1.0.1 / 2017-09-14 7 | ================== 8 | 9 | * Add release documentation 10 | * Ensure imports from created workers are relative to location 11 | * Add CHANGELOG.md 12 | * Add a bit more of an intro to the readme 13 | * Add repo info to package.json 14 | * Add basic installation instructions 15 | * Add npm badge 16 | 17 | v1.0.0 / 2017-09-13 18 | =================== 19 | 20 | * Setup Travis 21 | * Add create function 22 | * Finish v1 implementation 23 | * Finish v1 docs 24 | * Finish vast majority of initial implementation 25 | * More initial work on stub API 26 | * Create LICENSE 27 | * Set theme jekyll-theme-cayman 28 | * Set theme jekyll-theme-minimal 29 | * Initial work 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Trent Willis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | The following details how to perform a release for `worker-box`. 4 | 5 | ## Update Changelog 6 | 7 | First, we need to update the changelog using [`git-extras`](https://github.com/tj/git-extras). 8 | 9 | ```bash 10 | git changelog 11 | ``` 12 | 13 | Be sure to cleanup the changelog by removing merge commits or any commits that don't provide meaningful information. Then, commit the changes with the following message: 14 | 15 | ```bash 16 | git commit -am "Update changelog for vx.x.x" 17 | ``` 18 | 19 | ## Tag A New Version 20 | 21 | Next, we need to tag the new version. We do this using the built in `npm` command: 22 | 23 | ```bash 24 | npm version x.x.x 25 | ``` 26 | 27 | Then, we push the new commits and tag to the repo: 28 | 29 | ``` 30 | git push origin master --tags 31 | ``` 32 | 33 | ## Publish The New Version 34 | 35 | Almost there! We now publish the new version using: 36 | 37 | ```bash 38 | npm publish 39 | ``` 40 | 41 | ## Update release notes 42 | 43 | Finally, publish the [release on GitHub](https://github.com/trentmwillis/worker-box/releases) by drafting a new release. Use the changelog to populate the entry. 44 | 45 | And that's it! Congratulations! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Worker Box [![Build Status](https://travis-ci.org/trentmwillis/worker-box.svg?branch=master)](https://travis-ci.org/trentmwillis/worker-box) [![Documentation](https://media.readthedocs.org/static/projects/badges/passing.svg)](https://pretty-okay.com/worker-box) [![NPM Version](https://badge.fury.io/js/worker-box.svg)](https://www.npmjs.com/package/worker-box) 2 | 3 | Worker Box is a toolbox to help you test [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). It provides an easy way to create, stub, and modify your Workers without complicated test servers or monkey patches. For more info, be sure to check out the [documentation](https://pretty-okay.com/worker-box/)! 4 | 5 | ## Installation 6 | 7 | Install Worker Box through [npm](https://www.npmjs.com/): 8 | 9 | ```bash 10 | npm install --save-dev worker-box 11 | ``` 12 | 13 | And then load it via a script tag in your page: 14 | 15 | ```html 16 | 17 | ``` 18 | 19 | And that's it! The `WorkerBox` global should now be available for use. 20 | 21 | ## Usage 22 | 23 | For information on how to use Worker Box and the APIs it provides, check out [the official documentation]([documentation](https://pretty-okay.com/worker-box/)). 24 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | Worker Box is a toolbox to help you test [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API). It provides an easy way to stub and modify your Workers without complicated test servers or monkey patches. 2 | 3 | ## Installation 4 | 5 | Install Worker Box through [npm](https://www.npmjs.com/): 6 | 7 | ```bash 8 | npm install --save-dev worker-box 9 | ``` 10 | 11 | And then load it via a script tag in your page: 12 | 13 | ```html 14 | 15 | ``` 16 | 17 | And that's it! The `WorkerBox` global should now be available for use. 18 | 19 | ## Setup and Cleanup 20 | 21 | To use Worker Box, you start by simply calling: 22 | 23 | ```js 24 | WorkerBox.setup(); 25 | ``` 26 | 27 | By default, this won't do much except replace your global `Worker` class with a wrapper that allows Worker Box to work its magic. 28 | 29 | On the flip side, when you're ready to stop using Worker Box (such as at the end of a test), you simply call: 30 | 31 | ```js 32 | WorkerBox.cleanup(); 33 | ``` 34 | 35 | One notable thing that you get simply for setting up Worker Box is tracking of all Worker instances that get created. This means that when you decide to cleanup Worker Box can terminate all running instances immediately. Pretty nifty. 36 | 37 | ## Stubbing A Worker 38 | 39 | You can [stub](https://en.wikipedia.org/wiki/Method_stub) a Worker by using the `stub` method. 40 | 41 | ```js 42 | WorkerBox.stub(scriptPath, { 43 | importScripts: [], 44 | code: () => {} 45 | }); 46 | ``` 47 | 48 | When trying to create a stubbed Worker, the original script will be ignored and instead you will get a Worker that either does nothing or executes code/scripts you have provided. 49 | 50 | It takes two arguments: 51 | 52 | 1. A string path to the Worker to stub. 53 | 2. An options object. 54 | 55 | The path to the Worker will be resolved relative to your current path. So, if you have tests running at `localhost:8080/tests/index.html`, then using either `/workers/my-worker.js` or `../workers/my-worker.js` will yield the same result. 56 | 57 | The options object can define two optional properties. 58 | 59 | The first is `importScripts` which allows you to specify an array of paths to other scripts which should be imported into the Worker. These script paths are resolved relative to your current path. 60 | 61 | The second is `code` which is a function that defines code to execute within the Worker. This can be any arbitrary code that you wish to execute within the Worker and will have complete access to the Worker's scope. Note that since this code is executed within the Worker you will not have access to the scope outside your function as you normally would. 62 | 63 | Note: When using both `importScripts` and `code`, the `importScripts` will be imported _before_ executing the contents of `code`. 64 | 65 | ## Prepending Code To A Worker 66 | 67 | Similarly to stubbing, you can also prepend code to a Worker by using the `prepend` method. 68 | 69 | ```js 70 | WorkerBox.prepend(scriptPath, { 71 | importScripts: [], 72 | code: () => {} 73 | }); 74 | ``` 75 | 76 | This is particularly useful for scenarios where you wish to alter some state before the Worker executes, such as setting up a mock server to handle external requests. 77 | 78 | The parameters and options for `prepend` are the same as for `stub`. The only difference is that after `importScripts` and `code` execute, the original Worker script will also execute. Note that `importScripts` executed from within your original Worker will resolve paths relative to that Worker's path as they would in normal usage. 79 | 80 | ## Unregister Prepend or Stub Definitions 81 | 82 | By default, if you prepend or stub a Worker, you can not use prepend or stub again on that same Worker. If you need to redefine the override for any reason, you can use the `unregister` method. 83 | 84 | ```js 85 | WorkerBox.stub(scriptPath); 86 | WorkerBox.unregister(scriptPath); 87 | ``` 88 | 89 | ## Creating A Worker 90 | 91 | If you want to create a Worker directly, as opposed to stubbing/modifying a Worker script, you can use the `create` function. 92 | 93 | ```js 94 | WorkerBox.create(code); 95 | ``` 96 | 97 | This will create a Worker instance from the provided function, `code`, which you can then interact with like a normal Worker just without a separate file. 98 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | window.WorkerBox = (function initWorkerBox() { 2 | 3 | 'use strict'; 4 | 5 | function stubImportScripts() { 6 | 7 | let relativeTo; 8 | 9 | const originalImportScripts = self.importScripts; 10 | self.importScripts = function stubbedImportScripts(...scripts) { 11 | 12 | scripts.forEach((script) => { 13 | 14 | originalImportScripts(new URL(script, relativeTo)); 15 | 16 | }); 17 | 18 | }; 19 | 20 | self.importScripts.relativeTo = function setImportScriptsRelativeTo(base) { 21 | 22 | relativeTo = base; 23 | 24 | }; 25 | 26 | self.importScriptAndChangeRelativity = function importScriptAndChangeRelativity(script) { 27 | 28 | const absoluteScript = new URL(script, relativeTo); 29 | self.importScripts.relativeTo(absoluteScript); 30 | self.importScripts(absoluteScript); 31 | 32 | }; 33 | 34 | } 35 | 36 | function stringifyFunction(fn) { 37 | 38 | let functionString = fn.toString(); 39 | const needsFunctionKeyword = !functionString.startsWith('function') && !functionString.startsWith('() =>'); 40 | if (needsFunctionKeyword) { 41 | 42 | functionString = `function ${functionString}`; 43 | 44 | } 45 | 46 | return `(${functionString})();`; 47 | 48 | } 49 | 50 | function createWorkerPrepend(workerDefinition, workerOptions) { 51 | 52 | const { script, options: { code, importScripts } } = workerDefinition; 53 | const src = []; 54 | 55 | src.push(`${stubImportScripts.toString()}`); 56 | src.push('stubImportScripts();'); 57 | src.push(`importScripts.relativeTo('${location.href}');`); 58 | 59 | if (importScripts.length) { 60 | 61 | const importScriptList = importScripts.map(importScript => `'${importScript}'`).join(', '); 62 | src.push(`importScripts(${importScriptList});`); 63 | 64 | } 65 | 66 | src.push(`${stringifyFunction(code)}`); 67 | src.push(`importScriptAndChangeRelativity('${script}');`); 68 | 69 | const blob = new Blob([src.join('\n')], { type: 'application/javascript' }); 70 | const url = URL.createObjectURL(blob); 71 | return new Worker(url, workerOptions); 72 | 73 | } 74 | 75 | function createWorkerStub(workerDefinition, workerOptions) { 76 | 77 | const { options: { code, importScripts } } = workerDefinition; 78 | const src = []; 79 | 80 | src.push(`${stubImportScripts.toString()}`); 81 | src.push('stubImportScripts();'); 82 | src.push(`importScripts.relativeTo('${location.href}');`); 83 | 84 | if (importScripts.length) { 85 | 86 | const importScriptList = importScripts.map(script => `'${script}'`).join(', '); 87 | src.push(`importScripts(${importScriptList});`); 88 | 89 | } 90 | 91 | src.push(stringifyFunction(code)); 92 | 93 | const blob = new Blob([src.join('\n')], { type: 'application/javascript' }); 94 | const url = URL.createObjectURL(blob); 95 | return new Worker(url, workerOptions); 96 | 97 | } 98 | 99 | const creator = { 100 | prepend: createWorkerPrepend, 101 | stub: createWorkerStub, 102 | }; 103 | 104 | const workerDefinitions = []; 105 | const workers = []; 106 | 107 | function findWorkerDefinition(script) { 108 | 109 | return workerDefinitions.find(definition => definition.script === script); 110 | 111 | } 112 | 113 | /** 114 | * Creates a Worker from the given function and options. 115 | * 116 | * @public 117 | * @param {Function} code 118 | * @param {object} workerOptions 119 | * @return {Worker} 120 | */ 121 | function create(code, workerOptions) { 122 | 123 | const src = []; 124 | 125 | src.push(`${stubImportScripts.toString()}`); 126 | src.push('stubImportScripts();'); 127 | src.push(`importScripts.relativeTo('${location.href}');`); 128 | src.push(stringifyFunction(code)); 129 | 130 | const blob = new Blob([src.join('\n')], { type: 'application/javascript' }); 131 | const url = URL.createObjectURL(blob); 132 | return new Worker(url, workerOptions); 133 | 134 | } 135 | 136 | /** 137 | * Replaces the given worker with an augmented version that can execute 138 | * additional imports and code before running the actual script. 139 | * 140 | * @public 141 | * @param {string} script 142 | * @param {object} options 143 | */ 144 | function prepend(script, options = {}) { 145 | 146 | const absoluteScript = (new URL(script, location.href)).toString(); 147 | const workerDefinition = findWorkerDefinition(absoluteScript); 148 | if (workerDefinition) { 149 | 150 | throw new Error(`The Worker script "${script}" has already been registered with "${workerDefinition.type}".`); 151 | 152 | } 153 | 154 | options.importScripts = options.importScripts || []; 155 | options.code = options.code || (() => {}); 156 | 157 | workerDefinitions.push({ 158 | type: 'prepend', 159 | script: absoluteScript, 160 | options, 161 | }); 162 | 163 | } 164 | 165 | /** 166 | * Replaces the given worker with an empty version or the specified function. 167 | * 168 | * @public 169 | * @param {string} script 170 | * @param {object} options 171 | */ 172 | function stub(script, options = {}) { 173 | 174 | const absoluteScript = (new URL(script, location.href)).toString(); 175 | const workerDefinition = findWorkerDefinition(absoluteScript); 176 | if (workerDefinition) { 177 | 178 | throw new Error(`The Worker script "${script}" has already been registered with "${workerDefinition.type}".`); 179 | 180 | } 181 | 182 | options.importScripts = options.importScripts || []; 183 | options.code = options.code || (() => {}); 184 | 185 | workerDefinitions.push({ 186 | type: 'stub', 187 | script: absoluteScript, 188 | options, 189 | }); 190 | 191 | } 192 | 193 | 194 | /** 195 | * Unregisters a worker definition that was previously registered with either 196 | * prepend or stub. Returns a boolean indicating whether or not a registration 197 | * was removed. 198 | * 199 | * @public 200 | * @param {string} script 201 | * @return {boolean} 202 | */ 203 | function unregister(script) { 204 | 205 | const absoluteScript = (new URL(script, location.href)).toString(); 206 | const index = workerDefinitions.findIndex(definition => definition.script === absoluteScript); 207 | 208 | if (index !== -1) { 209 | 210 | workerDefinitions.splice(index, 1); 211 | return true; 212 | 213 | } 214 | 215 | return false; 216 | 217 | } 218 | 219 | function setup() { 220 | 221 | if (self.Worker.isWorkerBox) { 222 | 223 | return; 224 | 225 | } 226 | 227 | function FakeWorker(script, workerOptions) { 228 | 229 | const absoluteScript = (new URL(script, location.href)).toString(); 230 | const workerDefinition = findWorkerDefinition(absoluteScript); 231 | let worker; 232 | if (workerDefinition) { 233 | 234 | worker = creator[workerDefinition.type](workerDefinition, workerOptions); 235 | 236 | } else { 237 | 238 | worker = new FakeWorker.Original(script, workerOptions); 239 | 240 | } 241 | 242 | workers.push(worker); 243 | 244 | return worker; 245 | 246 | } 247 | FakeWorker.isWorkerBox = true; 248 | FakeWorker.Original = self.Worker; 249 | 250 | self.Worker = FakeWorker; 251 | 252 | } 253 | 254 | function cleanup() { 255 | 256 | if (self.Worker.isWorkerBox) { 257 | 258 | workerDefinitions.length = 0; 259 | workers.forEach(worker => worker.terminate()); 260 | workers.length = 0; 261 | self.Worker = self.Worker.Original; 262 | 263 | } 264 | 265 | } 266 | 267 | return { 268 | cleanup, 269 | create, 270 | prepend, 271 | setup, 272 | stub, 273 | unregister, 274 | }; 275 | 276 | }()); 277 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | QUnit.module('WorkerBox', function(hooks) { 2 | 3 | 'use strict'; 4 | 5 | hooks.afterEach(function() { 6 | 7 | WorkerBox.cleanup(); 8 | 9 | }); 10 | 11 | QUnit.test('ensure /tests/fixtures/simple-worker.js behaves as expected', async function(assert) { 12 | 13 | const done = assert.async(); 14 | const originalMessage = 'foo'; 15 | const worker = new Worker('/tests/fixtures/simple-worker.js'); 16 | worker.onmessage = (message) => { 17 | 18 | assert.strictEqual(message.data, originalMessage); 19 | worker.terminate(); 20 | done(); 21 | 22 | }; 23 | worker.postMessage(originalMessage); 24 | 25 | }); 26 | 27 | QUnit.test('ensure /tests/fixtures/environment-worker.js behaves as expected', async function(assert) { 28 | 29 | const done = assert.async(); 30 | const originalMessage = 'foo'; 31 | const worker = new Worker('/tests/fixtures/environment-worker.js'); 32 | worker.onmessage = (message) => { 33 | 34 | assert.deepEqual(message.data, { 35 | message: originalMessage, 36 | environment: undefined, 37 | }); 38 | worker.terminate(); 39 | done(); 40 | 41 | }; 42 | worker.postMessage(originalMessage); 43 | 44 | }); 45 | 46 | QUnit.module('setup', function() { 47 | 48 | QUnit.test('replaces the global Worker with a FakeWorker', function(assert) { 49 | 50 | const originalWorker = self.Worker; 51 | assert.notOk('isWorkerBox' in Worker); 52 | assert.notOk('Original' in Worker); 53 | 54 | WorkerBox.setup(); 55 | 56 | assert.ok('isWorkerBox' in Worker); 57 | assert.ok('Original' in Worker); 58 | 59 | assert.notStrictEqual(Worker, originalWorker); 60 | assert.strictEqual(Worker.Original, originalWorker); 61 | 62 | }); 63 | 64 | }); 65 | 66 | QUnit.module('create', function() { 67 | 68 | QUnit.test('creates a worker from the given function', function(assert) { 69 | 70 | const done = assert.async(); 71 | const originalMessage = 'foo'; 72 | const worker = WorkerBox.create(() => { 73 | 74 | self.onmessage = function onmessage(message) { 75 | 76 | postMessage(`it works, ${message.data}!`); 77 | 78 | }; 79 | 80 | }); 81 | worker.onmessage = (message) => { 82 | 83 | assert.strictEqual(message.data, 'it works, foo!'); 84 | worker.terminate(); 85 | done(); 86 | 87 | }; 88 | worker.postMessage(originalMessage); 89 | 90 | }); 91 | 92 | QUnit.test('imports scripts relative to the current url', function(assert) { 93 | 94 | const done = assert.async(); 95 | const originalMessage = 'foo'; 96 | const worker = WorkerBox.create(() => { 97 | 98 | self.importScripts('/tests/fixtures/define-env.js'); 99 | self.importScripts('./fixtures/environment-worker.js'); 100 | 101 | }); 102 | worker.onmessage = (message) => { 103 | 104 | assert.deepEqual(message.data, { 105 | environment: { 106 | from: 'define-env', 107 | }, 108 | message: 'foo', 109 | }); 110 | worker.terminate(); 111 | done(); 112 | 113 | }; 114 | worker.postMessage(originalMessage); 115 | 116 | }); 117 | 118 | }); 119 | 120 | QUnit.module('stub', function(nestedHooks) { 121 | 122 | nestedHooks.beforeEach(function() { 123 | 124 | this.baseWorker = new Worker('/tests/fixtures/simple-worker.js'); 125 | 126 | }); 127 | 128 | nestedHooks.afterEach(function() { 129 | 130 | this.baseWorker.terminate(); 131 | 132 | }); 133 | 134 | QUnit.test('replaces the specified script with a no-op', function(assert) { 135 | 136 | assert.expect(0); 137 | 138 | WorkerBox.setup(); 139 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 140 | 141 | const originalMessage = 'foo'; 142 | const worker = new Worker('/tests/fixtures/simple-worker.js'); 143 | worker.onmessage = () => assert.ok(false); 144 | worker.postMessage(originalMessage); 145 | 146 | const done = assert.async(); 147 | this.baseWorker.onmessage = done; 148 | this.baseWorker.postMessage(originalMessage); 149 | 150 | }); 151 | 152 | QUnit.test('replaces the specified script with the specified code', function(assert) { 153 | 154 | WorkerBox.setup(); 155 | WorkerBox.stub('/tests/fixtures/simple-worker.js', { 156 | code() { 157 | 158 | self.onmessage = function onmessage(message) { 159 | 160 | postMessage(`stubbed ${message.data}`); 161 | 162 | }; 163 | 164 | }, 165 | }); 166 | 167 | const done = assert.async(); 168 | const originalMessage = 'foo'; 169 | const worker = new Worker('/tests/fixtures/simple-worker.js'); 170 | worker.onmessage = (message) => { 171 | 172 | assert.equal(message.data, `stubbed ${originalMessage}`); 173 | done(); 174 | 175 | }; 176 | worker.postMessage(originalMessage); 177 | 178 | }); 179 | 180 | QUnit.test('replaces the specified script with the specified code and imports before it executes', function(assert) { 181 | 182 | WorkerBox.setup(); 183 | WorkerBox.stub('/tests/fixtures/simple-worker.js', { 184 | importScripts: ['/tests/fixtures/define-env.js'], 185 | code() { 186 | 187 | self.onmessage = function onmessage() { 188 | 189 | postMessage(self.env); 190 | 191 | }; 192 | 193 | }, 194 | }); 195 | 196 | const done = assert.async(); 197 | const originalMessage = 'foo'; 198 | const worker = new Worker('/tests/fixtures/simple-worker.js'); 199 | worker.onmessage = (message) => { 200 | 201 | assert.deepEqual(message.data, { from: 'define-env' }); 202 | done(); 203 | 204 | }; 205 | worker.postMessage(originalMessage); 206 | 207 | }); 208 | 209 | QUnit.test('throws an error if script has already been registered', function(assert) { 210 | 211 | WorkerBox.setup(); 212 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 213 | assert.throws(() => WorkerBox.stub('/tests/fixtures/simple-worker.js'), /The Worker script "\/tests\/fixtures\/simple-worker.js" has already been registered with "stub"/); 214 | 215 | }); 216 | 217 | QUnit.test('throws an error if script has already been registered with a different relative path', function(assert) { 218 | 219 | WorkerBox.setup(); 220 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 221 | assert.throws(() => WorkerBox.stub('../../tests/fixtures/simple-worker.js'), /The Worker script "..\/..\/tests\/fixtures\/simple-worker.js" has already been registered with "stub"/); 222 | 223 | }); 224 | 225 | QUnit.test('imported scripts are resolved relative to the current path', function(assert) { 226 | 227 | WorkerBox.setup(); 228 | WorkerBox.stub('/tests/fixtures/simple-worker.js', { 229 | importScripts: ['../../tests/fixtures/define-env.js'], 230 | code() { 231 | 232 | self.importScripts('../../tests/fixtures/define-env-2.js'); 233 | self.onmessage = function onmessage() { 234 | 235 | postMessage(self.env); 236 | 237 | }; 238 | 239 | }, 240 | }); 241 | 242 | const done = assert.async(); 243 | const originalMessage = 'foo'; 244 | const worker = new Worker('/tests/fixtures/simple-worker.js'); 245 | worker.onmessage = (message) => { 246 | 247 | assert.deepEqual(message.data, { 248 | from: 'define-env', 249 | again: 'define-env-2', 250 | }); 251 | done(); 252 | 253 | }; 254 | worker.postMessage(originalMessage); 255 | 256 | }); 257 | 258 | }); 259 | 260 | QUnit.module('prepend', function() { 261 | 262 | QUnit.test('prepends the specified code to the specified script', function(assert) { 263 | 264 | WorkerBox.setup(); 265 | WorkerBox.prepend('/tests/fixtures/environment-worker.js', { 266 | code() { 267 | 268 | self.env = 'derp;herp'; 269 | 270 | }, 271 | }); 272 | 273 | const done = assert.async(); 274 | const originalMessage = 'foo'; 275 | const worker = new Worker('/tests/fixtures/environment-worker.js'); 276 | worker.onmessage = (message) => { 277 | 278 | assert.deepEqual(message.data, { 279 | message: originalMessage, 280 | environment: 'derp;herp', 281 | }); 282 | worker.terminate(); 283 | done(); 284 | 285 | }; 286 | worker.postMessage(originalMessage); 287 | 288 | }); 289 | 290 | QUnit.test('prepends the specified imports to the specified script', function(assert) { 291 | 292 | WorkerBox.setup(); 293 | WorkerBox.prepend('/tests/fixtures/environment-worker.js', { 294 | importScripts: ['/tests/fixtures/define-env.js'], 295 | }); 296 | 297 | const done = assert.async(); 298 | const originalMessage = 'foo'; 299 | const worker = new Worker('/tests/fixtures/environment-worker.js'); 300 | worker.onmessage = (message) => { 301 | 302 | assert.deepEqual(message.data, { 303 | message: originalMessage, 304 | environment: { 305 | from: 'define-env', 306 | }, 307 | }); 308 | worker.terminate(); 309 | done(); 310 | 311 | }; 312 | worker.postMessage(originalMessage); 313 | 314 | }); 315 | 316 | QUnit.test('prepends the specified code and imports to the specified script', function(assert) { 317 | 318 | WorkerBox.setup(); 319 | WorkerBox.prepend('/tests/fixtures/environment-worker.js', { 320 | importScripts: ['/tests/fixtures/define-env.js'], 321 | code() { 322 | 323 | self.env = Object.assign(self.env, { prepend: 'derp;herp' }); 324 | 325 | }, 326 | }); 327 | 328 | const done = assert.async(); 329 | const originalMessage = 'foo'; 330 | const worker = new Worker('/tests/fixtures/environment-worker.js'); 331 | worker.onmessage = (message) => { 332 | 333 | assert.deepEqual(message.data, { 334 | message: originalMessage, 335 | environment: { 336 | from: 'define-env', 337 | prepend: 'derp;herp', 338 | }, 339 | }); 340 | worker.terminate(); 341 | done(); 342 | 343 | }; 344 | worker.postMessage(originalMessage); 345 | 346 | }); 347 | 348 | QUnit.test('throws an error if script has already been registered', function(assert) { 349 | 350 | WorkerBox.setup(); 351 | WorkerBox.prepend('/tests/fixtures/simple-worker.js'); 352 | assert.throws(() => WorkerBox.prepend('/tests/fixtures/simple-worker.js'), /The Worker script "\/tests\/fixtures\/simple-worker.js" has already been registered with "prepend"/); 353 | 354 | }); 355 | 356 | QUnit.test('throws an error if script has already been registered with a different relative path', function(assert) { 357 | 358 | WorkerBox.setup(); 359 | WorkerBox.prepend('/tests/fixtures/simple-worker.js'); 360 | assert.throws(() => WorkerBox.prepend('../../tests/fixtures/simple-worker.js'), /The Worker script "..\/..\/tests\/fixtures\/simple-worker.js" has already been registered with "prepend"/); 361 | 362 | }); 363 | 364 | QUnit.test('imported scripts are resolved relative to the current path', function(assert) { 365 | 366 | WorkerBox.setup(); 367 | WorkerBox.prepend('/tests/fixtures/environment-worker.js', { 368 | importScripts: ['../../tests/fixtures/define-env.js'], 369 | code() { 370 | 371 | self.importScripts('../../tests/fixtures/define-env-2.js'); 372 | 373 | }, 374 | }); 375 | 376 | const done = assert.async(); 377 | const originalMessage = 'foo'; 378 | const worker = new Worker('/tests/fixtures/environment-worker.js'); 379 | worker.onmessage = (message) => { 380 | 381 | assert.deepEqual(message.data, { 382 | message: originalMessage, 383 | environment: { 384 | from: 'define-env', 385 | again: 'define-env-2', 386 | }, 387 | }); 388 | worker.terminate(); 389 | done(); 390 | 391 | }; 392 | worker.postMessage(originalMessage); 393 | 394 | }); 395 | 396 | QUnit.test('imported scripts within original worker are resolved relative to the worker\'s path', function(assert) { 397 | 398 | WorkerBox.setup(); 399 | WorkerBox.prepend('/tests/fixtures/import-worker.js'); 400 | 401 | const done = assert.async(); 402 | const originalMessage = 'foo'; 403 | const worker = new Worker('/tests/fixtures/import-worker.js'); 404 | worker.onmessage = (message) => { 405 | 406 | assert.deepEqual(message.data, { 407 | message: originalMessage, 408 | environment: { 409 | from: 'define-env', 410 | }, 411 | }); 412 | worker.terminate(); 413 | done(); 414 | 415 | }; 416 | worker.postMessage(originalMessage); 417 | 418 | }); 419 | 420 | }); 421 | 422 | QUnit.module('unregister', function() { 423 | 424 | QUnit.test('allows you to re-prepend a worker', function(assert) { 425 | 426 | WorkerBox.setup(); 427 | 428 | WorkerBox.prepend('/tests/fixtures/simple-worker.js'); 429 | assert.throws(() => WorkerBox.prepend('/tests/fixtures/simple-worker.js'), /The Worker script "\/tests\/fixtures\/simple-worker.js" has already been registered with "prepend"/); 430 | 431 | WorkerBox.unregister('/tests/fixtures/simple-worker.js'); 432 | WorkerBox.prepend('/tests/fixtures/simple-worker.js'); 433 | 434 | }); 435 | 436 | QUnit.test('allows you to re-stub a worker', function(assert) { 437 | 438 | WorkerBox.setup(); 439 | 440 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 441 | assert.throws(() => WorkerBox.stub('/tests/fixtures/simple-worker.js'), /The Worker script "\/tests\/fixtures\/simple-worker.js" has already been registered with "stub"/); 442 | 443 | WorkerBox.unregister('/tests/fixtures/simple-worker.js'); 444 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 445 | 446 | }); 447 | 448 | QUnit.test('works with different relative paths', function(assert) { 449 | 450 | WorkerBox.setup(); 451 | 452 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 453 | assert.throws(() => WorkerBox.stub('/tests/fixtures/simple-worker.js'), /The Worker script "\/tests\/fixtures\/simple-worker.js" has already been registered with "stub"/); 454 | 455 | WorkerBox.unregister('../../tests/fixtures/simple-worker.js'); 456 | WorkerBox.stub('/tests/fixtures/simple-worker.js'); 457 | 458 | }); 459 | 460 | }); 461 | 462 | }); 463 | --------------------------------------------------------------------------------