diff --git a/.eslintrc.json b/.eslintrc.json index ebee35649..0c61c2b72 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -30,7 +30,7 @@ "accessor-pairs": "error", "array-bracket-spacing": "off", "array-callback-return": "error", - "arrow-body-style": "error", + "arrow-body-style": "off", "arrow-parens": "error", "arrow-spacing": "error", "block-scoped-var": "off", diff --git a/spec/disco.js b/spec/disco.js index 1d216b316..74ec50ea3 100644 --- a/spec/disco.js +++ b/spec/disco.js @@ -20,7 +20,7 @@ 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"]'); + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); }).length > 0; }, 300).then(function () { /* * */ - var info_IQ_id = IQ_ids[0]; - var stanza = $iq({ + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + })[0]; + var info_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + stanza = $iq({ 'type': 'result', 'from': 'localhost', 'to': 'dummy@localhost/resource', @@ -79,92 +82,97 @@ 'var': 'jabber:iq:version'}); _converse.connection._dataRecv(test_utils.createRequest(stanza)); - var entities = _converse.disco_entities; - expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID - expect(entities.get(_converse.domain).features.length).toBe(5); - expect(entities.get(_converse.domain).identities.length).toBe(3); - 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); + _converse.api.disco.entities.get().then(function (entities) { + expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID + expect(entities.get(_converse.domain).features.length).toBe(5); + expect(entities.get(_converse.domain).identities.length).toBe(3); + 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 () { - /* - * - * - * - * - * - * - * - * - * - * - * - */ - 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() + 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 () { + /* + * + * + * + * + * + * + * + * + * + * + * + */ + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]'); + })[0]; + var items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + 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', - '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)); + .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(5); // We have an extra entity, which is the user's JID - 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(); - }); + entities = _converse.disco_entities; + expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID + expect(entities.get(_converse.domain).items.length).toBe(3); + expect(_.includes(entities.get(_converse.domain).items.pluck('jid'), 'people.shakespeare.lit')).toBeTruthy(); + expect(_.includes(entities.get(_converse.domain).items.pluck('jid'), 'plays.shakespeare.lit')).toBeTruthy(); + expect(_.includes(entities.get(_converse.domain).items.pluck('jid'), 'words.shakespeare.lit')).toBeTruthy(); + 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(); + }); + }); }); })); }); diff --git a/spec/http-file-upload.js b/spec/http-file-upload.js index 61cb34622..ee2d750ba 100644 --- a/spec/http-file-upload.js +++ b/spec/http-file-upload.js @@ -10,6 +10,7 @@ var Strophe = converse.env.Strophe; var $iq = converse.env.$iq; var _ = converse.env._; + var f = converse.env.f; describe("XEP-0363: HTTP File Upload", function () { @@ -18,135 +19,171 @@ it("is done automatically", 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 () { - /* - * - * - * - * - * - * - */ - var info_IQ_id = IQ_ids[0]; - var stanza = $iq({ + + test_utils.waitUntilDiscoConfirmed(_converse, _converse.bare_jid, [], []).then(function () { + test_utils.waitUntil(function () { + return _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + }).length > 0; + }, 300).then(function () { + /* + * + * + * + * + * + * + */ + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + })[0]; + var info_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + + 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': 'server', + 'type': 'im'}).up() + .c('feature', { + 'var': 'http://jabber.org/protocol/disco#info'}).up() + .c('feature', { + 'var': 'http://jabber.org/protocol/disco#items'}); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + _converse.api.disco.entities.get().then(function(entities) { + expect(entities.length).toBe(2); + expect(_.includes(entities.pluck('jid'), 'localhost')).toBe(true); + expect(_.includes(entities.pluck('jid'), 'dummy@localhost')).toBe(true); + + expect(entities.get(_converse.domain).features.length).toBe(2); + expect(entities.get(_converse.domain).identities.length).toBe(1); + + return 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('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]'); + }).length > 0; + }, 300); + }); + }).then(function () { + /* + * + * + * + * + * + */ + var stanza = _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="localhost"] query[xmlns="http://jabber.org/protocol/disco#items"]'); + })[0]; + var items_IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + 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': 'server', - 'type': 'im'}).up() - .c('feature', { - 'var': 'http://jabber.org/protocol/disco#info'}).up() - .c('feature', { - 'var': 'http://jabber.org/protocol/disco#items'}); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); + 'id': items_IQ_id + }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#items'}) + .c('item', { + 'jid': 'upload.localhost', + 'name': 'HTTP File Upload'}); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); - var entities = _converse.disco_entities; - expect(entities.length).toBe(2); // We have an extra entity, which is the user's JID - expect(entities.get(_converse.domain).features.length).toBe(2); - expect(entities.get(_converse.domain).identities.length).toBe(1); - - return 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 () { - /* - * - * - * - * - * - */ - var items_IQ_id = IQ_ids[IQ_ids.length-1]; - var 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': 'upload.localhost', - 'name': 'HTTP File Upload'}).up() - .c('item', { - 'jid': 'conference.localhost', - 'name': 'Chatrooms Service'}); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); - - var entities = _converse.disco_entities; - expect(entities.length).toBe(4); // We have an extra entity, which is the user's JID - - return 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) { + _converse.api.disco.entities.get().then(function (entities) { + expect(entities.length).toBe(2); + expect(entities.get('localhost').items.length).toBe(1); + return test_utils.waitUntil(function () { + // Converse.js sees that the entity has a disco#info feature, + // so it will make a query for it. + return _.filter(IQ_stanzas, function (iq) { + return iq.nodeTree.querySelector('iq[to="upload.localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); + }).length > 0; + }, 300); + }); + }).then(function () { + var stanza = _.filter(IQ_stanzas, function (iq) { return iq.nodeTree.querySelector('iq[to="upload.localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); - }).length > 0; - }, 300); - }).then(function () { - var stanza = _.filter(IQ_stanzas, function (iq) { - return iq.nodeTree.querySelector('iq[to="upload.localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]'); - })[0]; - var IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; - expect(stanza.toLocaleString()).toBe( - ""+ - ""+ - ""); + })[0]; + var IQ_id = IQ_ids[IQ_stanzas.indexOf(stanza)]; + expect(stanza.toLocaleString()).toBe( + ""+ + ""+ + ""); - // Upload service responds and reports a maximum file size of 5MiB - /* - * - * - * - * - * - * urn:xmpp:http:upload:0 - * - * - * 5242880 - * - * - * - * - */ - stanza = $iq({'type': 'result', 'to': 'dummy@localhost/resource', 'id': IQ_id, 'from': 'upload.localhost'}) - .c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'}) - .c('identity', {'category':'store', 'type':'file', 'name':'HTTP File Upload'}).up() - .c('feature', {'var':'urn:xmpp:http:upload:0'}).up() - .c('x', {'type':'result', 'xmlns':'jabber:x:data'}) - .c('field', {'var':'FORM_TYPE', 'type':'hidden'}) - .c('value').t('urn:xmpp:http:upload:0').up().up() - .c('field', {'var':'max-file-size'}) - .c('value').t('5242880'); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); + // Upload service responds and reports a maximum file size of 5MiB + /* + * + * + * + * + * + * urn:xmpp:http:upload:0 + * + * + * 5242880 + * + * + * + * + */ + stanza = $iq({'type': 'result', 'to': 'dummy@localhost/resource', 'id': IQ_id, 'from': 'upload.localhost'}) + .c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'}) + .c('identity', {'category':'store', 'type':'file', 'name':'HTTP File Upload'}).up() + .c('feature', {'var':'urn:xmpp:http:upload:0'}).up() + .c('x', {'type':'result', 'xmlns':'jabber:x:data'}) + .c('field', {'var':'FORM_TYPE', 'type':'hidden'}) + .c('value').t('urn:xmpp:http:upload:0').up().up() + .c('field', {'var':'max-file-size'}) + .c('value').t('5242880'); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); - var entities = _converse.disco_entities; - expect(entities.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1); - done(); + _converse.api.disco.entities.get().then(function (entities) { + expect(entities.get('localhost').items.get('upload.localhost').identities.where({'category': 'store'}).length).toBe(1); + _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain).then( + function (result) { + expect(result.length).toBe(1); + expect(result[0].get('jid')).toBe('upload.localhost'); + done(); + } + ); + }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); + }) }) })); }); + + describe("When supported", function () { + + describe("A file upload toolbar button", function () { + + it("appears in private chats", mock.initConverseWithAsync(function (done, _converse) { + test_utils.createContacts(_converse, 'current'); + var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; + test_utils.openChatBoxFor(_converse, contact_jid); + done(); + })); + + it("appears in MUC chats", mock.initConverseWithAsync(function (done, _converse) { + done(); + })); + }); + }); }); })); diff --git a/spec/mam.js b/spec/mam.js index e4219d0d4..6d56011b3 100644 --- a/spec/mam.js +++ b/spec/mam.js @@ -69,507 +69,593 @@ done(); })); - it("can be used to query for all messages to/from a particular JID", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - _converse.api.archive.query({'with':'juliet@capulet.lit'}); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - "juliet@capulet.lit"+ - ""+ - ""+ - ""+ - "" - ); - })); + it("can be used to query for all messages to/from a particular JID", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { - it("can be used to query for archived messages from a chat room", mock.initConverse(function (_converse) { - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - var callback = jasmine.createSpy('callback'); - - _converse.api.archive.query({'with': 'coven@chat.shakespeare.lit', 'groupchat': true}, callback); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - ""+ - ""); - })); - - it("checks whether returned MAM messages from a MUC room are from the right JID", mock.initConverse(function (_converse) { - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - var callback = jasmine.createSpy('callback'); - - _converse.api.archive.query({'with': 'coven@chat.shakespear.lit', 'groupchat': true, 'max':'10'}, callback); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - - /* - * - * - * - * - * Thrice the brinded cat hath mew'd. - * - * - * - * - * - * - * - */ - var msg1 = $msg({'id':'iasd207', 'from': 'other@chat.shakespear.lit', 'to': 'dummy@localhost'}) - .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'34482-21985-73620'}) - .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) - .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() - .c('message', { - 'xmlns':'jabber:client', - 'to':'dummy@localhost', - 'id':'162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2', - 'from':'coven@chat.shakespeare.lit/firstwitch', - 'type':'groupchat' }) - .c('body').t("Thrice the brinded cat hath mew'd."); - _converse.connection._dataRecv(test_utils.createRequest(msg1)); - - /* Send an stanza to indicate the end of the result set. - * - * - * - * - * 28482-98726-73623 - * 09af3-cc343-b409f - * 20 - * - * - */ - var stanza = $iq({'type': 'result', 'id': IQ_id}) - .c('fin', {'xmlns': 'urn:xmpp:mam:2'}) - .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'}) - .c('first', {'index': '0'}).t('23452-4534-1').up() - .c('last').t('09af3-cc343-b409f').up() - .c('count').t('16'); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); - - expect(callback).toHaveBeenCalled(); - var args = callback.calls.argsFor(0); - expect(args[0].length).toBe(0); - })); - - it("can be used to query for all messages in a certain timespan", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var start = '2010-06-07T00:00:00Z'; - var end = '2010-07-07T13:23:54Z'; - _converse.api.archive.query({ - 'start': start, - 'end': end - - }); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - ""+moment(start).format()+""+ - ""+ - ""+ - ""+moment(end).format()+""+ - ""+ - ""+ - ""+ - "" - ); - })); - - it("throws a TypeError if an invalid date is provided", mock.initConverse(function (_converse) { - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - expect(_.partial(_converse.api.archive.query, {'start': 'not a real date'})).toThrow( - new TypeError('archive.query: invalid date provided for: start') - ); - })); - - it("can be used to query for all messages after a certain time", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var start = '2010-06-07T00:00:00Z'; - _converse.api.archive.query({'start': start}); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - ""+moment(start).format()+""+ - ""+ - ""+ - ""+ - "" - ); - })); - - it("can be used to query for a limited set of results", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var start = '2010-06-07T00:00:00Z'; - _converse.api.archive.query({'start': start, 'max':10}); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - ""+moment(start).format()+""+ - ""+ - ""+ - ""+ - "10"+ - ""+ - ""+ - "" - ); - })); - - it("can be used to page through results", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var start = '2010-06-07T00:00:00Z'; - _converse.api.archive.query({ - 'start': start, - 'after': '09af3-cc343-b409f', - 'max':10 - }); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - ""+moment(start).format()+""+ - ""+ - ""+ - ""+ - "10"+ - "09af3-cc343-b409f"+ - ""+ - ""+ - "" - ); - })); - - it("accepts \"before\" with an empty string as value to reverse the order", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - _converse.api.archive.query({'before': '', 'max':10}); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ - ""+ - "urn:xmpp:mam:2"+ - ""+ - ""+ - ""+ - "10"+ - ""+ - ""+ - ""+ - "" - ); - })); - - it("accepts a Strophe.RSM object for the query options", mock.initConverse(function (_converse) { - // Normally the user wouldn't manually make a Strophe.RSM object - // and pass it in. However, in the callback method an RSM object is - // returned which can be reused for easy paging. This test is - // more for that usecase. - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - var rsm = new Strophe.RSM({'max': '10'}); - rsm['with'] = 'romeo@montague.lit'; // eslint-disable-line dot-notation - rsm.start = '2010-06-07T00:00:00Z'; - _converse.api.archive.query(rsm); - - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - ""+ + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + _converse.api.archive.query({'with':'juliet@capulet.lit'}); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ ""+ "urn:xmpp:mam:2"+ ""+ ""+ - "romeo@montague.lit"+ + "juliet@capulet.lit"+ ""+ - ""+ - ""+moment(rsm.start).format()+""+ - ""+ - ""+ - ""+ - "10"+ - ""+ - ""+ - "" - ); + ""+ + ""+ + "" + ); + done(); + }); + })); + + it("can be used to query for archived messages from a chat room", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + var callback = jasmine.createSpy('callback'); + + _converse.api.archive.query({'with': 'coven@chat.shakespeare.lit', 'groupchat': true}, callback); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + ""+ + ""); + done(); + }); })); - it("accepts a callback function, which it passes the messages and a Strophe.RSM object", mock.initConverse(function (_converse) { - if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { - _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); - } + it("checks whether returned MAM messages from a MUC room are from the right JID", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + var callback = jasmine.createSpy('callback'); + + _converse.api.archive.query({'with': 'coven@chat.shakespear.lit', 'groupchat': true, 'max':'10'}, callback); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + + /* + * + * + * + * + * Thrice the brinded cat hath mew'd. + * + * + * + * + * + * + * + */ + var msg1 = $msg({'id':'iasd207', 'from': 'other@chat.shakespear.lit', 'to': 'dummy@localhost'}) + .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'34482-21985-73620'}) + .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) + .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() + .c('message', { + 'xmlns':'jabber:client', + 'to':'dummy@localhost', + 'id':'162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2', + 'from':'coven@chat.shakespeare.lit/firstwitch', + 'type':'groupchat' }) + .c('body').t("Thrice the brinded cat hath mew'd."); + _converse.connection._dataRecv(test_utils.createRequest(msg1)); + + /* Send an stanza to indicate the end of the result set. + * + * + * + * + * 28482-98726-73623 + * 09af3-cc343-b409f + * 20 + * + * + */ + var stanza = $iq({'type': 'result', 'id': IQ_id}) + .c('fin', {'xmlns': 'urn:xmpp:mam:2'}) + .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'}) + .c('first', {'index': '0'}).t('23452-4534-1').up() + .c('last').t('09af3-cc343-b409f').up() + .c('count').t('16'); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + expect(callback).toHaveBeenCalled(); + var args = callback.calls.argsFor(0); + expect(args[0].length).toBe(0); + done(); + }); + })); + + it("can be used to query for all messages in a certain timespan", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + var sent_stanza, IQ_id; var sendIQ = _converse.connection.sendIQ; spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { sent_stanza = iq; IQ_id = sendIQ.bind(this)(iq, callback, errback); }); - var callback = jasmine.createSpy('callback'); + _converse.api.disco.entities.get().then(function (entities) { + if (!entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var start = '2010-06-07T00:00:00Z'; + var end = '2010-07-07T13:23:54Z'; + _converse.api.archive.query({ + 'start': start, + 'end': end - _converse.api.archive.query({'with': 'romeo@capulet.lit', 'max':'10'}, callback); - var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); - - /* - * - * - * - * - * Call me but love, and I'll be new baptized; Henceforth I never will be Romeo. - * - * - * - * - */ - var msg1 = $msg({'id':'aeb213', 'to':'juliet@capulet.lit/chamber'}) - .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'28482-98726-73623'}) - .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) - .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() - .c('message', { - 'xmlns':'jabber:client', - 'to':'juliet@capulet.lit/balcony', - 'from':'romeo@montague.lit/orchard', - 'type':'chat' }) - .c('body').t("Call me but love, and I'll be new baptized;"); - _converse.connection._dataRecv(test_utils.createRequest(msg1)); - - var msg2 = $msg({'id':'aeb213', 'to':'juliet@capulet.lit/chamber'}) - .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'28482-98726-73624'}) - .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) - .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() - .c('message', { - 'xmlns':'jabber:client', - 'to':'juliet@capulet.lit/balcony', - 'from':'romeo@montague.lit/orchard', - 'type':'chat' }) - .c('body').t("Henceforth I never will be Romeo."); - _converse.connection._dataRecv(test_utils.createRequest(msg2)); - - /* Send an stanza to indicate the end of the result set. - * - * - * - * - * 28482-98726-73623 - * 09af3-cc343-b409f - * 20 - * - * - */ - var stanza = $iq({'type': 'result', 'id': IQ_id}) - .c('fin', {'xmlns': 'urn:xmpp:mam:2'}) - .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'}) - .c('first', {'index': '0'}).t('23452-4534-1').up() - .c('last').t('09af3-cc343-b409f').up() - .c('count').t('16'); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); - - expect(callback).toHaveBeenCalled(); - var args = callback.calls.argsFor(0); - expect(args[0].length).toBe(2); - expect(args[0][0].outerHTML).toBe(msg1.nodeTree.outerHTML); - expect(args[0][1].outerHTML).toBe(msg2.nodeTree.outerHTML); - expect(args[1]['with']).toBe('romeo@capulet.lit'); // eslint-disable-line dot-notation - expect(args[1].max).toBe('10'); - expect(args[1].count).toBe('16'); - expect(args[1].first).toBe('23452-4534-1'); - expect(args[1].last).toBe('09af3-cc343-b409f'); + }); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + ""+moment(start).format()+""+ + ""+ + ""+ + ""+moment(end).format()+""+ + ""+ + ""+ + ""+ + "" + ); + done(); + }); })); + it("throws a TypeError if an invalid date is provided", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + expect(_.partial(_converse.api.archive.query, {'start': 'not a real date'})).toThrow( + new TypeError('archive.query: invalid date provided for: start') + ); + done(); + }); + })); + + it("can be used to query for all messages after a certain time", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + if (!_converse.disco_entities.get(_converse.domain).features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var start = '2010-06-07T00:00:00Z'; + _converse.api.archive.query({'start': start}); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + ""+moment(start).format()+""+ + ""+ + ""+ + ""+ + "" + ); + done(); + }); + })); + + it("can be used to query for a limited set of results", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + var start = '2010-06-07T00:00:00Z'; + _converse.api.archive.query({'start': start, 'max':10}); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + ""+moment(start).format()+""+ + ""+ + ""+ + ""+ + "10"+ + ""+ + ""+ + "" + ); + done(); + }); + })); + + it("can be used to page through results", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + var start = '2010-06-07T00:00:00Z'; + _converse.api.archive.query({ + 'start': start, + 'after': '09af3-cc343-b409f', + 'max':10 + }); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + ""+moment(start).format()+""+ + ""+ + ""+ + ""+ + "10"+ + "09af3-cc343-b409f"+ + ""+ + ""+ + "" + ); + done(); + }); + })); + + it("accepts \"before\" with an empty string as value to reverse the order", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + _converse.api.archive.query({'before': '', 'max':10}); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + ""+ + "10"+ + ""+ + ""+ + ""+ + "" + ); + done(); + }); + })); + + it("accepts a Strophe.RSM object for the query options", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + // Normally the user wouldn't manually make a Strophe.RSM object + // and pass it in. However, in the callback method an RSM object is + // returned which can be reused for easy paging. This test is + // more for that usecase. + var rsm = new Strophe.RSM({'max': '10'}); + rsm['with'] = 'romeo@montague.lit'; // eslint-disable-line dot-notation + rsm.start = '2010-06-07T00:00:00Z'; + _converse.api.archive.query(rsm); + + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + ""+ + ""+ + "urn:xmpp:mam:2"+ + ""+ + ""+ + "romeo@montague.lit"+ + ""+ + ""+ + ""+moment(rsm.start).format()+""+ + ""+ + ""+ + ""+ + "10"+ + ""+ + ""+ + "" + ); + done(); + }); + })); + + it("accepts a callback function, which it passes the messages and a Strophe.RSM object", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { + _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM}); + } + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + var callback = jasmine.createSpy('callback'); + + _converse.api.archive.query({'with': 'romeo@capulet.lit', 'max':'10'}, callback); + var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); + + /* + * + * + * + * + * Call me but love, and I'll be new baptized; Henceforth I never will be Romeo. + * + * + * + * + */ + var msg1 = $msg({'id':'aeb213', 'to':'juliet@capulet.lit/chamber'}) + .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'28482-98726-73623'}) + .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) + .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() + .c('message', { + 'xmlns':'jabber:client', + 'to':'juliet@capulet.lit/balcony', + 'from':'romeo@montague.lit/orchard', + 'type':'chat' }) + .c('body').t("Call me but love, and I'll be new baptized;"); + _converse.connection._dataRecv(test_utils.createRequest(msg1)); + + var msg2 = $msg({'id':'aeb213', 'to':'juliet@capulet.lit/chamber'}) + .c('result', {'xmlns': 'urn:xmpp:mam:2', 'queryid':queryid, 'id':'28482-98726-73624'}) + .c('forwarded', {'xmlns':'urn:xmpp:forward:0'}) + .c('delay', {'xmlns':'urn:xmpp:delay', 'stamp':'2010-07-10T23:08:25Z'}).up() + .c('message', { + 'xmlns':'jabber:client', + 'to':'juliet@capulet.lit/balcony', + 'from':'romeo@montague.lit/orchard', + 'type':'chat' }) + .c('body').t("Henceforth I never will be Romeo."); + _converse.connection._dataRecv(test_utils.createRequest(msg2)); + + /* Send an stanza to indicate the end of the result set. + * + * + * + * + * 28482-98726-73623 + * 09af3-cc343-b409f + * 20 + * + * + */ + var stanza = $iq({'type': 'result', 'id': IQ_id}) + .c('fin', {'xmlns': 'urn:xmpp:mam:2'}) + .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'}) + .c('first', {'index': '0'}).t('23452-4534-1').up() + .c('last').t('09af3-cc343-b409f').up() + .c('count').t('16'); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + expect(callback).toHaveBeenCalled(); + var args = callback.calls.argsFor(0); + expect(args[0].length).toBe(2); + expect(args[0][0].outerHTML).toBe(msg1.nodeTree.outerHTML); + expect(args[0][1].outerHTML).toBe(msg2.nodeTree.outerHTML); + expect(args[1]['with']).toBe('romeo@capulet.lit'); // eslint-disable-line dot-notation + expect(args[1].max).toBe('10'); + expect(args[1].count).toBe('16'); + expect(args[1].first).toBe('23452-4534-1'); + expect(args[1].last).toBe('09af3-cc343-b409f'); + done() + }); + })); }); describe("The default preference", function () { - it("is set once server support for MAM has been confirmed", mock.initConverse(function (_converse) { - var sent_stanza, IQ_id; - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); + it("is set once server support for MAM has been confirmed", + mock.initConverseWithPromises( + null, [], {}, + function (done, _converse) { + + _converse.api.disco.entities.get(_converse.domain).then(function (entity) { + var sent_stanza, IQ_id; + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + sent_stanza = iq; + IQ_id = sendIQ.bind(this)(iq, callback, errback); + }); + spyOn(_converse, 'onMAMPreferences').and.callThrough(); + _converse.message_archiving = 'never'; + + var feature = new Backbone.Model({ + 'var': Strophe.NS.MAM + }); + spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set + + entity.onFeatureAdded(feature); + + expect(_converse.connection.sendIQ).toHaveBeenCalled(); + expect(sent_stanza.toLocaleString()).toBe( + ""+ + ""+ + "" + ); + + /* Example 20. Server responds with current preferences + * + * + * + * + * + * + * + */ + var stanza = $iq({'type': 'result', 'id': IQ_id}) + .c('prefs', {'xmlns': Strophe.NS.MAM, 'default':'roster'}) + .c('always').c('jid').t('romeo@montague.lit').up().up() + .c('never').c('jid').t('montague@montague.lit'); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + + expect(_converse.onMAMPreferences).toHaveBeenCalled(); + expect(_converse.connection.sendIQ.calls.count()).toBe(2); + + expect(sent_stanza.toString()).toBe( + ""+ + ""+ + "romeo@montague.lit"+ + "montague@montague.lit"+ + ""+ + "" + ); + + expect(feature.get('preference')).toBe(undefined); + /* + * + * + * romeo@montague.lit + * + * + * montague@montague.lit + * + * + * + */ + stanza = $iq({'type': 'result', 'id': IQ_id}) + .c('prefs', {'xmlns': Strophe.NS.MAM, 'default':'always'}) + .c('always').up() + .c('never').up(); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); + expect(feature.save).toHaveBeenCalled(); + expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation + done(); }); - spyOn(_converse, 'onMAMPreferences').and.callThrough(); - _converse.message_archiving = 'never'; - - var feature = new Backbone.Model({ - 'var': Strophe.NS.MAM - }); - spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set - _converse.disco_entities.get(_converse.domain).onFeatureAdded(feature); - - expect(_converse.connection.sendIQ).toHaveBeenCalled(); - expect(sent_stanza.toLocaleString()).toBe( - ""+ - ""+ - "" - ); - - /* Example 20. Server responds with current preferences - * - * - * - * - * - * - * - */ - var stanza = $iq({'type': 'result', 'id': IQ_id}) - .c('prefs', {'xmlns': Strophe.NS.MAM, 'default':'roster'}) - .c('always').c('jid').t('romeo@montague.lit').up().up() - .c('never').c('jid').t('montague@montague.lit'); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); - - expect(_converse.onMAMPreferences).toHaveBeenCalled(); - - expect(_converse.connection.sendIQ.calls.count()).toBe(2); - expect(sent_stanza.toString()).toBe( - ""+ - ""+ - "romeo@montague.lit"+ - "montague@montague.lit"+ - ""+ - "" - ); - - expect(feature.get('preference')).toBe(undefined); - /* - * - * - * romeo@montague.lit - * - * - * montague@montague.lit - * - * - * - */ - stanza = $iq({'type': 'result', 'id': IQ_id}) - .c('prefs', {'xmlns': Strophe.NS.MAM, 'default':'always'}) - .c('always').up() - .c('never').up(); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); - expect(feature.save).toHaveBeenCalled(); - expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation - - // Restore - _converse.message_archiving = 'never'; })); }); }); diff --git a/src/converse-bookmarks.js b/src/converse-bookmarks.js index dd08f1ce2..7d665dfb2 100644 --- a/src/converse-bookmarks.js +++ b/src/converse-bookmarks.js @@ -550,7 +550,7 @@ _converse.api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid), _converse.api.disco.supports(Strophe.NS.PUBSUB+'#publish-options', _converse.bare_jid) ]).then((args) => { - resolve(args[0] && (args[1].supported || _converse.allow_public_bookmarks)); + resolve(args[0] && (args[1].length || _converse.allow_public_bookmarks)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); } diff --git a/src/converse-chatview.js b/src/converse-chatview.js index 450e10a97..57c081604 100644 --- a/src/converse-chatview.js +++ b/src/converse-chatview.js @@ -341,8 +341,7 @@ Promise.all(_.map(_.keys(resources), (resource) => _converse.api.disco.supports(Strophe.NS.SPOILER, `${contact_jid}/${resource}`) )).then((results) => { - const supported = _.every(f.map(f.get('supported'))(results)); - if (supported) { + if (results.length) { const html = tpl_spoiler_button(this.model.toJSON()); if (_converse.visible_toolbar_buttons.emoji) { this.el.querySelector('.toggle-smiley').insertAdjacentHTML('afterEnd', html); diff --git a/src/converse-disco.js b/src/converse-disco.js index 72854db6d..dea095302 100644 --- a/src/converse-disco.js +++ b/src/converse-disco.js @@ -12,7 +12,7 @@ define(["converse-core", "sizzle", "strophe.disco"], factory); }(this, function (converse, sizzle) { - const { Backbone, Promise, Strophe, b64_sha1, utils, _ } = converse.env; + const { Backbone, Promise, Strophe, b64_sha1, utils, _, f } = converse.env; converse.plugins.add('converse-disco', { @@ -22,24 +22,10 @@ */ const { _converse } = this; - function onDiscoItems (stanza) { - _.each(stanza.querySelectorAll('query item'), (item) => { - if (item.getAttribute("node")) { - // XXX: ignore nodes for now. - // See: https://xmpp.org/extensions/xep-0030.html#items-nodes - return; - } - const jid = item.getAttribute('jid'); - const entities = _converse.disco_entities; - if (_.isUndefined(entities.get(jid))) { - entities.create({'jid': jid}); - } - }); - } - // Promises exposed by this plugin _converse.api.promises.add('discoInitialized'); + _converse.DiscoEntity = Backbone.Model.extend({ /* A Disco Entity is a JID addressable entity that can be queried * for features. @@ -63,6 +49,10 @@ ); this.fetchFeatures(); + this.items = new _converse.DiscoEntities(); + this.items.browserStorage = new Backbone.BrowserStorage[_converse.storage]( + b64_sha1(`converse.disco-items-${this.get('jid')}`) + ); }, getIdentity (category, type) { @@ -98,17 +88,10 @@ const entity = this; return new Promise((resolve, reject) => { function fulfillPromise () { - const model = entity.features.findWhere({'var': feature }); - if (model) { - resolve({ - 'supported': true, - 'feature': model - }); + if (entity.features.findWhere({'var': feature})) { + resolve(entity); } else { - resolve({ - 'supported': false, - 'feature': null - }); + resolve(); } } entity.waitUntilFeaturesDiscovered @@ -141,13 +124,27 @@ _converse.connection.disco.info(this.get('jid'), null, this.onInfo.bind(this)); }, + onDiscoItems (stanza) { + _.each(stanza.querySelectorAll('query item'), (item) => { + if (item.getAttribute("node")) { + // XXX: ignore nodes for now. + // See: https://xmpp.org/extensions/xep-0030.html#items-nodes + return; + } + const jid = item.getAttribute('jid'); + if (_.isUndefined(this.items.get(jid))) { + this.items.create({'jid': jid}); + } + }); + }, + queryForItems () { if (_.isEmpty(this.identities.where({'category': 'server'}))) { // Don't fetch features and items if this is not a // server or a conference component. return; } - _converse.connection.disco.items(this.get('jid'), null, onDiscoItems); + _converse.connection.disco.items(this.get('jid'), null, this.onDiscoItems.bind(this)); }, onInfo (stanza) { @@ -175,26 +172,11 @@ _converse.DiscoEntities = Backbone.Collection.extend({ model: _converse.DiscoEntity, - initialize () { - this.browserStorage = new Backbone.BrowserStorage[_converse.storage]( - b64_sha1(`converse.disco-entities-${_converse.bare_jid}`) - ); - this.fetchEntities().then( - _.partial(_converse.emit, 'discoInitialized'), - _.partial(_converse.emit, 'discoInitialized') - ).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); - }, - fetchEntities () { return new Promise((resolve, reject) => { this.fetch({ add: true, - success: function (collection) { - if (collection.length === 0 || !collection.get(_converse.domain)) { - this.create({'jid': _converse.domain}); - } - resolve(); - }.bind(this), + success: resolve, error () { reject (new Error("Could not fetch disco entities")); } @@ -227,6 +209,18 @@ function initializeDisco () { addClientFeatures(); _converse.disco_entities = new _converse.DiscoEntities(); + _converse.disco_entities.browserStorage = new Backbone.BrowserStorage[_converse.storage]( + b64_sha1(`converse.disco-entities-${_converse.bare_jid}`) + ); + + _converse.disco_entities.fetchEntities().then((collection) => { + if (collection.length === 0 || !collection.get(_converse.domain)) { + // If we don't have an entity for our own XMPP server, + // create one. + _converse.disco_entities.create({'jid': _converse.domain}); + } + _converse.emit('discoInitialized'); + }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); } _converse.api.listen.on('reconnected', initializeDisco); _converse.api.listen.on('connected', initializeDisco); @@ -247,28 +241,49 @@ 'disco': { 'entities': { 'get' (entity_jid, create=false) { - const entity = _converse.disco_entities.get(entity_jid); - if (entity || !create) { - return entity; - } - return _converse.disco_entities.create({'jid': entity_jid}); + return _converse.api.waitUntil('discoInitialized').then(() => { + if (_.isNil(entity_jid)) { + return _converse.disco_entities; + } + const entity = _converse.disco_entities.get(entity_jid); + if (entity || !create) { + return entity; + } + return _converse.disco_entities.create({'jid': entity_jid}); + }); } }, 'supports' (feature, entity_jid) { - /* Returns a Promise which resolves with a map indicating - * whether a given feature is supported. + /* Returns a Promise which resolves with a list containing + * _converse.Entity instances representing the entity + * itself or those items associated with the entity if + * they support the given feature. * * Parameters: * (String) feature - The feature that might be - * supported. In the XML stanza, this is the `var` - * attribute of the `` element. For - * example: 'http://jabber.org/protocol/muc' - * (String) entity_jid - The JID of the entity which might support the feature. + * supported. In the XML stanza, this is the `var` + * attribute of the `` element. For + * example: 'http://jabber.org/protocol/muc' + * (String) entity_jid - The JID of the entity + * (and its associated items) which should be queried */ - return _converse.api.waitUntil('discoInitialized').then(() => { - const entity = _converse.api.disco.entities.get(entity_jid, true); - return entity.hasFeature(feature); + if (_.isNil(entity_jid)) { + throw new TypeError('disco.supports: You need to provide an entity JID'); + } + return _converse.api.waitUntil('discoInitialized').then((entity) => { + return new Promise((resolve, reject) => { + _converse.api.disco.entities.get(entity_jid, true).then((entity) => { + Promise.all( + _.concat( + entity.items.map((item) => item.hasFeature(feature)), + entity.hasFeature(feature) + ) + ).then((result) => { + resolve(f.filter(f.isObject, result)); + }).catch(reject); + }) + }); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }, @@ -286,10 +301,13 @@ * In the XML stanza, this is the `type` * attribute of the `` element. * For example: 'pep' + * (String) entity_jid - The JID of the entity which might have the identity */ - return _converse.api.waitUntil('discoInitialized').then(() => { - const entity = _converse.api.disco.entities.get(entity_jid, true); - return entity.getIdentity(category, type); + return new Promise((resolve, reject) => { + _converse.api.waitUntil('discoInitialized').then(() => { + _converse.api.disco.entities.get(entity_jid, true) + .then((entity) => resolve(entity.getIdentity(category, type))); + }) }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); } } diff --git a/src/converse-http-file-upload.js b/src/converse-http-file-upload.js index f380365a4..7ba4d8998 100644 --- a/src/converse-http-file-upload.js +++ b/src/converse-http-file-upload.js @@ -1,16 +1,48 @@ (function (root, factory) { - define(["converse-http-file-upload"], factory); + define(["converse-core"], factory); }(this, function (converse) { "use strict"; + const { Promise, Strophe, _ } = converse.env; + const u = converse.env.utils; + + Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0'); + converse.plugins.add('converse-http-file-upload', { + /* Plugin dependencies are other plugins which might be + * overridden or relied upon, and therefore need to be loaded before + * this plugin. + * + * If the setting "strict_plugin_dependencies" is set to true, + * an error will be raised if the plugin is not found. By default it's + * false, which means these plugins are only loaded opportunistically. + * + * NB: These plugins need to have already been loaded via require.js. + */ + dependencies: ["converse-chatview"], + + overrides: { + + ChatBoxView: { + addFileUploadButton (options) { + }, + + renderToolbar (toolbar, options) { + const { _converse } = this.__super__; + const result = this.__super__.renderToolbar.apply(this, arguments); + // TODO: check results.length + _converse.api.disco.supports(Strophe.NS.HTTPUPLOAD, _converse.domain) + .then(this.addFileUploadButton.bind(this)); + return result; + } + } + }, initialize () { /* The initialize function gets called as soon as the plugin is * loaded by converse.js's plugin machinery. */ const { _converse } = this; - } }); })); diff --git a/src/converse-mam.js b/src/converse-mam.js index 7ddc56c1e..90ad4b71e 100644 --- a/src/converse-mam.js +++ b/src/converse-mam.js @@ -154,8 +154,8 @@ if (this.disable_mam) { return; } const { _converse } = this.__super__; _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid).then( - (result) => { // Success - if (result.supported) { + (results) => { // Success + if (result.length) { const most_recent_msg = utils.getMostRecentMessage(this.model); if (_.isNil(most_recent_msg)) { this.fetchArchivedMessages(); @@ -193,7 +193,7 @@ const { _converse } = this.__super__; _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid).then( (result) => { // Success - if (result.supported) { + if (result.length) { this.fetchArchivedMessages(); } this.model.save({'mam_initialized': true}); diff --git a/src/converse-vcard.js b/src/converse-vcard.js index ec46e7a5e..d59411c43 100644 --- a/src/converse-vcard.js +++ b/src/converse-vcard.js @@ -186,7 +186,7 @@ if (_.isNil(_converse.xmppstatus.get('vcard_updated'))) { _converse.api.disco.supports(Strophe.NS.VCARD, _converse.domain) .then((result) => { - if (result.supported) { + if (result.length) { _converse.api.vcard.get(_converse.bare_jid) .then((vcard) => _converse.xmppstatus.save(vcard)); }}) diff --git a/src/converse.js b/src/converse.js index 1e918f763..e5aad3f23 100644 --- a/src/converse.js +++ b/src/converse.js @@ -7,24 +7,25 @@ if (typeof define !== 'undefined') { * -------------------- * Any of the following components may be removed if they're not needed. */ - "converse-chatview", // Renders standalone chat boxes for single user chat - "converse-controlbox", // The control box - "converse-bookmarks", // XEP-0048 Bookmarks - "converse-roomslist", // Show currently open chat rooms - "converse-mam", // XEP-0313 Message Archive Management - "converse-muc", // XEP-0045 Multi-user chat - "converse-muc-views", // Views related to MUC + "converse-bookmarks", // XEP-0048 Bookmarks + "converse-chatview", // Renders standalone chat boxes for single user chat + "converse-controlbox", // The control box + "converse-dragresize", // Allows chat boxes to be resized by dragging them + "converse-fullscreen", + "converse-headline", // Support for headline messages + "converse-http-file-upload", + "converse-mam", // XEP-0313 Message Archive Management + "converse-minimize", // Allows chat boxes to be minimized + "converse-muc", // XEP-0045 Multi-user chat "converse-muc-embedded", "converse-muc-views", - "converse-vcard", // XEP-0054 VCard-temp - "converse-otr", // Off-the-record encryption for one-on-one messages - "converse-register", // XEP-0077 In-band registration - "converse-ping", // XEP-0199 XMPP Ping - "converse-notification",// HTML5 Notifications - "converse-minimize", // Allows chat boxes to be minimized - "converse-dragresize", // Allows chat boxes to be resized by dragging them - "converse-headline", // Support for headline messages - "converse-fullscreen" + "converse-muc-views", // Views related to MUC + "converse-notification", // HTML5 Notifications + "converse-otr", // Off-the-record encryption for one-on-one messages + "converse-ping", // XEP-0199 XMPP Ping + "converse-register", // XEP-0077 In-band registration + "converse-roomslist", // Show currently open chat rooms + "converse-vcard", // XEP-0054 VCard-temp /* END: Removable components */ ], function (converse) { return converse;