diff options
Diffstat (limited to 'spec/frontend/__helpers__/web_worker_fake.js')
-rw-r--r-- | spec/frontend/__helpers__/web_worker_fake.js | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/spec/frontend/__helpers__/web_worker_fake.js b/spec/frontend/__helpers__/web_worker_fake.js new file mode 100644 index 00000000000..041a9bd8540 --- /dev/null +++ b/spec/frontend/__helpers__/web_worker_fake.js @@ -0,0 +1,71 @@ +import path from 'path'; + +const isRelative = (pathArg) => pathArg.startsWith('.'); + +const transformRequirePath = (base, pathArg) => { + if (!isRelative(pathArg)) { + return pathArg; + } + + return path.resolve(base, pathArg); +}; + +const createRelativeRequire = (filename) => { + const rel = path.relative(__dirname, path.dirname(filename)); + const base = path.resolve(__dirname, rel); + + // reason: Dynamic require should be fine here since the code is dynamically evaluated anyways. + // eslint-disable-next-line import/no-dynamic-require, global-require + return (pathArg) => require(transformRequirePath(base, pathArg)); +}; + +/** + * Simulates a WebWorker module similar to the kind created by Webpack's [`worker-loader`][1] + * + * [1]: https://webpack.js.org/loaders/worker-loader/ + */ +export class FakeWebWorker { + /** + * Constructs a new FakeWebWorker instance + * + * @param {String} filename is the full path of the code, which is used to resolve relative imports. + * @param {String} code is the raw code of the web worker, which is dynamically evaluated on construction. + */ + constructor(filename, code) { + let isAlive = true; + + const clientTarget = new EventTarget(); + const workerTarget = new EventTarget(); + + this.addEventListener = (...args) => clientTarget.addEventListener(...args); + this.removeEventListener = (...args) => clientTarget.removeEventListener(...args); + this.postMessage = (message) => { + if (!isAlive) { + return; + } + + workerTarget.dispatchEvent(new MessageEvent('message', { data: message })); + }; + this.terminate = () => { + isAlive = false; + }; + + const workerScope = { + addEventListener: (...args) => workerTarget.addEventListener(...args), + removeEventListener: (...args) => workerTarget.removeEventListener(...args), + postMessage: (message) => { + if (!isAlive) { + return; + } + + clientTarget.dispatchEvent(new MessageEvent('message', { data: message })); + }, + }; + + // reason: `no-new-func` is like `eval` except it only executed on global scope and it's easy + // to pass in local references. `eval` is very unsafe in production, but in our test environment + // we shold be fine. + // eslint-disable-next-line no-new-func + Function('self', 'require', code)(workerScope, createRelativeRequire(filename)); + } +} |