| <!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> |