// Copyright 2012 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.net.xpc.IframePollingTransportTest'); goog.setTestOnly('goog.net.xpc.IframePollingTransportTest'); goog.require('goog.Timer'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.functions'); goog.require('goog.net.xpc.CfgFields'); goog.require('goog.net.xpc.CrossPageChannel'); goog.require('goog.net.xpc.CrossPageChannelRole'); goog.require('goog.net.xpc.TransportTypes'); goog.require('goog.object'); goog.require('goog.testing.MockClock'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.recordFunction'); /** @type {?goog.testing.MockClock} */ var mockClock = null; /** @type {?goog.net.xpc.CrossPageChannel} */ var outerChannel = null; /** @type {?goog.net.xpc.CrossPageChannel} */ var innerChannel = null; function setUp() { mockClock = new goog.testing.MockClock(true /* opt_autoInstall */); // Create the peer windows. var outerPeerHostName = 'https://www.youtube.com'; var outerPeerWindow = createMockPeerWindow(outerPeerHostName); var innerPeerHostName = 'https://www.google.com'; var innerPeerWindow = createMockPeerWindow(innerPeerHostName); // Create the channels. outerChannel = createChannel( goog.net.xpc.CrossPageChannelRole.OUTER, 'test', outerPeerHostName, outerPeerWindow, innerPeerHostName, innerPeerWindow); innerChannel = createChannel( goog.net.xpc.CrossPageChannelRole.INNER, 'test', innerPeerHostName, innerPeerWindow, outerPeerHostName, outerPeerWindow); } function tearDown() { outerChannel.dispose(); innerChannel.dispose(); mockClock.uninstall(); } /** Tests that connection happens normally and callbacks are invoked. */ function testConnect() { var outerConnectCallback = goog.testing.recordFunction(); var innerConnectCallback = goog.testing.recordFunction(); // Connect the two channels. outerChannel.connect(outerConnectCallback); innerChannel.connect(innerConnectCallback); mockClock.tick(1000); // Check that channels were connected and callbacks invoked. assertEquals(1, outerConnectCallback.getCallCount()); assertEquals(1, innerConnectCallback.getCallCount()); assertTrue(outerChannel.isConnected()); assertTrue(innerChannel.isConnected()); } /** Tests that messages are successfully delivered to the inner peer. */ function testSend_outerToInner() { var serviceCallback = goog.testing.recordFunction(); // Register a service handler in the inner channel. innerChannel.registerService('svc', function(payload) { assertEquals('hello', payload); serviceCallback(); }); // Connect the two channels. outerChannel.connect(); innerChannel.connect(); mockClock.tick(1000); // Send a message. outerChannel.send('svc', 'hello'); mockClock.tick(1000); // Check that the message was handled. assertEquals(1, serviceCallback.getCallCount()); } /** Tests that messages are successfully delivered to the outer peer. */ function testSend_innerToOuter() { var serviceCallback = goog.testing.recordFunction(); // Register a service handler in the inner channel. outerChannel.registerService('svc', function(payload) { assertEquals('hello', payload); serviceCallback(); }); // Connect the two channels. outerChannel.connect(); innerChannel.connect(); mockClock.tick(1000); // Send a message. innerChannel.send('svc', 'hello'); mockClock.tick(1000); // Check that the message was handled. assertEquals(1, serviceCallback.getCallCount()); } /** Tests that closing the outer peer does not cause an error. */ function testSend_outerPeerClosed() { // Connect the inner channel. innerChannel.connect(); mockClock.tick(1000); // Close the outer peer before it has a chance to connect. closeWindow(innerChannel.getPeerWindowObject()); // Allow timers to execute (and fail). mockClock.tick(1000); } /** Tests that closing the inner peer does not cause an error. */ function testSend_innerPeerClosed() { // Connect the outer channel. outerChannel.connect(); mockClock.tick(1000); // Close the inner peer before it has a chance to connect. closeWindow(outerChannel.getPeerWindowObject()); // Allow timers to execute (and fail). mockClock.tick(1000); } /** Tests that partially closing the outer peer does not cause an error. */ function testSend_outerPeerClosing() { // Connect the inner channel. innerChannel.connect(); mockClock.tick(1000); // Close the outer peer before it has a chance to connect, but // leave closed set to false to simulate a partially closed window. closeWindow(innerChannel.getPeerWindowObject()); innerChannel.getPeerWindowObject().closed = false; // Allow timers to execute (and fail). mockClock.tick(1000); } /** Tests that partially closing the inner peer does not cause an error. */ function testSend_innerPeerClosing() { // Connect the outer channel. outerChannel.connect(); mockClock.tick(1000); // Close the inner peer before it has a chance to connect, but // leave closed set to false to simulate a partially closed window. closeWindow(outerChannel.getPeerWindowObject()); outerChannel.getPeerWindowObject().closed = false; // Allow timers to execute (and fail). mockClock.tick(1000); } /** * Creates a channel with the specified configuration, using frame polling. * @param {!goog.net.xpc.CrossPageChannelRole} role The channel role. * @param {string} channelName The channel name. * @param {string} fromHostName The host name of the window hosting the channel. * @param {!Object} fromWindow The window hosting the channel. * @param {string} toHostName The host name of the peer window. * @param {!Object} toWindow The peer window. * @return {!goog.net.xpc.CrossPageChannel} */ function createChannel( role, channelName, fromHostName, fromWindow, toHostName, toWindow) { // Build a channel config using frame polling. var channelConfig = goog.object.create( goog.net.xpc.CfgFields.ROLE, role, goog.net.xpc.CfgFields.PEER_HOSTNAME, toHostName, goog.net.xpc.CfgFields.CHANNEL_NAME, channelName, goog.net.xpc.CfgFields.LOCAL_POLL_URI, fromHostName + '/robots.txt', goog.net.xpc.CfgFields.PEER_POLL_URI, toHostName + '/robots.txt', goog.net.xpc.CfgFields.TRANSPORT, goog.net.xpc.TransportTypes.IFRAME_POLLING); // Build the channel. var channel = new goog.net.xpc.CrossPageChannel(channelConfig); channel.setPeerWindowObject(toWindow); // Update the transport's getWindow, to return the correct host window. channel.createTransport_(); channel.transport_.getWindow = goog.functions.constant(fromWindow); return channel; } /** * Creates a mock window to use as a peer. The peer window will host the frame * elements. * @param {string} url The peer window's initial URL. */ function createMockPeerWindow(url) { var mockPeer = createMockWindow(url); // Update the appendChild method to use a mock frame window. mockPeer.document.body.appendChild = function(el) { assertEquals(String(goog.dom.TagName.IFRAME), el.tagName); mockPeer.frames[el.name] = createMockWindow(el.src); mockPeer.document.body.element.appendChild(el); }; return mockPeer; } /** * Creates a mock window. * @param {string} url The window's initial URL. */ function createMockWindow(url) { // Create the mock window, document and body. var mockWindow = {}; var mockDocument = {}; var mockBody = {}; var mockLocation = {}; // Configure the mock window's document body. mockBody.element = goog.dom.createDom(goog.dom.TagName.BODY); // Configure the mock window's document. mockDocument.body = mockBody; // Configure the mock window's location. mockLocation.href = url; mockLocation.replace = function(value) { mockLocation.href = value; }; // Configure the mock window. mockWindow.document = mockDocument; mockWindow.frames = {}; mockWindow.location = mockLocation; mockWindow.setTimeout = goog.Timer.callOnce; return mockWindow; } /** * Emulates closing the specified window by clearing frames, document and * location. */ function closeWindow(targetWindow) { // Close any child frame windows. for (var frameName in targetWindow.frames) { closeWindow(targetWindow.frames[frameName]); } // Clear the target window, set closed to true. targetWindow.closed = true; targetWindow.frames = null; targetWindow.document = null; targetWindow.location = null; }