voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
755 lines (708 loc) • 28 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 storageusermanager.js
*/
goog.provide('fireauth.storage.UserManagerTest');
goog.require('fireauth.AuthUser');
goog.require('fireauth.authStorage');
goog.require('fireauth.common.testHelper');
goog.require('fireauth.constants');
goog.require('fireauth.storage.MockStorage');
goog.require('fireauth.storage.UserManager');
goog.require('fireauth.util');
goog.require('goog.Promise');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.events');
goog.require('goog.testing.events.Event');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.recordFunction');
goog.setTestOnly('fireauth.storage.UserManagerTest');
var config = {
apiKey: 'apiKey1'
};
var appId = 'appId1';
var expectedUser;
var expectedUserWithAuthDomain;
var stubs = new goog.testing.PropertyReplacer();
var testUser;
var testUser2;
var mockLocalStorage;
var mockSessionStorage;
const now = Date.now();
const nowDate = new Date(now);
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);
// Simulate browser that synchronizes between and iframe and a popup.
stubs.replace(
fireauth.util,
'isLocalStorageNotSynchronized',
function() {
return false;
});
stubs.replace(Date, 'now', function() {
return now;
});
window.localStorage.clear();
window.sessionStorage.clear();
var config = {
'apiKey': 'API_KEY',
'appName': 'appId1'
};
var accountInfo = {
'uid': 'defaultUserId',
'email': 'user@default.com',
'displayName': 'defaultDisplayName',
'photoURL': 'https://www.default.com/default/default.png',
'emailVerified': true,
'multiFactor': {
'enrolledFactors': [
{
'uid': 'ENROLLMENT_UID1',
'displayName': 'Work phone number',
'enrollmentTime': nowDate.toUTCString(),
'factorId': fireauth.constants.SecondFactorType.PHONE,
'phoneNumber': '+16505551234'
},
{
'uid': 'ENROLLMENT_UID2',
'displayName': 'Spouse phone number',
'enrollmentTime': nowDate.toUTCString(),
'factorId': fireauth.constants.SecondFactorType.PHONE,
'phoneNumber': '+16505556789'
}
]
}
};
var accountInfo2 = {
'uid': 'defaultUserId2',
'email': 'user2@default.com',
'displayName': 'defaultDisplayName2',
'photoURL': 'https://www.default.com/default/default2.png',
'emailVerified': false
};
var tokenResponse = {
'idToken': fireauth.common.testHelper.createMockJwt(),
'refreshToken': 'refreshToken',
'expiresIn': '3600'
};
testUser = new fireauth.AuthUser(config, tokenResponse, accountInfo);
testUser2 = new fireauth.AuthUser(config, tokenResponse, accountInfo2);
}
function tearDown() {
if (expectedUser) {
expectedUser.destroy();
}
if (expectedUserWithAuthDomain) {
expectedUserWithAuthDomain.destroy();
}
if (testUser) {
testUser.destroy();
}
if (testUser2) {
testUser2.destroy();
}
}
/**
* @return {!fireauth.authStorage.Manager} The default local storage
* synchronized manager instance used for testing.
*/
function getDefaultStorageManagerInstance() {
return new fireauth.authStorage.Manager('firebase', ':', false, true);
}
function testGetSetRemoveCurrentUser() {
// Avoid triggering getProjectConfig RPC.
fireauth.AuthEventManager.ENABLED = false;
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager(appId, storageManager);
var config = {
'apiKey': 'API_KEY',
'appName': 'appId1'
};
var configWithAuthDomain = {
'apiKey': 'API_KEY',
'appName': 'appId1',
'authDomain': 'project.firebaseapp.com'
};
var accountInfo = {
'uid': 'defaultUserId',
'email': 'user@default.com',
'displayName': 'defaultDisplayName',
'photoURL': 'https://www.default.com/default/default.png',
'emailVerified': true,
'multiFactor': {
'enrolledFactors': [
{
'uid': 'ENROLLMENT_UID1',
'displayName': 'Work phone number',
'enrollmentTime': nowDate.toUTCString(),
'factorId': fireauth.constants.SecondFactorType.PHONE,
'phoneNumber': '+16505551234'
},
{
'uid': 'ENROLLMENT_UID2',
'displayName': 'Spouse phone number',
'enrollmentTime': nowDate.toUTCString(),
'factorId': fireauth.constants.SecondFactorType.PHONE,
'phoneNumber': '+16505556789'
}
]
}
};
var tokenResponse = {
'idToken': fireauth.common.testHelper.createMockJwt(),
'refreshToken': 'refreshToken',
'expiresIn': '3600'
};
expectedUser = new fireauth.AuthUser(config, tokenResponse, accountInfo);
// Expected user with authDomain.
expectedUserWithAuthDomain =
new fireauth.AuthUser(configWithAuthDomain, tokenResponse, accountInfo);
// Listen to calls on RPC Handler.
stubs.replace(
fireauth.RpcHandler.prototype,
'updateEmulatorConfig',
goog.testing.recordFunction(
fireauth.RpcHandler.prototype.updateEmulatorConfig));
var storageKey = 'firebase:authUser:appId1';
return goog.Promise.resolve()
.then(function() {
return userManager.setCurrentUser(expectedUser);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
assertObjectEquals(expectedUser.toPlainObject(), user.toPlainObject());
return mockLocalStorage.get(storageKey);
})
.then(function(user) {
assertObjectEquals(expectedUser.toPlainObject(), user);
// Get user with authDomain.
return userManager.getCurrentUser('project.firebaseapp.com');
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(
expectedUserWithAuthDomain, user);
// Get user with authDomain & emulator config.
return userManager.getCurrentUser('project.firebaseapp.com',
{
url: 'http://emulator.test.domain:1234'
});
})
.then(function () {
// Verify RpcHandler was notified of config change.
assertEquals(1,
fireauth.RpcHandler.prototype.updateEmulatorConfig.getCallCount());
assertObjectEquals(
{
url: 'http://emulator.test.domain:1234'
},
fireauth.RpcHandler.prototype.updateEmulatorConfig.getLastCall()
.getArgument(0));
return userManager.removeCurrentUser();
})
.then(function() {
return mockLocalStorage.get(storageKey);
})
.then(function(user) {
assertUndefined(user);
return userManager.getCurrentUser();
})
.then(function(user) {
assertNull(user);
});
}
function testAddRemoveCurrentUserChangeListener() {
var calls = 0;
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
var listener = function() {
calls++;
if (calls > 1) {
fail('Listener should be called once.');
}
};
// Save existing Auth users for appId1 and appId2.
mockLocalStorage.set('firebase:authUser:appId1', testUser.toPlainObject());
mockLocalStorage.set('firebase:authUser:appId2', testUser.toPlainObject());
return goog.Promise.resolve().then(function() {
return mockLocalStorage.set(
'firebase:authUser:appId1', testUser.toPlainObject());
})
.then(function() {
return mockLocalStorage.set(
'firebase:authUser:appId2', testUser.toPlainObject());
})
.then(function() {
userManager.addCurrentUserChangeListener(listener);
// Simulate appId1 user deletion.
var storageEvent =
new goog.testing.events.Event(goog.events.EventType.STORAGE, window);
storageEvent.key = 'firebase:authUser:appId1';
storageEvent.oldValue = JSON.stringify(testUser.toPlainObject());
storageEvent.newValue = null;
// This should trigger listener.
mockLocalStorage.fireBrowserEvent(storageEvent);
assertEquals(1, calls);
// Simulate appId2 user deletion.
storageEvent.key = 'firebase:authUser:appId2';
storageEvent.oldValue = JSON.stringify(testUser.toPlainObject());
storageEvent.newValue = null;
// This should not trigger listener.
mockLocalStorage.fireBrowserEvent(storageEvent);
assertEquals(1, calls);
// Remove listener.
userManager.removeCurrentUserChangeListener(listener);
// Simulate new user saved for appId1.
// This should not trigger listener.
storageEvent.key = 'firebase:authUser:appId1';
storageEvent.newValue = JSON.stringify(testUser.toPlainObject());
storageEvent.oldValue = null;
mockLocalStorage.fireBrowserEvent(storageEvent);
assertEquals(1, calls);
});
}
function testUserManager_initializedWithSession() {
// Save state in session storage.
var storageKey = 'firebase:authUser:appId1';
mockSessionStorage.set(storageKey, testUser.toPlainObject());
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.getCurrentUser()
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
// User should be saved in session storage only with everything else
// cleared.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser, storageManager);
}).then(function() {
// Should be saved in session storage.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser2, storageManager);
});
}
function testUserManager_initializedWithSession_duplicateStorage() {
// Confirm any duplicate storage is cleared on initialization.
var storageManager = getDefaultStorageManagerInstance();
var userManager;
// Save state in session storage.
var storageKey = 'firebase:authUser:appId1';
mockSessionStorage.set(storageKey, testUser.toPlainObject());
// Add state to other types of storage.
mockLocalStorage.set(storageKey, testUser2.toPlainObject());
// Set redirect persistence to none.
mockSessionStorage.set(
'firebase:persistence:appId1', 'none');
// Save state using in memory storage.
return storageManager.set(
{name: 'authUser', persistent: 'none'},
testUser.toPlainObject(),
'appId1')
.then(function() {
userManager = new fireauth.storage.UserManager(
'appId1', storageManager);
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
// User should be saved in session storage only with everything else
// cleared.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser, storageManager);
}).then(function() {
// Should be saved in session storage.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser2, storageManager);
});
}
function testUserManager_initializedWithInMemory() {
// Save state in in-memory storage.
var storageManager = getDefaultStorageManagerInstance();
var userManager;
return storageManager.set(
{name: 'authUser', persistent: 'none'},
testUser.toPlainObject(),
'appId1')
.then(function() {
userManager = new fireauth.storage.UserManager(
'appId1', storageManager);
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
// User should be saved in memory only with everything else
// cleared.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'none', testUser, storageManager);
}).then(function() {
// Should be saved using in memory storage only.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'none', testUser2, storageManager);
});
}
function testUserManager_initializedWithLocal() {
// Save state in local storage.
var storageKey = 'firebase:authUser:appId1';
mockLocalStorage.set(storageKey, testUser.toPlainObject());
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.getCurrentUser()
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
// User should be saved in local storage only with everything else
// cleared.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser, storageManager);
}).then(function() {
// Should be saved in local storage only.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser2, storageManager);
});
}
function testUserManager_initializedWithLocal_migratedFromLocalStorage() {
var storageKey = 'firebase:authUser:appId1';
// Save Auth state to localStorage. This will be migrated to mockLocalStorage.
window.localStorage.setItem(
storageKey, JSON.stringify(testUser.toPlainObject()));
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.getCurrentUser()
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
// User should be cleared from window.localStorage.
assertNull(window.localStorage.getItem(storageKey));
// User should be saved in mock local storage only with everything else
// cleared.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser, storageManager);
})
.then(function() {
// Should be saved in local storage only.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser2, storageManager);
});
}
function testUserManager_initializedWithLocal_multiplePersistentStorage() {
var storageKey = 'firebase:authUser:appId1';
// Save Auth state to window.localStorage. This will be cleared.
window.localStorage.setItem(
storageKey, JSON.stringify(testUser.toPlainObject()));
// Save another Auth state in mockLocalStorage. This will have precedence over
// window.localStorage.
mockLocalStorage.set(storageKey, testUser2.toPlainObject());
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.getCurrentUser()
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
// User should be ignored and cleared from window.localStorage.
assertNull(window.localStorage.getItem(storageKey));
// Existing user saved in mock local storage persisted with everything
// else cleared.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser2, storageManager);
})
.then(function() {
return userManager.setCurrentUser(testUser);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser, storageManager);
});
}
function testUserManager_initializedWithDefault() {
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.getCurrentUser()
.then(function(user) {
assertNull(user);
// Should be saved in default local storage.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser2, storageManager);
});
}
function testUserManager_initializedWithSavedPersistence() {
// Save redirect persistence.
mockSessionStorage.set('firebase:persistence:appId1', 'session');
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.getCurrentUser()
.then(function(user) {
assertNull(user);
// Should be saved in session storage as specified in redirect
// persistence.
return userManager.setCurrentUser(testUser2);
})
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser2, storageManager);
});
}
function testUserManager_savePersistenceForRedirect_default() {
// Confirm savePersistenceForRedirect behavior.
var storageKey = 'firebase:persistence:appId1';
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
return userManager.savePersistenceForRedirect()
.then(function() {
// Should store persistence value in session storage.
return mockSessionStorage.get(storageKey);
})
.then(function(value) {
// Should apply the current default persistence.
assertEquals('local', value);
});
}
function testUserManager_savePersistenceForRedirect_modifed() {
var storageKey = 'firebase:persistence:appId1';
var storageManager = getDefaultStorageManagerInstance();
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
// Update persistence.
userManager.setPersistence('session');
return userManager.savePersistenceForRedirect()
.then(function() {
// Should store persistence value in session storage.
return mockSessionStorage.get(storageKey);
})
.then(function(value) {
// The latest modified persistence value should be used.
assertEquals('session', value);
});
}
function testUserManager_clearState_setPersistence() {
// Test setPersistence behavior with initially no saved stated.
var storageManager = getDefaultStorageManagerInstance();
// As no existing state, the default is local.
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
// Switch to session persistence.
userManager.setPersistence('session');
// Should be saved in session.
return userManager.setCurrentUser(testUser2)
.then(function() {
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser2, storageManager);
})
.then(function() {
// Move to in memory.
return userManager.setPersistence('none');
})
.then(function() {
// User should be switched to in-memory storage.
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'none', testUser2, storageManager);
})
.then(function() {
// This should match.
return userManager.getCurrentUser();
})
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
// Internally switches to local storage.
userManager.setPersistence('local');
// Internally switches back to session storage.
userManager.setPersistence('session');
// This error should not affect last state change.
assertThrows(function() {
userManager.setPersistence('bla');
});
// Clears user (storage should be empty after).
userManager.removeCurrentUser();
// This should be saved in sessionStorage.
userManager.setCurrentUser(testUser);
return userManager.getCurrentUser();
})
.then(function(user) {
// Should only be saved in session storage.
fireauth.common.testHelper.assertUserEquals(testUser, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser, storageManager);
});
}
function testUserManager_existingState_setPersistence() {
// Test setPersistence behavior with some initial saved persistence state.
var storageKey = 'firebase:authUser:appId1';
// Save initial data in local storage.
mockLocalStorage.set(storageKey, testUser2.toPlainObject());
var storageManager = getDefaultStorageManagerInstance();
// As no existing state, the default is local.
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
// Switch persistence to session.
userManager.setPersistence('session');
// Should be switched to session.
return userManager.getCurrentUser()
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser2, storageManager);
})
.then(function() {
// Simulate some state duplication due to some unexpected error.
mockLocalStorage.set(storageKey, testUser.toPlainObject());
// Should switch state from session to none and clear everything else.
userManager.setPersistence('none');
return userManager.getCurrentUser();
})
.then(function(user) {
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'none', testUser2, storageManager);
});
}
function testUserManager_switchToLocalOnExternalEvents_noExistingUser() {
// Test when external storage event is detected with no existing user that
// persistence is switched to local.
var storageKey = 'firebase:authUser:appId1';
var listener = goog.testing.recordFunction();
// Fake storage event.
var storageEvent =
new goog.testing.events.Event(goog.events.EventType.STORAGE, window);
storageEvent.key = storageKey;
storageEvent.newValue = null;
var storageManager = getDefaultStorageManagerInstance();
// As no existing state, the default is local.
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
userManager.addCurrentUserChangeListener(listener);
// Should switch to session.
return userManager.setPersistence('session')
.then(function() {
// Simulate user signed in in another tab.
storageEvent.newValue = JSON.stringify(testUser2.toPlainObject());
// This should trigger listener and switch storage from session to
// local.
mockLocalStorage.fireBrowserEvent(storageEvent);
// Listener should be called.
assertEquals(1, listener.getCallCount());
return userManager.getCurrentUser();
})
.then(function(user) {
// User should be save in local storage.
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser2, storageManager);
})
.then(function() {
userManager.removeCurrentUserChangeListener(listener);
// This should not trigger listener.
mockLocalStorage.fireBrowserEvent(storageEvent);
assertEquals(1, listener.getCallCount());
});
}
function testUserManager_switchToLocalOnExternalEvents_existingUser() {
// Test when external storage event is detected with an existing user stored
// in a non-local storage that persistence is switched to local.
var storageKey = 'firebase:authUser:appId1';
var listener = goog.testing.recordFunction();
// Fake storage event.
var storageEvent =
new goog.testing.events.Event(goog.events.EventType.STORAGE, window);
storageEvent.key = storageKey;
storageEvent.newValue = null;
// Existing user in session storage.
mockSessionStorage.set(storageKey, testUser.toPlainObject());
var storageManager = getDefaultStorageManagerInstance();
// Due to existing state in session storage, the initial state is session.
var userManager = new fireauth.storage.UserManager('appId1', storageManager);
userManager.addCurrentUserChangeListener(listener);
// Should switch to session.
return userManager.getCurrentUser()
.then(function(user) {
fireauth.common.testHelper.assertUserEquals(testUser, user);
// Confirm user stored in session
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'session', testUser, storageManager);
})
.then(function(user) {
// Simulate user signed in in another tab.
storageEvent.newValue = JSON.stringify(testUser2.toPlainObject());
// This should trigger listener and switch storage to local.
mockLocalStorage.fireBrowserEvent(storageEvent);
assertEquals(1, listener.getCallCount());
return userManager.getCurrentUser();
})
.then(function(user) {
// New user should be stored in local storage.
fireauth.common.testHelper.assertUserEquals(testUser2, user);
return fireauth.common.testHelper.assertUserStorage(
'appId1', 'local', testUser2, storageManager);
})
.then(function() {
userManager.removeCurrentUserChangeListener(listener);
// This should not trigger listener.
mockLocalStorage.fireBrowserEvent(storageEvent);
assertEquals(1, listener.getCallCount());
});
}