Commit 35fc303b authored by Eduardo's avatar Eduardo

Apply suggestions from second code review

Co-authored-by: 's avatarRobert Knight <robertknight@gmail.com>
parent 3fb6843d
import { ListenerCollection } from './listener-collection'; import { ListenerCollection } from './listener-collection';
import { isMessageEqual } from './port-util'; import { isMessageEqual } from './port-util';
const MAX_WAIT_FOR_PORT = 1000 * 30; const MAX_WAIT_FOR_PORT = 1000 * 5;
const POLLING_INTERVAL_FOR_PORT = 250; const POLLING_INTERVAL_FOR_PORT = 250;
/** /**
...@@ -12,14 +12,14 @@ const POLLING_INTERVAL_FOR_PORT = 250; ...@@ -12,14 +12,14 @@ const POLLING_INTERVAL_FOR_PORT = 250;
*/ */
/** /**
* PortFinder helps to discover `MessagePort` on a specific channel. * PortFinder is used by non-host frames in the client to establish a
* MessagePort-based connection to other frames. It is used together with
* PortProvider which runs in the host frame. See PortProvider for an overview.
* *
* Channel nomenclature is `[frame1]-[frame2]` so that: * Channel nomenclature is `[frame1]-[frame2]` so that:
* - `port1` should be owned by/transferred to `frame1`, and * - `port1` should be owned by/transferred to `frame1`, and
* - `port2` should be owned by/transferred to `frame2` * - `port2` should be owned by/transferred to `frame2`
* *
* There should be the same amount of listener in this class as in PortProvider.
*
* @implements Destroyable * @implements Destroyable
*/ */
export class PortFinder { export class PortFinder {
...@@ -32,7 +32,7 @@ export class PortFinder { ...@@ -32,7 +32,7 @@ export class PortFinder {
} }
/** /**
* Polls the hostFrame for a specific port and returns a Promise of the port. * Request a specific port from `hostFrame`
* *
* @param {object} options * @param {object} options
* @param {Channel} options.channel - requested channel * @param {Channel} options.channel - requested channel
...@@ -41,7 +41,7 @@ export class PortFinder { ...@@ -41,7 +41,7 @@ export class PortFinder {
* @param {Port} options.port - requested port * @param {Port} options.port - requested port
* @return {Promise<MessagePort>} * @return {Promise<MessagePort>}
*/ */
discover({ channel, hostFrame, port }) { async discover({ channel, hostFrame, port }) {
let isValidRequest = false; let isValidRequest = false;
if ( if (
(channel === 'guest-host' && port === 'guest') || (channel === 'guest-host' && port === 'guest') ||
...@@ -52,12 +52,11 @@ export class PortFinder { ...@@ -52,12 +52,11 @@ export class PortFinder {
isValidRequest = true; isValidRequest = true;
} }
return new Promise((resolve, reject) => {
if (!isValidRequest) { if (!isValidRequest) {
reject(new Error('Invalid request of channel/port')); throw new Error('Invalid request of channel/port');
return;
} }
return new Promise((resolve, reject) => {
function postRequest() { function postRequest() {
hostFrame.postMessage( hostFrame.postMessage(
{ channel, port, source: 'hypothesis', type: 'request' }, { channel, port, source: 'hypothesis', type: 'request' },
...@@ -65,13 +64,16 @@ export class PortFinder { ...@@ -65,13 +64,16 @@ export class PortFinder {
); );
} }
const intervalId = setInterval( // In some situations, because `guest` iframe/s load in parallel to the `host`
() => postRequest(), // frame, we can not assume that the code in the `host` frame is executed before
POLLING_INTERVAL_FOR_PORT // the code in a `guest` frame. Hence, we can't assume that `PortProvider` (in
); // the `host` frame) is initialized before `PortFinder` (in the non-host frames).
// Therefore, for the `PortFinder`, we implement a polling strategy (sending a
// message every N milliseconds) until a response is received.
const intervalId = setInterval(postRequest, POLLING_INTERVAL_FOR_PORT);
// The `host` frame maybe busy, that's why we should wait. // The `host` frame maybe busy, that's why we should wait.
const timeoutId = window.setTimeout(() => { const timeoutId = setTimeout(() => {
clearInterval(intervalId); clearInterval(intervalId);
reject( reject(
new Error(`Unable to find '${port}' port on '${channel}' channel`) new Error(`Unable to find '${port}' port on '${channel}' channel`)
......
...@@ -4,7 +4,6 @@ import { ListenerCollection } from './listener-collection'; ...@@ -4,7 +4,6 @@ import { ListenerCollection } from './listener-collection';
import { isMessageEqual } from './port-util'; import { isMessageEqual } from './port-util';
/** /**
* @typedef {import('../types/config').SidebarConfig} SidebarConfig
* @typedef {import('../types/annotator').Destroyable} Destroyable * @typedef {import('../types/annotator').Destroyable} Destroyable
* @typedef {import('./port-util').Message} Message * @typedef {import('./port-util').Message} Message
* @typedef {Message['channel']} Channel * @typedef {Message['channel']} Channel
...@@ -12,18 +11,18 @@ import { isMessageEqual } from './port-util'; ...@@ -12,18 +11,18 @@ import { isMessageEqual } from './port-util';
*/ */
/** /**
* PortProvider creates a `MessageChannel` for the communication between two * PortProvider creates a `MessageChannel` for communication between two
* frames. * frames.
* *
* There are 4 types of frames: * There are 4 types of frames:
* - `host`: frame where the hypothesis client is initially loaded. * - `host`: frame where the hypothesis client is initially loaded.
* - `guest/s`: frame/s with annotatable content. In some instances the `guest` * - `guest`: frames with annotatable content. In some instances a `guest`
* frame can be the same as the `host` frame, in other cases, it is an iframe * frame can be the same as the `host` frame, in other cases, it is an iframe
* where either (1) the hypothesis client has been injected or (2) the * where either (1) the hypothesis client has been injected or (2) the
* hypothesis client has been configured to act exclusively as a `guest` (not * hypothesis client has been configured to act exclusively as a `guest` (not
* showing the sidebar). * showing the sidebar).
* - `notebook`: it is an another hypothesis app that runs on a separate iframe. * - `notebook`: another hypothesis app that runs in a separate iframe.
* - `sidebar`: the main hypothesis app. It runs on an iframe on a different * - `sidebar`: the main hypothesis app. It runs in an iframe on a different
* origin than the host and is responsible for the communication with the * origin than the host and is responsible for the communication with the
* backend (fetching and saving annotations). * backend (fetching and saving annotations).
* *
...@@ -32,9 +31,9 @@ import { isMessageEqual } from './port-util'; ...@@ -32,9 +31,9 @@ import { isMessageEqual } from './port-util';
* `host` frame * `host` frame
* |-> `sidebar` iframe * |-> `sidebar` iframe
* |-> `notebook` iframe * |-> `notebook` iframe
* |-> [`guest` iframe/s] * |-> [`guest` iframes]
* *
* Currently, we support the communication between the following pair of frames: * Currently, we support the communication between the following pairs of frames:
* - `guest-host` * - `guest-host`
* - `guest-sidebar` * - `guest-sidebar`
* - `host-sidebar` * - `host-sidebar`
...@@ -46,16 +45,15 @@ import { isMessageEqual } from './port-util'; ...@@ -46,16 +45,15 @@ import { isMessageEqual } from './port-util';
* particular `MessagePort` and dispatches the corresponding `MessagePort`. * particular `MessagePort` and dispatches the corresponding `MessagePort`.
* *
* *
* PortProvider | PortFinder * PortFinder (non-host frame) | PortProvider (host frame)
* -------------------------------------------------------------------|------------------------------------------------------ * -----------------------------------------------------------------------------------------------
* * 1. Request `MessagePort` via `window.postMessage` ---> 2. Receive request and create port pair
* 2. listens to requests of `MessagePort` <---------------------------- 1. request a `MessagePort` using `window#postMessage`
* |
* V
* 3. sends offers of `MessagePort` using `event#source#postMessage` ---> 4. listens to offers of `MessagePort`
* | * |
* V * V
* 5. send reciprocal port to the `sidebar` frame using the `host-sidebar` * 4. Receive requested port <---------------------- 3. Send first port to requesting frame
* 5. Send second port to other frame
* (eg. via MessageChannel connection
* between host and other frame)
* *
* Channel nomenclature is `[frame1]-[frame2]` so that: * Channel nomenclature is `[frame1]-[frame2]` so that:
* - `port1` should be owned by/transferred to `frame1`, and * - `port1` should be owned by/transferred to `frame1`, and
...@@ -63,20 +61,6 @@ import { isMessageEqual } from './port-util'; ...@@ -63,20 +61,6 @@ import { isMessageEqual } from './port-util';
* *
* @implements Destroyable * @implements Destroyable
*/ */
/*
* In some situations, because `guest` iframe/s load in parallel to the `host`
* frame, we can not assume that the code in the `host` frame is executed before
* the code in a `guest` frame. Hence, we can't assume that `PortProvider` (in
* the `host` frame) is initialized before `PortFinder` (in the `guest` frame).
* Therefore, for the `PortFinder`, we implement a polling strategy (sending a
* message every N milliseconds) until a response is received.
*
* Two important characteristics of `MessagePort`:
* - it can only be used by one frame: in Chrome the port is neutered if transferred twice
* - messages are queued until the other port is ready to listen (`port.start()`)
*/
export class PortProvider { export class PortProvider {
/** /**
* @param {string} hypothesisAppsOrigin - the origin of the hypothesis apps * @param {string} hypothesisAppsOrigin - the origin of the hypothesis apps
...@@ -96,6 +80,12 @@ export class PortProvider { ...@@ -96,6 +80,12 @@ export class PortProvider {
/** @type {Map<Channel, Map<Window, MessageChannel>>} */ /** @type {Map<Channel, Map<Window, MessageChannel>>} */
this._channels = new Map(); this._channels = new Map();
// Two important characteristics of `MessagePort`:
// - Once created, a MessagePort can only be transferred to a different
// frame once, and if any frame attempts to transfer it again it gets
// neutered.
// - Messages are queued until the other port is ready to listen (`port.start()`)
// Create the `host-sidebar` channel immediately, while other channels are // Create the `host-sidebar` channel immediately, while other channels are
// created on demand // created on demand
this._hostSidebarChannel = new MessageChannel(); this._hostSidebarChannel = new MessageChannel();
...@@ -129,11 +119,7 @@ export class PortProvider { ...@@ -129,11 +119,7 @@ export class PortProvider {
return false; return false;
} }
if (!isMessageEqual(data, allowedMessage)) { return isMessageEqual(data, allowedMessage);
return false;
}
return true;
} }
/** /**
...@@ -141,30 +127,32 @@ export class PortProvider { ...@@ -141,30 +127,32 @@ export class PortProvider {
* *
* @param {MessageEvent} event * @param {MessageEvent} event
* @param {Message} message - the message to be sent. * @param {Message} message - the message to be sent.
* @param {MessagePort} port - the port to be sent via `window#postMessage` * @param {MessagePort} port - the port requested.
* (the origin is set to match the MessageEvent's origin)frame that sends the initial request th * @param {MessagePort} [counterpartPort] - if a counterpart port is provided,
* @param {MessagePort} [reciprocalPort] - if a reciprocal port is provided, * send this port either, (1) to the `sidebar` frame using the `host-sidebar`
* send this port (1) to the `sidebar` frame using the `host-sidebar`
* channel or (2) through the `onHostPortRequest` event listener. * channel or (2) through the `onHostPortRequest` event listener.
*/ */
_sendPort(event, message, port, reciprocalPort) { _sendPort(event, message, port, counterpartPort) {
const source = /** @type {Window} */ (event.source); const source = /** @type {Window} */ (event.source);
source.postMessage(message, event.origin, [port]); source.postMessage(message, event.origin, [port]);
if (reciprocalPort) { if (!counterpartPort) {
return;
}
if (['notebook-sidebar', 'guest-sidebar'].includes(message.channel)) { if (['notebook-sidebar', 'guest-sidebar'].includes(message.channel)) {
this._hostSidebarChannel.port1.postMessage(message, [reciprocalPort]); this._hostSidebarChannel.port1.postMessage(message, [counterpartPort]);
} }
if (message.channel === 'guest-host' && message.port === 'guest') { if (message.channel === 'guest-host' && message.port === 'guest') {
this._emitter.emit('hostPortRequest', reciprocalPort, message.port); this._emitter.emit('hostPortRequest', message.port, counterpartPort);
}
} }
} }
/** /**
* @param {'hostPortRequest'} eventName * @param {'hostPortRequest'} eventName
* @param {(MessagePort, channel: 'guest') => void} handler - this handler * @param {(source: 'guest', port: MessagePort) => void} handler - this handler
* fires when a request for the 'guest-host' channel is listened. * fires when a request for the 'guest-host' channel is listened.
*/ */
on(eventName, handler) { on(eventName, handler) {
...@@ -173,7 +161,7 @@ export class PortProvider { ...@@ -173,7 +161,7 @@ export class PortProvider {
/** /**
* Returns a port from a channel. Currently, only returns the `host` port from * Returns a port from a channel. Currently, only returns the `host` port from
* the `host-Sidebar` channel. Otherwise, it returns `null`. * the `host-sidebar` channel. Otherwise, it returns `null`.
* *
* @param {object} options * @param {object} options
* @param {'host-sidebar'} options.channel * @param {'host-sidebar'} options.channel
...@@ -238,7 +226,7 @@ export class PortProvider { ...@@ -238,7 +226,7 @@ export class PortProvider {
let messageChannel = windowChannelMap.get(eventSource); let messageChannel = windowChannelMap.get(eventSource);
// Ignore the port request if the channel for the specified window has // Ignore the port request if the channel for the specified window has
// already been created. This is to avoid transfer the port more than once. // already been created. This is to avoid transfering the port more than once.
if (messageChannel) { if (messageChannel) {
return; return;
} }
......
...@@ -8,6 +8,7 @@ const SOURCE = 'hypothesis'; ...@@ -8,6 +8,7 @@ const SOURCE = 'hypothesis';
/** /**
* These types are the used in by `PortProvider` and `PortFinder` for the * These types are the used in by `PortProvider` and `PortFinder` for the
* inter-frame discovery and communication processes. * inter-frame discovery and communication processes.
*
* @typedef {'guest-host'|'guest-sidebar'|'host-sidebar'|'notebook-sidebar'} Channel * @typedef {'guest-host'|'guest-sidebar'|'host-sidebar'|'notebook-sidebar'} Channel
* @typedef {'guest'|'host'|'notebook'|'sidebar'} Port * @typedef {'guest'|'host'|'notebook'|'sidebar'} Port
* *
...@@ -19,23 +20,19 @@ const SOURCE = 'hypothesis'; ...@@ -19,23 +20,19 @@ const SOURCE = 'hypothesis';
*/ */
/** /**
* The function checks if the data conforms to the expected format. It returns * Return true if an object, eg. from the data field of a `MessageEvent`, is a
* `true` if all the properties are including the correct value in the `source` * valid `Message`.
* property, otherwise it returns `false`.
* *
* @param {any} data * @param {any} data
* @return {data is Message} * @return {data is Message}
*/ */
function isMessageValid(data) { function isMessageValid(data) {
if (!data || typeof data !== 'object') { if (data === null || typeof data !== 'object') {
return false; return false;
} }
for (let property of ['channel', 'port', 'source', 'type']) { for (let property of ['channel', 'port', 'source', 'type']) {
if ( if (typeof data[property] !== 'string') {
data.hasOwnProperty(property) === false ||
typeof data[property] !== 'string'
) {
return false; return false;
} }
} }
...@@ -59,7 +56,7 @@ export function isMessageEqual(data, message) { ...@@ -59,7 +56,7 @@ export function isMessageEqual(data, message) {
JSON.stringify(data, Object.keys(data).sort()) === JSON.stringify(data, Object.keys(data).sort()) ===
JSON.stringify(message, Object.keys(message).sort()) JSON.stringify(message, Object.keys(message).sort())
); );
} catch (error) { } catch {
return false; return false;
} }
} }
import { delay } from '../../test-util/wait'; import { delay } from '../../test-util/wait';
import { PortFinder } from '../port-finder'; import { PortFinder } from '../port-finder';
const MAX_WAIT_FOR_PORT = 1000 * 5;
describe('PortFinder', () => { describe('PortFinder', () => {
let portFinder; let portFinder;
function sendMessage({ data, ports = [] }) { function portProviderOffer({ data, ports = [] }) {
const event = new MessageEvent('message', { const event = new MessageEvent('message', {
data, data,
ports, ports,
...@@ -42,11 +44,11 @@ describe('PortFinder', () => { ...@@ -42,11 +44,11 @@ describe('PortFinder', () => {
}) })
.catch(e => (error = e)); .catch(e => (error = e));
portFinder.destroy(); portFinder.destroy();
sendMessage({ portProviderOffer({
data: { channel, port, source: 'hypothesis', type: 'offer' }, data: { channel, port, source: 'hypothesis', type: 'offer' },
ports: [port1], ports: [port1],
}); });
clock.tick(30000); clock.tick(MAX_WAIT_FOR_PORT);
} finally { } finally {
clock.restore(); clock.restore();
} }
...@@ -98,7 +100,7 @@ describe('PortFinder', () => { ...@@ -98,7 +100,7 @@ describe('PortFinder', () => {
port, port,
}) })
.then(port => (resolvedPort = port)); .then(port => (resolvedPort = port));
sendMessage({ portProviderOffer({
data: { channel, port, source: 'hypothesis', type: 'offer' }, data: { channel, port, source: 'hypothesis', type: 'offer' },
ports: [port1], ports: [port1],
}); });
...@@ -108,7 +110,7 @@ describe('PortFinder', () => { ...@@ -108,7 +110,7 @@ describe('PortFinder', () => {
}) })
); );
it("timeouts if host doesn't respond", async () => { it("times out if host doesn't respond", async () => {
let error; let error;
const channel = 'host-sidebar'; const channel = 'host-sidebar';
const port = 'sidebar'; const port = 'sidebar';
...@@ -122,12 +124,12 @@ describe('PortFinder', () => { ...@@ -122,12 +124,12 @@ describe('PortFinder', () => {
port, port,
}) })
.catch(e => (error = e)); .catch(e => (error = e));
clock.tick(30000); clock.tick(MAX_WAIT_FOR_PORT);
} finally { } finally {
clock.restore(); clock.restore();
} }
assert.callCount(window.postMessage, 121); assert.callCount(window.postMessage, 21);
assert.alwaysCalledWithExactly( assert.alwaysCalledWithExactly(
window.postMessage, window.postMessage,
{ channel, port, source: 'hypothesis', type: 'request' }, { channel, port, source: 'hypothesis', type: 'request' },
......
...@@ -6,7 +6,7 @@ const source = 'hypothesis'; ...@@ -6,7 +6,7 @@ const source = 'hypothesis';
describe('PortProvider', () => { describe('PortProvider', () => {
let portProvider; let portProvider;
function sendMessage({ function portFinderRequest({
data, data,
origin = window.location.origin, origin = window.location.origin,
source = window, source = window,
...@@ -36,7 +36,7 @@ describe('PortProvider', () => { ...@@ -36,7 +36,7 @@ describe('PortProvider', () => {
describe('#destroy', () => { describe('#destroy', () => {
it('ignores valid port request if `PortFinder` has been destroyed', async () => { it('ignores valid port request if `PortFinder` has been destroyed', async () => {
portProvider.destroy(); portProvider.destroy();
sendMessage({ portFinderRequest({
data: { data: {
channel: 'host-sidebar', channel: 'host-sidebar',
port: 'sidebar', port: 'sidebar',
...@@ -79,7 +79,7 @@ describe('PortProvider', () => { ...@@ -79,7 +79,7 @@ describe('PortProvider', () => {
}); });
describe('#listen', () => { describe('#listen', () => {
it('ignores all port request until start listening', async () => { it('ignores all port requests before `listen` is called', async () => {
portProvider.destroy(); portProvider.destroy();
portProvider = new PortProvider(window.location.origin); portProvider = new PortProvider(window.location.origin);
const data = { const data = {
...@@ -88,7 +88,7 @@ describe('PortProvider', () => { ...@@ -88,7 +88,7 @@ describe('PortProvider', () => {
source, source,
type: 'request', type: 'request',
}; };
sendMessage({ portFinderRequest({
data, data,
}); });
await delay(0); await delay(0);
...@@ -96,7 +96,7 @@ describe('PortProvider', () => { ...@@ -96,7 +96,7 @@ describe('PortProvider', () => {
assert.notCalled(window.postMessage); assert.notCalled(window.postMessage);
portProvider.listen(); portProvider.listen();
sendMessage({ portFinderRequest({
data, data,
}); });
await delay(0); await delay(0);
...@@ -114,12 +114,12 @@ describe('PortProvider', () => { ...@@ -114,12 +114,12 @@ describe('PortProvider', () => {
type: 'request', type: 'request',
}; };
sendMessage({ portFinderRequest({
data, data,
source: null, source: null,
}); });
sendMessage({ portFinderRequest({
data, data,
source: new MessageChannel().port1, source: new MessageChannel().port1,
}); });
...@@ -128,42 +128,65 @@ describe('PortProvider', () => { ...@@ -128,42 +128,65 @@ describe('PortProvider', () => {
assert.notCalled(window.postMessage); assert.notCalled(window.postMessage);
}); });
it('ignores port request with invalid message data', async () => { [
sendMessage({ // Disabled this check because it make axes-core to crash
// Reported: https://github.com/dequelabs/axe-core/pull/3249
//{ data: null, reason: 'if message is null' },
{
data: { data: {
channel: 'dummy1-dummy2', // invalid channel channel: 'sidebar-host', // invalid channel (swapped words)
port: 'sidebar', port: 'sidebar',
source, source,
type: 'request', type: 'request',
}, },
}); reason: 'if message contains an invalid channel',
},
// Disabled this check because it make axes-core to crash {
// Reported: https://github.com/dequelabs/axe-core/pull/3249 data: {
// sendMessage({ channel: 'host-sidebar',
// data: null, port: 'host', // invalid port
// }); source,
type: 'request',
await delay(0); },
reason: 'if message contains an invalid port',
assert.notCalled(window.postMessage); },
}); {
data: {
it('ignores port request with invalid message origin', async () => { channel: 'host-sidebar',
const data = { port: 'sidebar',
source: 'dummy',
type: 'request',
},
reason: 'if message contains an invalid source',
},
{
data: {
channel: 'host-sidebar',
port: 'dummy',
source,
type: 'offer', // invalid offer
},
reason: 'if message contains an invalid offer',
},
{
data: {
channel: 'host-sidebar', channel: 'host-sidebar',
port: 'sidebar', port: 'sidebar',
source, source,
type: 'request', type: 'request',
}; },
sendMessage({
data,
origin: 'https://dummy.com', origin: 'https://dummy.com',
}); reason: 'if message comes from invalid origin',
},
].forEach(({ data, reason, origin }) => {
it(`ignores port request ${reason}`, async () => {
portFinderRequest({ data, origin: origin ?? window.location.origin });
await delay(0); await delay(0);
assert.notCalled(window.postMessage); assert.notCalled(window.postMessage);
}); });
});
it('responds to a valid port request', async () => { it('responds to a valid port request', async () => {
const data = { const data = {
...@@ -172,7 +195,7 @@ describe('PortProvider', () => { ...@@ -172,7 +195,7 @@ describe('PortProvider', () => {
source, source,
type: 'request', type: 'request',
}; };
sendMessage({ portFinderRequest({
data, data,
}); });
await delay(0); await delay(0);
...@@ -187,14 +210,14 @@ describe('PortProvider', () => { ...@@ -187,14 +210,14 @@ describe('PortProvider', () => {
it('responds to the first valid port request, ignore additional requests', async () => { it('responds to the first valid port request, ignore additional requests', async () => {
const data = { const data = {
channel: 'host-sidebar', channel: 'guest-host',
port: 'sidebar', port: 'guest',
source, source,
type: 'request', type: 'request',
}; };
for (let i = 0; i < 4; ++i) { for (let i = 0; i < 4; ++i) {
sendMessage({ portFinderRequest({
data, data,
}); });
} }
...@@ -208,8 +231,8 @@ describe('PortProvider', () => { ...@@ -208,8 +231,8 @@ describe('PortProvider', () => {
); );
}); });
it('sends the reciprocal port of the `guest-sidebar` channel (via the sidebar port)', async () => { it('sends the counterpart port via the sidebar port', async () => {
sendMessage({ portFinderRequest({
data: { data: {
channel: 'host-sidebar', channel: 'host-sidebar',
port: 'sidebar', port: 'sidebar',
...@@ -222,13 +245,14 @@ describe('PortProvider', () => { ...@@ -222,13 +245,14 @@ describe('PortProvider', () => {
const [sidebarPort] = window.postMessage.getCall(0).args[2]; const [sidebarPort] = window.postMessage.getCall(0).args[2];
const handler = sinon.stub(); const handler = sinon.stub();
sidebarPort.onmessage = handler; sidebarPort.onmessage = handler;
const data = { const data = {
channel: 'guest-sidebar', channel: 'guest-sidebar',
port: 'guest', port: 'guest',
source, source,
type: 'request', type: 'request',
}; };
sendMessage({ portFinderRequest({
data, data,
}); });
await delay(0); await delay(0);
...@@ -241,7 +265,7 @@ describe('PortProvider', () => { ...@@ -241,7 +265,7 @@ describe('PortProvider', () => {
); );
}); });
it('sends the reciprocal port of the `guest-host` channel (via listener)', async () => { it('sends the counterpart port via the listener', async () => {
const handler = sinon.stub(); const handler = sinon.stub();
portProvider.on('hostPortRequest', handler); portProvider.on('hostPortRequest', handler);
const data = { const data = {
...@@ -250,12 +274,12 @@ describe('PortProvider', () => { ...@@ -250,12 +274,12 @@ describe('PortProvider', () => {
source, source,
type: 'request', type: 'request',
}; };
sendMessage({ portFinderRequest({
data, data,
}); });
await delay(0); await delay(0);
assert.calledWith(handler, sinon.match.instanceOf(MessagePort), 'guest'); assert.calledWith(handler, 'guest', sinon.match.instanceOf(MessagePort));
}); });
}); });
}); });
...@@ -103,7 +103,7 @@ describe('port-util', () => { ...@@ -103,7 +103,7 @@ describe('port-util', () => {
port: 'guest', port: 'guest',
source, source,
type: 'offer', type: 'offer',
window, // no serializable window, // not serializable
}, },
message: { message: {
channel: 'host-sidebar', channel: 'host-sidebar',
...@@ -128,7 +128,7 @@ describe('port-util', () => { ...@@ -128,7 +128,7 @@ describe('port-util', () => {
type: 'offer', type: 'offer',
}, },
expectedResult: false, expectedResult: false,
reason: 'data has one property that is different ', reason: 'data has one property that is different',
}, },
].forEach(({ data, message, expectedResult, reason }) => { ].forEach(({ data, message, expectedResult, reason }) => {
it(`returns '${expectedResult}' because the ${reason}`, () => { it(`returns '${expectedResult}' because the ${reason}`, () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment