diff --git a/examples/dynamicControls.ts b/examples/dynamicControls.ts index c5bd064..55acea7 100644 --- a/examples/dynamicControls.ts +++ b/examples/dynamicControls.ts @@ -96,7 +96,7 @@ client.open({ * then call ready so our controls show up. * then call loop() to begin our loop. */ - return client.synchronizeScenes(); + return client.synchronizeState(); }) .then(() => client.ready(true)) .then(() => loop()); diff --git a/examples/groups.ts b/examples/groups.ts index fa0fffd..c01193c 100644 --- a/examples/groups.ts +++ b/examples/groups.ts @@ -112,7 +112,7 @@ function createScenes(): Promise { sceneID: 'secondScene', controls: makeControls('second') }; - + return client.createScenes({ scenes: [secondScene] }); @@ -138,7 +138,7 @@ function createGroups(): Promise { sceneID: 'default' } ); - + return client // First update the default group .updateGroups({ @@ -165,12 +165,9 @@ client authToken: process.argv[2], versionId: parseInt(process.argv[3], 10), }) - - // Pull the scenes from the interactive server - .then(() => client.synchronizeScenes()) - // Pull the groups from the interactive server - .then(() => client.synchronizeGroups()) + // Pull the scenes and groups from the interactive server + .then(() => client.synchronizeState()) // Set the client as ready so that interactive controls show up .then(() => client.ready(true)) diff --git a/src/Client.spec.ts b/src/Client.spec.ts index 2b37a44..bff82df 100644 --- a/src/Client.spec.ts +++ b/src/Client.spec.ts @@ -1,8 +1,11 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; import * as WebSocket from 'ws'; -import { setWebSocket } from './'; -import { Method } from './wire/packets'; +import { setWebSocket } from './'; import { Client, ClientType } from './Client'; +import { IGroupData, ISceneData } from './state/interfaces'; +import { Method } from './wire/packets'; setWebSocket(WebSocket); const port = process.env.SERVER_PORT || 1339; @@ -49,6 +52,7 @@ describe('client', () => { done(); }); }); + after(done => tearDown(done)); }); describe('method handling', () => { @@ -61,5 +65,51 @@ describe('client', () => { }); }); - afterEach(done => tearDown(done)); + describe('state synchronization', () => { + let executeStub: sinon.SinonStub; + const scenes: ISceneData[] = [{sceneID: 'default', controls: []}]; + const groups: IGroupData[] = [{groupID: 'default'}]; + + beforeEach(() => { + client = createClient(); + executeStub = sinon.stub(client, 'execute'); + }); + afterEach(() => { + executeStub.restore(); + }); + + it('synchronizes scenes', () => { + executeStub.onCall(0).resolves(scenes); + const syncScenesStub = sinon.stub(client.state, 'synchronizeScenes'); + return client.synchronizeScenes().then(() => { + expect(syncScenesStub).to.have.been.calledWith(scenes); + + syncScenesStub.restore(); + }); + }); + + it('synchronizes groups', () => { + executeStub.onCall(0).resolves(groups); + const syncGroupsStub = sinon.stub(client.state, 'synchronizeGroups'); + return client.synchronizeGroups().then(() => { + expect(syncGroupsStub).to.have.been.calledWith(groups); + + syncGroupsStub.restore(); + }); + }); + + it('synchronizes state', () => { + executeStub.withArgs('getGroups', null, false).resolves(groups); + executeStub.withArgs('getScenes', null, false).resolves(scenes); + const syncGroupsStub = sinon.stub(client.state, 'synchronizeGroups'); + const syncScenesStub = sinon.stub(client.state, 'synchronizeScenes'); + return client.synchronizeState().then(() => { + expect(syncScenesStub).to.have.been.calledWith(scenes); + expect(syncGroupsStub).to.have.been.calledWith(groups); + + syncGroupsStub.restore(); + syncScenesStub.restore(); + }); + }); + }); }); diff --git a/src/Client.ts b/src/Client.ts index 7b2e35b..ed3b18c 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -133,7 +133,7 @@ export class Client extends EventEmitter implements IClient { this.createSocket(options); this.socket.connect(); return resolveOn(this, 'open') - .then(() => this); + .then(() => this); } /** @@ -196,6 +196,16 @@ export class Client extends EventEmitter implements IClient { .then(res => this.state.synchronizeGroups(res)); } + /** + * Retrieves and hydrates client side stores with state from the server + */ + public synchronizeState(): Promise<[IGroup[], IScene[]]> { + return Promise.all([ + this.synchronizeGroups(), + this.synchronizeScenes(), + ]); + } + /** * Gets the time from the server as a unix timestamp in UTC. */ diff --git a/src/EndpointDiscovery.spec.ts b/src/EndpointDiscovery.spec.ts index aec710f..ee64d0a 100644 --- a/src/EndpointDiscovery.spec.ts +++ b/src/EndpointDiscovery.spec.ts @@ -33,10 +33,10 @@ describe('endpoint discovery', () => { }); it('resolves with a list of endpoints', () => { stub.resolves(servers); - expect(discovery.retrieveEndpoints()).to.eventually.equal(servers); + return expect(discovery.retrieveEndpoints()).to.eventually.equal(servers); }); it('rejects with a NoInteractiveServersAvailable if the response contains no servers', () => { stub.resolves([]); - expect(discovery.retrieveEndpoints()).to.be.rejectedWith(NoInteractiveServersAvailable); + return expect(discovery.retrieveEndpoints()).to.be.rejectedWith(NoInteractiveServersAvailable); }); }); diff --git a/src/EndpointDiscovery.ts b/src/EndpointDiscovery.ts index a8b63ed..00f8792 100644 --- a/src/EndpointDiscovery.ts +++ b/src/EndpointDiscovery.ts @@ -21,7 +21,7 @@ export class EndpointDiscovery { if (res.length > 0) { return res; } - return new NoInteractiveServersAvailable('No Interactive servers are available, please try again.'); + throw new NoInteractiveServersAvailable('No Interactive servers are available, please try again.'); }); } } diff --git a/src/state/State.ts b/src/state/State.ts index 118b47e..fe197b7 100644 --- a/src/state/State.ts +++ b/src/state/State.ts @@ -114,7 +114,7 @@ export class State extends EventEmitter implements IState { } /** - * Syncronize scenes takes a collection of scenes from the server + * Synchronize scenes takes a collection of scenes from the server * and hydrates the Scene store with them. */ public synchronizeScenes(data: ISceneDataArray): IScene[] {