diff --git a/packages/portal/package.json b/packages/portal/package.json index d05e79a..9b1426c 100644 --- a/packages/portal/package.json +++ b/packages/portal/package.json @@ -15,7 +15,8 @@ "build:umd:gzip": "npm run build:umd:min && gzip -c9 dist/worker-portal.min.js > dist/worker-portal.min.js.gz", "build": "npm run build:umd:gzip && ls -l dist/", - "prepublish": "npm run clean && npm run build" + "prepublish": "npm run clean && npm run build", + "test": "npm run build:lib && ava --verbose" }, "repository": { "type": "git", @@ -29,6 +30,7 @@ }, "homepage": "https://gitlab.com/explorigin/worker-portal", "devDependencies": { + "ava": "^0.17.0", "babel-cli": "6.18.0", "babel-core": "6.21.0", "babel-eslint": "7.1.1", diff --git a/packages/portal/src/index.js b/packages/portal/src/index.js index f787777..ac61314 100644 --- a/packages/portal/src/index.js +++ b/packages/portal/src/index.js @@ -12,14 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -export function isWorker() { - try { - return self !== window; - } catch (e) { - return true; - } -} - function tryJSON(type, destination, id, data) { const packet = [type, destination, id, data]; try { @@ -29,7 +21,7 @@ function tryJSON(type, destination, id, data) { } } -export function WorkerPortal(context, worker, serialize) { +export function WorkerPortal(context, worker, isSlave, serialize) { const responseMap = new Map(); const _worker = worker || self; const contextIndex = Object.keys(context); @@ -84,29 +76,25 @@ export function WorkerPortal(context, worker, serialize) { } function injectionPointFactory(fnId, callbackFactory) { - return () => - new Promise((resolve, reject) => { - if (!enabled) { + return function(...args) { + return new Promise((resolve, reject) => { + if (!enabled && fnId !== 0) { reject(new Error('Portal disabled')); } const id = callCount; callCount += 1; responseMap.set(id, [callbackFactory ? callbackFactory(resolve) : resolve, reject]); - post(0, id, fnId, Array.from(arguments)); + post(0, id, fnId, args); }); + }; } function resolveExternalInterfaceFactory(resolve) { - return linkedFunctionNames => { + return (linkedFunctionNames, ...rest) => { const externalInterface = {}; linkedFunctionNames.forEach((fnName, index) => { externalInterface[fnName] = injectionPointFactory(index); }); - if (!isWorker()) { - externalInterface._cleanup = injectionPointFactory(linkedFunctionNames.length, resolve => - resolve(cleanup()) - ); - } enabled = true; resolve(externalInterface); return contextIndex; @@ -125,13 +113,16 @@ export function WorkerPortal(context, worker, serialize) { _worker.addEventListener('message', dispatcher); - if (isWorker()) { + if (isSlave) { return new Promise(resolve => { - contextIndex.splice(0, 0, '__init', '__cleanup'); + contextIndex.splice(0, 0, '__init', '__cleanupSlave'); context.__init = resolveExternalInterfaceFactory(resolve); - context.__cleanup = cleanup; + context.__cleanupSlave = cleanup; }); } - return injectionPointFactory(0, resolveExternalInterfaceFactory)(contextIndex); + return injectionPointFactory(0, resolveExternalInterfaceFactory)(contextIndex).then(api => ({ + ...api, + _cleanup: injectionPointFactory(1, resolve => resolve(cleanup())) + })); } diff --git a/packages/portal/test.js b/packages/portal/test.js new file mode 100644 index 0000000..c0e23e0 --- /dev/null +++ b/packages/portal/test.js @@ -0,0 +1,80 @@ +import test from 'ava'; + +import { WorkerPortal } from './lib'; + +function FakeWorkerPair() { + let cbA = null; + let cbB = null; + + const objA = { + postMessage: data => { + cbB({ data: data }); + }, + addEventListener: (eventName, fn) => { + cbA = (...r) => { + fn(...r); + }; + }, + removeEventListener: (eventName, fn) => { + cbA = null; + } + }; + + const objB = { + postMessage: data => { + cbA({ data: data }); + }, + addEventListener: (eventName, fn) => { + cbB = (...r) => { + fn(...r); + }; + }, + removeEventListener: (eventName, fn) => { + cbB = null; + } + }; + + return [objA, objB]; +} + +test('Workers can call and respond equally', async t => { + const [a, b] = FakeWorkerPair(); + + const slave = WorkerPortal( + { + slaveAdd: (a, b) => a + b + }, + a, + true + ); + const master = WorkerPortal( + { + masterSubtract: (a, b) => a - b + }, + b, + false + ); + + const masterApi = await master; + const slaveApi = await slave; + + t.deepEqual(Object.keys(masterApi), ['__init', '__cleanupSlave', 'slaveAdd', '_cleanup']); + t.deepEqual(Object.keys(slaveApi), ['masterSubtract']); + + t.is(await slaveApi.masterSubtract(9, 2), 7); + t.is(await masterApi.slaveAdd(9, 2), 11); + + t.is(await masterApi.slaveAdd(5, 2), 7); + t.is(await slaveApi.masterSubtract(2, 2), 0); + + await masterApi._cleanup(); + + return masterApi + .slaveAdd(9, 2) + .then(e => { + t.fail('Expected rejection'); + }) + .catch(e => { + t.is(e.message, 'Portal disabled'); + }); +});