UNPKG

veritatisdoloremque

Version:
1,414 lines (1,350 loc) 107 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 cordovahandler.js. */ goog.provide('fireauth.CordovaHandlerTest'); goog.require('fireauth.AuthError'); goog.require('fireauth.AuthEvent'); goog.require('fireauth.CordovaHandler'); goog.require('fireauth.EmailAuthProvider'); goog.require('fireauth.GoogleAuthProvider'); 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.AuthEventManager'); goog.require('fireauth.storage.MockStorage'); goog.require('fireauth.storage.OAuthHandlerManager'); goog.require('fireauth.util'); goog.require('goog.Promise'); goog.require('goog.Timer'); goog.require('goog.crypt'); goog.require('goog.crypt.Sha256'); goog.require('goog.testing.PropertyReplacer'); goog.require('goog.testing.TestCase'); goog.require('goog.testing.jsunit'); goog.require('goog.testing.recordFunction'); goog.setTestOnly('fireauth.CordovaHandlerTest'); var stubs = new goog.testing.PropertyReplacer(); var universalLinks; var BuildInfo; var cordova; var universalLinkCb; var cordovaHandler; var authDomain = 'subdomain.firebaseapp.com'; var apiKey = 'apiKey1'; var appName = 'appName1'; var version = '3.0.0'; var savePartialEventManager; var storageKey; 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 iOS8iPhoneUA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) A' + 'ppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A366 Safar' + 'i/600.1.4'; var iOS9iPhoneUA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) A' + 'ppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13C75 Safar' + 'i/601.1'; var mockLocalStorage; var mockSessionStorage; /** * @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()); } /** * 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()); } /** * Asserts that two Auth events are equivalent. * @param {!fireauth.AuthEvent} expectedEvent * @param {!fireauth.AuthEvent} actualEvent */ function assertAuthEventEquals(expectedEvent, actualEvent) { assertObjectEquals( expectedEvent.toPlainObject(), actualEvent.toPlainObject()); } /** * 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 } }; } function setUp() { // Create new mock storages for persistent and temporary storage before each // test. mockLocalStorage = new fireauth.storage.MockStorage(); mockSessionStorage = new fireauth.storage.MockStorage(); fireauth.common.testHelper.installMockStorages( stubs, mockLocalStorage, mockSessionStorage); // Initialize plugins. initializePlugins( function(eventName, cb) { universalLinkCb = cb; }, 'com.example.app', 'Test App', true, function(url, resolve, reject) {}, goog.testing.recordFunction(), goog.testing.recordFunction()); stubs.replace( fireauth.util, 'checkIfCordova', function() { return goog.Promise.resolve(); }); // Storage key. storageKey = 'apiKey1:appName1'; // Storage manager helpers. savePartialEventManager = new fireauth.storage.OAuthHandlerManager(); getAndDeletePartialEventManager = new fireauth.storage.AuthEventManager(storageKey); } function tearDown() { // Clear storage. window.localStorage.clear(); window.sessionStorage.clear(); // Reset stubs. stubs.reset(); // Clear plugins. universalLinks = {}; BuildInfo = {}; cordova = {}; cordovaHandler = null; universalLinkCb = null; if (goog.global['handleOpenURL']) { delete goog.global['handleOpenURL']; } fireauth.UniversalLinkSubscriber.clear(); } /** * Install the test to run and runs it. * @param {string} id The test identifier. * @param {function():!goog.Promise} func The test function to run. * @return {!goog.Promise} The result of the test. */ function installAndRunTest(id, func) { var testCase = new goog.testing.TestCase(); testCase.addNewTest(id, func); return testCase.runTestsReturningPromise().then(function(result) { assertTrue(result.complete); // Display error detected. if (result.errors.length) { fail(result.errors.join('\n')); } assertEquals(1, result.totalCount); assertEquals(1, result.runCount); assertEquals(1, result.successCount); assertEquals(0, result.errors.length); }); } function testCordovaHandler_initializeAndWait_success() { return installAndRunTest('initializeAndWait_success', function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); // Confirm should be initialized early. assertTrue(cordovaHandler.shouldBeInitializedEarly()); // Confirm has volatile sessionStorage. assertTrue(cordovaHandler.hasVolatileStorage()); // Confirm does not unload on redirect. assertFalse(cordovaHandler.unloadsOnRedirect()); // Should resolve successfully. return cordovaHandler.initializeAndWait(); }); } function testCordovaHandler_initializeAndWait_notCordovaError() { return installAndRunTest('initializeAndWait_notCordovaError', function() { var expectedError = new fireauth.AuthError( fireauth.authenum.Error.CORDOVA_NOT_READY); // Simulate non Cordova environment. stubs.replace( fireauth.util, 'checkIfCordova', function() { return goog.Promise.reject(expectedError); }); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); return cordovaHandler.initializeAndWait().then(function() { throw new Error('Unexpected success!'); }, function(error) { assertErrorEquals(expectedError, error); }); }); } function testCordovaHandler_initializeAndWait_universalLinkError() { return installAndRunTest('initializeAndWait_universalLinkError', function() { // Universal links plugin not installed. universalLinks = {}; var expectedError = new fireauth.AuthError( fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION, 'cordova-universal-links-plugin-fix is not installed'); var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); var p = new goog.Promise(function(resolve, reject) { cordovaHandler.addAuthEventListener(function(event) { assertAuthEventEquals(noEvent, event); resolve(); }); }); return cordovaHandler.initializeAndWait().then(function() { throw new Error('Unexpected success!'); }, function(error) { assertErrorEquals(expectedError, error); return p; }); }); } function testCordovaHandler_initializeAndWait_buildInfoError() { return installAndRunTest('initializeAndWait_buildInfoError', function() { // Buildinfo plugin not installed. BuildInfo = {}; var expectedError = new fireauth.AuthError( fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION, 'cordova-plugin-buildinfo is not installed'); var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); var p = new goog.Promise(function(resolve, reject) { cordovaHandler.addAuthEventListener(function(event) { assertAuthEventEquals(noEvent, event); resolve(); }); }); return cordovaHandler.initializeAndWait().then(function() { throw new Error('Unexpected success!'); }, function(error) { assertErrorEquals(expectedError, error); return p; }); }); } function testCordovaHandler_initializeAndWait_browserTabError() { return installAndRunTest('initializeAndWait_browserTabError', function() { // browsertab plugin not installed. cordova = {}; var expectedError = new fireauth.AuthError( fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION, 'cordova-plugin-browsertab is not installed'); var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); var p = new goog.Promise(function(resolve, reject) { cordovaHandler.addAuthEventListener(function(event) { assertAuthEventEquals(noEvent, event); resolve(); }); }); return cordovaHandler.initializeAndWait().then(function() { throw new Error('Unexpected success!'); }, function(error) { assertErrorEquals(expectedError, error); return p; }); }); } function testCordovaHandler_initializeAndWait_inAppBrowserError() { return installAndRunTest('initializeAndWait_inAppBrowserError', function() { // inappbrowser plugin not installed. cordova.InAppBrowser = null; var expectedError = new fireauth.AuthError( fireauth.authenum.Error.INVALID_CORDOVA_CONFIGURATION, 'cordova-plugin-inappbrowser is not installed'); var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); var p = new goog.Promise(function(resolve, reject) { cordovaHandler.addAuthEventListener(function(event) { assertAuthEventEquals(noEvent, event); resolve(); }); }); return cordovaHandler.initializeAndWait().then(function() { throw new Error('Unexpected success!'); }, function(error) { assertErrorEquals(expectedError, error); return p; }); }); } function testCordovaHandler_startPopupTimeout() { return installAndRunTest('startPopupTimeout', function() { // Should fail with operation not supported error. var expectedError = new fireauth.AuthError( fireauth.authenum.Error.OPERATION_NOT_SUPPORTED); var onError = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); return cordovaHandler.startPopupTimeout({}, onError, 1000).then(function() { assertEquals(1, onError.getCallCount()); assertErrorEquals(expectedError, onError.getLastCall().getArgument(0)); }); }); } function testCordovaHandler_processPopup() { return installAndRunTest('processPopup', function() { var onInit = goog.testing.recordFunction(); var onError = goog.testing.recordFunction(); var provider = new fireauth.GoogleAuthProvider(); // Popup requests should fail with operation not supported error. var expectedError = new fireauth.AuthError( fireauth.authenum.Error.OPERATION_NOT_SUPPORTED); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); return cordovaHandler.processPopup( {}, 'linkViaPopup', provider, onInit, onError, '1234', false) .then(function() { throw new Error('Unexpected success!'); }, function(error) { assertErrorEquals(expectedError, error); }); }); } function testCordovaHandler_addRemoveAuthEventListeners() { return installAndRunTest('addRemoveAuthEventListeners', function() { // Wait for event to be handled. var waitWhileInStorage = function() { return goog.Timer.promise(10).then(function() { return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(authEvent) { if (authEvent) { return waitWhileInStorage(); } }); }; var partialEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, 'SESSION_ID', new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', 'SESSION_ID'); var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var handler1 = goog.testing.recordFunction(); var handler2 = goog.testing.recordFunction(); var handler3 = goog.testing.recordFunction(); // Save some pending redirect that won't be handled and confirm it was // cleared. return savePartialEventManager.setAuthEvent(storageKey, partialEvent).then( function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10); cordovaHandler.addAuthEventListener(handler1); cordovaHandler.addAuthEventListener(handler2); cordovaHandler.addAuthEventListener(handler3); return cordovaHandler.initializeAndWait(); }).then(function() { // Wait for event to be cleared. return waitWhileInStorage(); }).then(function() { // All handlers should be called with no event. assertEquals(1, handler1.getCallCount()); assertAuthEventEquals(noEvent, handler1.getLastCall().getArgument(0)); assertEquals(1, handler2.getCallCount()); assertAuthEventEquals(noEvent, handler2.getLastCall().getArgument(0)); assertEquals(1, handler3.getCallCount()); assertAuthEventEquals(noEvent, handler3.getLastCall().getArgument(0)); // Remove 2 handlers and trigger a new event. cordovaHandler.removeAuthEventListener(handler1); cordovaHandler.removeAuthEventListener(handler2); // Simulate a redirect operation and the partial event is saved. return savePartialEventManager.setAuthEvent(storageKey, partialEvent); }).then(function() { // Trigger the universal link with the OAuth response. universalLinkCb({ url: incomingUrl }); // Wait for event to be cleared from storage. return waitWhileInStorage(); }).then(function() { // Only third handler called with completed event. assertEquals(1, handler1.getCallCount()); assertEquals(1, handler2.getCallCount()); assertEquals(2, handler3.getCallCount()); assertAuthEventEquals( completeEvent, handler3.getLastCall().getArgument(0)); }); }); } function testCordovaHandler_initialNoAuthEvent() { return installAndRunTest('initialNoAuthEvent', function() { var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var handler; var waitForHandler = new goog.Promise(function(resolve, reject) { handler = resolve; }); // Use quick timeout to simulate no event triggered on initialization. cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.initializeAndWait().then(function() { // Wait for handler to be called. return waitForHandler; }).then(function(authEvent) { // No Auth event triggered. assertAuthEventEquals(noEvent, authEvent); }); }); } function testCordovaHandler_initialNoAuthEvent_invalidLink() { return installAndRunTest('initialNoAuthEvent_invalidLink', function() { // This should have been previously saved in a process redirect call. var partialEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, 'SESSION_ID', new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var incomingUrl = 'http://example.firebaseapp.com/some/other/link'; // The final resolved event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var handler; var waitForHandler = new goog.Promise(function(resolve, reject) { handler = resolve; }); // Trigger some non callback link. universalLinks.subscribe = function(eventType, cb) { cb({url: incomingUrl}); }; // Assume pending redirect event. return savePartialEventManager.setAuthEvent(storageKey, partialEvent) .then(function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.initializeAndWait(); }).then(function() { // Wait for handler to be called. return waitForHandler; }).then(function(authEvent) { // Unknown event triggered. assertAuthEventEquals(noEvent, authEvent); }); }); } function testCordovaHandler_initialValidAuthEvent_direct() { return installAndRunTest('initialValidAuthEvent_direct', function() { // This should have been previously saved in a process redirect call. var partialEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, 'SESSION_ID', new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; // The final resolved event. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', 'SESSION_ID'); var handler; var waitForHandler = new goog.Promise(function(resolve, reject) { handler = resolve; }); // Trigger the universal link with the OAuth response on subscription. universalLinks.subscribe = function(eventType, cb) { cb({url: incomingUrl}); }; // Assume pending redirect event. return savePartialEventManager.setAuthEvent(storageKey, partialEvent) .then(function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.initializeAndWait(); }).then(function() { // Wait for handler to be called. return waitForHandler; }).then(function(authEvent) { // Auth event triggered. It should be the complete event. assertAuthEventEquals(completeEvent, authEvent); }); }); } function testCordovaHandler_initialValidAuthEvent_link() { return installAndRunTest('initialValidAuthEvent_link', function() { // This should have been previously saved in a process redirect call. var partialEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, 'SESSION_ID', new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var incomingUrl = 'https://example.app.goo.gl/?link=' + encodeURIComponent( 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'); // The expected event constructed from incoming link. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', 'SESSION_ID'); var handler; var waitForHandler = new goog.Promise(function(resolve, reject) { handler = resolve; }); // Trigger the universal link with the OAuth response on subscription. universalLinks.subscribe = function(eventType, cb) { cb({url: incomingUrl}); }; // Assume pending redirect event. return savePartialEventManager.setAuthEvent(storageKey, partialEvent) .then(function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.initializeAndWait(); }).then(function() { // Wait for handler to be called. return waitForHandler; }).then(function(authEvent) { // Auth event triggered. The expected event should be dispatched. assertAuthEventEquals(completeEvent, authEvent); }); }); } function testCordovaHandler_initialValidAuthEvent_deepLinkId() { return installAndRunTest('initialValidAuthEvent_deepLinkId', function() { // This should have been previously saved in a process redirect call. var partialEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, 'SESSION_ID', new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var incomingUrl = 'comexampleiosurl://google/link?deep_link_id=' + encodeURIComponent( 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'); var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', 'SESSION_ID'); var handler; var waitForHandler = new goog.Promise(function(resolve, reject) { handler = resolve; }); // Trigger the universal link with the OAuth response on subscription. universalLinks.subscribe = function(eventType, cb) { cb({url: incomingUrl}); }; // Assume pending redirect event. return savePartialEventManager.setAuthEvent(storageKey, partialEvent) .then(function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.initializeAndWait(); }).then(function() { // This should have been previously saved in a process redirect call. return waitForHandler; }).then(function(authEvent) { // Auth event triggered. The expected event should be triggered. assertAuthEventEquals(completeEvent, authEvent); }); }); } function testCordovaHandler_initialErrorAuthEvent() { return installAndRunTest('initialErrorAuthEvent', function() { // Expected error. var expectedError = new fireauth.AuthError( fireauth.authenum.Error.INTERNAL_ERROR); var partialEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, 'SESSION_ID', new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); // The callback URL should contain the error. var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback?firebaseError=' + JSON.stringify(expectedError.toPlainObject()); // Triggered event with the error. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', null, null, expectedError); var handler; var waitForHandler = new goog.Promise(function(resolve, reject) { handler = resolve; }); // Trigger the universal link with the OAuth response on subscription. universalLinks.subscribe = function(eventType, cb) { cb({url: incomingUrl}); }; // Assume pending redirect event. return savePartialEventManager.setAuthEvent(storageKey, partialEvent) .then(function() { cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.initializeAndWait(); }).then(function() { // Wait for handler to trigger. return waitForHandler; }).then(function(authEvent) { // Auth event triggered with the expected error event. assertAuthEventEquals(completeEvent, authEvent); }); }); } function testCordovaHandler_processRedirect_success_android() { return installAndRunTest('processRedirect_success_android', function() { var provider = new fireauth.GoogleAuthProvider(); // Construct OAuth handler URL. var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '1234', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }, // Confirm expected endpoint ID passed. fireauth.constants.Endpoint.STAGING.id); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function() { return 0; }); // Simulate Android environment. stubs.replace( fireauth.util, 'getUserAgentString', function() { return androidUA; }); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; // Completed event. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111'); // Initial unknown event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var savedCb = null; // Save the universal link callback on subscription. universalLinks.subscribe = function(eventType, cb) { savedCb = cb; }; cordova.plugins.browsertab.openUrl = function(url) { // Confirm expected URL. assertEquals(expectedUrl, url); // On openUrl, simulate completion by triggering the universal link // callback with the OAuth response. savedCb({url: incomingUrl}); }; var handler = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10, undefined, fireauth.constants.Endpoint.STAGING.id); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.processRedirect('linkViaRedirect', provider, '1234') .then(function() { // Confirm browsertab close called. assertEquals(1, cordova.plugins.browsertab.close.getCallCount()); // Handler triggered twice. assertEquals(2, handler.getCallCount()); // First with no event. assertAuthEventEquals( noEvent, handler.getCalls()[0].getArgument(0)); // Then with resolved event. assertAuthEventEquals( completeEvent, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(event) { assertNull(event); }); }); } function testCordovaHandler_processRedirect_success_android_tenantId() { return installAndRunTest( 'processRedirect_success_android_tenantId', function() { var tenantId = '123456789012'; var provider = new fireauth.GoogleAuthProvider(); // Construct OAuth handler URL. var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '1234', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }, // Confirm expected endpoint ID passed. fireauth.constants.Endpoint.STAGING.id, tenantId); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function() { return 0; }); // Simulate Android environment. stubs.replace( fireauth.util, 'getUserAgentString', function() { return androidUA; }); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; // Completed event. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111', null, null, tenantId); // Initial unknown event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var savedCb = null; // Save the universal link callback on subscription. universalLinks.subscribe = function(eventType, cb) { savedCb = cb; }; cordova.plugins.browsertab.openUrl = function(url) { // Confirm expected URL. assertEquals(expectedUrl, url); // On openUrl, simulate completion by triggering the universal link // callback with the OAuth response. savedCb({url: incomingUrl}); }; var handler = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10, undefined, fireauth.constants.Endpoint.STAGING.id); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.processRedirect( 'linkViaRedirect', provider, '1234', tenantId) .then(function() { // Confirm browsertab close called. assertEquals(1, cordova.plugins.browsertab.close.getCallCount()); // Handler triggered twice. assertEquals(2, handler.getCallCount()); // First with no event. assertAuthEventEquals( noEvent, handler.getCalls()[0].getArgument(0)); // Then with resolved event. assertAuthEventEquals( completeEvent, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(event) { assertNull(event); }); }); } function testCordovaHandler_processRedirect_success_parallelCalls() { return installAndRunTest('processRedirect_success_parallelCalls', function() { var provider = new fireauth.GoogleAuthProvider(); // Construct OAuth handler URL for first operation. var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '1234', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }); // Construct OAuth handler URL for second operation. var expectedUrl2 = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '5678', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function() { return 0; }); // Simulate Android environment. stubs.replace( fireauth.util, 'getUserAgentString', function() { return androidUA; }); var expectedError = new fireauth.AuthError( fireauth.authenum.Error.REDIRECT_OPERATION_PENDING); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; // Completed event for first completed call. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111'); // Completed event for second completed call. var completeEvent2 = new fireauth.AuthEvent( 'linkViaRedirect', '5678', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111'); // Initial unknown event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var savedCb = null; // Save the universal link callback on subscription. universalLinks.subscribe = function(eventType, cb) { savedCb = cb; }; var openUrlCalls = 0; cordova.plugins.browsertab.openUrl = function(url) { openUrlCalls++; // Confirm expected URL on each completed call. if (openUrlCalls == 1) { // First operation expected URL. assertEquals(expectedUrl, url); } else { // Second operation expected URL. assertEquals(expectedUrl2, url); } // On openUrl, simulate completion by triggering the universal link // callback with the OAuth response. savedCb({url: incomingUrl}); }; var handler = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10); cordovaHandler.addAuthEventListener(handler); var successiveCallResult = null; var p = cordovaHandler.processRedirect('linkViaRedirect', provider, '1234') .then(function() { // Confirm browsertab close called. assertEquals(1, cordova.plugins.browsertab.close.getCallCount()); // The other parallel operation should have failed with the expected // error. assertErrorEquals(expectedError, successiveCallResult); // Handler triggered twice. assertEquals(2, handler.getCallCount()); // First with no event. assertAuthEventEquals( noEvent, handler.getCalls()[0].getArgument(0)); // Then with resolved event. assertAuthEventEquals( completeEvent, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(event) { assertNull(event); // Trigger again. As the operation already completed, this should // eventually succeed too. return cordovaHandler.processRedirect( 'linkViaRedirect', provider, '5678'); }).then(function() { // Confirm browsertab close called again. assertEquals(2, cordova.plugins.browsertab.close.getCallCount()); // Handler triggered thrice. assertEquals(3, handler.getCallCount()); // Last call should have resolved event with the second event. assertAuthEventEquals( completeEvent2, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(event) { assertNull(event); }); // This should fail as there is a pending operation already. cordovaHandler.processRedirect('linkViaRedirect', provider, '5678') .thenCatch(function(error) { // Confirm browsertab close not called. assertEquals(0, cordova.plugins.browsertab.close.getCallCount()); // Save the pending redirect error. successiveCallResult = error; }); return p; }); } function testCordovaHandler_processRedirect_success_parallelCalls_tenantId() { return installAndRunTest( 'processRedirect_success_parallelCalls_tenantId', function() { var tenantId1 = '123456789012'; var tenantId2 = '987654321098'; var provider = new fireauth.GoogleAuthProvider(); // Construct OAuth handler URL for first operation. var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '1234', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }, null, tenantId1); // Construct OAuth handler URL for second operation. var expectedUrl2 = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '5678', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }, null, tenantId2); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function() { return 0; }); // Simulate Android environment. stubs.replace( fireauth.util, 'getUserAgentString', function() { return androidUA; }); var expectedError = new fireauth.AuthError( fireauth.authenum.Error.REDIRECT_OPERATION_PENDING); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; // Completed event for first completed call. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111', null, null, tenantId1); // Completed event for second completed call. var completeEvent2 = new fireauth.AuthEvent( 'linkViaRedirect', '5678', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111', null, null, tenantId2); // Initial unknown event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var savedCb = null; // Save the universal link callback on subscription. universalLinks.subscribe = function(eventType, cb) { savedCb = cb; }; var openUrlCalls = 0; cordova.plugins.browsertab.openUrl = function(url) { openUrlCalls++; // Confirm expected URL on each completed call. if (openUrlCalls == 1) { // First operation expected URL. assertEquals(expectedUrl, url); } else { // Second operation expected URL. assertEquals(expectedUrl2, url); } // On openUrl, simulate completion by triggering the universal link // callback with the OAuth response. savedCb({url: incomingUrl}); }; var handler = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10); cordovaHandler.addAuthEventListener(handler); var successiveCallResult = null; var p = cordovaHandler.processRedirect( 'linkViaRedirect', provider, '1234', tenantId1) .then(function() { // Confirm browsertab close called. assertEquals(1, cordova.plugins.browsertab.close.getCallCount()); // The other parallel operation should have failed with the expected // error. assertErrorEquals(expectedError, successiveCallResult); // Handler triggered twice. assertEquals(2, handler.getCallCount()); // First with no event. assertAuthEventEquals( noEvent, handler.getCalls()[0].getArgument(0)); // Then with resolved event. assertAuthEventEquals( completeEvent, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(event) { assertNull(event); // Trigger again. As the operation already completed, this should // eventually succeed too. return cordovaHandler.processRedirect( 'linkViaRedirect', provider, '5678', tenantId2); }).then(function() { // Confirm browsertab close called again. assertEquals(2, cordova.plugins.browsertab.close.getCallCount()); // Handler triggered thrice. assertEquals(3, handler.getCallCount()); // Last call should have resolved event with the second event. assertAuthEventEquals( completeEvent2, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function(event) { assertNull(event); }); // This should fail as there is a pending operation already. cordovaHandler.processRedirect('linkViaRedirect', provider, '5678') .thenCatch(function(error) { // Confirm browsertab close not called. assertEquals(0, cordova.plugins.browsertab.close.getCallCount()); // Save the pending redirect error. successiveCallResult = error; }); return p; }); } function testCordovaHandler_processRedirect_success_withEmulator() { var emulatorConfig = { url: 'http://emulator.test.domain:1234' }; return installAndRunTest( 'testCordovaHandler_processRedirect_success_withEmulator', function () { var provider = new fireauth.GoogleAuthProvider(); // Construct OAuth handler URL. var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '1234', version, { apn: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }, // Confirm expected endpoint ID passed. fireauth.constants.Endpoint.STAGING.id, null, emulatorConfig); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function () { return 0; }); // Simulate Android environment. stubs.replace( fireauth.util, 'getUserAgentString', function () { return androidUA; }); var incomingUrl = 'http://emulator.test.domain:1234/__/auth/callback#oauthResponse'; // Completed event. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', incomingUrl, '11111111111111111111'); // Initial unknown event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var savedCb = null; // Save the universal link callback on subscription. universalLinks.subscribe = function (eventType, cb) { savedCb = cb; }; cordova.plugins.browsertab.openUrl = function (url) { // Confirm expected URL. assertEquals(expectedUrl, url); // On openUrl, simulate completion by triggering the universal link // callback with the OAuth response. savedCb({ url: incomingUrl }); }; var handler = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10, undefined, fireauth.constants.Endpoint.STAGING.id, emulatorConfig); cordovaHandler.addAuthEventListener(handler); return cordovaHandler.processRedirect('linkViaRedirect', provider, '1234') .then(function () { // Confirm browsertab close called. assertEquals(1, cordova.plugins.browsertab.close.getCallCount()); // Handler triggered twice. assertEquals(2, handler.getCallCount()); // First with no event. assertAuthEventEquals( noEvent, handler.getCalls()[0].getArgument(0)); // Then with resolved event. assertAuthEventEquals( completeEvent, handler.getLastCall().getArgument(0)); // Confirm event deleted from storage. return getAndDeletePartialEventManager.getAuthEvent(); }).then(function (event) { assertNull(event); }); }); } function testCordovaHandler_processRedirect_success_ios() { return installAndRunTest('processRedirect_success_ios', function() { var provider = new fireauth.GoogleAuthProvider(); // Construct OAuth handler URL. var expectedUrl = fireauth.iframeclient.IfcHandler.getOAuthHelperWidgetUrl( authDomain, apiKey, appName, 'linkViaRedirect', provider, null, '1234', version, { ibi: 'com.example.app', appDisplayName: 'Test App', sessionId: sha256('11111111111111111111') }); // Stub this so the session ID generated can be predictable. stubs.replace( Math, 'random', function() { return 0; }); // Simulate iOS environment. stubs.replace( fireauth.util, 'getUserAgentString', function() { return iOS9iPhoneUA; }); var incomingUrl = 'http://example.firebaseapp.com/__/auth/callback#oauthResponse'; // Completed event. var completeEvent = new fireauth.AuthEvent( 'linkViaRedirect', '1234', 'http://example.firebaseapp.com/__/auth/callback#oauthResponse', '11111111111111111111'); // Initial no event. var noEvent = new fireauth.AuthEvent( fireauth.AuthEvent.Type.UNKNOWN, null, null, null, new fireauth.AuthError(fireauth.authenum.Error.NO_AUTH_EVENT)); var savedCb = null; // Save the universal link callback on subscription. universalLinks.subscribe = function(eventType, cb) { savedCb = cb; }; cordova.plugins.browsertab.openUrl = function(url) { // Confirm expected URL. assertEquals(expectedUrl, url); // On openUrl, simulate completion by triggering the universal link // callback with the OAuth response. // This is currently not the default behavior but will be supported in the // future. savedCb({url: incomingUrl}); }; var handler = goog.testing.recordFunction(); cordovaHandler = new fireauth.CordovaHandler( authDomain, apiKey, appName, version, 10); cordovaHandler.addAuthEventListener(handler); return cordovaHandle