393 lines
11 KiB
HTML
393 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<!--
|
|
Copyright 2010 The Closure Library Authors. All Rights Reserved.
|
|
|
|
Use of this source code is governed by the Apache License, Version 2.0.
|
|
See the COPYING file for details.
|
|
-->
|
|
<!--
|
|
-->
|
|
<head>
|
|
<title>
|
|
Closure Unit Tests - goog.messaging.PortChannel
|
|
</title>
|
|
<script src="../base.js"></script>
|
|
<script>
|
|
goog.require('goog.Timer');
|
|
goog.require('goog.dom');
|
|
goog.require('goog.events.EventTarget');
|
|
goog.require('goog.json');
|
|
goog.require('goog.messaging.PortChannel');
|
|
goog.require('goog.testing.AsyncTestCase');
|
|
goog.require('goog.testing.MockControl');
|
|
goog.require('goog.testing.async.MockControl');
|
|
goog.require('goog.testing.jsunit');
|
|
goog.require('goog.testing.messaging.MockMessageEvent');
|
|
goog.require('goog.userAgent.product');
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="frame"></div>
|
|
<script>
|
|
|
|
var mockControl;
|
|
var asyncMockControl;
|
|
var mockPort;
|
|
var portChannel;
|
|
|
|
var workerChannel;
|
|
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
|
|
var timer;
|
|
|
|
// Use a relatively long timeout because workers can take a while to start up.
|
|
asyncTestCase.stepTimeout = 3 * 1000;
|
|
|
|
function setUpPage() {
|
|
if (!('Worker' in goog.global)) {
|
|
return;
|
|
}
|
|
workerChannel = new goog.messaging.PortChannel(
|
|
new Worker('testdata/portchannel_worker.js'));
|
|
}
|
|
|
|
function tearDownPage() {
|
|
goog.dispose(workerChannel);
|
|
}
|
|
|
|
function setUp() {
|
|
timer = new goog.Timer(50);
|
|
mockControl = new goog.testing.MockControl();
|
|
asyncMockControl = new goog.testing.async.MockControl(mockControl);
|
|
mockPort = new goog.events.EventTarget();
|
|
mockPort.postMessage = mockControl.createFunctionMock('postMessage');
|
|
portChannel = new goog.messaging.PortChannel(mockPort);
|
|
}
|
|
|
|
function tearDown() {
|
|
goog.dispose(timer);
|
|
portChannel.dispose();
|
|
mockControl.$verifyAll();
|
|
}
|
|
|
|
function makeMessage(serviceName, payload) {
|
|
var msg = {'serviceName': serviceName, 'payload': payload};
|
|
msg[goog.messaging.PortChannel.FLAG] = true;
|
|
if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_) {
|
|
msg = goog.json.serialize(msg);
|
|
}
|
|
return msg;
|
|
}
|
|
|
|
function expectNoMessage() {
|
|
portChannel.registerDefaultService(
|
|
mockControl.createFunctionMock('expectNoMessage'));
|
|
}
|
|
|
|
function receiveMessage(serviceName, payload, opt_origin, opt_ports) {
|
|
mockPort.dispatchEvent(
|
|
goog.testing.messaging.MockMessageEvent.wrap(
|
|
makeMessage(serviceName, payload),
|
|
opt_origin || 'http://google.com',
|
|
undefined, undefined, opt_ports));
|
|
}
|
|
|
|
function receiveNonChannelMessage(data) {
|
|
if (goog.messaging.PortChannel.REQUIRES_SERIALIZATION_ &&
|
|
!goog.isString(data)) {
|
|
data = goog.json.serialize(data);
|
|
}
|
|
mockPort.dispatchEvent(
|
|
goog.testing.messaging.MockMessageEvent.wrap(
|
|
data, 'http://google.com'));
|
|
}
|
|
|
|
function testPostMessage() {
|
|
mockPort.postMessage(makeMessage('foobar', 'This is a value'), []);
|
|
mockControl.$replayAll();
|
|
portChannel.send('foobar', 'This is a value');
|
|
}
|
|
|
|
function testPostMessageWithPorts() {
|
|
if (!('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
var channel = new MessageChannel();
|
|
var port1 = channel.port1;
|
|
var port2 = channel.port2;
|
|
mockPort.postMessage(makeMessage('foobar', {'val': [
|
|
{'_port': {'type': 'real', 'index': 0}},
|
|
{'_port': {'type': 'real', 'index': 1}}
|
|
]}), [port1, port2]);
|
|
mockControl.$replayAll();
|
|
portChannel.send('foobar', {'val': [port1, port2]});
|
|
}
|
|
|
|
function testReceiveMessage() {
|
|
portChannel.registerService(
|
|
'foobar', asyncMockControl.asyncAssertEquals(
|
|
'testReceiveMessage', 'This is a string'));
|
|
mockControl.$replayAll();
|
|
receiveMessage('foobar', 'This is a string');
|
|
}
|
|
|
|
function testReceiveMessageWithPorts() {
|
|
if (!('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
var channel = new MessageChannel();
|
|
var port1 = channel.port1;
|
|
var port2 = channel.port2;
|
|
portChannel.registerService(
|
|
'foobar', asyncMockControl.asyncAssertEquals(
|
|
'testReceiveMessage', {'val': [port1, port2]}),
|
|
true);
|
|
mockControl.$replayAll();
|
|
receiveMessage('foobar', {'val': [
|
|
{'_port': {'type': 'real', 'index': 0}},
|
|
{'_port': {'type': 'real', 'index': 1}}
|
|
]}, null, [port1, port2]);
|
|
}
|
|
|
|
function testReceiveNonChannelMessageWithStringBody() {
|
|
expectNoMessage();
|
|
mockControl.$replayAll();
|
|
receiveNonChannelMessage('Foo bar');
|
|
}
|
|
|
|
function testReceiveNonChannelMessageWithArrayBody() {
|
|
expectNoMessage();
|
|
mockControl.$replayAll();
|
|
receiveNonChannelMessage([5, 'Foo bar']);
|
|
}
|
|
|
|
function testReceiveNonChannelMessageWithNoFlag() {
|
|
expectNoMessage();
|
|
mockControl.$replayAll();
|
|
receiveNonChannelMessage({
|
|
serviceName: 'foobar',
|
|
payload: 'this is a payload'
|
|
});
|
|
}
|
|
|
|
function testReceiveNonChannelMessageWithFalseFlag() {
|
|
expectNoMessage();
|
|
mockControl.$replayAll();
|
|
var body = {
|
|
serviceName: 'foobar',
|
|
payload: 'this is a payload'
|
|
};
|
|
body[goog.messaging.PortChannel.FLAG] = false;
|
|
receiveNonChannelMessage(body);
|
|
}
|
|
|
|
// Integration tests
|
|
|
|
function testWorker() {
|
|
if (!('Worker' in goog.global)) {
|
|
return;
|
|
}
|
|
workerChannel.registerService('pong', function(msg) {
|
|
assertObjectEquals({'val': 'fizzbang'}, msg);
|
|
asyncTestCase.continueTesting();
|
|
}, true);
|
|
workerChannel.send('ping', {'val': 'fizzbang'});
|
|
asyncTestCase.waitForAsync('worker response');
|
|
}
|
|
|
|
function testWorkerWithPorts() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
var messageChannel = new MessageChannel();
|
|
workerChannel.registerService('pong', function(msg) {
|
|
assertPortsEntangled(msg['port'], messageChannel.port2, function() {
|
|
asyncTestCase.continueTesting();
|
|
});
|
|
}, true);
|
|
workerChannel.send('ping', {'port': messageChannel.port1});
|
|
asyncTestCase.waitForAsync('worker response');
|
|
}
|
|
|
|
function testPort() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
var messageChannel = new MessageChannel();
|
|
workerChannel.send('addPort', messageChannel.port1);
|
|
messageChannel.port2.start();
|
|
var realPortChannel = new goog.messaging.PortChannel(messageChannel.port2);
|
|
realPortChannel.registerService('pong', function(msg) {
|
|
assertObjectEquals({'val': 'fizzbang'}, msg);
|
|
|
|
messageChannel.port2.close();
|
|
realPortChannel.dispose();
|
|
asyncTestCase.continueTesting();
|
|
}, true);
|
|
realPortChannel.send('ping', {'val': 'fizzbang'});
|
|
asyncTestCase.waitForAsync('port response');
|
|
}
|
|
|
|
function testPortIgnoresOrigin() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
var messageChannel = new MessageChannel();
|
|
workerChannel.send('addPort', messageChannel.port1);
|
|
messageChannel.port2.start();
|
|
var realPortChannel = new goog.messaging.PortChannel(
|
|
messageChannel.port2, 'http://somewhere-else.com');
|
|
realPortChannel.registerService('pong', function(msg) {
|
|
assertObjectEquals({'val': 'fizzbang'}, msg);
|
|
|
|
messageChannel.port2.close();
|
|
realPortChannel.dispose();
|
|
asyncTestCase.continueTesting();
|
|
}, true);
|
|
realPortChannel.send('ping', {'val': 'fizzbang'});
|
|
asyncTestCase.waitForAsync('port response');
|
|
}
|
|
|
|
function testWindow() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
|
|
// NOTE(nicksantos): This test is having problems in Safari4 on the
|
|
// test farm, but no one can reproduce them locally. Just turn it off.
|
|
if (goog.userAgent.product.SAFARI) {
|
|
return;
|
|
}
|
|
|
|
withIframe(function() {
|
|
var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
|
|
window.frames['inner'], '*', timer);
|
|
iframeChannel.registerService('pong', function(msg) {
|
|
assertEquals('fizzbang', msg);
|
|
|
|
goog.dispose(iframeChannel);
|
|
asyncTestCase.continueTesting();
|
|
});
|
|
iframeChannel.send('ping', 'fizzbang');
|
|
asyncTestCase.waitForAsync('window response');
|
|
});
|
|
}
|
|
|
|
function testWindowCancelled() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
withIframe(function() {
|
|
var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
|
|
window.frames['inner'], '*', timer);
|
|
iframeChannel.cancel();
|
|
|
|
iframeChannel.registerService('pong', function(msg) {
|
|
fail('no messages should be received due to cancellation');
|
|
goog.dispose(iframeChannel);
|
|
asyncTestCase.continueTesting();
|
|
});
|
|
|
|
iframeChannel.send('ping', 'fizzbang');
|
|
asyncTestCase.waitForAsync('window response');
|
|
|
|
// Leave plenty of time for the connection to be made if the test fails, but
|
|
// stop the test before the asyncTestCase timeout is hit.
|
|
setTimeout(goog.bind(asyncTestCase.continueTesting, asyncTestCase),
|
|
asyncTestCase.stepTimeout / 3);
|
|
});
|
|
}
|
|
|
|
function testWindowWontSendToWrongOrigin() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
withIframe(function() {
|
|
var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
|
|
window.frames['inner'], 'http://somewhere-else.com', timer);
|
|
iframeChannel.registerService('pong', function(msg) {
|
|
fail('Should not receive pong from unexpected origin');
|
|
iframeChannel.dispose();
|
|
asyncTestCase.continueTesting();
|
|
});
|
|
iframeChannel.send('ping', 'fizzbang');
|
|
|
|
setTimeout(function() {
|
|
iframeChannel.dispose();
|
|
asyncTestCase.continueTesting();
|
|
}, asyncTestCase.stepTimeout - 500);
|
|
asyncTestCase.waitForAsync('window response');
|
|
});
|
|
}
|
|
|
|
function testWindowWontReceiveFromWrongOrigin() {
|
|
if (!('Worker' in goog.global) || !('MessageChannel' in goog.global)) {
|
|
return;
|
|
}
|
|
withIframe(function() {
|
|
var iframeChannel = goog.messaging.PortChannel.forEmbeddedWindow(
|
|
window.frames['inner'], '*', timer);
|
|
iframeChannel.registerService('pong', function(msg) {
|
|
fail('Should not receive pong from unexpected origin');
|
|
iframeChannel.dispose();
|
|
asyncTestCase.continueTesting();
|
|
});
|
|
iframeChannel.send('ping', 'fizzbang');
|
|
|
|
setTimeout(function() {
|
|
iframeChannel.dispose();
|
|
asyncTestCase.continueTesting();
|
|
}, asyncTestCase.stepTimeout - 500);
|
|
asyncTestCase.waitForAsync('window response');
|
|
}, 'testdata/portchannel_wrong_origin_inner.html');
|
|
}
|
|
|
|
/**
|
|
* Assert that two HTML5 MessagePorts are entangled by posting messages from
|
|
* each to the other.
|
|
*
|
|
* @param {!MessagePort} port1
|
|
* @param {!MessagePort} port2
|
|
* @param {function()} callback Called when the assertion is finished.
|
|
*/
|
|
function assertPortsEntangled(port1, port2, callback) {
|
|
port1.onmessage = function(e) {
|
|
assertEquals('port 2 should send messages to port 1',
|
|
'port2 to port1', e.data);
|
|
callback();
|
|
};
|
|
|
|
port2.onmessage = function(e) {
|
|
assertEquals('port 1 should send messages to port 2',
|
|
'port1 to port2', e.data);
|
|
port2.postMessage('port2 to port1');
|
|
asyncTestCase.waitForAsync('port 1 receiving message');
|
|
};
|
|
|
|
port1.postMessage('port1 to port2');
|
|
asyncTestCase.waitForAsync('port 2 receiving message');
|
|
}
|
|
|
|
function withIframe(callback, opt_url) {
|
|
var frameDiv = goog.dom.getElement('frame');
|
|
goog.dom.removeChildren(frameDiv);
|
|
goog.dom.appendChild(frameDiv, goog.dom.createDom('iframe', {
|
|
style: 'display: none',
|
|
name: 'inner',
|
|
id: 'inner',
|
|
src: opt_url || 'testdata/portchannel_inner.html'
|
|
}));
|
|
|
|
asyncTestCase.waitForAsync('creating iframe');
|
|
// We need to pass control back to the event loop to give the iframe a chance
|
|
// to load.
|
|
setTimeout(function() {
|
|
asyncTestCase.continueTesting();
|
|
callback();
|
|
}, 0);
|
|
}
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|