-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5467ba7
commit ebb6255
Showing
4 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
(function() { | ||
'use strict'; | ||
|
||
/** | ||
* Provides implementation of transport-based cross-browser/cross-device | ||
* bi-directional communication layer for Socket.IO. | ||
* @constructor | ||
* @extends {lfr.Transport} | ||
*/ | ||
lfr.WebSocketTransport = function(uri) { | ||
lfr.WebSocketTransport.base(this, 'constructor', uri); | ||
}; | ||
lfr.inherits(lfr.WebSocketTransport, lfr.Transport); | ||
|
||
/** | ||
* Holds the underlying socket mechanism. Default mechanism uses Socket.IO. | ||
* @type {Socket.IO} | ||
* @default null | ||
*/ | ||
lfr.WebSocketTransport.prototype.socket = null; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
lfr.WebSocketTransport.prototype.close = function() { | ||
if (this.socket) { | ||
this.socket.close(); | ||
} | ||
return this; | ||
}; | ||
|
||
/** | ||
* Event handle for socket connect event. Fires transport open event. | ||
* @param {?Object} event | ||
* @protected | ||
*/ | ||
lfr.WebSocketTransport.prototype.onSocketConnect_ = function() { | ||
this.emit('open'); | ||
}; | ||
|
||
/** | ||
* Event handle for socket data event. Fires transport data event. | ||
* @param {*} data | ||
* @protected | ||
*/ | ||
lfr.WebSocketTransport.prototype.onSocketData_ = function(data) { | ||
this.emit('data', { | ||
data: data | ||
}); | ||
}; | ||
|
||
/** | ||
* Event handle for socket disconnect event. Fires transport close event. | ||
* @protected | ||
*/ | ||
lfr.WebSocketTransport.prototype.onSocketDisconnect_ = function() { | ||
this.emit('close'); | ||
}; | ||
|
||
/** | ||
* Event handle for socket error event. Fires transport error event. | ||
* @param {Object} event | ||
* @protected | ||
*/ | ||
lfr.WebSocketTransport.prototype.onSocketError_ = function(event) { | ||
var error = new Error('Transport request error'); | ||
error.socket = this.socket; | ||
error.message = event; | ||
this.emit('error', { | ||
error: error | ||
}); | ||
}; | ||
|
||
/** | ||
* Event handle for socket message event. Fires transport message event. | ||
* @protected | ||
*/ | ||
lfr.WebSocketTransport.prototype.onSocketMessage_ = function(message) { | ||
this.emit('message', { | ||
data: message | ||
}); | ||
}; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
lfr.WebSocketTransport.prototype.open = function() { | ||
if (this.isOpen()) { | ||
console.warn('Transport is already open'); | ||
return; | ||
} | ||
|
||
this.emit('opening'); | ||
|
||
/*global io*/ | ||
if (!this.socket) { | ||
if (!io) { | ||
throw new Error('Socket.IO client not found'); | ||
} | ||
this.socket = io(); | ||
this.socket.on('connect', lfr.bind(this.onSocketConnect_, this)); | ||
this.socket.on('disconnect', lfr.bind(this.onSocketDisconnect_, this)); | ||
this.socket.on('error', lfr.bind(this.onSocketError_, this)); | ||
this.socket.on('data', lfr.bind(this.onSocketData_, this)); | ||
this.socket.on('message', lfr.bind(this.onSocketMessage_, this)); | ||
} | ||
|
||
this.socket.open(); | ||
|
||
return this; | ||
}; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
lfr.WebSocketTransport.prototype.write = function(packet) { | ||
this.socket.send(packet); | ||
}; | ||
|
||
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
'use strict'; | ||
|
||
var assert = require('assert'); | ||
var sinon = require('sinon'); | ||
require('../fixture/sandbox.js'); | ||
|
||
describe('WebSocketTransport', function() { | ||
before(function() { | ||
global.FakeSocketIO = createFakeSocketIO(); | ||
}); | ||
|
||
beforeEach(function() { | ||
global.io = function() { | ||
return new global.FakeSocketIO(); | ||
}; | ||
}); | ||
|
||
it('should throw error when Socket.IO not found', function() { | ||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
global.io = null; | ||
assert.throws(function() { | ||
transport.open(); | ||
}, Error); | ||
}); | ||
|
||
it('should connection open', function(done) { | ||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
var stubOpen = sinon.stub(); | ||
transport.on('open', stubOpen); | ||
transport.open(); | ||
// Waits connection to open asynchronously | ||
assert.strictEqual(0, stubOpen.callCount); | ||
setTimeout(function() { | ||
assert.strictEqual(1, stubOpen.callCount); | ||
done(); | ||
}, 0); | ||
}); | ||
|
||
it('should connection warn when open multiple times', function(done) { | ||
var originalWarningFn = console.warn; | ||
console.warn = sinon.stub(); | ||
|
||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
var stubOpen = sinon.stub(); | ||
transport.on('open', stubOpen); | ||
transport.open(); | ||
transport.open(); | ||
transport.open(); | ||
// Waits connection to open asynchronously | ||
assert.strictEqual(0, stubOpen.callCount); | ||
setTimeout(function() { | ||
assert.strictEqual(2, console.warn.callCount, 'Should warn when open'); | ||
assert.strictEqual(1, stubOpen.callCount, 'Should not emit open twice'); | ||
|
||
console.warn = originalWarningFn; | ||
done(); | ||
}, 0); | ||
}); | ||
|
||
it('should connection reopen without warn', function(done) { | ||
var originalWarningFn = console.warn; | ||
console.warn = sinon.stub(); | ||
|
||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
transport.close(); | ||
|
||
var stubClose = sinon.stub(); | ||
var stubOpen = sinon.stub(); | ||
transport.on('close', stubClose); | ||
transport.on('open', stubOpen); | ||
transport.open(); | ||
// Waits connection to open asynchronously | ||
assert.strictEqual(0, stubOpen.callCount); | ||
setTimeout(function() { | ||
assert.strictEqual(1, stubOpen.callCount); | ||
transport.close(); | ||
// Waits connection to close asynchronously | ||
assert.strictEqual(0, stubClose.callCount); | ||
setTimeout(function() { | ||
transport.open(); | ||
// Waits connection to open asynchronously | ||
assert.strictEqual(1, stubOpen.callCount); | ||
setTimeout(function() { | ||
assert.strictEqual(1, stubClose.callCount, 'Should not emit close twice'); | ||
assert.strictEqual(2, stubOpen.callCount, 'Should emit open twice'); | ||
assert.strictEqual(0, console.warn.callCount, 'Should warn when open'); | ||
|
||
console.warn = originalWarningFn; | ||
done(); | ||
}, 0); | ||
}, 0); | ||
}, 0); | ||
}); | ||
|
||
it('should handle successful send message', function(done) { | ||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
var stubMessage = sinon.stub(); | ||
transport.on('message', stubMessage); | ||
transport.open(); | ||
// Waits connection to open asynchronously | ||
setTimeout(function() { | ||
transport.send('message'); | ||
// Waits connection to send asynchronously | ||
setTimeout(function() { | ||
assert.strictEqual('message', stubMessage.getCall(0).args[0].data, 'Should set message content'); | ||
done(); | ||
}, 0); | ||
}, 0); | ||
}); | ||
|
||
it('should handle successful receive data', function(done) { | ||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
var stubData = sinon.stub(); | ||
transport.on('data', stubData); | ||
transport.open(); | ||
// Waits connection to open asynchronously | ||
setTimeout(function() { | ||
transport.socket.emit('data', 'data'); | ||
// Waits connection to send asynchronously | ||
setTimeout(function() { | ||
assert.strictEqual('data', stubData.getCall(0).args[0].data, 'Should receive emitted data'); | ||
done(); | ||
}, 0); | ||
}, 0); | ||
}); | ||
|
||
it('should handle failing send data', function(done) { | ||
var transport = new lfr.WebSocketTransport('http://liferay.com'); | ||
var stubError = sinon.stub(); | ||
transport.on('error', stubError); | ||
|
||
transport.open(); | ||
// Waits connection to open asynchronously | ||
setTimeout(function() { | ||
transport.send(); | ||
transport.socket.emit('error', 'reason'); | ||
// Waits connection to send asynchronously | ||
setTimeout(function() { | ||
var error = stubError.getCall(0).args[0].error; | ||
assert.ok(error instanceof Error); | ||
assert.ok(error.socket instanceof global.FakeSocketIO); | ||
assert.strictEqual('reason', error.message); | ||
done(); | ||
}, 0); | ||
}, 0); | ||
}); | ||
}); | ||
|
||
function createFakeSocketIO() { | ||
var FakeSocketIO = function() { | ||
FakeSocketIO.base(this, 'constructor'); | ||
}; | ||
lfr.inherits(FakeSocketIO, lfr.EventEmitter); | ||
|
||
FakeSocketIO.prototype.close = function() { | ||
var self = this; | ||
setTimeout(function() { | ||
self.emit('disconnect'); | ||
}, 0); | ||
}; | ||
FakeSocketIO.prototype.open = function() { | ||
var self = this; | ||
setTimeout(function() { | ||
self.emit('connect'); | ||
}, 0); | ||
}; | ||
FakeSocketIO.prototype.send = function(message) { | ||
var self = this; | ||
setTimeout(function() { | ||
self.emit('message', message); | ||
}, 0); | ||
}; | ||
return FakeSocketIO; | ||
} |