diff --git a/app/assets/javascripts/fakeJamClient.js b/app/assets/javascripts/fakeJamClient.js
new file mode 100644
index 000000000..60fb1ca2b
--- /dev/null
+++ b/app/assets/javascripts/fakeJamClient.js
@@ -0,0 +1,21 @@
+(function(context,$) {
+
+ context.JK = context.JK || {};
+ context.JK.FakeJamClient = function(app) {
+ var logger = context.JK.logger;
+ logger.info("*** Fake JamClient instance initialized. ***");
+
+ function testLatency(client, callback) {
+ var response = {
+ id: client.id,
+ latency: 50
+ };
+ callback(response);
+ }
+
+ // Javascript Bridge seems to camel-case
+ this.TestLatency = testLatency;
+
+ };
+
+ })(window,jQuery);
\ No newline at end of file
diff --git a/app/assets/javascripts/findSession.js b/app/assets/javascripts/findSession.js
index fdbc09eab..a30ee95e4 100644
--- a/app/assets/javascripts/findSession.js
+++ b/app/assets/javascripts/findSession.js
@@ -4,32 +4,73 @@
context.JK.FindSessionScreen = function(app) {
var logger = context.JK.logger;
var sessionLatency;
+ var sessions = {};
+ var selectors = {
+ TABLE_BODY: '#findSession-tableBody'
+ };
function loadSessions() {
$.ajax({
type: "GET",
url: "/api/sessions",
- // TODO: Change to buildLatencyMap
- success: renderSessions
+ success: startSessionLatencyChecks
});
}
- function renderSessions(response) {
- $tb = $('#findSession-tableBody');
- $tb.empty();
- var rowTemplate = $('#template-findSession-row').html();
- $.each(response, function() {
- var vals = {
- id: this.id,
- participants: this.participants.length,
- description: this.description || "(No description)"
- };
- var row = JK.fillTemplate(rowTemplate, vals);
- $tb.append(row);
+ function startSessionLatencyChecks(response) {
+ sessionLatency.subscribe(latencyResponse);
+ $.each(response, function(index, session) {
+ sessions[session.id] = session;
+ sessionLatency.sessionPings(session);
});
}
+ function latencyResponse(sessionId) {
+ renderSession(sessionId);
+ }
+
+ /**
+ * Not used normally. Allows modular unit testing
+ * of the renderSession method without having to do
+ * as much heavy setup.
+ */
+ function setSession(session) { sessions[session.id] = session; }
+
+ /**
+ * Render a single session line into the table.
+ * It will be inserted at the appropriate place according to the
+ * sortScore in sessionLatency.
+ */
+ function renderSession(sessionId) {
+ $tb = $(selectors.TABLE_BODY);
+ var rowTemplate = $('#template-findSession-row').html();
+ var session = sessions[sessionId];
+ var latencyInfo = sessionLatency.sessionInfo(sessionId);
+ var vals = {
+ id: session.id,
+ genres: session.genres.join (','),
+ sortScore: latencyInfo.sortScore,
+ participants: session.participants.length,
+ description: session.description || "(No description)"
+ };
+ var row = JK.fillTemplate(rowTemplate, vals);
+ var insertedEarly = false;
+ $.each($('tr', $tb), function() {
+ $this = $(this);
+ var rowSortScore = parseInt($this.attr('data-sortScore'), 10);
+ if (vals.sortScore > rowSortScore) {
+ $this.before(row);
+ insertedEarly = true;
+ }
+ });
+ if (!(insertedEarly)) {
+ return $tb.append(row);
+ }
+ }
+
function afterShow(data) {
+ $tb = $(selectors.TABLE_BODY);
+ $tb.empty();
loadSessions();
}
@@ -47,20 +88,30 @@
$('#findSession-tableBody').on("click", '[action="delete"]', deleteSession);
}
- this.afterShow = afterShow;
-
- this.initialize = function() {
- if ("jamClient" in context) {
- sessionLatency = new context.JK.SessionLatency(jamClient);
+ /**
+ * Initialize, providing an instance of the SessionLatency class.
+ */
+ function initialize(_sessionLatency) {
+ if (_sessionLatency) {
+ sessionLatency = _sessionLatency;
} else {
- logger.warn("No jamClient available. Session pings will not be performed.");
+ logger.warn("No sessionLatency provided.");
}
screenBindings = {
'afterShow': afterShow
};
app.bindScreen('findSession', screenBindings);
events();
- };
+ }
+
+ this.initialize = initialize;
+ this.afterShow = afterShow;
+ this.renderSession = renderSession;
+
+ // Following exposed for easier testing.
+ this.setSession = setSession;
+ this.selectors = selectors;
+
};
})(window,jQuery);
\ No newline at end of file
diff --git a/app/assets/javascripts/sessionLatency.js b/app/assets/javascripts/sessionLatency.js
index 0a7d1e739..79e4fa6eb 100644
--- a/app/assets/javascripts/sessionLatency.js
+++ b/app/assets/javascripts/sessionLatency.js
@@ -8,6 +8,7 @@
var sessionPingsOut = {};
var clientsToSessions = {};
var sessionLatency = {};
+ var subscribers = [];
function getSortScore(sessionId) {
return sessionLatency[sessionId].sortScore;
@@ -51,7 +52,7 @@
sessionPingsOut[session.id] = 0;
}
sessionPingsOut[session.id]++;
- jamClient.testLatency(client, clientPingResponse);
+ jamClient.TestLatency(client, clientPingResponse);
});
}
@@ -59,6 +60,9 @@
var sessionId = clientsToSessions[response.id];
sessionPingsOut[sessionId]--;
updateSessionLatency(sessionId, response);
+ $.each(subscribers, function() {
+ this(sessionId);
+ });
}
function updateSessionLatency(sessionId, latencyResponse) {
@@ -96,9 +100,14 @@
return sessionLatency[sessionId];
}
+ function subscribe(cb) {
+ subscribers.push(cb);
+ }
+
this.sessionPings = sessionPings;
this.sessionInfo = sessionInfo;
this.getSortScore = getSortScore;
+ this.subscribe = subscribe;
return this;
};
diff --git a/app/views/clients/_findSession.html.erb b/app/views/clients/_findSession.html.erb
index e5696f6cb..d910ab47f 100644
--- a/app/views/clients/_findSession.html.erb
+++ b/app/views/clients/_findSession.html.erb
@@ -4,9 +4,13 @@
Home
- | Members |
- Name |
- Delete |
+ Genre |
+ Description |
+ Musicians |
+ Audience |
+ Latency |
+ Listen |
+ Join |
@@ -14,9 +18,13 @@
diff --git a/app/views/clients/index.html.erb b/app/views/clients/index.html.erb
index 3a86d3cd8..1c1952532 100644
--- a/app/views/clients/index.html.erb
+++ b/app/views/clients/index.html.erb
@@ -19,6 +19,11 @@
JK = JK || {};
+
+ // The following allows testing of native (jamClient) functionality
+ // with a fake. Uncomment when developing without the native client.
+ window.jamClient = new JK.FakeJamClient();
+
<% if current_user %>
JK.currentUserId = '<%= current_user.id %>';
<% else %>
@@ -40,7 +45,11 @@
createSessionScreen.initialize();
var findSessionScreen = new JK.FindSessionScreen(jk);
- findSessionScreen.initialize();
+ var sessionLatency = null;
+ if ("jamClient" in window) {
+ sessionLatency = new JK.SessionLatency(window.jamClient);
+ }
+ findSessionScreen.initialize(sessionLatency);
var sessionScreen = new JK.SessionScreen(jk);
sessionScreen.initialize();
diff --git a/spec/javascripts/findSession.spec.js b/spec/javascripts/findSession.spec.js
index 0f4b57c18..bb57370e6 100644
--- a/spec/javascripts/findSession.spec.js
+++ b/spec/javascripts/findSession.spec.js
@@ -3,11 +3,6 @@
describe("FindSession", function() {
var fss;
- var ajaxSpy;
-
- var selectors = {
- sessions: '#findSession-tableBody'
- };
var appFake = {
clientId: '12345',
@@ -16,34 +11,130 @@
ajaxError: function() { console.debug("ajaxError"); }
};
+ var jamClientFake = {
+ TestLatency: function(client, cb) {
+ cb({id: client.id, latency: 50});
+ }
+ };
+
beforeEach(function() {
+ fss = null;
// Use the actual screen markup
jasmine.getFixtures().fixturesPath = '/app/views/clients';
loadFixtures('_findSession.html.erb');
spyOn(appFake, 'notify');
- fss = new context.JK.FindSessionScreen(appFake);
- fss.initialize();
});
- describe("loadSessions", function() {
- beforeEach(function() {
- spyOn($, "ajax");
- });
- it("should query ajax for sessions", function() {
- fss.afterShow({});
- expect($.ajax).toHaveBeenCalled();
- });
- });
+ var sessionLatencyReal;
- describe("renderSessions", function() {
+ describe("RealSessionLatency", function() {
beforeEach(function() {
- spyOn($, "ajax").andCallFake(function(opts) {
- opts.success(TestResponses.getSessions);
+ sessionLatencyReal = new JK.SessionLatency(jamClientFake);
+ spyOn(sessionLatencyReal, 'sessionPings').andCallThrough();
+ fss = new context.JK.FindSessionScreen(appFake);
+ fss.initialize(sessionLatencyReal);
+ $(fss.selectors.TABLE_BODY).empty();
+ });
+
+ describe("loadSessions", function() {
+ beforeEach(function() {
+ spyOn($, "ajax");
+ });
+ it("should query ajax for sessions", function() {
+ fss.afterShow({});
+ expect($.ajax).toHaveBeenCalled();
});
});
- it("should output table rows for sessions", function() {
- fss.afterShow({});
- expect($(selectors.sessions + ' tr').length).toEqual(1);
+
+ describe("afterShow flow", function() {
+ beforeEach(function() {
+ spyOn($, "ajax").andCallFake(function(opts) {
+ opts.success(TestGetSessionResponses.oneOfEach);
+ });
+ });
+
+ it("should output table rows for sessions", function() {
+ fss.afterShow({});
+ expect($(fss.selectors.TABLE_BODY + ' tr').length).toEqual(5);
+ });
+
+ it("should call sessionPings", function() {
+ fss.afterShow({});
+ expect(sessionLatencyReal.sessionPings).toHaveBeenCalled();
+ });
+
+ });
+ });
+
+ describe("FakeSessionLatency", function() {
+
+ beforeEach(function() {
+ sessionInfoResponses = {
+ "1": {id:"1", sortScore: 3},
+ "2": {id:"2", sortScore: 2}
+ };
+ sessionLatencyFake = {
+ sessionInfo: null
+ };
+ spyOn(sessionLatencyFake, 'sessionInfo').andCallFake(function(id) {
+ return sessionInfoResponses[id];
+ });
+ fss = new context.JK.FindSessionScreen(appFake);
+ fss.initialize(sessionLatencyFake);
+ $(fss.selectors.TABLE_BODY).empty();
+ });
+
+ describe("renderSession", function() {
+
+ describe("layout", function() {
+ var tbody;
+
+ beforeEach(function() {
+ var session = TestGetSessionResponses.oneOfEach[0];
+ fss.setSession(session);
+ fss.renderSession(session.id);
+ tbody = $(fss.selectors.TABLE_BODY);
+ });
+
+ it("single session should render", function() {
+ expect($('tr', tbody).length).toEqual(1);
+ });
+
+ it("Should render genre", function() {
+ expect($('tr td', tbody).first().text()).toEqual('classical');
+ });
+
+ it("Should render description", function() {
+ expect($('tr td', tbody).first().next().text()).toEqual('Invited');
+ });
+
+ it("Should render musician count", function() {
+ expect($('tr td', tbody).first().next().next().text()).toEqual('1');
+ });
+
+ // TODO - test audience
+ // TODO - test latency
+ // TODO - test Listen
+
+ it("Should render join link", function() {
+ expect($('tr td', tbody).last().text()).toEqual('Join');
+ });
+
+ });
+
+ it("higher sortScore inserted before lower sortScore", function() {
+ var sessionLow = TestGetSessionResponses.oneOfEach[1];
+ var sessionHigh = TestGetSessionResponses.oneOfEach[0];
+ fss.setSession(sessionLow);
+ fss.setSession(sessionHigh);
+ fss.renderSession(sessionLow.id);
+ fss.renderSession(sessionHigh.id);
+
+ var tbody = $(fss.selectors.TABLE_BODY);
+ expect($('tr', tbody).length).toEqual(2);
+ expect($('tr', tbody).first().attr('data-sortScore')).toEqual('3');
+ expect($('tr', tbody).first().next().attr('data-sortScore')).toEqual('2');
+ });
});
});
diff --git a/spec/javascripts/helpers/test_getSessionResponses.js b/spec/javascripts/helpers/test_getSessionResponses.js
new file mode 100644
index 000000000..b526587be
--- /dev/null
+++ b/spec/javascripts/helpers/test_getSessionResponses.js
@@ -0,0 +1,120 @@
+window.TestGetSessionResponses = {
+ oneOfEach: [
+
+ // Session 1 - you're invited to this.
+ {
+ "id": "1",
+ "description": "Invited",
+ "musician_access": true,
+ "genres" : [ "classical" ],
+ "participants": [
+ {
+ "client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
+ "ip_address":"1.1.1.1",
+ "user_id" : "02303020402042040",
+ "tracks" : [
+ {
+ "id" : "xxxx",
+ "instrument_id" : "electric guitar",
+ "sound" : "mono"
+ }
+ ]
+ }
+ ],
+ "invitations" : [
+ {
+ "id" : "3948797598275987",
+ "sender_id" : "02303020402042040"
+
+ }
+ ]
+ },
+
+ // Session 2 - no invite, but friend #1 (good latency)
+ {
+ "id": "2",
+ "description": "Friends 1",
+ "musician_access": true,
+ "genres" : [ "blues" ],
+ "participants": [
+ {
+ "client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
+ "ip_address":"1.1.1.1",
+ "user_id" : "02303020402042040",
+ "tracks" : [
+ {
+ "id" : "xxxx",
+ "instrument_id" : "electric guitar",
+ "sound" : "mono"
+ }
+ ]
+ }
+ ]
+ },
+
+ // Session 3 - no invite, but friend #2 (med latency)
+ {
+ "id": "3",
+ "description": "Friends 2",
+ "musician_access": true,
+ "genres" : [ "blues" ],
+ "participants": [
+ {
+ "client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
+ "ip_address":"1.1.1.1",
+ "user_id" : "02303020402042040",
+ "tracks" : [
+ {
+ "id" : "xxxx",
+ "instrument_id" : "electric guitar",
+ "sound" : "mono"
+ }
+ ]
+ }
+ ]
+ },
+
+ // Session 4 - no invite, no friends 1
+ {
+ "id": "4",
+ "description": "Anonymous 1",
+ "musician_access": true,
+ "genres" : [ "blues" ],
+ "participants": [
+ {
+ "client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
+ "ip_address":"1.1.1.1",
+ "tracks" : [
+ {
+ "id" : "xxxx",
+ "instrument_id" : "electric guitar",
+ "sound" : "mono"
+ }
+ ]
+ }
+ ]
+ },
+
+ // Session 5 - no invite, no friends 2
+ {
+ "id": "5",
+ "description": "Anonymous 2",
+ "musician_access": true,
+ "genres" : [ "blues" ],
+ "participants": [
+ {
+ "client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
+ "ip_address":"1.1.1.1",
+ "tracks" : [
+ {
+ "id" : "xxxx",
+ "instrument_id" : "electric guitar",
+ "sound" : "mono"
+ }
+ ]
+ }
+ ]
+ }
+
+ ]
+};
\ No newline at end of file
diff --git a/spec/javascripts/helpers/test_responses.js b/spec/javascripts/helpers/test_responses.js
index a83371be9..ca9d890cb 100644
--- a/spec/javascripts/helpers/test_responses.js
+++ b/spec/javascripts/helpers/test_responses.js
@@ -1,34 +1,4 @@
window.TestResponses = {
- getSessions: [
- {
- "id": "1234",
- "description": "Hello",
- "musician_access": true,
- "genres" : [ "classical" ],
- "participants": [
- {
- "client_id": "0f8f7987-29a0-4e5d-a60c-6b23103e3533",
- "ip_address":"1.1.1.1",
- "user_id" : "02303020402042040", // NOTE THIS WILL BE UNDEFINED (ABSENT) IF THIS CLIENT IS NOT YOUR FRIEND
- "tracks" : [
- {
- "id" : "xxxx",
- "instrument_id" : "electric guitar",
- "sound" : "mono"
- }
- ]
- }
- ],
- "invitations" : [
- {
- "id" : "3948797598275987",
- "sender_id" : "02303020402042040"
-
- }
- ]
- }
- ],
-
sessionPost: {
"id": "1234",
"description": "Hello",
diff --git a/spec/javascripts/sessionLatency.spec.js b/spec/javascripts/sessionLatency.spec.js
index 364281dc0..bfe3c86d3 100644
--- a/spec/javascripts/sessionLatency.spec.js
+++ b/spec/javascripts/sessionLatency.spec.js
@@ -5,7 +5,7 @@
var sessionLatency; // Instance of SessionLatency class for test
fakeJamClient = {
- testLatency: function() {} // Will be overridden by test cases
+ TestLatency: function() {} // Will be overridden by test cases
};
var sessions = [
@@ -33,16 +33,16 @@
beforeEach(function() {
callCount = 0;
sessionLatency = new context.JK.SessionLatency(fakeJamClient);
- spyOn(fakeJamClient, "testLatency").andCallFake(function(client, callback) {
+ spyOn(fakeJamClient, "TestLatency").andCallFake(function(client, callback) {
callback(testLatencyResponses[client.id]);
callCount++;
});
});
describe("SessionPings", function() {
- it("should call jamClient.testLatency and compute new average", function() {
+ it("should call jamClient.TestLatency and compute new average", function() {
sessionLatency.sessionPings(sessions[0]);
- expect(fakeJamClient.testLatency).toHaveBeenCalled();
+ expect(fakeJamClient.TestLatency).toHaveBeenCalled();
var info = sessionLatency.sessionInfo(sessions[0].id);
expect(info.averageLatency).toEqual(35);
});
@@ -87,6 +87,21 @@
});
});
+ describe("Register for Events", function() {
+ it("should register successfully", function() {
+ var cb = jasmine.createSpy();
+ sessionLatency.subscribe(cb);
+ });
+ it("should invoke callback on latency result", function() {
+ var cb = jasmine.createSpy("Latency Subscription Callback");
+ sessionLatency.subscribe(cb);
+ $.each(sessions, function(index, session) {
+ sessionLatency.sessionPings(session);
+ });
+ expect(cb).toHaveBeenCalled();
+ });
+ });
+
});