UNPKG

voluptasmollitia

Version:
1,447 lines (1,369 loc) 117 kB
/** * @license * Copyright 2017 Google LLC * * 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. */ /** * @fileoverview Tests for autheventmanager.js */ goog.provide('fireauth.AuthEventManagerTest'); goog.require('fireauth.AuthError'); goog.require('fireauth.AuthEvent'); goog.require('fireauth.AuthEventManager'); goog.require('fireauth.CordovaHandler'); goog.require('fireauth.EmailAuthProvider'); goog.require('fireauth.GoogleAuthProvider'); goog.require('fireauth.InvalidOriginError'); goog.require('fireauth.PopupAuthEventProcessor'); goog.require('fireauth.RedirectAuthEventProcessor'); goog.require('fireauth.RpcHandler'); goog.require('fireauth.UniversalLinkSubscriber'); goog.require('fireauth.authenum.Error'); goog.require('fireauth.common.testHelper'); goog.require('fireauth.constants'); goog.require('fireauth.iframeclient.IfcHandler'); goog.require('fireauth.storage.MockStorage'); goog.require('fireauth.storage.OAuthHandlerManager'); goog.require('fireauth.storage.PendingRedirectManager'); goog.require('fireauth.util'); goog.require('goog.Promise'); goog.require('goog.Uri'); goog.require('goog.crypt'); goog.require('goog.crypt.Sha256'); goog.require('goog.testing.AsyncTestCase'); goog.require('goog.testing.MockClock'); goog.require('goog.testing.MockControl'); goog.require('goog.testing.PropertyReplacer'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.mockmatchers'); goog.require('goog.testing.recordFunction'); goog.setTestOnly('fireauth.AuthEventManagerTest'); var handler; var stubs = new goog.testing.PropertyReplacer(); var appName1 = 'APP1'; var apiKey1 = 'API_KEY1'; var authDomain1 = 'subdomain1.firebaseapp.com'; var appName2 = 'APP2'; var apiKey2 = 'API_KEY2'; var authDomain2 = 'subdomain2.firebaseapp.com'; var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall(); // Firebase SDK version in case not available. firebase.SDK_VERSION = firebase.SDK_VERSION || '3.0.0'; var clock; var expectedVersion; var universalLinks; var BuildInfo; var cordova; var OAuthSignInHandler; var androidUA = 'Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Buil' + 'd/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Sa' + 'fari/534.30'; var savePartialEventManager; var timeoutDelay = 30000; var mockControl; var ignoreArgument; var mockLocalStorage; var mockSessionStorage; function setUp() { // Create new mock storages for persistent and temporary storage before each // test. mockLocalStorage = new fireauth.storage.MockStorage(); mockSessionStorage = new fireauth.storage.MockStorage(); mockControl = new goog.testing.MockControl(); ignoreArgument = goog.testing.mockmatchers.ignoreArgument; mockControl.$resetAll(); handler = { 'canHandleAuthEvent': goog.testing.recordFunction(), 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; fireauth.common.testHelper.installMockStorages( stubs, mockLocalStorage, mockSessionStorage); // Default OAuth sign in handler is IfcHandler. setOAuthSignInHandlerEnvironment(false); } function tearDown() { fireauth.AuthEventManager.manager_ = {}; window.localStorage.clear(); window.sessionStorage.clear(); stubs.reset(); goog.dispose(clock); // Clear plugins. universalLinks = {}; BuildInfo = {}; cordova = {}; cordovaHandler = null; OAuthSignInHandler = null; try { mockControl.$verifyAll(); } finally { mockControl.$tearDown(); } fireauth.UniversalLinkSubscriber.clear(); } /** * @param {string} str The string to hash. * @return {string} The hashed string. */ function sha256(str) { var sha256 = new goog.crypt.Sha256(); sha256.update(str); return goog.crypt.byteArrayToHex(sha256.digest()); } /** * Utility function to initialize the Cordova mock plugins. * @param {?function(?string, function(!Object))} subscribe The universal link * subscriber. * @param {?string} packageName The package name. * @param {?string} displayName The app display name. * @param {boolean} isAvailable Whether browsertab is supported. * @param {?function(string, ?function(), ?function())} openUrl The URL opener. * @param {?function()} close The browsertab closer if applicable. * @param {?function()} open The inappbrowser opener if available. */ function initializePlugins( subscribe, packageName, displayName, isAvailable, openUrl, close, open) { // Initializes all mock plugins. universalLinks = { subscribe: subscribe }; BuildInfo = { packageName: packageName, displayName: displayName }; cordova = { plugins: { browsertab: { isAvailable: function(cb) { cb(isAvailable); }, openUrl: openUrl, close: close } }, InAppBrowser: { open: open } }; } /** * Helper function to set the current OAuth sign in handler. * @param {boolean} isCordova Whether to simulate a Cordova environment. */ function setOAuthSignInHandlerEnvironment(isCordova) { stubs.replace( fireauth.util, 'isAndroidOrIosCordovaScheme', function() { return isCordova; }); stubs.replace( fireauth.util, 'checkIfCordova', function() { if (isCordova) { return goog.Promise.resolve(); } else { return goog.Promise.reject(); } }); if (isCordova) { OAuthSignInHandler = fireauth.CordovaHandler; // Storage manager helpers. savePartialEventManager = new fireauth.storage.OAuthHandlerManager(); // Simulate Android environment. stubs.replace( fireauth.util, 'getUserAgentString', function() { return androidUA; }); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function() { return 0; }); // Initialize plugins. initializePlugins( goog.testing.recordFunction(), 'com.example.app', 'Test App', true, goog.testing.recordFunction(), goog.testing.recordFunction(), goog.testing.recordFunction()); } else { OAuthSignInHandler = fireauth.iframeclient.IfcHandler; // Override iframewrapper to prevent iframe from being embedded in tests. stubs.replace(fireauth.iframeclient, 'IframeWrapper', function(url) { return { registerEvent: function(eventName, callback) {}, onReady: function() { return goog.Promise.resolve(); } }; }); // Assume origin is a valid one. stubs.replace( fireauth.RpcHandler.prototype, 'getAuthorizedDomains', function() { var uri = goog.Uri.parse(fireauth.util.getCurrentUrl()); var domain = uri.getDomain(); return goog.Promise.resolve([domain]); }); } } /** * Asserts that two errors are equivalent. Plain assertObjectEquals cannot be * used as Internet Explorer adds the stack trace as a property of the object. * @param {!fireauth.AuthError} expected * @param {!fireauth.AuthError} actual */ function assertErrorEquals(expected, actual) { assertObjectEquals(expected.toPlainObject(), actual.toPlainObject()); } function testGetManager() { var manager1 = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); assertEquals( fireauth.AuthEventManager.manager_[ fireauth.AuthEventManager.getKey_(apiKey1, appName1)], manager1); assertEquals( fireauth.AuthEventManager.getManager(authDomain1, apiKey1, appName1), manager1); var manager2 = fireauth.AuthEventManager.getManager( authDomain2, apiKey2, appName2); assertEquals( fireauth.AuthEventManager.manager_[ fireauth.AuthEventManager.getKey_(apiKey2, appName2)], manager2); assertEquals( fireauth.AuthEventManager.getManager(authDomain2, apiKey2, appName2), manager2); var emulatorConfig = { url: 'http://emulator.test.domain:1234' }; var managerWithEmulator = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1, emulatorConfig); assertEquals( fireauth.AuthEventManager.manager_[ fireauth.AuthEventManager.getKey_(apiKey1, appName1)], manager1); assertEquals( fireauth.AuthEventManager.manager_[ fireauth.AuthEventManager.getKey_(apiKey1, appName1, emulatorConfig)], managerWithEmulator); } function testInstantiateOAuthSignInHandler_ifcHandler() { // Simulate browser environment. setOAuthSignInHandlerEnvironment(false); // IfcHandler should be instantiated. var ifcHandler = mockControl.createStrictMock( fireauth.iframeclient.IfcHandler); var ifcHandlerConstructor = mockControl.createConstructorMock( fireauth.iframeclient, 'IfcHandler'); // Confirm expected endpoint used. ifcHandlerConstructor( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, fireauth.constants.Endpoint.STAGING.id, ignoreArgument).$returns(ifcHandler); mockControl.$replayAll(); fireauth.AuthEventManager.instantiateOAuthSignInHandler( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, fireauth.constants.Endpoint.STAGING.id); } /** Asserts that emulator config is propagated to ifcHandler. */ function testInstantiateOAuthSignInHandler_ifcHandler_withEmulator() { // Simulate browser environment. setOAuthSignInHandlerEnvironment(false); // IfcHandler should be instantiated. var ifcHandler = mockControl.createStrictMock( fireauth.iframeclient.IfcHandler); var ifcHandlerConstructor = mockControl.createConstructorMock( fireauth.iframeclient, 'IfcHandler'); var emulatorConfig = { url: 'http://emulator.test.domain:1234' }; // Confirm expected endpoint used. ifcHandlerConstructor( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, fireauth.constants.Endpoint.STAGING.id, emulatorConfig).$returns(ifcHandler); mockControl.$replayAll(); fireauth.AuthEventManager.instantiateOAuthSignInHandler( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, fireauth.constants.Endpoint.STAGING.id, emulatorConfig); } function testInstantiateOAuthSignInHandler_cordovaHandler() { // Simulate Cordova environment setOAuthSignInHandlerEnvironment(true); // CordovaHandler should be instantiated. var cordovaHandler = mockControl.createStrictMock( fireauth.CordovaHandler); var cordovaHandlerConstructor = mockControl.createConstructorMock( fireauth, 'CordovaHandler'); // Confirm expected endpoint used. cordovaHandlerConstructor( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, undefined, undefined, fireauth.constants.Endpoint.STAGING.id, ignoreArgument) .$returns(cordovaHandler); mockControl.$replayAll(); fireauth.AuthEventManager.instantiateOAuthSignInHandler( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, fireauth.constants.Endpoint.STAGING.id); } /** Asserts that emulator config is propagated to cordovaHandler. */ function testInstantiateOAuthSignInHandler_cordovaHandler_withEmulator() { // Simulate Cordova environment setOAuthSignInHandlerEnvironment(true); // CordovaHandler should be instantiated. var cordovaHandler = mockControl.createStrictMock( fireauth.CordovaHandler); var cordovaHandlerConstructor = mockControl.createConstructorMock( fireauth, 'CordovaHandler'); var emulatorConfig = { url: 'http://emulator.test.domain:1234' }; // Confirm expected endpoint used. cordovaHandlerConstructor( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, undefined, undefined, fireauth.constants.Endpoint.STAGING.id, emulatorConfig) .$returns(cordovaHandler); mockControl.$replayAll(); fireauth.AuthEventManager.instantiateOAuthSignInHandler( authDomain1, apiKey1, appName1, firebase.SDK_VERSION, fireauth.constants.Endpoint.STAGING.id, emulatorConfig); } function testAuthEventManager_initialize_manually_withSubscriber() { var expectedAuthEvent = new fireauth.AuthEvent( 'linkViaPopup', '1234', 'http://www.example.com/#response', 'SESSION_ID'); // This test is not environment specific. stubs.replace( fireauth.AuthEventManager, 'instantiateOAuthSignInHandler', function(authDomain, apiKey, appName, version) { assertEquals('subdomain1.firebaseapp.com', authDomain); assertEquals('API_KEY1', apiKey); assertEquals('APP1', appName); assertEquals(firebase.SDK_VERSION, version); return { 'addAuthEventListener': function(handler) { asyncTestCase.signal(); // Trigger immediately to test that handleAuthEvent_ // is triggered with expected event. handler(expectedAuthEvent); }, 'initializeAndWait': function() { return goog.Promise.resolve(); }, 'shouldBeInitializedEarly': function() { return false; }, 'hasVolatileStorage': function() { return false; } }; }); var handler1 = { 'canHandleAuthEvent': function(type, id) { // Auth event should be passed to handler to check if it can handle it. assertEquals('linkViaPopup', type); assertEquals('1234', id); asyncTestCase.signal(); return false; }, 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; asyncTestCase.waitForSignals(2); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.subscribe(handler1); manager.initialize(); } function testAuthEventManager_initialize_manually_withNoSubscriber() { // Test manual initialization with no subscriber. var expectedAuthEvent = new fireauth.AuthEvent( 'linkViaPopup', '1234', 'http://www.example.com/#response', 'SESSION_ID'); var isReady = false; // This test is not environment specific. stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // This should not be called twice on initialization. assertFalse(isReady); // Trigger expected event. isReady = true; handler(expectedAuthEvent); }); asyncTestCase.waitForSignals(2); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.initialize().then(function() { assertTrue(isReady); asyncTestCase.signal(); }); // This will return the above cached response and should not try to // initialize a new handler. manager.initialize().then(function() { assertTrue(isReady); asyncTestCase.signal(); }); } function testAuthEventManager_initialize_manually_withEmulator() { var expectedEmulatorConfig = { url: 'http://emulator.test.domain:1234' }; var expectedAuthEvent = new fireauth.AuthEvent( 'linkViaPopup', '1234', 'http://www.example.com/#response', 'SESSION_ID'); var isReady = false; // This test is not environment specific. stubs.replace( fireauth.AuthEventManager, 'instantiateOAuthSignInHandler', function (authDomain, apiKey, appName, version, endpoint, emulatorConfig) { assertEquals('subdomain1.firebaseapp.com', authDomain); assertEquals('API_KEY1', apiKey); assertEquals('APP1', appName); assertEquals(firebase.SDK_VERSION, version); assertUndefined(endpoint); assertObjectEquals(emulatorConfig, expectedEmulatorConfig); isReady = true; return { 'addAuthEventListener': function (handler) { handler(expectedAuthEvent); }, 'initializeAndWait': function () { return goog.Promise.resolve(); }, 'shouldBeInitializedEarly': function () { return false; }, 'hasVolatileStorage': function () { return false; } }; }); asyncTestCase.waitForSignals(1); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1, expectedEmulatorConfig); manager.initialize().then(function () { assertTrue(isReady); asyncTestCase.signal(); }); } function testAuthEventManager_initialize_automatically_pendingRedirect() { // Test automatic initialization when pending redirect available on // subscription. // This test is relevant to a browser environment. setOAuthSignInHandlerEnvironment(false); var expectedAuthEvent = new fireauth.AuthEvent( 'signInViaRedirect', '1234', 'http://www.example.com/#response', 'SESSION_ID'); var isInitialized = false; // Used to trigger the Auth event. stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { isInitialized = true; // Trigger expected event. handler(expectedAuthEvent); }); asyncTestCase.waitForSignals(1); var expectedResult = { 'user': {}, 'credential': {} }; var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); var handler1 = { 'canHandleAuthEvent': function(type, id) { // Auth event should be passed to handler to check if it can handle it. assertEquals('signInViaRedirect', type); assertEquals('1234', id); return true; }, 'resolvePendingPopupEvent': function( mode, popupRedirectResult, error, opt_eventId) { }, 'getAuthEventHandlerFinisher': function(mode, opt_eventId) { return function(requestUri, sessionId, tenantId, postBody) { return goog.Promise.resolve(expectedResult); }; } }; var storageKey = apiKey1 + ':' + appName1; var pendingRedirectManager = new fireauth.storage.PendingRedirectManager(storageKey); // Simulate pending result. pendingRedirectManager.setPendingStatus().then(function() { // This will trigger initialize due to pending redirect. manager.subscribe(handler1); // This will resolve with the expected result. manager.getRedirectResult().then(function(result) { assertTrue(isInitialized); assertObjectEquals(expectedResult, result); // Pending result should be cleared. pendingRedirectManager.getPendingStatus().then(function(status) { assertFalse(status); asyncTestCase.signal(); }); }); }); } function testAuthEventManager_initialize_automatically_volatileStorage() { // If interface has volatile storage, handler should be automatically // initialized even when no pending redirect is detected. // Cordova environment. setOAuthSignInHandlerEnvironment(true); var expectedAuthEvent = new fireauth.AuthEvent( 'signInViaRedirect', '1234', 'http://www.example.com/#response', 'SESSION_ID'); var isInitialized = false; // Used to trigger the Auth event. stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { isInitialized = true; // Trigger expected event. handler(expectedAuthEvent); }); asyncTestCase.waitForSignals(1); var expectedResult = { 'user': {}, 'credential': {} }; var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); var handler1 = { 'canHandleAuthEvent': function(type, id) { // auth event should be passed to handler to check if it can handle it. assertEquals('signInViaRedirect', type); assertEquals('1234', id); return true; }, 'resolvePendingPopupEvent': function( mode, popupRedirectResult, error, opt_eventId) { }, 'getAuthEventHandlerFinisher': function(mode, opt_eventId) { return function(requestUri, sessionId, tenantId, postBody) { return goog.Promise.resolve(expectedResult); }; } }; // This will trigger initialize even though there is no pending redirect in // session storage. manager.subscribe(handler1); // This will resolve with the expected result. manager.getRedirectResult().then(function(result) { assertTrue(isInitialized); assertEquals(expectedResult, result); asyncTestCase.signal(); }); } function testAuthEventManager_initialize_automatically_safariMobile() { // Browser only environment. setOAuthSignInHandlerEnvironment(false); // Test automatic initialization on subscription for Safari mobile. var ua = 'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 ' + '(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25'; stubs.replace( fireauth.util, 'getUserAgentString', function() { return ua; }); // OAuth handler should be initialized automatically on subscription. stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { asyncTestCase.signal(); }); asyncTestCase.waitForSignals(1); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); var handler = { 'canHandleAuthEvent': goog.testing.recordFunction(), 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; manager.subscribe(handler); } function testAuthEventManager_getRedirectResult_noRedirect() { // Browser environment where sessionStorage is not volatile. // Test getRedirect when no pending redirect is found. setOAuthSignInHandlerEnvironment(false); asyncTestCase.waitForSignals(1); stubs.replace( fireauth.AuthEventManager.prototype, 'initialize', function() { fail('This should not initialize automatically.'); }); var expectedResult = { 'user': null }; var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); var handler1 = { 'canHandleAuthEvent': goog.testing.recordFunction(), 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; // This will resolve redirect result to default null result. manager.subscribe(handler1); // This should resolve quickly since there is no pending redirect without the // need to initialize the OAuth sign-in handler. manager.getRedirectResult().then(function(result) { assertObjectEquals(expectedResult, result); asyncTestCase.signal(); }); } function testAuthEventManager_subscribeAndUnsubscribe() { var recordedHandler = null; var expectedAuthEvent = new fireauth.AuthEvent( 'linkViaPopup', '1234', 'http://www.example.com/#response', 'SESSION_ID'); stubs.replace( fireauth.PopupAuthEventProcessor.prototype, 'processAuthEvent', goog.testing.recordFunction()); // This test is not environment specific. stubs.replace( fireauth.AuthEventManager, 'instantiateOAuthSignInHandler', function(authDomain, apiKey, appName, version) { assertEquals('subdomain1.firebaseapp.com', authDomain); assertEquals('API_KEY1', apiKey); assertEquals('APP1', appName); assertEquals(firebase.SDK_VERSION, version); return { 'addAuthEventListener': function(handler) { recordedHandler = handler; asyncTestCase.signal(); }, 'initializeAndWait': function() { return goog.Promise.resolve(); }, 'shouldBeInitializedEarly': function() { return false; }, 'hasVolatileStorage': function() { return false; } }; }); // Create a handler that can't handle the provided event. var handler1 = { 'canHandleAuthEvent': goog.testing.recordFunction(function(type, eventId) { assertEquals('linkViaPopup', type); assertEquals('1234', eventId); return false; }), 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; // Create another handler that can't handle the provided event. var handler2 = { 'canHandleAuthEvent': goog.testing.recordFunction(function(type, eventId) { assertEquals('linkViaPopup', type); assertEquals('1234', eventId); return false; }), 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; // Create a handler that can handle a specified Auth event. var handler3 = { 'canHandleAuthEvent': goog.testing.recordFunction(function(type, eventId) { assertEquals('linkViaPopup', type); assertEquals('1234', eventId); return true; }), 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; asyncTestCase.waitForSignals(1); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.initialize(); assertFalse(manager.isSubscribed(handler1)); // Subscribe first handler and trigger event. manager.subscribe(handler1); assertTrue(manager.isSubscribed(handler1)); assertFalse(recordedHandler(expectedAuthEvent)); assertEquals(1, handler1.canHandleAuthEvent.getCallCount()); assertEquals(0, handler2.canHandleAuthEvent.getCallCount()); assertEquals( 0, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Subscribe second handler and trigger event. assertFalse(manager.isSubscribed(handler2)); manager.subscribe(handler2); assertTrue(manager.isSubscribed(handler2)); assertFalse(recordedHandler(expectedAuthEvent)); assertEquals(2, handler1.canHandleAuthEvent.getCallCount()); assertEquals(1, handler2.canHandleAuthEvent.getCallCount()); assertEquals( 0, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Unsubscribe first handler and trigger event. manager.unsubscribe(handler1); assertFalse(manager.isSubscribed(handler1)); assertFalse(recordedHandler(expectedAuthEvent)); assertEquals(2, handler1.canHandleAuthEvent.getCallCount()); assertEquals(2, handler2.canHandleAuthEvent.getCallCount()); assertEquals( 0, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Unsubscribe second handler and trigger event. manager.unsubscribe(handler2); assertFalse(manager.isSubscribed(handler2)); assertFalse(recordedHandler(expectedAuthEvent)); assertEquals(2, handler1.canHandleAuthEvent.getCallCount()); assertEquals(2, handler2.canHandleAuthEvent.getCallCount()); assertEquals( 0, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Reset handler. handler1.canHandleAuthEvent.reset(); handler2.canHandleAuthEvent.reset(); handler3.canHandleAuthEvent.reset(); // Subscribe all handlers and add handler3. manager.subscribe(handler1); manager.subscribe(handler3); manager.subscribe(handler2); // Trigger event, once it reaches the correct handler, it will stop checking // other handlers and return true. assertTrue(recordedHandler(expectedAuthEvent)); assertEquals(1, handler1.canHandleAuthEvent.getCallCount()); assertEquals(0, handler2.canHandleAuthEvent.getCallCount()); assertEquals(1, handler3.canHandleAuthEvent.getCallCount()); assertEquals( 1, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // processAuthEvent should be called with the expected event and handler3 as // owner of that event. assertEquals( expectedAuthEvent, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent.getLastCall() .getArgument(0)); assertEquals( handler3, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent.getLastCall() .getArgument(1)); } function testAuthEventManager_testEventToProcessor() { var recordedHandler; clock = new goog.testing.MockClock(true); asyncTestCase.waitForSignals(1); // This test is not environment specific. stubs.replace( fireauth.AuthEventManager, 'instantiateOAuthSignInHandler', function(authDomain, apiKey, appName, version) { assertEquals('subdomain1.firebaseapp.com', authDomain); assertEquals('API_KEY1', apiKey); assertEquals('APP1', appName); assertEquals(firebase.SDK_VERSION, version); return { 'addAuthEventListener': function(handler) { recordedHandler = handler; asyncTestCase.signal(); }, 'removeAuthEventListener': function(handler) { recordedHandler = null; }, 'initializeAndWait': function() { return goog.Promise.resolve(); }, 'shouldBeInitializedEarly': function() { return false; }, 'hasVolatileStorage': function() { return false; } }; }); stubs.replace( fireauth.PopupAuthEventProcessor.prototype, 'processAuthEvent', goog.testing.recordFunction()); stubs.replace( fireauth.RedirectAuthEventProcessor.prototype, 'processAuthEvent', goog.testing.recordFunction()); // For testing all cases, use a handler that can handler everything. var handler = { 'canHandleAuthEvent': function(type, eventId) {return true;}, 'resolvePendingPopupEvent': goog.testing.recordFunction(), 'getAuthEventHandlerFinisher': goog.testing.recordFunction() }; var unknownEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var signInViaPopupEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.SIGN_IN_VIA_POPUP, '1234', 'http://www.example.com/#response', 'SESSION_ID', null, 'POST_BODY'); var signInViaRedirectEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.SIGN_IN_VIA_REDIRECT, '1234', 'http://www.example.com/#response', 'SESSION_ID', null, 'POST_BODY'); var linkViaPopupEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.LINK_VIA_POPUP, '1234', 'http://www.example.com/#response', 'SESSION_ID', null, 'POST_BODY'); var linkViaRedirectEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.LINK_VIA_REDIRECT, '1234', 'http://www.example.com/#response', 'SESSION_ID', null, 'POST_BODY'); var reauthViaPopupEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.REAUTH_VIA_POPUP, '1234', 'http://www.example.com/#response', 'SESSION_ID', null, 'POST_BODY'); var reauthViaRedirectEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.REAUTH_VIA_REDIRECT, '1234', 'http://www.example.com/#response', 'SESSION_ID', null, 'POST_BODY'); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.initialize(); manager.subscribe(handler); // Test with unknown event. recordedHandler(unknownEvent); assertEquals( 1, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [unknownEvent, handler], fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); assertEquals( 0, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Test with sign up via popup event. recordedHandler(signInViaPopupEvent); assertEquals( 1, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertEquals( 1, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [signInViaPopupEvent, handler], fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); // Test with sign in via redirect event. recordedHandler(signInViaRedirectEvent); assertEquals( 2, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [signInViaRedirectEvent, handler], fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); assertEquals( 1, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Test with link via popup event. recordedHandler(linkViaPopupEvent); assertEquals( 2, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertEquals( 2, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [linkViaPopupEvent, handler], fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); // Test with link via redirect event. recordedHandler(linkViaRedirectEvent); assertEquals( 3, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [linkViaRedirectEvent, handler], fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); assertEquals( 2, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Test with reauth via popup event. recordedHandler(reauthViaPopupEvent); assertEquals( 3, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertEquals( 3, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [reauthViaPopupEvent, handler], fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); // Test with reauth via redirect event. recordedHandler(reauthViaRedirectEvent); assertEquals( 4, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertArrayEquals( [reauthViaRedirectEvent, handler], fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getLastCall().getArguments()); assertEquals( 3, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Duplicate events with event IDs or session IDs should be ignored. assertFalse(recordedHandler(signInViaPopupEvent)); assertFalse(recordedHandler(signInViaRedirectEvent)); assertFalse(recordedHandler(linkViaPopupEvent)); assertFalse(recordedHandler(linkViaRedirectEvent)); assertFalse(recordedHandler(reauthViaPopupEvent)); assertFalse(recordedHandler(reauthViaRedirectEvent)); assertEquals( 4, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertEquals( 3, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Unknown events are allowed to be duplicated. assertTrue(recordedHandler(unknownEvent)); assertEquals( 5, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertEquals( 3, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); // Reset should clear processed events. manager.reset(); manager.initialize(); assertTrue(recordedHandler(signInViaPopupEvent)); assertEquals( 5, fireauth.RedirectAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertEquals( 4, fireauth.PopupAuthEventProcessor.prototype.processAuthEvent. getCallCount()); assertFalse(recordedHandler(signInViaPopupEvent)); // Simulate 1 millisecond before cachebuster triggers. clock.tick(fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION - 1); // Event uid should still be saved. assertFalse(recordedHandler(signInViaPopupEvent)); // Simulate one more millisecond to clear cache. clock.tick(1); // Event uid should be cleared. assertTrue(recordedHandler(signInViaPopupEvent)); // This should be cached until next time cache is cleared. clock.tick(fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION - 1); assertFalse(recordedHandler(signInViaPopupEvent)); clock.tick(1); assertTrue(recordedHandler(signInViaPopupEvent)); // Halfway through timeout duration. clock.tick(fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION / 2); // Trigger second event. assertTrue(recordedHandler(linkViaPopupEvent)); // Halfway through timeout. clock.tick(fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION / 2); // Both events still cached (second event should reset the counter). assertFalse(recordedHandler(signInViaPopupEvent)); assertFalse(recordedHandler(linkViaPopupEvent)); // Trigger timeout (half timeout duration). clock.tick(fireauth.AuthEventManager.EVENT_DUPLICATION_CACHE_DURATION / 2); // Cache should be cleared from both events (full timeout duration from last // event). assertTrue(recordedHandler(signInViaPopupEvent)); assertTrue(recordedHandler(linkViaPopupEvent)); } function testProcessPopup_success() { // This is only relevant to OAuth handlers that support popups. setOAuthSignInHandlerEnvironment(false); var provider = new fireauth.GoogleAuthProvider(); provider.addScope('scope1'); provider.addScope('scope2'); var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain1, apiKey1, appName1, 'linkViaPopup', provider, null, '1234', firebase.SDK_VERSION); var expectedAuthEvent = new fireauth.AuthEvent( 'unknown', null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); // Fake popup window. var popupWin = {}; // Keep track of when the popup is redirected. var popupRedirected = false; // Catch popup window redirection. stubs.replace( fireauth.util, 'goTo', function(url, win) { popupRedirected = true; assertEquals(expectedUrl, url); assertEquals(popupWin, win); asyncTestCase.signal(); }); stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Trigger expected event. handler(expectedAuthEvent); }); asyncTestCase.waitForSignals(3); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.processPopup(popupWin, 'linkViaPopup', provider, '1234') .then(function() { assertTrue(popupRedirected); // This should resolve now as it is already initialized. asyncTestCase.signal(); }); // Confirm OAuth handler initialized before redirect. manager.initialize().then(function() { // Should not be redirected yet. assertFalse(popupRedirected); asyncTestCase.signal(); }); } function testProcessPopup_success_tenantId() { // This is only relevant to OAuth handlers that support popups. setOAuthSignInHandlerEnvironment(false); var tenantId = '123456789012'; var provider = new fireauth.GoogleAuthProvider(); provider.addScope('scope1'); provider.addScope('scope2'); var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain1, apiKey1, appName1, 'linkViaPopup', provider, null, '1234', firebase.SDK_VERSION, null, null, tenantId); var expectedAuthEvent = new fireauth.AuthEvent( 'unknown', null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); // Fake popup window. var popupWin = {}; // Keep track of when the popup is redirected. var popupRedirected = false; // Catch popup window redirection. stubs.replace( fireauth.util, 'goTo', function(url, win) { popupRedirected = true; assertEquals(expectedUrl, url); assertEquals(popupWin, win); asyncTestCase.signal(); }); stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Trigger expected event. handler(expectedAuthEvent); }); asyncTestCase.waitForSignals(3); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.processPopup( popupWin, 'linkViaPopup', provider, '1234', false, tenantId) .then(function() { assertTrue(popupRedirected); // This should resolve now as it is already initialized. asyncTestCase.signal(); }); // Confirm OAuth handler initialized before redirect. manager.initialize().then(function() { // Should not be redirected yet. assertFalse(popupRedirected); asyncTestCase.signal(); }); } function testProcessPopup_popupNotSupported() { // Test for environments where popup sign in is not supported. // Cordova environment. setOAuthSignInHandlerEnvironment(true); var expectedError = new fireauth.AuthError( fireauth.authenum.Error.OPERATION_NOT_SUPPORTED); var provider = new fireauth.GoogleAuthProvider(); provider.addScope('scope1'); provider.addScope('scope2'); // Fake popup window. var popupWin = {}; stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Trigger expected event. handler(expectedAuthEvent); }); asyncTestCase.waitForSignals(1); var manager = fireauth.AuthEventManager.getManager( authDomain1, apiKey1, appName1); manager.processPopup(popupWin, 'linkViaPopup', provider, '1234') .thenCatch(function(error) { assertErrorEquals(expectedError, error); asyncTestCase.signal(); }); } function testReset_ifcHandler() { // Browser only environment. setOAuthSignInHandlerEnvironment(false); var oauthHandlerInstance = null; var calls = 0; // Listener to initializations of ifchandler. stubs.replace( OAuthSignInHandler.prototype, 'initializeAndWait', goog.testing.recordFunction( OAuthSignInHandler.prototype.initializeAndWait)); stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Each call should be run on a new instance as reset will force a new // instance to be created. assertNotEquals(oauthHandlerInstance, this); // Save current instance. oauthHandlerInstance = this; calls++; }); var manager = fireauth.AuthEventManager.getManager(authDomain1, apiKey1, appName1); // This should be cancelled by reset. manager.initialize(); // Note first initialized ifchandler instance. var initializedInstance1 = OAuthSignInHandler.prototype .initializeAndWait.getLastCall().getThis(); manager.reset(); // This call should succeed. manager.initialize(); // Note second initialized ifchandler instance. var initializedInstance2 = OAuthSignInHandler.prototype .initializeAndWait.getLastCall().getThis(); // Confirm both instances are not the same. assertNotEquals(initializedInstance1, initializedInstance2); assertEquals(2, calls); } function testReset_cordovaHandler() { // Cordova environment. setOAuthSignInHandlerEnvironment(true); var oauthHandlerInstance = null; var calls = 0; // Listener to initializations of the current OAuth sign in handler. stubs.replace( OAuthSignInHandler.prototype, 'initializeAndWait', goog.testing.recordFunction( OAuthSignInHandler.prototype.initializeAndWait)); stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Each call should be run on a new instance as reset will force a new // instance to be created. assertNotEquals(oauthHandlerInstance, this); // Save current instance. oauthHandlerInstance = this; calls++; }); var manager = fireauth.AuthEventManager.getManager(authDomain1, apiKey1, appName1); // This should be cancelled by reset. manager.initialize(); // Note first initialized ifchandler instance. var initializedInstance1 = OAuthSignInHandler.prototype .initializeAndWait.getLastCall().getThis(); manager.reset(); // This call should succeed. manager.initialize(); // Note second initialized ifchandler instance. var initializedInstance2 = OAuthSignInHandler.prototype .initializeAndWait.getLastCall().getThis(); // Confirm both instances are not the same. assertNotEquals(initializedInstance1, initializedInstance2); assertEquals(2, calls); } function testInitialize_errorLoadingOAuthHandler() { // Browser only environment. setOAuthSignInHandlerEnvironment(false); // Test manager initialization when the OAuth handler fails to load. var provider = new fireauth.GoogleAuthProvider(); provider.addScope('scope1'); provider.addScope('scope2'); var expectedError = new fireauth.AuthError(fireauth.authenum.Error.NETWORK_REQUEST_FAILED); var expectedAuthEvent = new fireauth.AuthEvent( 'unknown', null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); // Listen to reset calls. stubs.replace( fireauth.AuthEventManager.prototype, 'reset', goog.testing.recordFunction(fireauth.AuthEventManager.prototype.reset)); // If the OAuth handler is to be initialized, trigger no redirect event to // notify event manager that it is ready. stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Only when handler is not failing, handle event. if (!willFail) { handler(expectedAuthEvent); } }); stubs.replace( OAuthSignInHandler.prototype, 'initializeAndWait', function() { if (willFail) { // When handler fails to load. return goog.Promise.reject(new fireauth.AuthError( fireauth.authenum.Error.NETWORK_REQUEST_FAILED)); } // Handler succeeds to load. return goog.Promise.resolve(); }); asyncTestCase.waitForSignals(1); var manager = fireauth.AuthEventManager.getManager(authDomain1, apiKey1, appName1); // Simulate the handler first fails to load. var willFail = true; // Reset not called yet. assertEquals(0, fireauth.AuthEventManager.prototype.reset.getCallCount()); manager.initialize().thenCatch(function(error) { // OAuth handler should be reset. assertEquals(1, fireauth.AuthEventManager.prototype.reset.getCallCount()); // Network error triggered. assertErrorEquals(expectedError, error); // Simulate on next trial the handler will load correctly. willFail = false; // Try to initialize again. This time it should work. manager.initialize().then(function() { // No additional call to reset. assertEquals(1, fireauth.AuthEventManager.prototype.reset.getCallCount()); // This should resolve now as it is already initialized. asyncTestCase.signal(); }); }); } function testProcessPopup_errorLoadingIframe() { // Browser only environment. setOAuthSignInHandlerEnvironment(false); // Test when the handler fails to load. var expectedError = new fireauth.AuthError(fireauth.authenum.Error.NETWORK_REQUEST_FAILED); var provider = new fireauth.GoogleAuthProvider(); provider.addScope('scope1'); provider.addScope('scope2'); var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain1, apiKey1, appName1, 'linkViaPopup', provider, null, '1234', firebase.SDK_VERSION); var expectedAuthEvent = new fireauth.AuthEvent( 'unknown', null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); // Fake popup window. var popupWin = {}; var popupRedirected = false; // Listen to reset calls. stubs.replace( fireauth.AuthEventManager.prototype, 'reset', goog.testing.recordFunction(fireauth.AuthEventManager.prototype.reset)); stubs.replace( fireauth.RpcHandler.prototype, 'getAuthorizedDomains', function() { // Assume connection works for this call and only fails on handler load. var uri = goog.Uri.parse(fireauth.util.getCurrentUrl()); var domain = uri.getDomain(); return goog.Promise.resolve([domain]); }); // Catch popup window redirection. stubs.replace(fireauth.util, 'goTo', function(url, win) { if (willFail) { // When handler fails, no redirect should happen. fail('OAuth handler loading failure should not lead to a popup ' + 'redirect.'); } else { // When OAuth handler succeeds, it should redirect the popup window. popupRedirected = true; assertEquals(expectedUrl, url); assertEquals(popupWin, win); } }); // If the OAuth handler is to be initialized, trigger no redirect event to // notify event manager that it is ready. stubs.replace( OAuthSignInHandler.prototype, 'addAuthEventListener', function(handler) { // Only when handler is not failing, handle event. if (!willFail) { ha