voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
1,447 lines (1,369 loc) • 117 kB
JavaScript
/**
* @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