quodolores
Version:
Monorepo for the Firebase JavaScript SDK
1,702 lines (1,597 loc) • 314 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 rpchandler.js.
*/
goog.provide('fireauth.RpcHandlerTest');
goog.require('fireauth.AuthError');
goog.require('fireauth.AuthErrorWithCredential');
goog.require('fireauth.AuthProvider');
goog.require('fireauth.FacebookAuthProvider');
goog.require('fireauth.GoogleAuthProvider');
goog.require('fireauth.OAuthCredential');
goog.require('fireauth.OAuthProvider');
goog.require('fireauth.RpcHandler');
goog.require('fireauth.authenum.Error');
goog.require('fireauth.common.testHelper');
goog.require('fireauth.constants');
goog.require('fireauth.util');
goog.require('goog.Promise');
goog.require('goog.json');
goog.require('goog.net.CorsXmlHttpFactory');
goog.require('goog.net.EventType');
goog.require('goog.net.FetchXmlHttpFactory');
goog.require('goog.net.XhrIo');
goog.require('goog.net.XhrLike');
goog.require('goog.object');
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.recordFunction');
goog.setTestOnly('fireauth.RpcHandlerTest');
var ignoreArgument;
var gapi = gapi || {};
var stubs = new goog.testing.PropertyReplacer();
var rpcHandler = null;
var expectedResponse = {
'resp1': 'val1',
'resp2': 'val2'
};
// STS token server response.
var expectedStsTokenResponse = {
'access_token': 'accessToken',
'refresh_token': 'refreshToken',
'expires_in': '3600'
};
// Token response with expiresIn.
var tokenResponseWithExpiresIn = {
'idToken': 'accessToken',
'refreshToken': 'refreshToken',
'expiresIn': '3600'
};
// New token response without expiresIn.
var tokenResponse = {
'idToken': 'accessToken',
'refreshToken': 'refreshToken'
};
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
var CURRENT_URL = 'http://www.example.com:8080/foo.htm';
var clock;
var mockControl;
var delay = 30000;
var identityPlatformEndpoint =
fireauth.constants.Endpoint.PRODUCTION.identityPlatformEndpoint;
var now = new Date();
var pendingCredResponse;
var pendingCredResponseWithAdditionalInfo;
function setUp() {
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
stubs.replace(
goog.net.XhrIo.prototype,
'send',
goog.testing.recordFunction());
stubs.replace(
goog.net.XhrIo.prototype,
'listen',
goog.testing.recordFunction());
stubs.replace(
goog.net.XhrIo.prototype,
'listenOnce',
goog.testing.recordFunction());
stubs.replace(
goog.net.XhrIo.prototype,
'setTimeoutInterval',
goog.testing.recordFunction());
stubs.replace(fireauth.util, 'getCurrentUrl', function() {
return CURRENT_URL;
});
rpcHandler = new fireauth.RpcHandler('apiKey');
ignoreArgument = goog.testing.mockmatchers.ignoreArgument;
mockControl = new goog.testing.MockControl();
mockControl.$resetAll();
pendingCredResponse = {
'mfaInfo': {
'mfaEnrollmentId': 'ENROLLMENT_UID1',
'enrolledAt': now.toISOString(),
'phoneInfo': '+16505551234'
},
'mfaPendingCredential': 'PENDING_CREDENTIAL'
};
pendingCredResponseWithAdditionalInfo =
goog.object.clone(pendingCredResponse);
goog.object.extend(pendingCredResponseWithAdditionalInfo, {
// Credential returned.
'providerId': 'google.com',
'oauthAccessToken': 'googleAccessToken',
'oauthIdToken': 'googleIdToken',
'oauthExpireIn': 3600,
// Additional user info data.
'rawUserInfo': '{"kind":"plus#person","displayName":"John Doe",' +
'"name":{"givenName":"John","familyName":"Doe"}}'
});
}
/**
* @param {string} url The URL to make a request to.
* @param {string} method The HTTP send method.
* @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string}
* data The request content.
* @param {?Object} headers The request content headers.
* @param {number} timeout The request timeout.
* @param {?Object} response The response to return.
*/
function assertSendXhrAndRunCallback(
url, method, data, headers, timeout, response) {
stubs.replace(
fireauth.RpcHandler.prototype,
'sendXhr_',
function(actualUrl, callback, actualMethod, actualData, actualHeaders,
actualTimeout) {
assertEquals(url, actualUrl);
assertEquals(method, actualMethod);
assertEquals(data, actualData);
assertObjectEquals(headers, actualHeaders);
assertEquals(timeout, actualTimeout);
callback(response);
});
}
/**
* Asserts that server errors are handled correctly.
* @param {function() : !goog.Promise} methodToTest The method that we are
* testing, which returns a Promise that we expect to reject with an error.
* @param {!Object<string, string>} errorMap A map from server errors to the
* errors we expect from the method under test.
* @param {string} url The expected URL to which a request is made.
* @param {!Object<string, string>} body The expected body of the request.
*/
function assertServerErrorsAreHandled(methodToTest, errorMap, url, body) {
errorMap = goog.object.clone(errorMap);
asyncTestCase.waitForSignals(goog.object.getKeys(errorMap).length);
var promise = goog.Promise.resolve();
goog.object.forEach(errorMap, function(expectedError, serverErrorCode) {
promise = promise.then(function() {
assertSendXhrAndRunCallback(
url,
'POST',
goog.json.serialize(body),
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_,
delay,
{
'error': {
'message': serverErrorCode
}
});
return methodToTest().thenCatch(function(error) {
fireauth.common.testHelper.assertErrorEquals(
new fireauth.AuthError(expectedError), error);
asyncTestCase.signal();
});
});
});
}
function tearDown() {
pendingCredResponse = null;
pendingCredResponseWithAdditionalInfo = null;
stubs.reset();
rpcHandler = null;
fireauth.RpcHandler.loadGApi_ = null;
goog.dispose(clock);
try {
mockControl.$verifyAll();
} finally {
mockControl.$tearDown();
}
delete goog.global['self'];
}
function testGetApiKey() {
assertEquals('apiKey', rpcHandler.getApiKey());
}
function testUpdateGetTenantId() {
assertNull(rpcHandler.getTenantId());
rpcHandler.updateTenantId('123456789012');
assertEquals('123456789012', rpcHandler.getTenantId());
rpcHandler.updateTenantId(null);
assertNull(rpcHandler.getTenantId());
}
function testRpcHandler_XMLHttpRequest_notSupported() {
stubs.replace(
fireauth.RpcHandler,
'getXMLHttpRequest',
function() {return undefined;});
var expectedError = new fireauth.AuthError(
fireauth.authenum.Error.INTERNAL_ERROR,
'The XMLHttpRequest compatibility library was not found.');
var error = assertThrows(function() { new fireauth.RpcHandler('apiKey'); });
fireauth.common.testHelper.assertErrorEquals(expectedError, error);
}
function testRpcHandler_XMLHttpRequest_worker() {
// Test worker environment that FetchXmlHttpFactory is used in initialization
// of goog.net.XhrIo.
// Install mock clock.
clock = new goog.testing.MockClock(true);
// Simulates global self in a worker environment.
goog.global['self'] = {};
var xhrInstance = mockControl.createStrictMock(goog.net.XhrLike);
var createInstance = mockControl.createMethodMock(
goog.net.FetchXmlHttpFactory.prototype, 'createInstance');
stubs.reset();
// Simulate worker environment.
stubs.replace(
fireauth.util,
'isWorker',
function() {return true;});
// Simulate fetch, Request and Headers API supported.
stubs.replace(
fireauth.util,
'isFetchSupported',
function() {return true;});
// No XMLHttpRequest available.
stubs.replace(
fireauth.RpcHandler,
'getXMLHttpRequest',
function() {return undefined;});
// Confirm RPC handler calls XHR instance from FetchXmlHttpFactory XHR.
createInstance().$returns(xhrInstance);
xhrInstance.open(ignoreArgument, ignoreArgument, ignoreArgument).$once();
xhrInstance.setRequestHeader(ignoreArgument, ignoreArgument).$once();
xhrInstance.send(ignoreArgument).$once();
xhrInstance.abort().$once();
asyncTestCase.waitForSignals(1);
mockControl.$replayAll();
rpcHandler = new fireauth.RpcHandler('apiKey');
// Simulate RPC and then timeout.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(error) {
asyncTestCase.signal();
});
// Timeout XHR request.
clock.tick(delay * 2);
}
function testRpcHandler_XMLHttpRequest_worker_fetchNotSupported() {
// Test worker environment where fetch, Headers and Request are not supported.
// Simulates global self in a worker environment.
goog.global['self'] = {};
var expectedError = new fireauth.AuthError(
fireauth.authenum.Error.OPERATION_NOT_SUPPORTED,
'fetch, Headers and Request native APIs or equivalent Polyfills ' +
'must be available to support HTTP requests from a Worker environment.');
stubs.reset();
// Simulate worker environment.
stubs.replace(
fireauth.util,
'isWorker',
function() {return true;});
// Simulate fetch, Request and Headers API not supported.
stubs.replace(
fireauth.util,
'isFetchSupported',
function() {return false;});
// No XMLHttpRequest available.
stubs.replace(
fireauth.RpcHandler,
'getXMLHttpRequest',
function() {return undefined;});
asyncTestCase.waitForSignals(1);
rpcHandler = new fireauth.RpcHandler('apiKey');
// Simulate RPC and then expected error thrown.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(actualError) {
fireauth.common.testHelper.assertErrorEquals(
expectedError, actualError);
asyncTestCase.signal();
});
}
function testRpcHandler_XMLHttpRequest_corsBrowser() {
// Test CORS browser environment that CorsXmlHttpFactory is used in
// initialization of goog.net.XhrIo.
// Install mock clock.
clock = new goog.testing.MockClock(true);
var xhrInstance = mockControl.createStrictMock(goog.net.XhrLike);
var createInstance = mockControl.createMethodMock(
goog.net.CorsXmlHttpFactory.prototype, 'createInstance');
stubs.reset();
// Non-worker environment.
stubs.replace(
fireauth.util,
'isWorker',
function() {return false;});
// CORS supporting browser.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Non-native environment.
stubs.replace(
fireauth.util,
'isNativeEnvironment',
function() {return false;});
// Confirm RPC handler calls XHR instance from CorsXmlHttpFactory XHR.
createInstance().$returns(xhrInstance);
xhrInstance.open(ignoreArgument, ignoreArgument, ignoreArgument).$once();
xhrInstance.setRequestHeader(ignoreArgument, ignoreArgument).$once();
xhrInstance.send(ignoreArgument).$once();
xhrInstance.abort().$once();
asyncTestCase.waitForSignals(1);
mockControl.$replayAll();
rpcHandler = new fireauth.RpcHandler('apiKey');
// Simulate RPC and then timeout.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(error) {
asyncTestCase.signal();
});
// Timeout XHR request.
clock.tick(delay * 2);
}
function testRpcHandler_XMLHttpRequest_reactNative() {
// Test react-native environment that built-in XMLHttpRequest is used in
// xhrFactory.
// Install mock clock.
clock = new goog.testing.MockClock(true);
var xhrInstance = mockControl.createStrictMock(goog.net.XhrLike);
var xhrConstructor = mockControl.createConstructorMock(
goog.net, 'XhrLike');
stubs.reset();
// CORS supporting environment.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Return native XMLHttpRequest..
stubs.replace(
fireauth.RpcHandler,
'getXMLHttpRequest',
function() {return xhrConstructor;});
// React-native environment.
stubs.replace(
fireauth.util,
'isNativeEnvironment',
function() {return true;});
stubs.replace(
fireauth.util,
'getEnvironment',
function() {return fireauth.util.Env.REACT_NATIVE;});
// Confirm RPC handler calls XHR instance from factory XHR.
xhrConstructor().$returns(xhrInstance);
xhrInstance.open(ignoreArgument, ignoreArgument, ignoreArgument).$once();
xhrInstance.setRequestHeader(ignoreArgument, ignoreArgument).$once();
xhrInstance.send(ignoreArgument).$once();
xhrInstance.abort().$once();
asyncTestCase.waitForSignals(1);
mockControl.$replayAll();
rpcHandler = new fireauth.RpcHandler('apiKey');
// Simulate RPC and then timeout.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(error) {
asyncTestCase.signal();
});
// Timeout XHR request.
clock.tick(delay * 2);
}
function testRpcHandler_XMLHttpRequest_node() {
// Test node environment that Node.js implementation is used in xhrfactory.
// Install mock clock.
clock = new goog.testing.MockClock(true);
var xhrInstance = mockControl.createStrictMock(goog.net.XhrLike);
var xhrConstructor = mockControl.createConstructorMock(
goog.net, 'XhrLike');
stubs.reset();
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Return mock XHR constructor. In a Node.js environment the polyfill library
// would be used.
stubs.replace(
fireauth.RpcHandler,
'getXMLHttpRequest',
function() {return xhrConstructor;});
// Node.js environment.
stubs.replace(
fireauth.util,
'getEnvironment',
function() {return fireauth.util.Env.NODE;});
// Confirm RPC handler calls XHR instance from factory XHR.
xhrConstructor().$returns(xhrInstance);
xhrInstance.open(ignoreArgument, ignoreArgument, ignoreArgument).$once();
xhrInstance.setRequestHeader(ignoreArgument, ignoreArgument).$once();
xhrInstance.send(ignoreArgument).$once();
xhrInstance.abort().$once();
asyncTestCase.waitForSignals(1);
mockControl.$replayAll();
rpcHandler = new fireauth.RpcHandler('apiKey');
// Simulate RPC and then timeout.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(error) {
asyncTestCase.signal();
});
// Timeout XHR request.
clock.tick(delay * 2);
}
/**
* Asserts and applies the goog.net.XhrIo send call.
* @param {string} url The expected XHR URL.
* @param {string} method The XHR expected HTTP method.
* @param {?ArrayBuffer|?ArrayBufferView|?Blob|?Document|?FormData|string=} data
* The expected request data.
* @param {?Object|undefined} headers The expected HTTP headers.
* @param {number} timeout The expected timeout.
* @param {?Object|undefined} resp The expected response to return.
*/
function assertXhrIoAndRunCallback(url, method, data, headers, timeout, resp) {
// Confirm correct parameters passed to goog.net.XhrIo.send.
assertEquals(
1,
goog.net.XhrIo.prototype.send.getCallCount());
assertEquals(
url,
goog.net.XhrIo.prototype.send.getLastCall().getArgument(0));
assertEquals(
method,
goog.net.XhrIo.prototype.send.getLastCall().getArgument(1));
assertEquals(
data,
goog.net.XhrIo.prototype.send.getLastCall().getArgument(2));
assertObjectEquals(
headers,
goog.net.XhrIo.prototype.send.getLastCall().getArgument(3));
assertEquals(
1,
goog.net.XhrIo.prototype.setTimeoutInterval.getCallCount());
assertEquals(
timeout,
goog.net.XhrIo.prototype.setTimeoutInterval.getLastCall().getArgument(0));
// Get on complete callback.
var callback = goog.net.XhrIo.prototype.listen.getLastCall().getArgument(1);
// Returned expected response.
var self = {
// Return the response text.
getResponseText: function() {
return goog.json.serialize(resp);
}
};
// Run on complete callback, pass self as this to return expected response.
callback.apply(self);
}
function testSendXhr_post() {
rpcHandler = new fireauth.RpcHandler('apiKey');
// Check response is passed to provided callback.
var responseRecorded = null;
var func = function(response) {
responseRecorded = response;
};
var data = 'key1=value1&key2=value2';
var headers = {
'Content-Type': 'application/json'
};
// Send XHR with test parameters.
rpcHandler.sendXhr_(
'url1',
func,
'POST',
data,
headers,
5000);
// Confirm correct parameters passed and run on complete.
assertXhrIoAndRunCallback(
'url1',
'POST',
data,
headers,
5000,
expectedResponse);
// Confirm callback called with expected response.
assertObjectEquals(expectedResponse, responseRecorded);
}
/**
* Tests client version being correctly sent with requests to Firebase Auth
* server.
*/
function testSendFirebaseBackendRequest_clientVersion() {
var clientVersion = 'Chrome/JsCore/3.0.0';
// Simulate clock.
clock = new goog.testing.MockClock();
clock.install();
clock.tick(50);
// Pass client version in constructor.
var rpcHandler = new fireauth.RpcHandler(
'apiKey', null, clientVersion);
var expectedDomains = [
'domain.com',
'www.mydomain.com'
];
var serverResponse = {
'authorizedDomains': [
'domain.com',
'www.mydomain.com'
]
};
// The client version should be passed to header.
var expectedHeaders = {
'Content-Type': 'application/json',
'X-Client-Version': clientVersion
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/' +
'getProjectConfig?key=apiKey&cb=50',
'GET',
undefined,
expectedHeaders,
delay,
serverResponse);
rpcHandler.getAuthorizedDomains().then(function(domains) {
assertArrayEquals(expectedDomains, domains);
asyncTestCase.signal();
});
}
function testSendFirebaseBackendRequest_timeout() {
// Test network timeout error for Firebase backend request.
var actualError;
// Allow xhrIo requests.
stubs.reset();
// Simulate CORS support.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Expected timeout error.
var timeoutError = new fireauth.AuthError(
fireauth.authenum.Error.NETWORK_REQUEST_FAILED);
// Install mock clock.
clock = new goog.testing.MockClock(true);
rpcHandler = new fireauth.RpcHandler('apiKey');
// Send request for backend API.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(error) {
// Record error.
actualError = error;
});
// Timeout XHR request.
clock.tick(delay * 2);
// Timeout error should have been returned.
fireauth.common.testHelper.assertErrorEquals(timeoutError, actualError);
}
function testSendFirebaseBackendRequest_offline_falseAlert() {
// Install mock clock.
clock = new goog.testing.MockClock(true);
var expectedResponse = [
'google.com',
'myauthprovider.com'
];
var serverResponse = {
'kind': 'identitytoolkit#CreateAuthUriResponse',
'authUri': 'https://accounts.google.com/o/oauth2/auth?foo=bar',
'providerId': 'google.com',
'allProviders': [
'google.com',
'myauthprovider.com'
],
'registered': true,
'forExistingProvider': true,
'sessionId': 'MY_SESSION_ID'
};
var identifier = 'MY_ID';
stubs.reset();
// Simulate browser supports CORS.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Simulate expected URL returned for current URL.
stubs.replace(
fireauth.util,
'getCurrentUrl',
function() {
return CURRENT_URL;
});
// Simulate false alert navigator.onLine.
stubs.replace(
fireauth.util,
'isOnline',
function() {return false;});
// Overwrite XHR IO send to simulate a 4999ms delay before the response.
stubs.replace(
goog.net.XhrIo.prototype,
'send',
function(url, httpMethod, data, headers) {
assertEquals(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/' +
'createAuthUri?key=apiKey',
url);
assertEquals('POST', httpMethod);
assertEquals(goog.json.serialize(request), data);
assertObjectEquals(
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_, headers);
clock.tick(4999);
this.dispatchEvent(goog.net.EventType.COMPLETE);
});
// Simulate expected response returned.
stubs.replace(
goog.net.XhrIo.prototype,
'getResponseText',
function() {
return JSON.stringify(serverResponse);
});
asyncTestCase.waitForSignals(1);
var request = {
'identifier': identifier,
'continueUri': CURRENT_URL
};
rpcHandler.fetchProvidersForIdentifier(identifier)
.then(function(response) {
assertArrayEquals(expectedResponse, response);
asyncTestCase.signal();
});
}
function testSendFirebaseBackendRequest_offline_slowResponse() {
// Install mock clock.
clock = new goog.testing.MockClock(true);
var serverResponse = {
'kind': 'identitytoolkit#CreateAuthUriResponse',
'authUri': 'https://accounts.google.com/o/oauth2/auth?foo=bar',
'providerId': 'google.com',
'allProviders': [
'google.com',
'myauthprovider.com'
],
'registered': true,
'forExistingProvider': true,
'sessionId': 'MY_SESSION_ID'
};
var identifier = 'MY_ID';
stubs.reset();
// Simulate browser supports CORS.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Simulate expected URL returned for current URL.
stubs.replace(
fireauth.util,
'getCurrentUrl',
function() {
return CURRENT_URL;
});
// Simulate false alert navigator.onLine.
stubs.replace(
fireauth.util,
'isOnline',
function() {return false;});
// Overwrite XHR IO send to simulate a 5000mx delay before the response.
stubs.replace(
goog.net.XhrIo.prototype,
'send',
function(url, httpMethod, data, headers) {
assertEquals(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/' +
'createAuthUri?key=apiKey',
url);
assertEquals('POST', httpMethod);
assertEquals(goog.json.serialize(request), data);
assertObjectEquals(
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_, headers);
clock.tick(5000);
this.dispatchEvent(goog.net.EventType.COMPLETE);
});
// Simulate expected response returned.
stubs.replace(
goog.net.XhrIo.prototype,
'getResponseText',
function() {
return JSON.stringify(serverResponse);
});
asyncTestCase.waitForSignals(1);
var request = {
'identifier': identifier,
'continueUri': CURRENT_URL
};
// Expected timeout error even though the request was eventually returned.
var timeoutError = new fireauth.AuthError(
fireauth.authenum.Error.NETWORK_REQUEST_FAILED);
rpcHandler.fetchProvidersForIdentifier(identifier)
.thenCatch(function(actualError) {
// Timeout error should have been returned.
fireauth.common.testHelper.assertErrorEquals(timeoutError, actualError);
asyncTestCase.signal();
});
}
function testSendFirebaseBackendRequest_offline() {
// Test network timeout error for offline Firebase backend request.
asyncTestCase.waitForSignals(1);
// Allow xhrIo requests.
stubs.reset();
// Simulate app offline.
stubs.replace(
fireauth.util,
'isOnline',
function() {return false;});
// Install mock clock.
clock = new goog.testing.MockClock(true);
// Expected timeout error.
var timeoutError = new fireauth.AuthError(
fireauth.authenum.Error.NETWORK_REQUEST_FAILED);
rpcHandler = new fireauth.RpcHandler('apiKey');
// Send request for backend API.
rpcHandler.fetchProvidersForIdentifier('user@example.com')
.thenCatch(function(error) {
// Timeout error event without any wait (no tick in mockclock).
fireauth.common.testHelper.assertErrorEquals(timeoutError, error);
asyncTestCase.signal();
});
// Simulate short timeout when navigator.onLine is false.
clock.tick(5000);
}
function testSendStsTokenBackendRequest_timeout() {
// Test network timeout error for STS token backend request.
var actualError;
// Allow xhrIo requests.
stubs.reset();
// Simulate CORS support.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return true;});
// Expected timeout error.
var timeoutError = new fireauth.AuthError(
fireauth.authenum.Error.NETWORK_REQUEST_FAILED);
// Install mock clock.
clock = new goog.testing.MockClock(true);
rpcHandler = new fireauth.RpcHandler('apiKey');
// Send request for backend API.
rpcHandler.requestStsToken({
'grant_type': 'authorization_code',
'code': 'idToken'
}).thenCatch(function(error) {
// Record error.
actualError = error;
});
// Timeout XHR request.
clock.tick(delay * 2);
// Timeout error should have been returned.
fireauth.common.testHelper.assertErrorEquals(timeoutError, actualError);
}
function testSendStsTokenBackendRequest_offline() {
// Test network timeout error for offline STS token backend request.
asyncTestCase.waitForSignals(1);
// Allow xhrIo requests.
stubs.reset();
// Simulate app offline.
stubs.replace(
fireauth.util,
'isOnline',
function() {return false;});
// Install mock clock.
clock = new goog.testing.MockClock(true);
// Expected timeout error.
var timeoutError = new fireauth.AuthError(
fireauth.authenum.Error.NETWORK_REQUEST_FAILED);
rpcHandler = new fireauth.RpcHandler('apiKey');
// Send request for backend API.
rpcHandler.requestStsToken({
'grant_type': 'authorization_code',
'code': 'idToken'
}).thenCatch(function(error) {
// Timeout error event without any wait (no tick in mockclock).
fireauth.common.testHelper.assertErrorEquals(timeoutError, error);
asyncTestCase.signal();
});
// Simulate short timeout when navigator.onLine is false.
clock.tick(5000);
}
function testSendXhr_corsUnsupported() {
var expectedResponse = {
'key1': 'value1',
'key2': 'value2'
};
var recordedToken = 'token';
gapi.auth = gapi.auth || {};
gapi.client = gapi.client || {};
stubs.reset();
// Simulate GApi loaded.
stubs.set(
gapi.auth,
'getToken',
function() {
return recordedToken;
});
stubs.set(
gapi.auth,
'setToken',
function(token) {
recordedToken = token;
});
stubs.set(
gapi.client,
'request',
function(request) {
assertEquals('none', request['authType']);
assertEquals('url1', request['path']);
assertEquals('GET', request['method']);
assertEquals(data, request['body']);
assertObjectEquals(headers, request['headers']);
request['callback'](expectedResponse);
asyncTestCase.signal();
});
stubs.set(
gapi.client,
'setApiKey',
function(apiKey) {
assertEquals('apiKey', apiKey);
asyncTestCase.signal();
});
// Simulate browser that does not support CORS.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return false;});
var func = function(response) {
assertObjectEquals(expectedResponse, response);
// Verify token updated.
assertEquals('token', gapi.auth.getToken());
asyncTestCase.signal();
};
var data = 'key1=value1&key2=value2';
var headers = {
'Content-Type': 'application/json'
};
asyncTestCase.waitForSignals(3);
// Simulate GApi dependencies loaded.
fireauth.RpcHandler.loadGApi_ = goog.Promise.resolve();
rpcHandler.sendXhr_(
'url1',
func,
'GET',
data,
headers,
5000);
}
function testSendXhr_corsUnsupported_error() {
var expectedResponse = {
'error': {
'message': fireauth.RpcHandler.ServerError.CORS_UNSUPPORTED
}
};
// Simulate browser that does not support CORS.
stubs.replace(
fireauth.util,
'supportsCors',
function() {return false;});
var func = function(response) {
assertObjectEquals(expectedResponse, response);
asyncTestCase.signal();
};
var data = 'key1=value1&key2=value2';
var headers = {
'Content-Type': 'application/json'
};
asyncTestCase.waitForSignals(1);
fireauth.RpcHandler.loadGApi_ = goog.Promise.reject();
rpcHandler.sendXhr_(
'url1',
func,
'GET',
data,
headers,
5000);
}
function testSendSecureTokenBackendRequest_clientVersion() {
var clientVersion = 'Chrome/JsCore/3.0.0';
// The client version should be passed to header.
var expectedHeaders = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Client-Version': clientVersion
};
// Pass client version in constructor.
var rpcHandler = new fireauth.RpcHandler(
'apiKey', null, clientVersion);
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'https://securetoken.googleapis.com/v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
expectedHeaders,
delay,
expectedStsTokenResponse);
// Send STS token request, default config will be used.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(function(response) {
assertObjectEquals(
expectedStsTokenResponse,
response);
asyncTestCase.signal();
});
}
function testRequestStsToken_updateClientVersion() {
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'https://securetoken.googleapis.com/v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
{
'Content-Type': 'application/x-www-form-urlencoded',
'X-Client-Version': 'Chrome/JsCore/3.0.0/FirebaseCore-web'
},
delay,
expectedStsTokenResponse);
// Update client version.
rpcHandler.updateClientVersion('Chrome/JsCore/3.0.0/FirebaseCore-web');
// Send STS token request.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(function(response) {
assertObjectEquals(
expectedStsTokenResponse,
response);
asyncTestCase.signal();
});
}
function testRequestStsToken_removeClientVersion() {
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'https://securetoken.googleapis.com/v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
{
'Content-Type': 'application/x-www-form-urlencoded'
},
delay,
expectedStsTokenResponse);
// Remove client version.
rpcHandler.updateClientVersion(null);
// Send STS token request.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(function(response) {
assertObjectEquals(
expectedStsTokenResponse,
response);
asyncTestCase.signal();
});
}
function testRequestStsToken_default() {
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'https://securetoken.googleapis.com/v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_,
delay,
expectedStsTokenResponse);
// Send STS token request, default config will be used.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(function(response) {
assertObjectEquals(
expectedStsTokenResponse,
response);
asyncTestCase.signal();
});
}
function testRequestStsToken_custom() {
asyncTestCase.waitForSignals(1);
// Reinitialize RPC handler using custom config.
rpcHandler = new fireauth.RpcHandler(
'apiKey',
{
'secureTokenEndpoint': 'http://localhost/token',
'secureTokenTimeout': new fireauth.util.Delay(5000, 5000),
'secureTokenHeaders': {'Content-Type': 'application/json'}
});
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'http://localhost/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
{
'Content-Type': 'application/json'
},
5000,
expectedStsTokenResponse);
// Send STS token request, custom config will be used.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(function(response) {
assertObjectEquals(
expectedStsTokenResponse,
response);
asyncTestCase.signal();
});
}
function testRequestStsToken_invalidRequest() {
asyncTestCase.waitForSignals(1);
// Reinitialize RPC handler.
rpcHandler = new fireauth.RpcHandler(
'apiKey');
// Send STS token request, no XHR, invalid request.
rpcHandler.requestStsToken(
{
'invalid': 'authorization_code',
'code': 'idToken'
}).then(
function(response) {},
function(error) {
fireauth.common.testHelper.assertErrorEquals(
new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR),
error);
asyncTestCase.signal();
});
}
function testRequestStsToken_unknownServerResponse() {
var serverResponse = {'error': 'INTERNAL_SERVER_ERROR'};
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'https://securetoken.googleapis.com/v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_,
delay,
serverResponse);
// Send STS token request, default config will be used.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(
function(response) {},
function(error) {
fireauth.common.testHelper.assertErrorEquals(
new fireauth.AuthError(fireauth.authenum.Error.INTERNAL_ERROR,
goog.json.serialize(serverResponse)),
error);
asyncTestCase.signal();
});
}
function testRequestStsToken_specificErrorResponse() {
// Server error response when token is expired.
var serverResponse = {
"error": {
"code": 400,
"message": "TOKEN_EXPIRED",
"status": "INVALID_ARGUMENT"
}
};
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'https://securetoken.googleapis.com/v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_,
delay,
serverResponse);
// Send STS token request, default config will be used.
rpcHandler.requestStsToken(
{
'grant_type': 'authorization_code',
'code': 'idToken'
}).then(
function(response) {},
function(error) {
fireauth.common.testHelper.assertErrorEquals(
new fireauth.AuthError(fireauth.authenum.Error.TOKEN_EXPIRED),
error);
asyncTestCase.signal();
});
}
function testRequestStsToken_emulator() {
asyncTestCase.waitForSignals(1);
// Confirm correct parameters passed and run on complete.
assertSendXhrAndRunCallback(
'http://emulator.test.domain:1234/securetoken.googleapis.com/' +
'v1/token?key=apiKey',
'POST',
'grant_type=authorization_code&code=idToken',
fireauth.RpcHandler.DEFAULT_SECURE_TOKEN_HEADERS_,
delay,
expectedStsTokenResponse);
// Set an emulator config.
rpcHandler.updateEmulatorConfig(
{ url: 'http://emulator.test.domain:1234' });
// Send STS token request, emulator config will be used.
rpcHandler
.requestStsToken({ 'grant_type': 'authorization_code', 'code': 'idToken' })
.then(function (response) {
assertObjectEquals(expectedStsTokenResponse, response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_,
delay,
expectedResponse);
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_emulator() {
var expectedResponse = { 'status': 'success' };
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'http://emulator.test.domain:1234/www.googleapis.com/identitytoolkit' +
'/v3/relyingparty/method1?key=apiKey',
'POST',
goog.json.serialize({ 'key1': 'value1', 'key2': 'value2' }),
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_,
delay,
expectedResponse);
// Set an emulator config.
rpcHandler.updateEmulatorConfig(
{ url: 'http://emulator.test.domain:1234' });
rpcHandler
.requestFirebaseEndpoint(
'method1', 'POST', { 'key1': 'value1', 'key2': 'value2' })
.then(function (response) {
assertObjectEquals(expectedResponse, response);
asyncTestCase.signal();
});
}
function testRequestIdentityPlatformEndpoint_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
identityPlatformEndpoint + 'method1?key=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_,
delay,
expectedResponse);
rpcHandler.requestIdentityPlatformEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestIdentityPlatformEndpoint_emulator() {
var expectedResponse = { 'status': 'success' };
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'http://emulator.test.domain:1234/identitytoolkit.googleapis.com' +
'/v2/method1?key=apiKey',
'POST',
goog.json.serialize({ 'key1': 'value1', 'key2': 'value2' }),
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_,
delay,
expectedResponse);
// Set an emulator config.
rpcHandler.updateEmulatorConfig(
{ url: 'http://emulator.test.domain:1234' });
rpcHandler
.requestIdentityPlatformEndpoint(
'method1', 'POST', { 'key1': 'value1', 'key2': 'value2' })
.then(function (response) {
assertObjectEquals(expectedResponse, response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_updateClientVersion() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json',
'X-Client-Version': 'Chrome/JsCore/3.0.0/FirebaseCore-web'
},
delay,
expectedResponse);
// Update client version.
rpcHandler.updateClientVersion('Chrome/JsCore/3.0.0/FirebaseCore-web');
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestIdentityPlatformEndpoint_updateClientVersion() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
identityPlatformEndpoint + 'method1?key=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json',
'X-Client-Version': 'Chrome/JsCore/3.0.0/FirebaseCore-web'
},
delay,
expectedResponse);
// Update client version.
rpcHandler.updateClientVersion('Chrome/JsCore/3.0.0/FirebaseCore-web');
rpcHandler.requestIdentityPlatformEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_removeClientVersion() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json'
},
delay,
expectedResponse);
// Remove client version.
rpcHandler.updateClientVersion(null);
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestIdentityPlatformEndpoint_removeClientVersion() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
identityPlatformEndpoint + 'method1?key=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json'
},
delay,
expectedResponse);
// Remove client version.
rpcHandler.updateClientVersion(null);
rpcHandler.requestIdentityPlatformEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_setCustomLocaleHeader_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json',
'X-Firebase-Locale': 'fr'
},
delay,
expectedResponse);
// Set French as custom Firebase locale header.
rpcHandler.updateCustomLocaleHeader('fr');
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestIdentityPlatformEndpoint_setCustomLocaleHeader_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
identityPlatformEndpoint + 'method1?key=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json',
'X-Firebase-Locale': 'fr'
},
delay,
expectedResponse);
// Set French as custom Firebase locale header.
rpcHandler.updateCustomLocaleHeader('fr');
rpcHandler.requestIdentityPlatformEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_updateCustomLocaleHeader_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json',
'X-Firebase-Locale': 'de'
},
delay,
expectedResponse);
// Set French as custom Firebase locale header.
rpcHandler.updateCustomLocaleHeader('fr');
// Change to German.
rpcHandler.updateCustomLocaleHeader('de');
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestIdPlatformEndpoint_updateCustomLocaleHeader_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
identityPlatformEndpoint + 'method1?key=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json',
'X-Firebase-Locale': 'de'
},
delay,
expectedResponse);
// Set French as custom Firebase locale header.
rpcHandler.updateCustomLocaleHeader('fr');
// Change to German.
rpcHandler.updateCustomLocaleHeader('de');
rpcHandler.requestIdentityPlatformEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_removeCustomLocaleHeader_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json'
},
delay,
expectedResponse);
// Set French as custom Firebase locale header.
rpcHandler.updateCustomLocaleHeader('fr');
// Remove custom locale header.
rpcHandler.updateCustomLocaleHeader(null);
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestIdPlatformEndpoint_removeCustomLocaleHeader_success() {
var expectedResponse = {
'status': 'success'
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
identityPlatformEndpoint + 'method1?key=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
{
'Content-Type': 'application/json'
},
delay,
expectedResponse);
// Set French as custom Firebase locale header.
rpcHandler.updateCustomLocaleHeader('fr');
// Remove custom locale header.
rpcHandler.updateCustomLocaleHeader(null);
rpcHandler.requestIdentityPlatformEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(function(response) {
assertObjectEquals(
expectedResponse,
response);
asyncTestCase.signal();
});
}
function testRequestFirebaseEndpoint_error() {
// Error case.
var errorResponse = {
'error': {
'message': 'ERROR_CODE'
}
};
asyncTestCase.waitForSignals(1);
assertSendXhrAndRunCallback(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/method1?key' +
'=apiKey',
'POST',
goog.json.serialize({
'key1': 'value1',
'key2': 'value2'
}),
fireauth.RpcHandler.DEFAULT_FIREBASE_HEADERS_,
delay,
errorResponse);
rpcHandler.requestFirebaseEndpoint(
'method1',
'POST',
{
'key1': 'value1',
'key2': 'value2'
}).then(