diff --git a/Gruntfile.js b/Gruntfile.js index ce4fa59190..99a3813abe 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -264,6 +264,21 @@ module.exports = function(grunt) { "dest": "<%= path.asset %>/requirejs/", "flatten": true }, + "sinonjs": { + "expand": true, + "src": "<%= path.bower %>/sinonjs/sinon.js", + "dest": "<%= path.asset %>/sinon", + "flatten": true + }, + "sinon-chai": { + "expand": true, + "src": [ + "<%= path.bower %>/sinon-chai/lib/sinon-chai.js", + "<%= path.bower %>/sinon-chai/LICENSE.txt" + ], + "dest": "<%= path.asset %>/sinon-chai", + "flatten": true + }, "swfobject": { "expand": true, "src": "<%= path.bower %>/swfobject/swfobject/swfobject.js", @@ -393,6 +408,8 @@ module.exports = function(grunt) { "copy:modernizr", "copy:normalize", "copy:requirejs", + "copy:sinonjs", + "copy:sinon-chai", "copy:swfobject", "copy:tinymce", "copy:underscore", diff --git a/bower.json b/bower.json index 0214a9a5ef..5590989a00 100644 --- a/bower.json +++ b/bower.json @@ -30,7 +30,9 @@ }, "devDependencies": { "mocha": "latest", + "sinonjs": "~1.7.0", "chai": "~1.6", + "sinon-chai": "~2.5", "qunit": "https://github.com/jquery/qunit.git#1.11.0", "js-fixtures": "https://github.com/badunk/js-fixtures/archive/master.zip" } diff --git a/www/scripts/common/websockets/connection.js b/www/scripts/common/websockets/connection.js index 4a6364f3af..b270d6b58b 100644 --- a/www/scripts/common/websockets/connection.js +++ b/www/scripts/common/websockets/connection.js @@ -1,36 +1,71 @@ define([ - "underscore" -], function (_) { - return function (options) { - if (!"url" in options) { - throw "You must set the websocket 'url'" - } - if (!"topic" in options) { - throw "You must set the websocket 'topic'" - } - if (!"eventAggregator" in options) { - throw "You must set an event aggregator" - } + "underscore", + "backbone" +], function (_, Backbone) { + var instance; - var eventAggregator = options.eventAggregator; + function init(url) { + var activeSession = null; - return { - run: function() { + return _.extend({ + connect: function() { + if (this.hasSession()) { + return; + } + var $this = this; // autobahn js is defined as a global object there is no way to load // it as a UMD module - ab.connect(options.url, function (session) { - eventAggregator.trigger("ws:connect", session); - session.subscribe(options.topic, function (topic, msg) { - // double encoded string - var msg = JSON.parse(JSON.parse(msg)); - eventAggregator.trigger("ws:"+msg.event, msg, session); - } - ); + ab.connect(url, function (session) { + $this.setSession(session); + $this.trigger("ws:connect", activeSession); }, function (code, reason) { - eventAggregator.trigger("ws:session-gone", code,reason); + $this.trigger("ws:session-gone", code, reason); }); + }, + close: function() { + if (false === this.hasSession()) { + return; + } + this.getSession().close(); + this.setSession(null); + this.trigger("ws:session-close"); + }, + hasSession: function() { + return this.getSession() !== null; + }, + getSession: function() { + return activeSession; + }, + setSession: function(session) { + activeSession = session; + }, + subscribe: function(topic, callback) { + if (false === this.hasSession()) { + this.on("ws:connect", function(session) { + session.subscribe(topic, callback); + }); + return; + } + this.getSession().subscribe(topic, callback); + this.trigger("ws:session-subscribe", topic); + }, + unsubscribe: function(topic, callback) { + if (false === this.hasSession()) { + return; + } + this.getSession().unsubscribe(topic, callback); + this.trigger("ws:session-unsubscribe", topic); } - } + }, Backbone.Events); } + + return { + getInstance: function(url) { + if (!instance) { + instance = init(url); + } + return instance; + } + }; }); diff --git a/www/scripts/tests/index.html b/www/scripts/tests/index.html index 4e70216198..b51d6d92b6 100644 --- a/www/scripts/tests/index.html +++ b/www/scripts/tests/index.html @@ -8,6 +8,7 @@
+ diff --git a/www/scripts/tests/main.js b/www/scripts/tests/main.js index 1cfc2d1b9e..51a8431bbc 100644 --- a/www/scripts/tests/main.js +++ b/www/scripts/tests/main.js @@ -9,7 +9,8 @@ require.config({ underscore: "../assets/underscore-amd/underscore", backbone: "../assets/backbone-amd/backbone", i18n: "../assets/i18next/i18next.amd-1.6.3", - bootstrap: "../assets/bootstrap/js/bootstrap.min" + bootstrap: "../assets/bootstrap/js/bootstrap.min", + sinonchai: "../assets/sinon-chai/sinon-chai" }, shim: { bootstrap: ["jquery"], diff --git a/www/scripts/tests/specs/websockets/connection.js b/www/scripts/tests/specs/websockets/connection.js new file mode 100644 index 0000000000..7b326361a6 --- /dev/null +++ b/www/scripts/tests/specs/websockets/connection.js @@ -0,0 +1,74 @@ +define([ + 'chai', + 'sinonchai', + 'underscore', + 'common/websockets/connection' +], function (chai, sinonchai, _, connection) { + var expect = chai.expect; + var assert = chai.assert; + var should = chai.should(); + chai.use(sinonchai); + + describe("Connection", function () { + describe("Functionnal", function () { + beforeEach(function () { + this.session = {"hello":"session"}; + this.session.close = sinon.spy(); + this.session.subscribe = sinon.spy(); + this.session.unsubscribe = sinon.spy(); + + this.wsConnection = connection.getInstance(); + var $this = this; + var cbSuccess = function (session) { + $this.wsConnection.setSession($this.session); + }; + window.ab = { + connect: function(url, cbSuccess, cbError) { + cbSuccess($this.session); + } + } + }); + + it("should have a session", function () { + this.wsConnection.connect(); + assert.ok(this.wsConnection.hasSession()); + }); + + it("should retrieve the session", function () { + this.wsConnection.connect(); + assert.equal(this.wsConnection.getSession().hello, this.session.hello); + }); + + it("should close the session", function () { + this.wsConnection.connect(); + assert.ok(this.wsConnection.hasSession()); + this.wsConnection.close(); + assert.ok(!this.wsConnection.hasSession()); + assert.equal(this.wsConnection.getSession(), null); + }); + + it("should not connect anymore after first connect", function () { + this.wsConnection.connect(); + ab.connect = sinon.spy(); + this.wsConnection.connect(); + this.wsConnection.connect(); + this.wsConnection.connect(); + expect(ab.connect.should.have.callCount(0)).to.be.ok; + }); + + it("should call session subscribe once", function () { + this.wsConnection.connect(); + this.wsConnection.subscribe(); + expect(this.wsConnection.getSession().subscribe.should.have.callCount(1)).to.be.ok; + }); + + it("should call session unsubscribe once", function () { + this.wsConnection.connect(); + this.wsConnection.unsubscribe(); + expect(this.wsConnection.getSession().unsubscribe.should.have.callCount(1)).to.be.ok; + }); + }); + }); +}); + +