Commit 439e37fe authored by JC Brand's avatar JC Brand

disco: Refactor service discovery and add tests.

* `disco#items` are now only fetched when advertised by the entity.
* `identity` information is now also stored on the `DiscoEntity` model.
parent 084d9914
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"lodash/prefer-lodash-method": [2, { "lodash/prefer-lodash-method": [2, {
"ignoreMethods": [ "ignoreMethods": [
"find", "endsWith", "startsWith", "filter", "reduce", "find", "endsWith", "startsWith", "filter", "reduce",
"map", "replace", "toLower", "split", "trim", "forEach" "map", "replace", "toLower", "split", "trim", "forEach", "toUpperCase"
] ]
}], }],
"lodash/prefer-startswith": "off", "lodash/prefer-startswith": "off",
......
...@@ -8,8 +8,161 @@ ...@@ -8,8 +8,161 @@
} (this, function (jasmine, $, converse, mock, test_utils) { } (this, function (jasmine, $, converse, mock, test_utils) {
"use strict"; "use strict";
var Strophe = converse.env.Strophe; var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq;
var _ = converse.env._;
describe("Service Discovery", function () { describe("Service Discovery", function () {
describe("Whenever converse.js queries a server for its features", function () {
it("stores the features it receives", mock.initConverseWithAsync(function (done, _converse) {
var IQ_stanzas = _converse.connection.IQ_stanzas;
var IQ_ids = _converse.connection.IQ_ids;
test_utils.waitUntil(function () {
return _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('query[xmlns="http://jabber.org/protocol/disco#info"]');
}).length > 0;
}, 300).then(function () {
/* <iq type='result'
* from='plays.shakespeare.lit'
* to='romeo@montague.net/orchard'
* id='info1'>
* <query xmlns='http://jabber.org/protocol/disco#info'>
* <identity
* category='conference'
* type='text'
* name='Play-Specific Chatrooms'/>
* <identity
* category='directory'
* type='chatroom'
* name='Play-Specific Chatrooms'/>
* <feature var='http://jabber.org/protocol/disco#info'/>
* <feature var='http://jabber.org/protocol/disco#items'/>
* <feature var='http://jabber.org/protocol/muc'/>
* <feature var='jabber:iq:register'/>
* <feature var='jabber:iq:search'/>
* <feature var='jabber:iq:time'/>
* <feature var='jabber:iq:version'/>
* </query>
* </iq>
*/
var info_IQ_id = IQ_ids[0];
var stanza = $iq({
'type': 'result',
'from': 'localhost',
'to': 'dummy@localhost/resource',
'id': info_IQ_id
}).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
.c('identity', {
'category': 'conference',
'type': 'text',
'name': 'Play-Specific Chatrooms'}).up()
.c('identity', {
'category': 'directory',
'type': 'chatroom',
'name': 'Play-Specific Chatrooms'}).up()
.c('feature', {
'var': 'http://jabber.org/protocol/disco#info'}).up()
.c('feature', {
'var': 'http://jabber.org/protocol/disco#items'}).up()
.c('feature', {
'var': 'jabber:iq:register'}).up()
.c('feature', {
'var': 'jabber:iq:time'}).up()
.c('feature', {
'var': 'jabber:iq:version'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
var entities = _converse.disco_entities;
expect(entities.length).toBe(1);
expect(entities.get('localhost').features.length).toBe(5);
expect(entities.get('localhost').features.where({'var': 'jabber:iq:version'}).length).toBe(1);
expect(entities.get('localhost').features.where({'var': 'jabber:iq:time'}).length).toBe(1);
expect(entities.get('localhost').features.where({'var': 'jabber:iq:register'}).length).toBe(1);
expect(entities.get('localhost').features.where(
{'var': 'http://jabber.org/protocol/disco#items'}).length).toBe(1);
expect(entities.get('localhost').features.where(
{'var': 'http://jabber.org/protocol/disco#info'}).length).toBe(1);
test_utils.waitUntil(function () {
// Converse.js sees that the entity has a disco#items feature,
// so it will make a query for it.
return _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('query[xmlns="http://jabber.org/protocol/disco#items"]');
}).length > 0;
}, 300).then(function () {
/* <iq type='result'
* from='catalog.shakespeare.lit'
* to='romeo@montague.net/orchard'
* id='items2'>
* <query xmlns='http://jabber.org/protocol/disco#items'>
* <item jid='people.shakespeare.lit'
* name='Directory of Characters'/>
* <item jid='plays.shakespeare.lit'
* name='Play-Specific Chatrooms'/>
* <item jid='mim.shakespeare.lit'
* name='Gateway to Marlowe IM'/>
* <item jid='words.shakespeare.lit'
* name='Shakespearean Lexicon'/>
*
* <item jid='catalog.shakespeare.lit'
* node='books'
* name='Books by and about Shakespeare'/>
* <item jid='catalog.shakespeare.lit'
* node='clothing'
* name='Wear your literary taste with pride'/>
* <item jid='catalog.shakespeare.lit'
* node='music'
* name='Music from the time of Shakespeare'/>
* </query>
* </iq>
*/
var items_IQ_id = IQ_ids.pop();
stanza = $iq({
'type': 'result',
'from': 'localhost',
'to': 'dummy@localhost/resource',
'id': items_IQ_id
}).c('query', {'xmlns': 'http://jabber.org/protocol/disco#items'})
.c('item', {
'jid': 'people.shakespeare.lit',
'name': 'Directory of Characters'}).up()
.c('item', {
'jid': 'plays.shakespeare.lit',
'name': 'Play-Specific Chatrooms'}).up()
.c('item', {
'jid': 'words.shakespeare.lit',
'name': 'Gateway to Marlowe IM'}).up()
.c('item', {
'jid': 'localhost',
'name': 'Shakespearean Lexicon'}).up()
.c('item', {
'jid': 'localhost',
'node': 'books',
'name': 'Books by and about Shakespeare'}).up()
.c('item', {
'node': 'localhost',
'name': 'Wear your literary taste with pride'}).up()
.c('item', {
'jid': 'localhost',
'node': 'music',
'name': 'Music from the time of Shakespeare'
});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
entities = _converse.disco_entities;
expect(entities.length).toBe(4);
expect(entities.get(_converse.domain).features.length).toBe(5);
expect(entities.get(_converse.domain).identities.length).toBe(2);
expect(entities.get(_converse.domain).identities.where({'category': 'conference'}).length).toBe(1);
expect(entities.get(_converse.domain).identities.where({'category': 'directory'}).length).toBe(1);
done();
});
});
}));
});
describe("Whenever converse.js discovers a new server feature", function () { describe("Whenever converse.js discovers a new server feature", function () {
it("emits the serviceDiscovered event", it("emits the serviceDiscovered event",
mock.initConverseWithPromises( mock.initConverseWithPromises(
...@@ -19,6 +172,7 @@ ...@@ -19,6 +172,7 @@
sinon.spy(_converse, 'emit'); sinon.spy(_converse, 'emit');
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
expect(_converse.emit.called).toBe(true); expect(_converse.emit.called).toBe(true);
expect(_converse.emit.args[0][0]).toBe('serviceDiscovered');
expect(_converse.emit.args[0][1].get('var')).toBe(Strophe.NS.MAM); expect(_converse.emit.args[0][1].get('var')).toBe(Strophe.NS.MAM);
done(); done();
})); }));
......
...@@ -381,7 +381,7 @@ ...@@ -381,7 +381,7 @@
'var': Strophe.NS.MAM 'var': Strophe.NS.MAM
}); });
spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set
_converse.disco_entities.get(_converse.domain).features.onFeatureAdded(feature); _converse.disco_entities.get(_converse.domain).onFeatureAdded(feature);
expect(_converse.connection.sendIQ).toHaveBeenCalled(); expect(_converse.connection.sendIQ).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( expect(sent_stanza.toLocaleString()).toBe(
......
This diff is collapsed.
...@@ -49,6 +49,17 @@ ...@@ -49,6 +49,17 @@
return function () { return function () {
Strophe.Bosh.prototype._processRequest = function () {}; // Don't attempt to send out stanzas Strophe.Bosh.prototype._processRequest = function () {}; // Don't attempt to send out stanzas
var c = new Strophe.Connection('jasmine tests'); var c = new Strophe.Connection('jasmine tests');
var sendIQ = c.sendIQ;
c.IQ_stanzas = [];
c.IQ_ids = [];
c.sendIQ = function (iq, callback, errback) {
this.IQ_stanzas.push(iq);
var id = sendIQ.bind(this)(iq, callback, errback);
this.IQ_ids.push(id);
return id;
}
c.vcard = { c.vcard = {
'get': function (callback, jid) { 'get': function (callback, jid) {
var fullname; var fullname;
...@@ -111,18 +122,15 @@ ...@@ -111,18 +122,15 @@
return function (done) { return function (done) {
var _converse = initConverse(settings, spies); var _converse = initConverse(settings, spies);
var promises = _.map(promise_names, _converse.api.waitUntil); var promises = _.map(promise_names, _converse.api.waitUntil);
Promise.all(promises).then(_.partial(func, done, _converse)); Promise.all(promises)
.then(_.partial(func, done, _converse))
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
} }
}; };
mock.initConverseWithConnectionSpies = function (spies, settings, func) { mock.initConverseWithConnectionSpies = function (spies, settings, func) {
if (_.isFunction(settings)) { return function (done) {
var _func = settings; return func(done, initConverse(settings, spies));
settings = func;
func = _func;
}
return function () {
return func(initConverse(settings, spies));
}; };
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment