UNPKG

sync-client

Version:

Module which uses Dexie to save data and can later use a server to synchronize the data in the database with other devices

312 lines (265 loc) 9.55 kB
import Dexie from 'dexie'; import 'dexie-syncable'; import cuid from '../../src/cuid'; import initSyncClient from '../../src/sync_client'; describe('SyncClient', () => { const Syncable = { StatusTexts: Dexie.Syncable.StatusTexts, Statuses: Dexie.Syncable.Statuses, registerSyncProtocol: jasmine.createSpy('register sync protocol spy'), }; const syncable = { on: jasmine.createSpy('on spy'), }; const storesSpy = jasmine.createSpy('stores spy'); class MyDexie { constructor(...args) { this.syncable = syncable; } on() {} } MyDexie.prototype.version = jasmine.createSpy('version spy') .and.returnValue({ stores: storesSpy }); MyDexie.Syncable = Syncable; const isOnlineSpy = jasmine.createSpy('isOnline spy'); const onlineStatusChangedSpy = jasmine.createSpy('onlineStatusChanged spy'); const syncFn = () => {}; const SyncClient = initSyncClient({ Dexie: MyDexie, sync: syncFn, isOnline: isOnlineSpy, onlineStatusChanged: onlineStatusChangedSpy, cuid, }); describe('constructor', () => { it('should setup stores, register the protocol and setup statusChanged', () => { const dbName = 'myDB'; const versions = [{ version: 1, stores: { foo: 'id' } }]; const opts = { partialsThreshold: 10, }; new SyncClient(dbName, versions, opts); // eslint-disable-line no-new expect(MyDexie.prototype.version).toHaveBeenCalledWith(1); expect(storesSpy).toHaveBeenCalledWith({ foo: 'id' }); expect(syncable.on.calls.argsFor(0)[0]).toBe('statusChanged'); expect(Syncable.registerSyncProtocol) .toHaveBeenCalledWith('sync_client_protocol', { sync: syncFn, partialsThreshold: 10 }); }); }); describe('disconnect', () => { it('should call syncable.disconnect and remove the given URL from this.urls', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; syncable.disconnect = jasmine.createSpy('disconnect spy') .and.returnValue(new Promise((resolve) => { resolve(); })); syncClient.urls.push(url); syncClient.disconnect(url) .then(() => { expect(syncable.disconnect).toHaveBeenCalledWith(url); expect(syncClient.urls).toEqual([]); done(); }) .catch((e) => { done(e); }); }); it('should not remove the URL if the syncable.disconnect call failed', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; syncable.disconnect = jasmine.createSpy('disconnect spy') .and.returnValue(new Promise((resolve, reject) => { reject(); })); syncClient.urls.push(url); syncClient.disconnect(url) .catch(() => { try { expect(syncable.disconnect).toHaveBeenCalledWith(url); expect(syncClient.urls).toEqual([url]); done(); } catch (e) { done(e); } }); }); }); describe('removeUrl', () => { it('should call syncable.delete and remove any status listener and the URL', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; syncable.delete = jasmine.createSpy('delete spy') .and.returnValue(new Promise((resolve) => { resolve(); })); syncClient.urls.push(url); syncClient.statusChangeListeners[url] = 'foo'; syncClient.removeUrl(url) .then(() => { expect(syncable.delete).toHaveBeenCalledWith(url); expect(syncClient.urls).toEqual([]); expect(syncClient.statusChangeListeners[url]).toBeUndefined(); done(); }) .catch((e) => { done(e); }); }); it('should do nothing if syncable.delete failed', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; syncable.delete = jasmine.createSpy('delete spy') .and.returnValue(new Promise((resolve, reject) => { reject(); })); syncClient.urls.push(url); syncClient.statusChangeListeners[url] = 'foo'; syncClient.removeUrl(url) .catch(() => { try { expect(syncable.delete).toHaveBeenCalledWith(url); expect(syncClient.urls).toEqual([url]); expect(syncClient.statusChangeListeners[url]).toBe('foo'); done(); } catch (e) { done(e); } }); }); }); describe('getStatuses', () => { it('should return a list with url and status', (done) => { const urls = ['http://foo.invalid']; const statuses = [-1]; syncable.list = function () { return new Promise((resolve) => { resolve(urls); }); }; syncable.getStatus = function () { return new Promise((resolve) => { resolve(statuses); }); }; const syncClient = new SyncClient('', []); syncClient.getStatuses() .then((res) => { expect(res.length).toBe(1); expect(res[0].url).toBe(urls[0]); expect(res[0].status).toBe(SyncClient.statuses.ERROR); done(); }) .catch((e) => { done(e); }); }); }); describe('connect', () => { beforeEach(() => { isOnlineSpy.calls.reset(); onlineStatusChangedSpy.calls.reset(); }); it('should try to connect, if it succeeds it should add listeners and the url', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; const options = { foo: 'bar' }; syncClient._connect = jasmine.createSpy() .and.returnValue(new Promise((resolve) => { resolve(); })); isOnlineSpy.and.returnValue(new Promise((resolve) => { resolve(true); })); syncClient.connect(url, options) .then(() => { expect(syncClient.urls).toEqual([url]); expect(syncClient.options[url]).toEqual({ foo: 'bar', pollInterval: 10000, credentials: 'omit', }); expect(onlineStatusChangedSpy).toHaveBeenCalled(); done(); }) .catch((e) => { done(e); }); }); it('should reject if we are not online', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; const options = { foo: 'bar' }; isOnlineSpy.and.returnValue(new Promise((resolve) => { resolve(false); })); syncClient.connect(url, options) .catch((e) => { expect(e instanceof Error).toBe(true); done(); }); }); it('should reject if connect fails', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; const options = { foo: 'bar' }; syncClient._connect = jasmine.createSpy() .and.returnValue(new Promise((resolve, reject) => { reject(); })); isOnlineSpy.and.returnValue(new Promise((resolve) => { resolve(true); })); syncClient.connect(url, options) .then(() => { expect(syncClient.urls).toEqual([url]); expect(syncClient.options[url]).toEqual({ foo: 'bar', pollInterval: 10000, }); expect(onlineStatusChangedSpy).toHaveBeenCalled(); done(); }) .catch((e) => { done(e); }); }); }); describe('connect - onlineStatusChanged', () => { beforeEach(() => { isOnlineSpy.calls.reset(); onlineStatusChangedSpy.calls.reset(); }); it('should call disconnect if the status changed to offline', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; const options = { foo: 'bar' }; syncClient._connect = jasmine.createSpy() .and.returnValue(new Promise((resolve) => { resolve(); })); isOnlineSpy.and.returnValue(new Promise((resolve) => { resolve(true); })); syncClient.disconnect = jasmine.createSpy('disconnect spy'); syncClient.connect(url, options) .then(() => { const cb = onlineStatusChangedSpy.calls.argsFor(0)[1]; cb(false); expect(syncClient.disconnect).toHaveBeenCalledWith(url); done(); }) .catch((e) => { done(e); }); }); it('should call _connect if the status changed to online', (done) => { const syncClient = new SyncClient('', []); const url = 'http://foo.invalid'; const options = { foo: 'bar' }; syncClient._connect = jasmine.createSpy('_connect spy') .and.returnValue(new Promise((resolve) => { resolve(); })); isOnlineSpy.and.returnValue(new Promise((resolve) => { resolve(true); })); syncClient.connect(url, options) .then(() => { const cb = onlineStatusChangedSpy.calls.argsFor(0)[1]; syncClient._connect.calls.reset(); cb(true); expect(syncClient._connect) .toHaveBeenCalledWith(url, { foo: 'bar', pollInterval: 10000, credentials: 'omit' }); done(); }) .catch((e) => { done(e); }); }); }); });