From 31abcf3c1a2a9ebc4d39d5f7d70d45cc294f8275 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Fri, 2 Jun 2017 15:30:20 +0200 Subject: [PATCH 1/9] feat: Add a utility method `synchronizeState` which syncs scenes and groups. A lot of our examples did the same thing as a promise chain, it made sense to create a utility method. --- examples/dynamicControls.ts | 2 +- examples/groups.ts | 11 ++++------- src/Client.ts | 8 ++++++++ 3 files changed, 13 insertions(+), 8 deletions(-) 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..38328b9 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 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.ts b/src/Client.ts index 7b2e35b..ef28f37 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -196,6 +196,14 @@ 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 { + return Promise.all([this.synchronizeGroups(), this.synchronizeScenes()]) + .then(() => {/** */}); + } + /** * Gets the time from the server as a unix timestamp in UTC. */ From fa20fe433d6487c1e61a36e324134a1b02bea2eb Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Fri, 2 Jun 2017 16:08:25 +0200 Subject: [PATCH 2/9] correct comment --- examples/groups.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/groups.ts b/examples/groups.ts index 38328b9..c01193c 100644 --- a/examples/groups.ts +++ b/examples/groups.ts @@ -166,7 +166,7 @@ client versionId: parseInt(process.argv[3], 10), }) - // Pull the scenes from the interactive server + // Pull the scenes and groups from the interactive server .then(() => client.synchronizeState()) // Set the client as ready so that interactive controls show up From 133051e0dd729b1693169643a9105749222e114b Mon Sep 17 00:00:00 2001 From: Kateract Date: Sat, 3 Jun 2017 10:55:19 -0700 Subject: [PATCH 3/9] Change return type for synchronizeState to [IGroup[], IScene[]] (#58) * add syncScenes * attempt to add tests * merge changes * added trailing comma for linting --- src/Client.spec.ts | 38 ++++++++++++++++++++++++++++++++++++-- src/Client.ts | 10 ++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/Client.spec.ts b/src/Client.spec.ts index 2b37a44..439b75e 100644 --- a/src/Client.spec.ts +++ b/src/Client.spec.ts @@ -1,8 +1,10 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; import * as WebSocket from 'ws'; import { setWebSocket } from './'; -import { Method } from './wire/packets'; - import { Client, ClientType } from './Client'; +import { IClient } from './IClient'; +import { Method } from './wire/packets'; setWebSocket(WebSocket); const port = process.env.SERVER_PORT || 1339; @@ -61,5 +63,37 @@ describe('client', () => { }); }); + describe('state synchronization', () => { + let mockClient: IClient; + beforeEach(() => { + client = createClient(); + client.execute = sinon.stub().returns(new Promise(resolve => { resolve(); })); + //client.open(socketOptions); + }); + it('synchronizes scenes', () => { + const scenes = client.getScenes(); + mockClient = client; + //const stub = sinon.stub(mockClient, 'execute'); + client.synchronizeScenes(); + expect(client.execute).to.be.calledWith(new Promise(resolve => {resolve(scenes); })); + }); + it('synchronizes groups', () => { + const groups = client.getGroups(); + mockClient = client; + //const stub = sinon.stub(mockClient, 'execute'); + client.synchronizeGroups(); + expect(client.execute).to.be.calledWith(groups); + }); + it('synchronizes state', () => { + const scenes = client.getScenes(); + const groups = client.getGroups(); + mockClient = client; + //const stub = sinon.stub(mockClient, 'execute'); + client.synchronizeState(); + expect(client.execute).to.be.calledWith(scenes); + expect(client.execute).to.be.calledWith(groups); + }); + }); + afterEach(done => tearDown(done)); }); diff --git a/src/Client.ts b/src/Client.ts index ef28f37..deaf3ba 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); } /** @@ -199,9 +199,11 @@ export class Client extends EventEmitter implements IClient { /** * Retrieves and hydrates client side stores with state from the server */ - public synchronizeState(): Promise { - return Promise.all([this.synchronizeGroups(), this.synchronizeScenes()]) - .then(() => {/** */}); + public synchronizeState(): Promise<[IGroup[], IScene[]]> { + return Promise.all([ + this.getGroups().then(res => this.state.synchronizeGroups(res)), + this.getScenes().then(res => this.state.synchronizeScenes(res)), + ]); } /** From 30ef0f5e7b0f495887c601c0641dcba0e5d4465c Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Sat, 3 Jun 2017 22:01:34 +0200 Subject: [PATCH 4/9] spelling --- src/state/State.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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[] { From cc7ba562709da80a6c81744bd2659cb9bbf78650 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Sun, 4 Jun 2017 00:38:28 +0200 Subject: [PATCH 5/9] TESTS! --- src/Client.spec.ts | 64 +++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/src/Client.spec.ts b/src/Client.spec.ts index 439b75e..3d82a93 100644 --- a/src/Client.spec.ts +++ b/src/Client.spec.ts @@ -1,9 +1,9 @@ +import { IGroupData, ISceneData } from './state/interfaces'; import { expect } from 'chai'; import * as sinon from 'sinon'; import * as WebSocket from 'ws'; import { setWebSocket } from './'; import { Client, ClientType } from './Client'; -import { IClient } from './IClient'; import { Method } from './wire/packets'; setWebSocket(WebSocket); @@ -51,6 +51,7 @@ describe('client', () => { done(); }); }); + after(done => tearDown(done)); }); describe('method handling', () => { @@ -64,36 +65,51 @@ describe('client', () => { }); describe('state synchronization', () => { - let mockClient: IClient; + let executeStub: sinon.SinonStub; + const scenes: ISceneData[] = [{sceneID: 'default', controls: []}]; + const groups: IGroupData[] = [{groupID: 'default'}]; + beforeEach(() => { client = createClient(); - client.execute = sinon.stub().returns(new Promise(resolve => { resolve(); })); - //client.open(socketOptions); + executeStub = sinon.stub(client, 'execute'); + }); + afterEach(() => { + executeStub.restore(); }); + it('synchronizes scenes', () => { - const scenes = client.getScenes(); - mockClient = client; - //const stub = sinon.stub(mockClient, 'execute'); - client.synchronizeScenes(); - expect(client.execute).to.be.calledWith(new Promise(resolve => {resolve(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', () => { - const groups = client.getGroups(); - mockClient = client; - //const stub = sinon.stub(mockClient, 'execute'); - client.synchronizeGroups(); - expect(client.execute).to.be.calledWith(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', () => { - const scenes = client.getScenes(); - const groups = client.getGroups(); - mockClient = client; - //const stub = sinon.stub(mockClient, 'execute'); - client.synchronizeState(); - expect(client.execute).to.be.calledWith(scenes); - expect(client.execute).to.be.calledWith(groups); - }); - }); + 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); - afterEach(done => tearDown(done)); + syncGroupsStub.restore(); + syncScenesStub.restore(); + }); + }) + + }); }); From 026c8c4505c44372f1cc26a95724603c0b4acad7 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Sun, 4 Jun 2017 00:38:57 +0200 Subject: [PATCH 6/9] FAILING TESTS D: --- src/EndpointDiscovery.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); }); }); From 568dedcea2fffb9a476e23999b7d6a9f43837ff0 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Sun, 4 Jun 2017 00:55:18 +0200 Subject: [PATCH 7/9] Fix endpoint discovery tests --- src/EndpointDiscovery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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.'); }); } } From f5ecdd6653909e15c5c45ab41706efe7941dce22 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Sun, 4 Jun 2017 01:06:25 +0200 Subject: [PATCH 8/9] lint --- src/Client.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Client.spec.ts b/src/Client.spec.ts index 3d82a93..bff82df 100644 --- a/src/Client.spec.ts +++ b/src/Client.spec.ts @@ -1,9 +1,10 @@ -import { IGroupData, ISceneData } from './state/interfaces'; import { expect } from 'chai'; import * as sinon from 'sinon'; import * as WebSocket from 'ws'; + import { setWebSocket } from './'; import { Client, ClientType } from './Client'; +import { IGroupData, ISceneData } from './state/interfaces'; import { Method } from './wire/packets'; setWebSocket(WebSocket); @@ -109,7 +110,6 @@ describe('client', () => { syncGroupsStub.restore(); syncScenesStub.restore(); }); - }) - + }); }); }); From 291e6e55a3fda3b74d9ed63a104d35607f790022 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Sun, 4 Jun 2017 01:39:25 +0200 Subject: [PATCH 9/9] This reads better --- src/Client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.ts b/src/Client.ts index deaf3ba..ed3b18c 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -201,8 +201,8 @@ export class Client extends EventEmitter implements IClient { */ public synchronizeState(): Promise<[IGroup[], IScene[]]> { return Promise.all([ - this.getGroups().then(res => this.state.synchronizeGroups(res)), - this.getScenes().then(res => this.state.synchronizeScenes(res)), + this.synchronizeGroups(), + this.synchronizeScenes(), ]); }