linagora-rse
Version:
1,261 lines (1,170 loc) • 46.6 kB
JavaScript
;
/* global chai: false */
var expect = chai.expect;
describe('The esn.websocket Angular module', function() {
var handlerSubscribe, handlerUnsubscribe, handlerEmit;
var namespace1 = 'ns1',
namespace2 = 'ns2',
room1 = 'room1',
room2 = 'room2',
event = 'event';
var socketIOClientMock = {
/**
* namespaces = {
* "namespace": {
* callbacks: []
* }
* }
*/
namespaces: {},
connect: function(namespace) {
socketIOClientMock.namespaces[namespace] = {
callbacks: []
};
return {
emit: function(event, data) {
if (event === 'subscribe' && handlerSubscribe) {
return handlerSubscribe(data);
}
if (event === 'unsubscribe' && handlerUnsubscribe) {
return handlerUnsubscribe(data);
}
if (event !== 'subscribe' && event !== 'unsubscribe' && handlerEmit) {
handlerEmit(event, data);
}
},
on: function(event, callback) {
socketIOClientMock.namespaces[namespace].callbacks.push(callback);
},
removeListener: function(event, callback) {
socketIOClientMock.namespaces[namespace].callbacks =
socketIOClientMock.namespaces[namespace].callbacks.filter(function(element) {
return element !== callback;
});
}
};
}
};
var socketIOServerMock = {
of: function(namespace) {
return {
to: function(room) {
return {
emit: function(event, data) {
if (!socketIOClientMock.namespaces[namespace] ||
!socketIOClientMock.namespaces[namespace].callbacks) {
return;
}
socketIOClientMock.namespaces[namespace].callbacks.forEach(function(callback) {
callback({room: room, data: data});
});
}
};
}
};
}
};
afterEach(function() {
socketIOClientMock.namespaces = {};
handlerSubscribe = null;
handlerUnsubscribe = null;
});
describe('socketIORoom service', function() {
beforeEach(function() {
var asLog = {
debug: function() {},
warn: function() {}
};
angular.mock.module('esn.websocket');
angular.mock.module(function($provide) {
$provide.value('$log', asLog);
});
});
beforeEach(inject(function(socketIORoom) {
this.socketIORoom = socketIORoom;
}));
it('should have needed function', function() {
expect(this.socketIORoom).to.be.a.function;
expect(this.socketIORoom().on).to.be.a.function;
expect(this.socketIORoom().removeListener).to.be.a.function;
expect(this.socketIORoom().subscribeToRoom).to.be.a.function;
expect(this.socketIORoom().send).to.be.a.function;
});
describe('on() method', function() {
it('should emit "subscribe" for the first "on()"', function(done) {
handlerSubscribe = function() {
done();
};
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
socketIORoom.on(event, function() {});
socketIORoom.on(event, function() {});
});
it('should execute the handler', function(done) {
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
function handler(data) {
expect(data).to.exist;
expect(data).to.deep.equal({content: 'test'});
done();
}
socketIORoom.on(event, handler);
socketIOServerMock.of(namespace1).to(room1).emit(event, {content: 'test'});
});
it('should execute only one handler', function(done) {
var sio = socketIOClientMock.connect(namespace1);
var socketIORoom = this.socketIORoom(namespace1, room1, sio);
function handler(data) {
expect(data).to.exist;
expect(data).to.deep.equal({content: 'test'});
// Wait to see if the second handler is called
setTimeout(function() {
done();
}, 100);
}
socketIORoom.on(event, handler);
var socketIORoom2 = this.socketIORoom(namespace1, room2, sio);
function handler2() {
done(new Error('Test should not pass here !'));
}
socketIORoom2.on(event, handler2);
socketIOServerMock.of(namespace1).to(room1).emit(event, {content: 'test'});
});
it('should not execute the handler', function(done) {
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
function handler() {
done(new Error('Test should not pass here !'));
}
socketIORoom.on(event, handler);
socketIOServerMock.of(namespace1).to(room2).emit(event, {content: 'test'});
socketIOServerMock.of(namespace2).to(room1).emit(event, {content: 'test'});
socketIOServerMock.of(namespace2).to(room2).emit(event, {content: 'test'});
// Wait to see if the second handler is called
setTimeout(function() {
done();
}, 100);
});
it('should execute the handlers', function(done) {
var verifHandlerExecuted = {
handler: false,
handler2: false
};
function isHandlerExecuted() {
if (verifHandlerExecuted.handler && verifHandlerExecuted.handler2) {
done();
}
}
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
function handler(data) {
expect(data).to.exist;
expect(data).to.deep.equal({content: 'test'});
verifHandlerExecuted.handler = true;
isHandlerExecuted();
}
socketIORoom.on(event, handler);
function handler2(data) {
expect(data).to.exist;
expect(data).to.deep.equal({content: 'test'});
verifHandlerExecuted.handler2 = true;
isHandlerExecuted();
}
socketIORoom.on(event, handler2);
socketIOServerMock.of(namespace1).to(room1).emit(event, {content: 'test'});
});
});
describe('removeListener() method', function() {
it('should do nothing if the callback does not exist', function(done) {
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
function handler() {
done(new Error('Test should not pass here !'));
}
socketIORoom.removeListener(event, handler);
expect(socketIOClientMock.namespaces[namespace1].callbacks.length).to.equal(0);
done();
});
it('should remove the handler and emit "unsubscribe" when the last handler is removed', function(done) {
handlerUnsubscribe = function() {
// Wait to see if the handler is called
setTimeout(function() {
done();
}, 100);
};
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
function handler() {
done(new Error('Test should not pass here !'));
}
socketIORoom.on(event, handler);
expect(socketIOClientMock.namespaces[namespace1].callbacks.length).to.equal(1);
socketIORoom.removeListener(event, handler);
expect(socketIOClientMock.namespaces[namespace1].callbacks.length).to.equal(0);
socketIOServerMock.of(namespace1).to(room1).emit(event, {content: 'test'});
});
});
describe('send() method', function() {
it('should emit message to client', function(done) {
var event = 'message';
var data = {foo: 'bar'};
handlerEmit = function(_event, _data) {
expect(event).to.equal(event);
expect(_data).to.equal(data);
setTimeout(function() {
done();
}, 100);
};
var socketIORoom = this.socketIORoom(namespace1, room1, socketIOClientMock.connect(namespace1));
function handler() {
done(new Error('Test should not pass here !'));
}
socketIORoom.on(event, handler);
socketIORoom.send(event, data);
});
});
});
describe('livenotification service', function() {
beforeEach(function() {
var asLog = {
debug: function() {}
};
var asSocket = function(namespace) {
return socketIOClientMock.connect(namespace);
};
angular.mock.module('esn.websocket');
angular.mock.module(function($provide) {
$provide.value('$log', asLog);
$provide.value('socket', asSocket);
});
});
beforeEach(inject(function(livenotification) {
this.livenotification = livenotification;
}));
it('should have needed function', function() {
expect(this.livenotification).to.be.a.function;
expect(this.livenotification().on).to.be.a.function;
expect(this.livenotification().removeListener).to.be.a.function;
});
it('should not duplicate interface for same channel and same room', function() {
expect(this.livenotification('channel', 'room')).to.equal(this.livenotification('channel', 'room'));
});
it('should create two different interface for differents room', function() {
expect(this.livenotification('channel', 'room')).to.not.equal(this.livenotification('channel', 'room2'));
});
it('should not duplicate interface for same channel if no room', function() {
expect(this.livenotification('channel', null)).to.equal(this.livenotification('channel', undefined));
});
it('should create two different interface for differents channel without room', function() {
expect(this.livenotification('channel')).to.not.equal(this.livenotification('channel2'));
});
});
describe('IoAction service', function() {
beforeEach(function() {
var self = this;
angular.mock.module('esn.websocket');
angular.mock.module(function($provide) {
$provide.value('$timeout', function() {});
});
angular.mock.inject(function(IoAction) {
self.IoAction = IoAction;
});
});
it('should be a function', function() {
expect(this.IoAction).to.be.a.function;
});
it('should have default settings set to null', function() {
var ioa = new this.IoAction();
['message', 'broadcast', 'namespace', 'subscription', 'removeListenerRequest', 'ngMessage', 'ngSubscription'].forEach(function(p) {
expect(ioa).to.have.property(p);
expect(ioa[p]).to.be.null;
});
});
it('should upgrade default settings with options passed in arguments', function() {
var opts = {
message: 'message',
broadcast: 'broadcast',
namespace: 'namespace',
subscription: 'subscription',
removeListenerRequest: 'removeListenerRequest',
ngMessage: 'ngMessage',
ngSubscription: 'ngSubscription'
};
var ioa = new this.IoAction(opts);
expect(ioa.message).to.equal('message');
expect(ioa.broadcast).to.equal('broadcast');
expect(ioa.namespace).to.equal('namespace');
expect(ioa.subscription).to.equal('subscription');
expect(ioa.removeListenerRequest).to.equal('removeListenerRequest');
expect(ioa.ngMessage).to.equal('ngMessage');
expect(ioa.ngSubscription).to.equal('ngSubscription');
});
describe('isSubscription method()', function() {
it('should return true if the action is a subscription', function() {
var opts = { subscription: 'subscription' };
var ioa = new this.IoAction(opts);
expect(ioa.isSubscription()).to.be.true;
});
it('should return false if the action is not a subscription', function() {
var opts = { subscription: false };
var ioa = new this.IoAction(opts);
expect(ioa.isSubscription()).to.be.false;
});
});
describe('isUnsubscribe method()', function() {
it('should return true if the action got a removeListenerRequest property', function() {
var opts = { removeListenerRequest: 'subscription' };
var ioa = new this.IoAction(opts);
expect(ioa.isUnsubscribe()).to.be.true;
});
it('should return false if the action does not have a removeListenerRequest property', function() {
var ioa = new this.IoAction();
expect(ioa.isUnsubscribe()).to.be.false;
});
});
describe('equalsSubscription method()', function() {
it('should return false if the two IoActions does not have the same namespaces', function() {
var opts1 = { namespace: 'namespace1' };
var opts2 = { namespace: 'namespace2' };
var ioa1 = new this.IoAction(opts1);
var ioa2 = new this.IoAction(opts2);
expect(ioa1.equalsSubscription(ioa2)).to.be.false;
});
it('should return false if one IoAction have a namespace, and not the other', function() {
var opts1 = { namespace: 'namespace1' };
var opts2 = { };
var ioa1 = new this.IoAction(opts1);
var ioa2 = new this.IoAction(opts2);
expect(ioa1.equalsSubscription(ioa2)).to.be.false;
});
it('should return false if the two IoAction does not have the same subscriptions data', function() {
var opts1 = { namespace: 'namespace1', subscription: [1, 2] };
var opts2 = { namespace: 'namespace1', subscription: [1, 3] };
var ioa1 = new this.IoAction(opts1);
var ioa2 = new this.IoAction(opts2);
expect(ioa1.equalsSubscription(ioa2)).to.be.false;
});
it('should return true if the two IoAction have the same namespaces and the same subscriptions data', function() {
var opts1 = { namespace: 'namespace1', subscription: [1, 2] };
var opts2 = { namespace: 'namespace1', subscription: [1, 2] };
var ioa1 = new this.IoAction(opts1);
var ioa2 = new this.IoAction(opts2);
expect(ioa1.equalsSubscription(ioa2)).to.be.true;
});
it('should return true if the two IoAction have no namespaces and the same subscriptions data', function() {
var opts1 = { subscription: [1, 2] };
var opts2 = { subscription: [1, 2] };
var ioa1 = new this.IoAction(opts1);
var ioa2 = new this.IoAction(opts2);
expect(ioa1.equalsSubscription(ioa2)).to.be.true;
});
});
describe('on() method', function() {
it('should fill the subscription array with the on method arguments', function() {
var ioa = new this.IoAction();
ioa.on('one', 'two');
expect(ioa.subscription).to.be.an.array;
expect(ioa.subscription).to.have.length(2);
expect(ioa.subscription).to.deep.equal(['one', 'two']);
});
describe('with a non function as the second argument', function() {
it('should fill the ngSubscription property with the same values as the subscription property', function() {
var ioa = new this.IoAction();
ioa.on('one', 'two');
expect(ioa.ngSubscription).to.deep.equal(['one', 'two']);
expect(ioa.ngSubscription).to.deep.equal(ioa.subscription);
});
});
describe('with a function as the second argument', function() {
it('should fill the ngSubscription property with another function', function() {
var ioa = new this.IoAction();
var fn = function() {};
ioa.on('one', fn);
expect(ioa.ngSubscription).to.have.length(2);
expect(ioa.ngSubscription[0]).to.equal('one');
var same = (ioa.ngSubscription[1] === fn);
expect(same).to.be.false;
});
});
});
describe('emit() method', function() {
it('should fill the message array with the on method arguments', function() {
var ioa = new this.IoAction();
ioa.emit('one', 'two', 'three');
expect(ioa.message).to.be.an.array;
expect(ioa.message).to.have.length(3);
expect(ioa.message).to.deep.equal(['one', 'two', 'three']);
});
describe('with a non function as the last argument', function() {
it('should fill the ngMessage property with the same values as the message property', function() {
var ioa = new this.IoAction();
ioa.emit('one', 'two', 'three');
expect(ioa.ngMessage).to.deep.equal(['one', 'two', 'three']);
expect(ioa.ngMessage).to.deep.equal(ioa.message);
});
});
describe('with a function as the last argument', function() {
it('should fill the message property with another function', function() {
var ioa = new this.IoAction();
var fn = function() {};
ioa.emit('one', 'two', 'three', fn);
expect(ioa.ngMessage).to.have.length(4);
expect(ioa.ngMessage[0]).to.equal('one');
expect(ioa.ngMessage[1]).to.equal('two');
expect(ioa.ngMessage[2]).to.equal('three');
expect(ioa.ngMessage[3]).to.be.a.function;
var same = (ioa.ngMessage[1] === fn);
expect(same).to.be.false;
});
});
});
describe('of() method', function() {
it('should fill the namespace property', function() {
var ioa = new this.IoAction();
expect(ioa.namespace).to.be.null;
ioa.of('ns1');
expect(ioa.namespace).to.equal('ns1');
});
});
describe('removeListener() method', function() {
it('should set subscription property with method arguments', function() {
var ioa = new this.IoAction();
ioa.removeListener('one', 'two');
expect(ioa.subscription).to.deep.equal(['one', 'two']);
});
it('should set removeListenerRequest property to true', function() {
var ioa = new this.IoAction();
expect(ioa.removeListenerRequest).to.not.be.ok;
ioa.removeListener('one', 'two');
expect(ioa.removeListenerRequest).to.be.true;
});
});
describe('applyToSocketIO() method', function() {
describe('message style IoAction', function() {
it('should propagate namespace and broadcast properties to socketIO', function() {
var ns = null, broadcast = false;
var sioMock = {
broadcast: {
emit: function() {
broadcast = true;
}
}
};
var ioSocketConnectionMock = {
getSio: function(namespace) {
ns = namespace;
return sioMock;
}
};
var ioa = new this.IoAction({namespace: 'ns1', broadcast: true});
ioa.emit('one', 'two');
ioa.applyToSocketIO(ioSocketConnectionMock);
expect(ns).to.equal('ns1');
expect(broadcast).to.be.true;
});
it('should call socketIO emit method', function() {
var event = null, data = false;
var sioMock = {
broadcast: {
emit: function(e, d) {
event = e;
data = d;
}
}
};
var ioSocketConnectionMock = {
getSio: function() {
return sioMock;
}
};
var ioa = new this.IoAction({namespace: 'ns1', broadcast: true});
ioa.emit('one', 'two');
ioa.applyToSocketIO(ioSocketConnectionMock);
expect(event).to.equal('one');
expect(data).to.equal('two');
});
});
describe('subscription style IoAction', function() {
it('should propagate namespace and broadcast properties to socketIO', function() {
var ns = null, broadcast = false;
var sioMock = {
broadcast: {
on: function() {
broadcast = true;
}
}
};
var ioSocketConnectionMock = {
getSio: function(namespace) {
ns = namespace;
return sioMock;
}
};
var ioa = new this.IoAction({namespace: 'ns1', broadcast: true});
ioa.on('one', 'two');
ioa.applyToSocketIO(ioSocketConnectionMock);
expect(ns).to.equal('ns1');
expect(broadcast).to.be.true;
});
it('should call socketIO on method', function() {
var event = null, data = false;
var sioMock = {
broadcast: {
on: function(e, d) {
event = e;
data = d;
}
}
};
var ioSocketConnectionMock = {
getSio: function() {
return sioMock;
}
};
var ioa = new this.IoAction({namespace: 'ns1', broadcast: true});
ioa.on('one', 'two');
ioa.applyToSocketIO(ioSocketConnectionMock);
expect(event).to.equal('one');
expect(data).to.equal('two');
});
});
describe('unsubscription style IoAction', function() {
it('should call SocketIO removeListener with the ioOfflineBuffer buffered action', function() {
var event, data;
var sioMock = {
removeListener: function(e, d) {
event = e;
data = d;
}
};
var ioSocketConnectionMock = {
getSio: function() {
return sioMock;
}
};
var ioOfflineBufferMock = {
findSubscription: function() {
return {
ngSubscription: ['buffer1', 'buffer2']
};
}
};
var ioa = new this.IoAction();
ioa.removeListener('one', 'two');
ioa.applyToSocketIO(ioSocketConnectionMock, ioOfflineBufferMock);
expect(event).to.equal('buffer1');
expect(data).to.equal('buffer2');
});
});
});
});
describe('ioInterface service', function() {
beforeEach(function() {
var self = this;
angular.mock.module('esn.websocket');
angular.mock.inject(function(ioInterface) {
self.ioInterface = ioInterface;
});
});
it('should return a SocketIO like object', function() {
var ioi = this.ioInterface();
expect(ioi.emit).to.be.a.function;
expect(ioi.of).to.be.a.function;
expect(ioi.on).to.be.a.function;
expect(ioi.removeListener).to.be.a.function;
expect(ioi.broadcast).to.be.an.object;
expect(ioi.broadcast.emit).to.be.a.function;
});
describe('on() method', function() {
it('should fill the IoAction subscription object', function(done) {
var ioi = this.ioInterface(function(ioAction) {
expect(ioAction.subscription).to.have.length(2);
expect(ioAction.subscription[0]).to.equal('evt');
expect(ioAction.subscription[1]).to.equal('callback');
done();
});
ioi.on('evt', 'callback');
});
});
describe('emit() method', function() {
it('should fill the IoAction message object', function(done) {
var ioi = this.ioInterface(function(ioAction) {
expect(ioAction.message).to.have.length(2);
expect(ioAction.message[0]).to.equal('evt');
expect(ioAction.message[1]).to.equal('callback');
done();
});
ioi.emit('evt', 'callback');
});
});
describe('of() method', function() {
it('should send back a SocketIO like object', function() {
var ioi = this.ioInterface();
var obj = ioi.of('namespace1');
expect(obj.emit).to.be.a.function;
expect(obj.of).to.be.a.function;
expect(obj.on).to.be.a.function;
expect(obj.removeListener).to.be.a.function;
expect(obj.broadcast).to.be.an.object;
expect(obj.broadcast.emit).to.be.a.function;
});
it('should fill the IoAction namespace object', function(done) {
var ioi = this.ioInterface(function(ioAction) {
expect(ioAction.namespace).to.equal('namespace1');
done();
});
ioi.of('namespace1').emit('evt', 'callback');
});
});
describe('removeListener() method', function() {
it('should fill the subscription and removeListenerRequest properties of IoAction', function(done) {
var ioi = this.ioInterface(function(ioAction) {
expect(ioAction.subscription).to.have.length(2);
expect(ioAction.subscription[0]).to.equal('evt');
expect(ioAction.subscription[1]).to.equal('callback');
expect(ioAction.removeListenerRequest).to.be.true;
done();
});
ioi.removeListener('evt', 'callback');
});
});
describe('broadcast property', function() {
it('should fill the broadcast property of IoAction', function(done) {
var ioi = this.ioInterface(function(ioAction) {
expect(ioAction.broadcast).to.be.true;
done();
});
ioi.broadcast.emit('evt', 'callback');
});
});
it('should remember the namespace for subsequent requests', function(done) {
var counter = 0;
var ioi = this.ioInterface(function(ioAction) {
if (!counter) {
counter++;
return;
}
expect(ioAction.namespace).to.equal('namespace1');
done();
});
ioi.of('namespace1').emit('evt', 'callback');
ioi.emit('evt2', 'callback2');
});
});
describe('ioOfflineBuffer service', function() {
beforeEach(function() {
var self = this;
angular.mock.module('esn.websocket');
angular.mock.inject(function(ioOfflineBuffer) {
self.ioOfflineBuffer = ioOfflineBuffer;
});
});
it('should have push, handleSubscription, findSubscription, getSubscriptions, getBuffer, flushBuffer methods', function() {
expect(this.ioOfflineBuffer.push).to.be.a.function;
expect(this.ioOfflineBuffer.handleSubscription).to.be.a.function;
expect(this.ioOfflineBuffer.findSubscription).to.be.a.function;
expect(this.ioOfflineBuffer.getSubscriptions).to.be.a.function;
expect(this.ioOfflineBuffer.getBuffer).to.be.a.function;
expect(this.ioOfflineBuffer.flushBuffer).to.be.a.function;
});
describe('push() & getBuffer() methods', function() {
it('should allow pushing actions and getting them back', function() {
var a1 = {id: 'action1'};
var a2 = {id: 'action2'};
var a3 = {id: 'action3'};
this.ioOfflineBuffer.push(a1);
this.ioOfflineBuffer.push(a2);
this.ioOfflineBuffer.push(a3);
var buffer = this.ioOfflineBuffer.getBuffer();
expect(buffer).to.have.length(3);
expect(buffer[0]).to.deep.equal({id: 'action1'});
expect(buffer[1]).to.deep.equal({id: 'action2'});
expect(buffer[2]).to.deep.equal({id: 'action3'});
});
});
describe('getBuffer() method', function() {
it('should return a copy of the buffer array', function() {
var a1 = {id: 'action1'};
var a2 = {id: 'action2'};
var a3 = {id: 'action3'};
this.ioOfflineBuffer.push(a1);
this.ioOfflineBuffer.push(a2);
this.ioOfflineBuffer.push(a3);
var buffer = this.ioOfflineBuffer.getBuffer();
buffer.push({id: 'action4'});
var buffer2 = this.ioOfflineBuffer.getBuffer();
expect(buffer2).to.have.length(3);
});
});
describe('flushBuffer() method', function() {
it('should flush the buffer', function() {
var a1 = {id: 'action1'};
var a2 = {id: 'action2'};
var a3 = {id: 'action3'};
this.ioOfflineBuffer.push(a1);
this.ioOfflineBuffer.push(a2);
this.ioOfflineBuffer.push(a3);
this.ioOfflineBuffer.flushBuffer();
var buffer = this.ioOfflineBuffer.getBuffer();
expect(buffer).to.have.length(0);
});
});
describe('handleSubscription() & getSubscriptions() methods', function() {
it('should allow pushing actions and getting them back', function() {
var a1 = {id: 'action1', isUnsubscribe: function() {return false;}};
var a2 = {id: 'action2', isUnsubscribe: function() {return false;}};
var a3 = {id: 'action3', isUnsubscribe: function() {return false;}};
this.ioOfflineBuffer.handleSubscription(a1);
this.ioOfflineBuffer.handleSubscription(a2);
this.ioOfflineBuffer.handleSubscription(a3);
var buffer = this.ioOfflineBuffer.getSubscriptions();
expect(buffer).to.have.length(3);
expect(buffer[0].id).to.equal('action1');
expect(buffer[1].id).to.equal('action2');
expect(buffer[2].id).to.equal('action3');
});
});
describe('handleSubscription() method', function() {
it('should remove the corresponding subscription(s)', function() {
var a1 = {id: 'action1', isUnsubscribe: function() {return false;}};
var a2 = {id: 'action2', isUnsubscribe: function() {return false;}};
var a3 = {id: 'action3', isUnsubscribe: function() {return false;}};
this.ioOfflineBuffer.handleSubscription(a1);
this.ioOfflineBuffer.handleSubscription(a2);
this.ioOfflineBuffer.handleSubscription(a3);
var a4 = {
isUnsubscribe: function() {return true;},
equalsSubscription: function(action) {
if (action.id === 'action1' || action.id === 'action3') {
return true;
}
return false;
}
};
this.ioOfflineBuffer.handleSubscription(a4);
var buffer = this.ioOfflineBuffer.getSubscriptions();
expect(buffer).to.have.length(1);
expect(buffer[0].id).to.equal('action2');
});
});
describe('findSubscription() method', function() {
it('should return the first corresponding subscription', function() {
var a1 = {id: 'action1', isUnsubscribe: function() {return false;}};
var a2 = {id: 'action2', isUnsubscribe: function() {return false;}};
var a3 = {id: 'action3', isUnsubscribe: function() {return false;}};
this.ioOfflineBuffer.handleSubscription(a1);
this.ioOfflineBuffer.handleSubscription(a2);
this.ioOfflineBuffer.handleSubscription(a3);
var a4 = {
equalsSubscription: function(action) {
if (action.id === 'action2' || action.id === 'action3') {
return true;
}
return false;
}
};
var action = this.ioOfflineBuffer.findSubscription(a4);
expect(action.id).to.equal('action2');
});
});
});
describe('ioSocketConnection service', function() {
beforeEach(function() {
var self = this;
angular.mock.module('esn.websocket');
angular.mock.inject(function(ioSocketConnection) {
self.isc = ioSocketConnection;
});
var evts = {};
this.sioMock = {
evts: evts,
on: function(evt, cb) {
evts[evt] = cb;
},
socket: {
on: function(evt, cb) {
evts['socket:' + evt] = cb;
}
}
};
});
it('should expose isConnected, getSio, setSio, addDisconnectCallback, addConnectCallback, addReconnectCallback methods', function() {
expect(this.isc.getSio).to.be.a.function;
expect(this.isc.setSio).to.be.a.function;
expect(this.isc.isConnected).to.be.a.function;
expect(this.isc.addDisconnectCallback).to.be.a.function;
expect(this.isc.addDisconnectCallback).to.be.a.function;
expect(this.isc.addReconnectCallback).to.be.a.function;
});
describe('setSio() method', function() {
it('should bind the connect, connecting, disconnect, error events', function() {
this.isc.setSio(this.sioMock);
expect(this.sioMock.evts.connect).to.be.a.function;
expect(this.sioMock.evts.connecting).to.be.a.function;
expect(this.sioMock.evts.disconnect).to.be.a.function;
expect(this.sioMock.evts['socket:error']).to.be.a.function;
});
it('should set connected to false', function() {
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(this.isc.isConnected()).to.be.true;
this.isc.setSio(this.sioMock);
expect(this.isc.isConnected()).to.be.false;
});
});
describe('getSio() method', function() {
it('should return the socketIO instance', function() {
this.isc.setSio(this.sioMock);
var sio = this.isc.getSio();
expect(sio).to.deep.equal(this.sioMock);
});
});
describe('on connect event', function() {
it('should set connected to true', function() {
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(this.isc.isConnected()).to.be.true;
});
it('should run the callbacks registered using addConnectCallback', function() {
var run1 = false, run2 = false;
this.isc.addConnectCallback(function() { run1 = true; });
this.isc.addConnectCallback(function() { run2 = true; });
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(run1).to.be.true;
expect(run2).to.be.true;
});
});
describe('on disconnect event', function() {
it('should set connected to false', function() {
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(this.isc.isConnected()).to.be.true;
this.sioMock.evts.disconnect();
expect(this.isc.isConnected()).to.be.false;
});
it('should run the callbacks registered using addDisconnectCallback', function() {
var run1 = false, run2 = false;
this.isc.addDisconnectCallback(function() { run1 = true; });
this.isc.addDisconnectCallback(function() { run2 = true; });
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(run1).to.be.false;
expect(run2).to.be.false;
this.sioMock.evts.disconnect();
expect(run1).to.be.true;
expect(run2).to.be.true;
});
});
describe('on connect->disconnect->connect event', function() {
it('should run the callbacks registered using addReconnectCallback', function() {
var run1 = false, run2 = false;
this.isc.addReconnectCallback(function() { run1 = true; });
this.isc.addReconnectCallback(function() { run2 = true; });
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(run1).to.be.false;
expect(run2).to.be.false;
this.sioMock.evts.disconnect();
expect(run1).to.be.false;
expect(run2).to.be.false;
this.sioMock.evts.connect();
expect(run1).to.be.true;
expect(run2).to.be.true;
});
it('should run the callbacks registered using addConnectCallback', function() {
var run1 = false, run2 = false;
this.isc.addConnectCallback(function() { run1 = true; });
this.isc.addConnectCallback(function() { run2 = true; });
this.isc.setSio(this.sioMock);
this.sioMock.evts.connect();
expect(run1).to.be.true;
expect(run2).to.be.true;
run1 = false;
run2 = false;
this.sioMock.evts.disconnect();
expect(run1).to.be.false;
expect(run2).to.be.false;
this.sioMock.evts.connect();
expect(run1).to.be.true;
expect(run2).to.be.true;
});
});
});
describe('ioSocketProxy service', function() {
beforeEach(function() {
var self = this;
this.isc = {
getSio: function() {},
isConnected: function() {}
};
this.ioaction = function IoAction() {
this.applyToSocketIO = IoAction.applyToSocketIO;
this.isSubscription = IoAction.isSubscription;
this.isUnsubscribe = IoAction.isUnsubscribe;
this.emit = function() {};
};
this.ioaction.applyToSocketIO = function() {};
this.ioaction.isSubscription = function() {};
this.ioaction.isUnsubscribe = function() { return false; };
angular.mock.module('esn.websocket');
angular.mock.module(function($provide) {
$provide.value('ioSocketConnection', self.isc);
$provide.value('IoAction', self.ioaction);
});
});
beforeEach(inject(function(ioOfflineBuffer, ioSocketProxy) {
this.ioOfflineBuffer = ioOfflineBuffer;
this.ioSocketProxy = ioSocketProxy;
}));
it('should return an ioInterface', function() {
var test = this.ioSocketProxy();
expect(test.emit).to.be.a.function;
expect(test.on).to.be.a.function;
expect(test.of).to.be.a.function;
expect(test.broadcast).to.be.an.object;
});
describe('when io is connected', function() {
it('should call the applyToSocketIO method of IoAction', function(done) {
this.isc.isConnected = function() { return true; };
this.ioaction.applyToSocketIO = function() { done(); };
var test = this.ioSocketProxy();
test.emit('test', 'data');
});
it('should store the action in the buffer if it\'s a subscription', function() {
this.isc.isConnected = function() { return true; };
this.ioaction.isSubscription = function() { return true; };
var test = this.ioSocketProxy();
test.emit('test', 'data');
expect(this.ioOfflineBuffer.getSubscriptions()).to.have.length(1);
});
it('should not store the action in the buffer if it\'s not a subscription', function() {
this.isc.isConnected = function() { return true; };
this.ioaction.isSubscription = function() { return false; };
var test = this.ioSocketProxy();
test.emit('test', 'data');
expect(this.ioOfflineBuffer.getSubscriptions()).to.have.length(0);
});
});
describe('when io is not connected', function() {
it('should store the action if it\'s not a subscription', function() {
this.isc.isConnected = function() { return false; };
var test = this.ioSocketProxy();
test.emit('test', 'data');
expect(this.ioOfflineBuffer.getSubscriptions()).to.have.length(0);
expect(this.ioOfflineBuffer.getBuffer()).to.have.length(1);
});
it('should not store the action as message if it\'s a subscription', function() {
this.isc.isConnected = function() { return false; };
this.ioaction.isSubscription = function() { return true; };
var test = this.ioSocketProxy();
test.emit('test', 'data');
expect(this.ioOfflineBuffer.getSubscriptions()).to.have.length(1);
expect(this.ioOfflineBuffer.getBuffer()).to.have.length(0);
});
});
});
describe('ioConnectionManager service', function() {
var self = this;
var $rootScope;
beforeEach(function() {
self.isc = {
sio: null,
callbacks: {},
getSio: function() {return this.sio; },
setSio: function(sio) { this.sio = sio; },
isConnected: function() {},
addDisconnectCallback: function(cb) {
this.callbacks.disconnect = cb;
},
addConnectCallback: function(cb) {
this.callbacks.connect = cb;
}
};
self.ioaction = function IoAction() {
this.applyToSocketIO = IoAction.applyToSocketIO;
this.isSubscription = IoAction.isSubscription;
this.isUnsubscribe = IoAction.isUnsubscribe;
this.emit = function() {};
};
self.ioaction.applyToSocketIO = function() {};
self.ioaction.isSubscription = function() {};
self.ioaction.isUnsubscribe = function() { return false; };
self.tokenAPI = {
getNewToken: function() {
return {
then: function(callback) {
self.tokenAPI.callback = callback;
}
};
}
};
self.io = function() {
return self.ioMock;
};
self.ioMock = function() {};
self.ioMock.managers = {};
self.sessionMock = {
user: {
_id: 'user1'
},
domain: {},
ready: {
then: function() {}
},
setLogin: function() {},
setLogout: function() {},
isLoggedIn: function() { return true; }
};
angular.mock.module('esn.websocket');
angular.mock.module(function($provide) {
$provide.value('ioSocketConnection', self.isc);
$provide.value('IoAction', self.ioaction);
$provide.value('io', self.io);
$provide.value('tokenAPI', self.tokenAPI);
$provide.value('session', self.sessionMock);
});
});
beforeEach(inject(function(ioOfflineBuffer, ioSocketProxy, ioConnectionManager, _$rootScope_) {
self.ioOfflineBuffer = ioOfflineBuffer;
self.ioSocketProxy = ioSocketProxy;
self.icm = ioConnectionManager;
$rootScope = _$rootScope_;
}));
it('should expose a connect method', function() {
expect(self.icm.connect).to.be.a('function');
});
it('should add a connect callback to ioSocketConnection', function() {
expect(self.isc.callbacks.connect).to.be.a('function');
});
it('should add a disconnect callback to ioSocketConnection', function() {
expect(self.isc.callbacks.disconnect).to.be.a('function');
});
describe('connect method', function() {
it('should not try to connect when user is not logged in', function(done) {
self.sessionMock.isLoggedIn = function() { return false; };
self.icm.connect().catch(function(error) {
expect(error.message).to.equal('User not logged in');
done();
});
$rootScope.$digest();
});
it('should call tokenAPI.getNewToken()', function() {
expect(self.tokenAPI.callback).to.be.not.ok;
self.icm.connect();
expect(self.tokenAPI.callback).to.be.a('function');
});
it('should resolve when token API response succeed', function(done) {
self.tokenAPI.getNewToken = function() {
return $q.when({
data: {
token: 'apitoken'
}
});
};
self.icm.connect().then(function() {
done();
}, done);
$rootScope.$digest();
});
it('should reject when token API response fails', function(done) {
self.tokenAPI.getNewToken = function() {
return $q.reject(new Error('test'));
};
self.icm.connect().then(function() {
done(true);
}, function() {
done();
});
$rootScope.$digest();
});
});
describe('tokenAPI.getNewToken() callback', function() {
it('should call socketIO connect method, with token in arguments', function(done) {
self.ioMock = function(ns, options) {
expect(options.query).to.contain('token=token1');
expect(options.query).to.contain('user=user1');
done();
};
self.icm.connect();
self.tokenAPI.callback({data: {token: 'token1'}});
});
it('should set socketIO connect method response in ioSocketConnection', function(done) {
self.ioMock = function() { return {io: true}; };
self.icm.connect();
self.isc.setSio = function(sio) {
expect(sio).to.deep.equal({io: true});
done();
};
self.tokenAPI.callback({data: {token: 'token1'}});
});
});
describe('disconnect callback', function() {
it('should try to reconnect', function(done) {
self.ioMock = function() { return {io: true}; };
self.ioMock.managers = {};
self.icm.connect();
self.tokenAPI.callback({data: {token: 'token1'}});
self.isc.isConnected = function() { return false; };
self.tokenAPI.getNewToken = function() {
done();
return {then: function() {}};
};
self.isc.callbacks.disconnect();
});
it('should not try to reconnect if user is not logged in', function(done) {
self.ioMock = function() { return { io: true }; };
self.ioMock.managers = {};
self.icm.connect();
self.isc.isConnected = function() { return false; };
self.sessionMock.isLoggedIn = function() { return false; };
self.isc.callbacks.disconnect().catch(function(error) {
expect(error.message).to.equal('User not logged in');
done();
});
$rootScope.$digest();
});
});
describe('connect callback', function() {
it('should call ioOfflineBuffer.flushBuffer()', function(done) {
self.ioOfflineBuffer.flushBuffer = done;
self.ioMock = function() { return {io: true}; };
self.icm.connect();
self.tokenAPI.callback({data: {token: 'token1'}});
self.isc.callbacks.connect();
});
it('should apply the buffered messages to socketio', function() {
var appliedActions = [];
self.ioaction.applyToSocketIO = function() {
appliedActions.push(this);
};
var a1 = new self.ioaction();
a1.id = 'action1';
var a2 = new self.ioaction();
a2.id = 'action2';
self.ioOfflineBuffer.push(a1);
self.ioOfflineBuffer.push(a2);
self.ioMock = function() { return {io: true}; };
self.icm.connect();
self.tokenAPI.callback({data: {token: 'token1'}});
self.isc.callbacks.connect();
expect(appliedActions).to.have.length(2);
expect(appliedActions[0].id).to.equal('action1');
expect(appliedActions[1].id).to.equal('action2');
});
it('should apply the buffered subscriptions to socketio', function() {
var appliedActions = [];
self.ioaction.applyToSocketIO = function() {
appliedActions.push(this);
};
var a1 = new self.ioaction();
a1.id = 'action1';
var a2 = new self.ioaction();
a2.id = 'action2';
self.ioOfflineBuffer.handleSubscription(a1);
self.ioOfflineBuffer.handleSubscription(a2);
self.ioMock = function() { return {io: true}; };
self.icm.connect();
self.tokenAPI.callback({data: {token: 'token1'}});
self.isc.callbacks.connect();
expect(appliedActions).to.have.length(2);
expect(appliedActions[0].id).to.equal('action1');
expect(appliedActions[1].id).to.equal('action2');
});
});
});
});