voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
264 lines (216 loc) • 8.08 kB
text/typescript
/**
* @license
* Copyright 2020 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 { expect, use } from 'chai';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import { testAuth, testUser } from '../../../test/helpers/mock_auth';
import {
PersistedBlob,
PersistenceInternal,
PersistenceType
} from '../../core/persistence';
import { _getInstance } from '../../core/util/instantiator';
import { browserLocalPersistence, _POLLING_INTERVAL_MS } from './local_storage';
use(sinonChai);
describe('platform_browser/persistence/local_storage', () => {
const persistence: PersistenceInternal = _getInstance(
browserLocalPersistence
);
beforeEach(() => {
localStorage.clear();
});
afterEach(() => sinon.restore());
it('should work with persistence type', async () => {
const key = 'my-super-special-persistence-type';
const value = PersistenceType.LOCAL;
expect(await persistence._get(key)).to.be.null;
await persistence._set(key, value);
expect(await persistence._get(key)).to.be.eq(value);
expect(await persistence._get('other-key')).to.be.null;
await persistence._remove(key);
expect(await persistence._get(key)).to.be.null;
});
it('should return persistedblob from user', async () => {
const key = 'my-super-special-user';
const auth = await testAuth();
const value = testUser(auth, 'some-uid');
expect(await persistence._get(key)).to.be.null;
await persistence._set(key, value.toJSON());
const out = await persistence._get<PersistedBlob>(key);
expect(out!['uid']).to.eql(value.uid);
await persistence._remove(key);
expect(await persistence._get(key)).to.be.null;
});
describe('#isAvailable', () => {
it('should emit false if localStorage setItem throws', async () => {
sinon.stub(localStorage, 'setItem').throws(new Error('nope'));
expect(await persistence._isAvailable()).to.be.false;
});
it('should emit false if localStorage removeItem throws', async () => {
sinon.stub(localStorage, 'removeItem').throws(new Error('nope'));
expect(await persistence._isAvailable()).to.be.false;
});
it('should emit true if everything works properly', async () => {
expect(await persistence._isAvailable()).to.be.true;
});
});
describe('#addEventListener', () => {
const key = 'my-key';
const newValue = 'new-value';
let callback: sinon.SinonSpy;
beforeEach(() => {
callback = sinon.spy();
});
context('with events', () => {
beforeEach(() => {
persistence._addListener(key, callback);
});
afterEach(() => {
persistence._removeListener(key, callback);
});
context('with multiple listeners', () => {
let otherCallback: sinon.SinonSpy;
beforeEach(() => {
otherCallback = sinon.spy();
persistence._addListener(key, otherCallback);
localStorage.setItem(key, JSON.stringify(newValue));
});
afterEach(() => {
persistence._removeListener(key, otherCallback);
});
it('should trigger both listeners if multiple listeners are registered', () => {
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(callback).to.have.been.calledWith(newValue);
expect(otherCallback).to.have.been.calledWith(newValue);
});
});
context('with a change in the underlying storage', () => {
beforeEach(() => {
localStorage.setItem(key, JSON.stringify(newValue));
});
it('should trigger on storage event for the same key', () => {
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(callback).to.have.been.calledWith(newValue);
});
it('should not trigger after unsubscribe', () => {
persistence._removeListener(key, callback);
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(callback).not.to.have.been.called;
});
it('should trigger even if the event had no key', () => {
window.dispatchEvent(new StorageEvent('storage', {}));
expect(callback).to.have.been.calledWith(newValue);
});
});
context('without a change in the underlying storage', () => {
it('should not trigger', () => {
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(callback).not.to.have.been.called;
});
it('should not trigger on storage event for a different key', () => {
localStorage.setItem('other-key', JSON.stringify(newValue));
window.dispatchEvent(
new StorageEvent('storage', {
key: 'other-key',
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(callback).not.to.have.been.called;
});
it('should not trigger if the listener was added after the storage was updated', () => {
const otherCallback = sinon.spy();
persistence._addListener(key, otherCallback);
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(otherCallback).not.to.have.been.called;
persistence._removeListener(key, otherCallback);
});
});
});
context('with polling (mobile browsers)', () => {
let clock: sinon.SinonFakeTimers;
beforeEach(() => {
clock = sinon.useFakeTimers();
(persistence as any)['fallbackToPolling'] = true;
persistence._addListener(key, callback);
});
afterEach(() => {
persistence._removeListener(key, callback);
clock.restore();
});
it('should catch persistence changes', async () => {
localStorage.setItem(key, JSON.stringify(newValue));
clock.tick(_POLLING_INTERVAL_MS + 1);
expect(callback).to.have.been.calledWith(newValue);
});
it('should not trigger twice if event still occurs after poll', async () => {
localStorage.setItem(key, JSON.stringify(newValue));
clock.tick(_POLLING_INTERVAL_MS + 1);
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
expect(callback).to.have.been.calledOnceWith(newValue);
});
it('should not trigger twice if poll occurs after event', async () => {
localStorage.setItem(key, JSON.stringify(newValue));
window.dispatchEvent(
new StorageEvent('storage', {
key,
oldValue: null,
newValue: JSON.stringify(newValue)
})
);
clock.tick(_POLLING_INTERVAL_MS + 1);
expect(callback).to.have.been.calledOnceWith(newValue);
});
});
});
});