voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
280 lines (243 loc) • 10.1 kB
text/typescript
/**
* @license
* Copyright 2019 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.
*/
import * as chai from 'chai';
import { expect } from 'chai';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import { testAuth, testUser, TestAuth } from '../../../test/helpers/mock_auth';
import { UserImpl } from '../user/user_impl';
import { _getInstance } from '../util/instantiator';
import {
PersistenceInternal,
PersistenceType,
PersistenceValue,
StorageEventListener
} from './';
import { inMemoryPersistence } from './in_memory';
import { KeyName, PersistenceUserManager } from './persistence_user_manager';
chai.use(sinonChai);
function makePersistence(
type = PersistenceType.NONE
): {
persistence: PersistenceInternal;
stub: sinon.SinonStubbedInstance<PersistenceInternal>;
} {
const persistence: PersistenceInternal = {
type,
_isAvailable: () => Promise.resolve(true),
_set: async () => {},
_get() {
return Promise.resolve(null);
},
_remove: async () => {},
_addListener(_key: string, _listener: StorageEventListener) {},
_removeListener(_key: string, _listener: StorageEventListener) {}
};
const stub = sinon.stub(persistence);
return { persistence, stub };
}
describe('core/persistence/persistence_user_manager', () => {
let auth: TestAuth;
beforeEach(async () => {
auth = await testAuth();
});
describe('.create', () => {
it('defaults to inMemory if no list provided', async () => {
const manager = await PersistenceUserManager.create(auth, []);
expect(manager.persistence).to.eq(_getInstance(inMemoryPersistence));
});
it('chooses the first one available', async () => {
const a = makePersistence();
const b = makePersistence();
const c = makePersistence();
const search = [a.persistence, b.persistence, c.persistence];
const auth = await testAuth();
a.stub._isAvailable.resolves(false);
a.stub._get.onFirstCall().resolves(testUser(auth, 'uid').toJSON());
b.stub._isAvailable.resolves(true);
const out = await PersistenceUserManager.create(auth, search);
expect(a.stub._isAvailable).to.have.been.calledOnce;
expect(b.stub._isAvailable).to.have.been.calledOnce;
expect(c.stub._isAvailable).to.not.have.been.called;
// a should not be chosen since it is not available (despite having a user).
expect(out.persistence).to.eq(b.persistence);
});
it('searches in order for a user', async () => {
const a = makePersistence();
const b = makePersistence();
const c = makePersistence();
const search = [a.persistence, b.persistence, c.persistence];
const auth = await testAuth();
const user = testUser(auth, 'uid');
a.stub._isAvailable.resolves(true);
a.stub._get.resolves(user.toJSON());
b.stub._get.resolves(testUser(auth, 'wrong-uid').toJSON());
const out = await PersistenceUserManager.create(auth, search);
expect(a.stub._get).to.have.been.calledOnce;
expect(b.stub._get).not.to.have.been.called;
expect(c.stub._get).not.to.have.been.called;
expect(out.persistence).to.eq(a.persistence);
expect((await out.getCurrentUser())!.uid).to.eq(user.uid);
});
it('migrate found user to the selected persistence and clear others', async () => {
const a = makePersistence();
const b = makePersistence();
const c = makePersistence();
const search = [a.persistence, b.persistence, c.persistence];
const auth = await testAuth();
const user = testUser(auth, 'uid');
a.stub._isAvailable.resolves(true);
b.stub._get.resolves(user.toJSON());
c.stub._get.resolves(testUser(auth, 'wrong-uid').toJSON());
let persistedUserInA: PersistenceValue | null = null;
a.stub._set.callsFake(async (_, value) => {
persistedUserInA = value;
});
a.stub._get.callsFake(async () => persistedUserInA);
const out = await PersistenceUserManager.create(auth, search);
expect(a.stub._set).to.have.been.calledOnceWith(
'firebase:authUser:test-api-key:test-app',
user.toJSON()
);
expect(b.stub._set).to.not.have.been.called;
expect(c.stub._set).to.not.have.been.called;
expect(b.stub._remove).to.have.been.calledOnceWith(
'firebase:authUser:test-api-key:test-app'
);
expect(c.stub._remove).to.have.been.calledOnceWith(
'firebase:authUser:test-api-key:test-app'
);
expect(out.persistence).to.eq(a.persistence);
expect((await out.getCurrentUser())!.uid).to.eq(user.uid);
});
it('uses default user key if none provided', async () => {
const { stub, persistence } = makePersistence();
await PersistenceUserManager.create(auth, [persistence]);
expect(stub._get).to.have.been.calledWith(
'firebase:authUser:test-api-key:test-app'
);
});
it('uses user key if provided', async () => {
const { stub, persistence } = makePersistence();
await PersistenceUserManager.create(
auth,
[persistence],
KeyName.REDIRECT_USER
);
expect(stub._get).to.have.been.calledWith(
'firebase:redirectUser:test-api-key:test-app'
);
});
it('returns in-memory persistence if all else fails', async () => {
const a = makePersistence();
const b = makePersistence();
const c = makePersistence();
const search = [a.persistence, b.persistence, c.persistence];
a.stub._isAvailable.resolves(false);
b.stub._isAvailable.resolves(false);
c.stub._isAvailable.resolves(false);
const out = await PersistenceUserManager.create(auth, search);
expect(out.persistence).to.eq(_getInstance(inMemoryPersistence));
expect(a.stub._get).to.have.been.calledOnce;
expect(b.stub._get).to.have.been.calledOnce;
expect(c.stub._get).to.have.been.called;
});
});
describe('manager methods', () => {
let persistenceStub: sinon.SinonStubbedInstance<PersistenceInternal>;
let manager: PersistenceUserManager;
beforeEach(async () => {
const { persistence, stub } = makePersistence(PersistenceType.SESSION);
stub._isAvailable.resolves(true);
persistenceStub = stub;
manager = await PersistenceUserManager.create(auth, [persistence]);
});
it('#setCurrentUser calls underlying persistence w/ key', async () => {
const user = testUser(auth, 'uid');
await manager.setCurrentUser(user);
expect(persistenceStub._set).to.have.been.calledWith(
'firebase:authUser:test-api-key:test-app',
user.toJSON()
);
});
it('#removeCurrentUser calls underlying persistence', async () => {
await manager.removeCurrentUser();
expect(persistenceStub._remove).to.have.been.calledWith(
'firebase:authUser:test-api-key:test-app'
);
});
it('#getCurrentUser calls with instantiator', async () => {
const rawObject = {};
const userImplStub = sinon.stub(UserImpl, '_fromJSON');
persistenceStub._get.returns(Promise.resolve(rawObject));
await manager.getCurrentUser();
expect(userImplStub).to.have.been.calledWith(auth, rawObject);
userImplStub.restore();
});
it('#savePersistenceForRedirect calls through', async () => {
await manager.savePersistenceForRedirect();
expect(persistenceStub._set).to.have.been.calledWith(
'firebase:persistence:test-api-key:test-app',
'SESSION'
);
});
describe('#setPersistence', () => {
it('returns immediately if persistence is not changed', async () => {
const spy = sinon.spy(manager, 'getCurrentUser');
await manager.setPersistence(manager.persistence);
expect(spy).not.to.have.been.called;
spy.restore();
});
it('removes current user & sets it in the new persistene', async () => {
const {
persistence: nextPersistence,
stub: nextStub
} = makePersistence();
const auth = await testAuth();
const user = testUser(auth, 'uid');
persistenceStub._get.returns(Promise.resolve(user.toJSON()));
await manager.setPersistence(nextPersistence);
expect(persistenceStub._get).to.have.been.called;
expect(persistenceStub._remove).to.have.been.called;
expect(nextStub._set).to.have.been.calledWith(
'firebase:authUser:test-api-key:test-app',
user.toJSON()
);
});
it('migrates user for a different persistence even if .type matches', async () => {
const { persistence, stub } = makePersistence(PersistenceType.LOCAL);
await manager.setPersistence(persistence);
const auth = await testAuth();
const user = testUser(auth, 'uid');
stub._get.returns(Promise.resolve(user.toJSON()));
const {
persistence: nextPersistence,
stub: nextStub
} = makePersistence(PersistenceType.LOCAL);
// This should migrate the user even if both has type LOCAL. For example, developer may want
// to switch from localStorage to indexedDB (both type LOCAL) and we should honor that.
await manager.setPersistence(nextPersistence);
expect(stub._get).to.have.been.called;
expect(stub._remove).to.have.been.called;
expect(nextStub._set).to.have.been.calledWith(
'firebase:authUser:test-api-key:test-app',
user.toJSON()
);
});
});
});
});