Commit c583678c authored by JC Brand's avatar JC Brand

Use `_coverse.api.sendIQ` instead of `_converse.connection.sendIQ`

So that we know an event will be emitted when the stanza is sent.
parent 6ebf6f6e
...@@ -63028,13 +63028,15 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins ...@@ -63028,13 +63028,15 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
updateRoomsList() { updateRoomsList() {
/* Send an IQ stanza to the server asking for all groupchats /* Send an IQ stanza to the server asking for all groupchats
*/ */
_converse.connection.sendIQ($iq({ const iq = $iq({
'to': this.model.get('muc_domain'), 'to': this.model.get('muc_domain'),
'from': _converse.connection.jid, 'from': _converse.connection.jid,
'type': "get" 'type': "get"
}).c("query", { }).c("query", {
xmlns: Strophe.NS.DISCO_ITEMS xmlns: Strophe.NS.DISCO_ITEMS
}), this.onRoomsFound.bind(this), this.informNoRoomsFound.bind(this), 5000); });
_converse.api.sendIQ(iq).then(iq => this.onRoomsFound(iq)).catch(iq => this.informNoRoomsFound());
}, },
showRooms(ev) { showRooms(ev) {
...@@ -63512,7 +63514,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins ...@@ -63512,7 +63514,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
iq.c("reason", reason); iq.c("reason", reason);
} }
return _converse.connection.sendIQ(iq, onSuccess, onError); return _converse.api.sendIQ(iq).then(onSuccess).catch(onError);
}, },
verifyRoles(roles) { verifyRoles(roles) {
...@@ -73262,9 +73264,9 @@ _converse.api = { ...@@ -73262,9 +73264,9 @@ _converse.api = {
* @returns {Promise} A promise which resolves when we receive a `result` stanza * @returns {Promise} A promise which resolves when we receive a `result` stanza
* or is rejected when we receive an `error` stanza. * or is rejected when we receive an `error` stanza.
*/ */
'sendIQ'(stanza) { 'sendIQ'(stanza, timeout) {
return new es6_promise_dist_es6_promise_auto__WEBPACK_IMPORTED_MODULE_2___default.a((resolve, reject) => { return new es6_promise_dist_es6_promise_auto__WEBPACK_IMPORTED_MODULE_2___default.a((resolve, reject) => {
_converse.connection.sendIQ(stanza, resolve, reject, _converse.IQ_TIMEOUT); _converse.connection.sendIQ(stanza, resolve, reject, timeout || _converse.IQ_TIMEOUT);
_converse.emit('send', stanza); _converse.emit('send', stanza);
}); });
...@@ -74295,7 +74297,7 @@ function queryForArchivedMessages(_converse, options, callback, errback) { ...@@ -74295,7 +74297,7 @@ function queryForArchivedMessages(_converse, options, callback, errback) {
return true; return true;
}, Strophe.NS.MAM); }, Strophe.NS.MAM);
_converse.connection.sendIQ(stanza, function (iq) { _converse.api.sendIQ(stanza, _converse.message_archiving_timeout).then(iq => {
_converse.connection.deleteHandler(message_handler); _converse.connection.deleteHandler(message_handler);
if (_.isFunction(callback)) { if (_.isFunction(callback)) {
...@@ -74312,13 +74314,15 @@ function queryForArchivedMessages(_converse, options, callback, errback) { ...@@ -74312,13 +74314,15 @@ function queryForArchivedMessages(_converse, options, callback, errback) {
callback(messages, rsm); callback(messages, rsm);
} }
}, function () { }).catch(e => {
_converse.connection.deleteHandler(message_handler); _converse.connection.deleteHandler(message_handler);
if (_.isFunction(errback)) { if (_.isFunction(errback)) {
errback.apply(this, arguments); errback.apply(this, arguments);
} }
}, _converse.message_archiving_timeout);
return;
});
} }
_converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam', { _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam', {
...@@ -74556,7 +74560,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam ...@@ -74556,7 +74560,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
}); });
_converse.onMAMError = function (model, iq) { _converse.onMAMError = function (iq) {
if (iq.querySelectorAll('feature-not-implemented').length) { if (iq.querySelectorAll('feature-not-implemented').length) {
_converse.log("Message Archive Management (XEP-0313) not supported by this server", Strophe.LogLevel.WARN); _converse.log("Message Archive Management (XEP-0313) not supported by this server", Strophe.LogLevel.WARN);
} else { } else {
...@@ -74588,20 +74592,16 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam ...@@ -74588,20 +74592,16 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
'default': _converse.message_archiving 'default': _converse.message_archiving
}); });
_.each(preference.children, function (child) { _.each(preference.children, child => stanza.cnode(child).up()); // XXX: Strictly speaking, the server should respond with the updated prefs
stanza.cnode(child).up(); // (see example 18: https://xmpp.org/extensions/xep-0313.html#config)
}); // but Prosody doesn't do this, so we don't rely on it.
_converse.connection.sendIQ(stanza, _.partial(function (feature, iq) {
// XXX: Strictly speaking, the server should respond with the updated prefs _converse.api.sendIQ(stanza).then(() => feature.save({
// (see example 18: https://xmpp.org/extensions/xep-0313.html#config) 'preferences': {
// but Prosody doesn't do this, so we don't rely on it. 'default': _converse.message_archiving
feature.save({ }
'preferences': { })).catch(_converse.onMAMError);
'default': _converse.message_archiving
}
});
}, feature), _converse.onMAMError);
} else { } else {
feature.save({ feature.save({
'preferences': { 'preferences': {
...@@ -74619,11 +74619,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam ...@@ -74619,11 +74619,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-mam
if (feature.get('var') === Strophe.NS.MAM && prefs['default'] !== _converse.message_archiving && // eslint-disable-line dot-notation if (feature.get('var') === Strophe.NS.MAM && prefs['default'] !== _converse.message_archiving && // eslint-disable-line dot-notation
!_.isUndefined(_converse.message_archiving)) { !_.isUndefined(_converse.message_archiving)) {
// Ask the server for archiving preferences // Ask the server for archiving preferences
_converse.connection.sendIQ($iq({ _converse.api.sendIQ($iq({
'type': 'get' 'type': 'get'
}).c('prefs', { }).c('prefs', {
'xmlns': Strophe.NS.MAM 'xmlns': Strophe.NS.MAM
}), _.partial(_converse.onMAMPreferences, feature), _.partial(_converse.onMAMError, feature)); })).then(_.partial(_converse.onMAMPreferences, feature)).catch(_converse.onMAMError);
} }
}); });
...@@ -75552,14 +75552,12 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc ...@@ -75552,14 +75552,12 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
* Returns a promise which resolves once the response IQ * Returns a promise which resolves once the response IQ
* has been received. * has been received.
*/ */
return new Promise((resolve, reject) => { return _converse.api.sendIQ($iq({
_converse.connection.sendIQ($iq({ 'to': this.get('jid'),
'to': this.get('jid'), 'type': "get"
'type': "get" }).c("query", {
}).c("query", { xmlns: Strophe.NS.MUC_OWNER
xmlns: Strophe.NS.MUC_OWNER }));
}), resolve, reject);
});
}, },
sendConfiguration(config, callback, errback) { sendConfiguration(config, callback, errback) {
...@@ -75592,7 +75590,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc ...@@ -75592,7 +75590,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
callback = _.isUndefined(callback) ? _.noop : _.partial(callback, iq.nodeTree); callback = _.isUndefined(callback) ? _.noop : _.partial(callback, iq.nodeTree);
errback = _.isUndefined(errback) ? _.noop : _.partial(errback, iq.nodeTree); errback = _.isUndefined(errback) ? _.noop : _.partial(errback, iq.nodeTree);
return _converse.connection.sendIQ(iq, callback, errback); return _converse.api.sendIQ(iq).then(callback).catch(errback);
}, },
saveAffiliationAndRole(pres) { saveAffiliationAndRole(pres) {
...@@ -75632,24 +75630,22 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc ...@@ -75632,24 +75630,22 @@ _converse_core__WEBPACK_IMPORTED_MODULE_6__["default"].plugins.add('converse-muc
* (Object) member: Map containing the member's jid and * (Object) member: Map containing the member's jid and
* optionally a reason and affiliation. * optionally a reason and affiliation.
*/ */
return new Promise((resolve, reject) => { const iq = $iq({
const iq = $iq({ to: this.get('jid'),
to: this.get('jid'), type: "set"
type: "set" }).c("query", {
}).c("query", { xmlns: Strophe.NS.MUC_ADMIN
xmlns: Strophe.NS.MUC_ADMIN }).c("item", {
}).c("item", { 'affiliation': member.affiliation || affiliation,
'affiliation': member.affiliation || affiliation, 'nick': member.nick,
'nick': member.nick, 'jid': member.jid
'jid': member.jid });
});
if (!_.isUndefined(member.reason)) { if (!_.isUndefined(member.reason)) {
iq.c("reason", member.reason); iq.c("reason", member.reason);
} }
_converse.connection.sendIQ(iq, resolve, reject); return _converse.api.sendIQ(iq);
});
}, },
setAffiliations(members) { setAffiliations(members) {
...@@ -77073,7 +77069,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins ...@@ -77073,7 +77069,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
subscription: "remove" subscription: "remove"
}); });
_converse.connection.sendIQ(iq, callback, errback); _converse.api.sendIQ(iq).then(callback).catch(errback);
return this; return this;
} }
...@@ -77197,7 +77193,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins ...@@ -77197,7 +77193,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
this.addContactToRoster(jid, name, groups, attributes).then(handler, handler); this.addContactToRoster(jid, name, groups, attributes).then(handler, handler);
}, },
sendContactAddIQ(jid, name, groups, callback, errback) { sendContactAddIQ(jid, name, groups) {
/* Send an IQ stanza to the XMPP server to add a new roster contact. /* Send an IQ stanza to the XMPP server to add a new roster contact.
* *
* Parameters: * Parameters:
...@@ -77209,22 +77205,20 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins ...@@ -77209,22 +77205,20 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
*/ */
name = _.isEmpty(name) ? jid : name; name = _.isEmpty(name) ? jid : name;
const iq = $iq({ const iq = $iq({
type: 'set' 'type': 'set'
}).c('query', { }).c('query', {
xmlns: Strophe.NS.ROSTER 'xmlns': Strophe.NS.ROSTER
}).c('item', { }).c('item', {
jid, jid,
name name
}); });
_.each(groups, function (group) { _.each(groups, group => iq.c('group').t(group).up());
iq.c('group').t(group).up();
});
_converse.connection.sendIQ(iq, callback, errback); _converse.api.sendIQ(iq);
}, },
addContactToRoster(jid, name, groups, attributes) { async addContactToRoster(jid, name, groups, attributes) {
/* Adds a RosterContact instance to _converse.roster and /* Adds a RosterContact instance to _converse.roster and
* registers the contact on the XMPP server. * registers the contact on the XMPP server.
* Returns a promise which is resolved once the XMPP server has * Returns a promise which is resolved once the XMPP server has
...@@ -77236,27 +77230,26 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins ...@@ -77236,27 +77230,26 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
* (Array of Strings) groups - Any roster groups the user might belong to * (Array of Strings) groups - Any roster groups the user might belong to
* (Object) attributes - Any additional attributes to be stored on the user's model. * (Object) attributes - Any additional attributes to be stored on the user's model.
*/ */
return new Promise((resolve, reject) => { groups = groups || [];
groups = groups || [];
this.sendContactAddIQ(jid, name, groups, () => {
const contact = this.create(_.assignIn({
'ask': undefined,
'nickname': name,
groups,
jid,
'requesting': false,
'subscription': 'none'
}, attributes), {
sort: false
});
resolve(contact);
}, function (err) {
alert(__('Sorry, there was an error while trying to add %1$s as a contact.', name));
_converse.log(err, Strophe.LogLevel.ERROR); try {
await this.sendContactAddIQ(jid, name, groups);
} catch (e) {
_converse.log(e, Strophe.LogLevel.ERROR);
resolve(err); alert(__('Sorry, there was an error while trying to add %1$s as a contact.', name));
}); return e;
}
return this.create(_.assignIn({
'ask': undefined,
'nickname': name,
groups,
jid,
'requesting': false,
'subscription': 'none'
}, attributes), {
'sort': false
}); });
}, },
...@@ -77346,34 +77339,32 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins ...@@ -77346,34 +77339,32 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
return _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version'); return _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version');
}, },
fetchFromServer() { async fetchFromServer() {
/* Fetch the roster from the XMPP server */ /* Fetch the roster from the XMPP server */
return new Promise((resolve, reject) => { const stanza = $iq({
const iq = $iq({ 'type': 'get',
'type': 'get', 'id': _converse.connection.getUniqueId('roster')
'id': _converse.connection.getUniqueId('roster') }).c('query', {
}).c('query', { xmlns: Strophe.NS.ROSTER
xmlns: Strophe.NS.ROSTER });
});
if (this.rosterVersioningSupported()) {
iq.attrs({
'ver': this.data.get('version')
});
}
const callback = _.flow(this.onReceivedFromServer.bind(this), resolve); if (this.rosterVersioningSupported()) {
stanza.attrs({
'ver': this.data.get('version')
});
}
const errback = function errback(iq) { let iq;
const errmsg = "Error while trying to fetch roster from the server";
_converse.log(errmsg, Strophe.LogLevel.ERROR); try {
iq = await _converse.api.sendIQ(stanza);
} catch (e) {
_converse.log(e, Strophe.LogLevel.ERROR);
reject(new Error(errmsg)); return _converse.log("Error while trying to fetch roster from the server", Strophe.LogLevel.ERROR);
}; }
return _converse.connection.sendIQ(iq, callback, errback); return this.onReceivedFromServer(iq);
});
}, },
onReceivedFromServer(iq) { onReceivedFromServer(iq) {
...@@ -77834,7 +77825,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca ...@@ -77834,7 +77825,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca
}); });
function onVCardData(jid, iq, callback) { async function onVCardData(jid, iq) {
const vcard = iq.querySelector('vCard'); const vcard = iq.querySelector('vCard');
let result = {}; let result = {};
...@@ -77855,23 +77846,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca ...@@ -77855,23 +77846,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca
if (result.image) { if (result.image) {
const buffer = u.base64ToArrayBuffer(result['image']); const buffer = u.base64ToArrayBuffer(result['image']);
crypto.subtle.digest('SHA-1', buffer).then(ab => { const ab = await crypto.subtle.digest('SHA-1', buffer);
result['image_hash'] = u.arrayBufferToHex(ab); result['image_hash'] = u.arrayBufferToHex(ab);
if (callback) callback(result);
});
} else {
if (callback) callback(result);
} }
}
function onVCardError(jid, iq, errback) { return result;
if (errback) {
errback({
'stanza': iq,
'jid': jid,
'vcard_error': moment().format()
});
}
} }
function createStanza(type, jid, vcard_el) { function createStanza(type, jid, vcard_el) {
...@@ -77902,7 +77881,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca ...@@ -77902,7 +77881,7 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca
return _converse.api.sendIQ(createStanza("set", jid, vcard_el)); return _converse.api.sendIQ(createStanza("set", jid, vcard_el));
} }
function getVCard(_converse, jid) { async function getVCard(_converse, jid) {
/* Request the VCard of another user. Returns a promise. /* Request the VCard of another user. Returns a promise.
* *
* Parameters: * Parameters:
...@@ -77910,9 +77889,19 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca ...@@ -77910,9 +77889,19 @@ _converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins.add('converse-vca
* is being requested. * is being requested.
*/ */
const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid; const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid;
return new Promise((resolve, reject) => { let iq;
_converse.connection.sendIQ(createStanza("get", to), _.partial(onVCardData, jid, _, resolve), _.partial(onVCardError, jid, _, resolve), _converse.IQ_TIMEOUT);
}); try {
iq = await _converse.api.sendIQ(createStanza("get", to));
} catch (iq) {
return {
'stanza': iq,
'jid': jid,
'vcard_error': moment().format()
};
}
return onVCardData(jid, iq);
} }
/* Event handlers */ /* Event handlers */
...@@ -3737,9 +3737,9 @@ ...@@ -3737,9 +3737,9 @@
})); }));
it("contains a link to a modal which can list groupchats publically available on the server", it("contains a link to a modal which can list groupchats publically available on the server",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {}, null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) { async function (done, _converse) {
var sendIQ = _converse.connection.sendIQ; var sendIQ = _converse.connection.sendIQ;
var sent_stanza, IQ_id; var sent_stanza, IQ_id;
...@@ -3753,53 +3753,49 @@ ...@@ -3753,53 +3753,49 @@
roomspanel.el.querySelector('.show-list-muc-modal').click(); roomspanel.el.querySelector('.show-list-muc-modal').click();
test_utils.closeControlBox(_converse); test_utils.closeControlBox(_converse);
const modal = roomspanel.list_rooms_modal; const modal = roomspanel.list_rooms_modal;
test_utils.waitUntil(() => u.isVisible(modal.el), 1000) await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
.then(() => { spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve());
spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(() => Promise.resolve()); roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
// See: http://xmpp.org/extensions/xep-0045.html#disco-rooms
// See: http://xmpp.org/extensions/xep-0045.html#disco-rooms expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
const input = modal.el.querySelector('input[name="server"]').value = 'chat.shakespear.lit';
const input = modal.el.querySelector('input[name="server"]').value = 'chat.shakespear.lit'; modal.el.querySelector('input[type="submit"]').click();
modal.el.querySelector('input[type="submit"]').click(); await test_utils.waitUntil(() => _converse.chatboxes.length);
return test_utils.waitUntil(() => _converse.chatboxes.length); expect(sent_stanza.toLocaleString()).toBe(
}).then(() => { `<iq from="dummy@localhost/resource" id="${IQ_id}" to="chat.shakespear.lit" type="get" xmlns="jabber:client">`+
expect(sent_stanza.toLocaleString()).toBe( `<query xmlns="http://jabber.org/protocol/disco#items"/>`+
`<iq from="dummy@localhost/resource" id="${IQ_id}" to="chat.shakespear.lit" type="get" xmlns="jabber:client">`+ `</iq>`
`<query xmlns="http://jabber.org/protocol/disco#items"/>`+ );
`</iq>`
);
var iq = $iq({ const iq = $iq({
from:'muc.localhost', from:'muc.localhost',
to:'dummy@localhost/pda', to:'dummy@localhost/pda',
id: IQ_id, id: IQ_id,
type:'result' type:'result'
}).c('query') }).c('query')
.c('item', { jid:'heath@chat.shakespeare.lit', name:'A Lonely Heath'}).up() .c('item', { jid:'heath@chat.shakespeare.lit', name:'A Lonely Heath'}).up()
.c('item', { jid:'coven@chat.shakespeare.lit', name:'A Dark Cave'}).up() .c('item', { jid:'coven@chat.shakespeare.lit', name:'A Dark Cave'}).up()
.c('item', { jid:'forres@chat.shakespeare.lit', name:'The Palace'}).up() .c('item', { jid:'forres@chat.shakespeare.lit', name:'The Palace'}).up()
.c('item', { jid:'inverness@chat.shakespeare.lit', name:'Macbeth&apos;s Castle'}).nodeTree; .c('item', { jid:'inverness@chat.shakespeare.lit', name:'Macbeth&apos;s Castle'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(iq)); _converse.connection._dataRecv(test_utils.createRequest(iq));
expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(5); await test_utils.waitUntil(() => modal.el.querySelectorAll('.available-chatrooms li').length === 5);
const rooms = modal.el.querySelectorAll('.available-chatrooms li'); const rooms = modal.el.querySelectorAll('.available-chatrooms li');
expect(rooms[0].textContent.trim()).toBe("Groupchats found:"); expect(rooms[0].textContent.trim()).toBe("Groupchats found:");
expect(rooms[1].textContent.trim()).toBe("A Lonely Heath"); expect(rooms[1].textContent.trim()).toBe("A Lonely Heath");
expect(rooms[2].textContent.trim()).toBe("A Dark Cave"); expect(rooms[2].textContent.trim()).toBe("A Dark Cave");
expect(rooms[3].textContent.trim()).toBe("The Palace"); expect(rooms[3].textContent.trim()).toBe("The Palace");
expect(rooms[4].textContent.trim()).toBe("Macbeth's Castle"); expect(rooms[4].textContent.trim()).toBe("Macbeth's Castle");
rooms[4].querySelector('.open-room').click(); rooms[4].querySelector('.open-room').click();
return test_utils.waitUntil(() => _converse.chatboxes.length > 1); await test_utils.waitUntil(() => _converse.chatboxes.length > 1);
}).then(() => { expect(sizzle('.chatroom', _converse.el).filter(u.isVisible).length).toBe(1); // There should now be an open chatroom
expect(sizzle('.chatroom', _converse.el).filter(u.isVisible).length).toBe(1); // There should now be an open chatroom var view = _converse.chatboxviews.get('inverness@chat.shakespeare.lit');
var view = _converse.chatboxviews.get('inverness@chat.shakespeare.lit'); expect(view.el.querySelector('.chat-head-chatroom').textContent.trim()).toBe("Macbeth's Castle");
expect(view.el.querySelector('.chat-head-chatroom').textContent.trim()).toBe("Macbeth's Castle"); done();
done();
}).catch(_.partial(console.error, _));
})); }));
it("shows the number of unread mentions received", it("shows the number of unread mentions received",
......
...@@ -157,79 +157,79 @@ ...@@ -157,79 +157,79 @@
it("checks whether returned MAM messages from a MUC room are from the right JID", it("checks whether returned MAM messages from a MUC room are from the right JID",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, [], {}, null, [], {},
function (done, _converse) { async 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); const entity = await _converse.api.disco.entities.get(_converse.domain);
var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid'); 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;
const 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');
/* <message id='iasd207' from='coven@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda'> _converse.api.archive.query({'with': 'coven@chat.shakespear.lit', 'groupchat': true, 'max':'10'}, callback);
* <result xmlns='urn:xmpp:mam:2' queryid='g27' id='34482-21985-73620'> var queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
* <forwarded xmlns='urn:xmpp:forward:0'>
* <delay xmlns='urn:xmpp:delay' stamp='2002-10-13T23:58:37Z'/>
* <message xmlns="jabber:client"
* from='coven@chat.shakespeare.lit/firstwitch'
* id='162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2'
* type='groupchat'>
* <body>Thrice the brinded cat hath mew'd.</body>
* <x xmlns='http://jabber.org/protocol/muc#user'>
* <item affiliation='none'
* jid='witch1@shakespeare.lit'
* role='participant' />
* </x>
* </message>
* </forwarded>
* </result>
* </message>
*/
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 <iq> stanza to indicate the end of the result set.
*
* <iq type='result' id='juliet1'>
* <fin xmlns='urn:xmpp:mam:2'>
* <set xmlns='http://jabber.org/protocol/rsm'>
* <first index='0'>28482-98726-73623</first>
* <last>09af3-cc343-b409f</last>
* <count>20</count>
* </set>
* </iq>
*/
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(); /* <message id='iasd207' from='coven@chat.shakespeare.lit' to='hag66@shakespeare.lit/pda'>
var args = callback.calls.argsFor(0); * <result xmlns='urn:xmpp:mam:2' queryid='g27' id='34482-21985-73620'>
expect(args[0].length).toBe(0); * <forwarded xmlns='urn:xmpp:forward:0'>
done(); * <delay xmlns='urn:xmpp:delay' stamp='2002-10-13T23:58:37Z'/>
}); * <message xmlns="jabber:client"
* from='coven@chat.shakespeare.lit/firstwitch'
* id='162BEBB1-F6DB-4D9A-9BD8-CFDCC801A0B2'
* type='groupchat'>
* <body>Thrice the brinded cat hath mew'd.</body>
* <x xmlns='http://jabber.org/protocol/muc#user'>
* <item affiliation='none'
* jid='witch1@shakespeare.lit'
* role='participant' />
* </x>
* </message>
* </forwarded>
* </result>
* </message>
*/
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 <iq> stanza to indicate the end of the result set.
*
* <iq type='result' id='juliet1'>
* <fin xmlns='urn:xmpp:mam:2'>
* <set xmlns='http://jabber.org/protocol/rsm'>
* <first index='0'>28482-98726-73623</first>
* <last>09af3-cc343-b409f</last>
* <count>20</count>
* </set>
* </iq>
*/
const 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));
await test_utils.waitUntil(() => callback.calls.count());
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", it("can be used to query for all messages in a certain timespan",
...@@ -277,19 +277,18 @@ ...@@ -277,19 +277,18 @@
})); }));
it("throws a TypeError if an invalid date is provided", it("throws a TypeError if an invalid date is provided",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, [], {}, null, [], {},
function (done, _converse) { async function (done, _converse) {
_converse.api.disco.entities.get(_converse.domain).then(function (entity) { const entity = await _converse.api.disco.entities.get(_converse.domain);
if (!entity.features.findWhere({'var': Strophe.NS.MAM})) { if (!entity.features.findWhere({'var': Strophe.NS.MAM})) {
_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(_.partial(_converse.api.archive.query, {'start': 'not a real date'})).toThrow( expect(_.partial(_converse.api.archive.query, {'start': 'not a real date'})).toThrow(
new TypeError('archive.query: invalid date provided for: start') new TypeError('archive.query: invalid date provided for: start')
); );
done(); done();
});
})); }));
it("can be used to query for all messages after a certain time", it("can be used to query for all messages after a certain time",
...@@ -498,175 +497,176 @@ ...@@ -498,175 +497,176 @@
})); }));
it("accepts a callback function, which it passes the messages and a Strophe.RSM object", it("accepts a callback function, which it passes the messages and a Strophe.RSM object",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, [], {}, null, [], {},
function (done, _converse) { async 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');
/* <message id='aeb213' to='juliet@capulet.lit/chamber'>
* <result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
* <forwarded xmlns='urn:xmpp:forward:0'>
* <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
* <message xmlns='jabber:client'
* to='juliet@capulet.lit/balcony'
* from='romeo@montague.lit/orchard'
* type='chat'>
* <body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>
* </message>
* </forwarded>
* </result>
* </message>
*/
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 <iq> stanza to indicate the end of the result set.
*
* <iq type='result' id='juliet1'>
* <fin xmlns='urn:xmpp:mam:2'>
* <set xmlns='http://jabber.org/protocol/rsm'>
* <first index='0'>28482-98726-73623</first>
* <last>09af3-cc343-b409f</last>
* <count>20</count>
* </set>
* </iq>
*/
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(); const entity = await _converse.api.disco.entities.get(_converse.domain);
var args = callback.calls.argsFor(0); if (!entity.features.findWhere({'var': Strophe.NS.MAM})) {
expect(args[0].length).toBe(2); _converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
expect(args[0][0].outerHTML).toBe(msg1.nodeTree.outerHTML); }
expect(args[0][1].outerHTML).toBe(msg2.nodeTree.outerHTML); let sent_stanza, IQ_id;
expect(args[1]['with']).toBe('romeo@capulet.lit'); // eslint-disable-line dot-notation const sendIQ = _converse.connection.sendIQ;
expect(args[1].max).toBe('10'); spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
expect(args[1].count).toBe('16'); sent_stanza = iq;
expect(args[1].first).toBe('23452-4534-1'); IQ_id = sendIQ.bind(this)(iq, callback, errback);
expect(args[1].last).toBe('09af3-cc343-b409f');
done()
}); });
const callback = jasmine.createSpy('callback');
_converse.api.archive.query({'with': 'romeo@capulet.lit', 'max':'10'}, callback);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
/* <message id='aeb213' to='juliet@capulet.lit/chamber'>
* <result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
* <forwarded xmlns='urn:xmpp:forward:0'>
* <delay xmlns='urn:xmpp:delay' stamp='2010-07-10T23:08:25Z'/>
* <message xmlns='jabber:client'
* to='juliet@capulet.lit/balcony'
* from='romeo@montague.lit/orchard'
* type='chat'>
* <body>Call me but love, and I'll be new baptized; Henceforth I never will be Romeo.</body>
* </message>
* </forwarded>
* </result>
* </message>
*/
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 <iq> stanza to indicate the end of the result set.
*
* <iq type='result' id='juliet1'>
* <fin xmlns='urn:xmpp:mam:2'>
* <set xmlns='http://jabber.org/protocol/rsm'>
* <first index='0'>28482-98726-73623</first>
* <last>09af3-cc343-b409f</last>
* <count>20</count>
* </set>
* </iq>
*/
const 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));
await test_utils.waitUntil(() => callback.calls.count());
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 () { describe("The default preference", function () {
it("is set once server support for MAM has been confirmed", it("is set once server support for MAM has been confirmed",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, [], {}, null, [], {},
function (done, _converse) { async 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(
`<iq id="${IQ_id}" type="get" xmlns="jabber:client">`+
`<prefs xmlns="urn:xmpp:mam:2"/>`+
`</iq>`);
/* Example 20. Server responds with current preferences
*
* <iq type='result' id='juliet2'>
* <prefs xmlns='urn:xmpp:mam:0' default='roster'>
* <always/>
* <never/>
* </prefs>
* </iq>
*/
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(); const entity = await _converse.api.disco.entities.get(_converse.domain);
expect(_converse.connection.sendIQ.calls.count()).toBe(2); let sent_stanza, IQ_id;
const 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';
expect(sent_stanza.toString()).toBe( var feature = new Backbone.Model({
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+ 'var': Strophe.NS.MAM
`<prefs default="never" xmlns="urn:xmpp:mam:2">`+ });
`<always><jid>romeo@montague.lit</jid></always>`+ spyOn(feature, 'save').and.callFake(feature.set); // Save will complain about a url not being set
`<never><jid>montague@montague.lit</jid></never>`+
`</prefs>`+ entity.onFeatureAdded(feature);
`</iq>`
); expect(_converse.connection.sendIQ).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
`<iq id="${IQ_id}" type="get" xmlns="jabber:client">`+
`<prefs xmlns="urn:xmpp:mam:2"/>`+
`</iq>`);
/* Example 20. Server responds with current preferences
*
* <iq type='result' id='juliet2'>
* <prefs xmlns='urn:xmpp:mam:0' default='roster'>
* <always/>
* <never/>
* </prefs>
* </iq>
*/
let 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));
await test_utils.waitUntil(() => _converse.onMAMPreferences.calls.count());
expect(_converse.onMAMPreferences).toHaveBeenCalled();
expect(_converse.connection.sendIQ.calls.count()).toBe(2);
expect(feature.get('preference')).toBe(undefined); expect(sent_stanza.toString()).toBe(
/* <iq type='result' id='juliet3'> `<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
* <prefs xmlns='urn:xmpp:mam:0' default='always'> `<prefs default="never" xmlns="urn:xmpp:mam:2">`+
* <always> `<always><jid>romeo@montague.lit</jid></always>`+
* <jid>romeo@montague.lit</jid> `<never><jid>montague@montague.lit</jid></never>`+
* </always> `</prefs>`+
* <never> `</iq>`
* <jid>montague@montague.lit</jid> );
* </never>
* </prefs> expect(feature.get('preference')).toBe(undefined);
* </iq> /* <iq type='result' id='juliet3'>
*/ * <prefs xmlns='urn:xmpp:mam:0' default='always'>
stanza = $iq({'type': 'result', 'id': IQ_id}) * <always>
.c('prefs', {'xmlns': Strophe.NS.MAM, 'default':'always'}) * <jid>romeo@montague.lit</jid>
* </always>
* <never>
* <jid>montague@montague.lit</jid>
* </never>
* </prefs>
* </iq>
*/
stanza = $iq({'type': 'result', 'id': IQ_id})
.c('prefs', {'xmlns': Strophe.NS.MAM, 'default':'always'})
.c('always').up() .c('always').up()
.c('never').up(); .c('never');
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(feature.save).toHaveBeenCalled(); await test_utils.waitUntil(() => feature.save.calls.count());
expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation expect(feature.save).toHaveBeenCalled();
done(); expect(feature.get('preferences')['default']).toBe('never'); // eslint-disable-line dot-notation
}); done();
})); }));
}); });
}); });
......
...@@ -52,327 +52,317 @@ ...@@ -52,327 +52,317 @@
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], null, ['rosterGroupsFetched'],
{ roster_groups: false }, { roster_groups: false },
function (done, _converse) { async function (done, _converse) {
var contact, sent_stanza, IQ_id, stanza, modal; var contact, sent_stanza, IQ_id, stanza, modal;
test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp']) await test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp']);
.then(function () { await test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'), 300);
return test_utils.waitUntil(function () { /* The process by which a user subscribes to a contact, including
return _converse.xmppstatus.vcard.get('fullname'); * the interaction between roster items and subscription states.
}, 300); */
}).then(function () { test_utils.openControlBox(_converse);
/* The process by which a user subscribes to a contact, including const cbview = _converse.chatboxviews.get('controlbox');
* the interaction between roster items and subscription states.
*/ spyOn(_converse.roster, "addAndSubscribe").and.callThrough();
test_utils.openControlBox(_converse); spyOn(_converse.roster, "addContactToRoster").and.callThrough();
var cbview = _converse.chatboxviews.get('controlbox'); spyOn(_converse.roster, "sendContactAddIQ").and.callThrough();
spyOn(_converse.api.vcard, "get").and.callThrough();
spyOn(_converse.roster, "addAndSubscribe").and.callThrough();
spyOn(_converse.roster, "addContactToRoster").and.callThrough(); const sendIQ = _converse.connection.sendIQ;
spyOn(_converse.roster, "sendContactAddIQ").and.callThrough(); spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
spyOn(_converse.api.vcard, "get").and.callThrough(); sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
var sendIQ = _converse.connection.sendIQ; });
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
sent_stanza = iq; cbview.el.querySelector('.add-contact').click()
IQ_id = sendIQ.bind(this)(iq, callback, errback); modal = _converse.rosterview.add_contact_modal;
}); await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
spyOn(modal, "addContactFromForm").and.callThrough();
cbview.el.querySelector('.add-contact').click() modal.delegateEvents();
modal = _converse.rosterview.add_contact_modal;
return test_utils.waitUntil(function () { // Fill in the form and submit
return u.isVisible(modal.el); const form = modal.el.querySelector('form.add-xmpp-contact');
}, 1000); form.querySelector('input').value = 'contact@example.org';
}).then(function () { form.querySelector('[type="submit"]').click();
spyOn(modal, "addContactFromForm").and.callThrough();
modal.delegateEvents(); /* In preparation for being able to render the contact in the
* user's client interface and for the server to keep track of the
// Fill in the form and submit * subscription, the user's client SHOULD perform a "roster set"
var form = modal.el.querySelector('form.add-xmpp-contact'); * for the new roster item.
form.querySelector('input').value = 'contact@example.org'; */
form.querySelector('[type="submit"]').click(); expect(modal.addContactFromForm).toHaveBeenCalled();
expect(_converse.roster.addAndSubscribe).toHaveBeenCalled();
/* In preparation for being able to render the contact in the expect(_converse.roster.addContactToRoster).toHaveBeenCalled();
* user's client interface and for the server to keep track of the
* subscription, the user's client SHOULD perform a "roster set" /* _converse request consists of sending an IQ
* for the new roster item. * stanza of type='set' containing a <query/> element qualified by
*/ * the 'jabber:iq:roster' namespace, which in turn contains an
expect(modal.addContactFromForm).toHaveBeenCalled(); * <item/> element that defines the new roster item; the <item/>
expect(_converse.roster.addAndSubscribe).toHaveBeenCalled(); * element MUST possess a 'jid' attribute, MAY possess a 'name'
expect(_converse.roster.addContactToRoster).toHaveBeenCalled(); * attribute, MUST NOT possess a 'subscription' attribute, and MAY
* contain one or more <group/> child elements:
/* _converse request consists of sending an IQ *
* stanza of type='set' containing a <query/> element qualified by * <iq type='set' id='set1'>
* the 'jabber:iq:roster' namespace, which in turn contains an * <query xmlns='jabber:iq:roster'>
* <item/> element that defines the new roster item; the <item/> * <item
* element MUST possess a 'jid' attribute, MAY possess a 'name' * jid='contact@example.org'
* attribute, MUST NOT possess a 'subscription' attribute, and MAY * name='MyContact'>
* contain one or more <group/> child elements: * <group>MyBuddies</group>
* * </item>
* <iq type='set' id='set1'> * </query>
* <query xmlns='jabber:iq:roster'> * </iq>
* <item */
* jid='contact@example.org' expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled();
* name='MyContact'> expect(sent_stanza.toLocaleString()).toBe(
* <group>MyBuddies</group> `<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
* </item> `<query xmlns="jabber:iq:roster">`+
* </query> `<item jid="contact@example.org" name="contact@example.org"/>`+
* </iq> `</query>`+
*/ `</iq>`
expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled(); );
expect(sent_stanza.toLocaleString()).toBe( /* As a result, the user's server (1) MUST initiate a roster push
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+ * for the new roster item to all available resources associated
`<query xmlns="jabber:iq:roster">`+ * with _converse user that have requested the roster, setting the
`<item jid="contact@example.org" name="contact@example.org"/>`+ * 'subscription' attribute to a value of "none"; and (2) MUST
`</query>`+ * reply to the sending resource with an IQ result indicating the
`</iq>` * success of the roster set:
); *
/* As a result, the user's server (1) MUST initiate a roster push * <iq type='set'>
* for the new roster item to all available resources associated * <query xmlns='jabber:iq:roster'>
* with _converse user that have requested the roster, setting the * <item
* 'subscription' attribute to a value of "none"; and (2) MUST * jid='contact@example.org'
* reply to the sending resource with an IQ result indicating the * subscription='none'
* success of the roster set: * name='MyContact'>
* * <group>MyBuddies</group>
* <iq type='set'> * </item>
* <query xmlns='jabber:iq:roster'> * </query>
* <item * </iq>
* jid='contact@example.org' */
* subscription='none' const create = _converse.roster.create;
* name='MyContact'> const sent_stanzas = [];
* <group>MyBuddies</group> spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
* </item> sent_stanza = stanza;
* </query> sent_stanzas.push(stanza.toLocaleString());
* </iq> });
*/ spyOn(_converse.roster, 'create').and.callFake(function () {
var create = _converse.roster.create; contact = create.apply(_converse.roster, arguments);
var sent_stanzas = []; spyOn(contact, 'subscribe').and.callThrough();
spyOn(_converse.connection, 'send').and.callFake(function (stanza) { return contact;
sent_stanza = stanza; });
sent_stanzas.push(stanza.toLocaleString()); stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
}); .c('item', {
spyOn(_converse.roster, 'create').and.callFake(function () { 'jid': 'contact@example.org',
contact = create.apply(_converse.roster, arguments); 'subscription': 'none',
spyOn(contact, 'subscribe').and.callThrough(); 'name': 'contact@example.org'});
return contact; _converse.connection._dataRecv(test_utils.createRequest(stanza));
}); /* <iq type='result' id='set1'/>
stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'}) */
.c('item', { stanza = $iq({'type': 'result', 'id':IQ_id});
'jid': 'contact@example.org', _converse.connection._dataRecv(test_utils.createRequest(stanza));
'subscription': 'none',
'name': 'contact@example.org'}); await test_utils.waitUntil(() => _converse.roster.create.calls.count());
_converse.connection._dataRecv(test_utils.createRequest(stanza));
/* // A contact should now have been created
* <iq type='result' id='set1'/> expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy();
*/ expect(contact.get('jid')).toBe('contact@example.org');
stanza = $iq({'type': 'result', 'id':IQ_id}); expect(_converse.api.vcard.get).toHaveBeenCalled();
_converse.connection._dataRecv(test_utils.createRequest(stanza));
/* To subscribe to the contact's presence information,
// A contact should now have been created * the user's client MUST send a presence stanza of
expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy(); * type='subscribe' to the contact:
expect(contact.get('jid')).toBe('contact@example.org'); *
expect(_converse.api.vcard.get).toHaveBeenCalled(); * <presence to='contact@example.org' type='subscribe'/>
*/
/* To subscribe to the contact's presence information, await test_utils.waitUntil(() => sent_stanzas.filter(s => s.match('presence')));
* the user's client MUST send a presence stanza of expect(contact.subscribe).toHaveBeenCalled();
* type='subscribe' to the contact: expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
* `<presence to="contact@example.org" type="subscribe" xmlns="jabber:client">`+
* <presence to='contact@example.org' type='subscribe'/> `<nick xmlns="http://jabber.org/protocol/nick">Max Mustermann</nick>`+
*/ `</presence>`
return test_utils.waitUntil(function () { );
return sent_stanzas.length == 1; /* As a result, the user's server MUST initiate a second roster
}, 300); * push to all of the user's available resources that have
}).then(function () { * requested the roster, setting the contact to the pending
expect(contact.subscribe).toHaveBeenCalled(); * sub-state of the 'none' subscription state; _converse pending
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec) * sub-state is denoted by the inclusion of the ask='subscribe'
`<presence to="contact@example.org" type="subscribe" xmlns="jabber:client">`+ * attribute in the roster item:
`<nick xmlns="http://jabber.org/protocol/nick">Max Mustermann</nick>`+ *
`</presence>` * <iq type='set'>
); * <query xmlns='jabber:iq:roster'>
/* As a result, the user's server MUST initiate a second roster * <item
* push to all of the user's available resources that have * jid='contact@example.org'
* requested the roster, setting the contact to the pending * subscription='none'
* sub-state of the 'none' subscription state; _converse pending * ask='subscribe'
* sub-state is denoted by the inclusion of the ask='subscribe' * name='MyContact'>
* attribute in the roster item: * <group>MyBuddies</group>
* * </item>
* <iq type='set'> * </query>
* <query xmlns='jabber:iq:roster'> * </iq>
* <item */
* jid='contact@example.org' spyOn(_converse.roster, "updateContact").and.callThrough();
* subscription='none' stanza = $iq({'type': 'set', 'from': _converse.bare_jid})
* ask='subscribe' .c('query', {'xmlns': 'jabber:iq:roster'})
* name='MyContact'> .c('item', {
* <group>MyBuddies</group> 'jid': 'contact@example.org',
* </item> 'subscription': 'none',
* </query> 'ask': 'subscribe',
* </iq> 'name': 'contact@example.org'});
*/ _converse.connection._dataRecv(test_utils.createRequest(stanza));
spyOn(_converse.roster, "updateContact").and.callThrough(); expect(_converse.roster.updateContact).toHaveBeenCalled();
stanza = $iq({'type': 'set', 'from': _converse.bare_jid}) // Check that the user is now properly shown as a pending
.c('query', {'xmlns': 'jabber:iq:roster'}) // contact in the roster.
.c('item', { await test_utils.waitUntil(function () {
'jid': 'contact@example.org',
'subscription': 'none',
'ask': 'subscribe',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.updateContact).toHaveBeenCalled();
// Check that the user is now properly shown as a pending
// contact in the roster.
return test_utils.waitUntil(function () {
var $header = $('a:contains("Pending contacts")');
var $contacts = $header.parent().find('li:visible');
return $contacts.length;
}, 600);
}).then(function () {
var $header = $('a:contains("Pending contacts")'); var $header = $('a:contains("Pending contacts")');
var $contacts = $header.parent().find('li'); var $contacts = $header.parent().find('li:visible');
expect($contacts.length).toBe(1); return $contacts.length;
expect($contacts.is(':visible')).toBeTruthy(); }, 600);
spyOn(contact, "ackSubscribe").and.callThrough(); var $header = $('a:contains("Pending contacts")');
/* Here we assume the "happy path" that the contact var $contacts = $header.parent().find('li');
* approves the subscription request expect($contacts.length).toBe(1);
* expect($contacts.is(':visible')).toBeTruthy();
* <presence
* to='user@example.com' spyOn(contact, "ackSubscribe").and.callThrough();
* from='contact@example.org' /* Here we assume the "happy path" that the contact
* type='subscribed'/> * approves the subscription request
*/ *
stanza = $pres({ * <presence
'to': _converse.bare_jid, * to='user@example.com'
'from': 'contact@example.org', * from='contact@example.org'
'type': 'subscribed' * type='subscribed'/>
}); */
sent_stanza = ""; // Reset stanza = $pres({
_converse.connection._dataRecv(test_utils.createRequest(stanza)); 'to': _converse.bare_jid,
/* Upon receiving the presence stanza of type "subscribed", 'from': 'contact@example.org',
* the user SHOULD acknowledge receipt of that 'type': 'subscribed'
* subscription state notification by sending a presence
* stanza of type "subscribe".
*/
expect(contact.ackSubscribe).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
`<presence to="contact@example.org" type="subscribe" xmlns="jabber:client"/>`
);
/* The user's server MUST initiate a roster push to all of the user's
* available resources that have requested the roster,
* containing an updated roster item for the contact with
* the 'subscription' attribute set to a value of "to";
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='to'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
IQ_id = _converse.connection.getUniqueId('roster');
stanza = $iq({'type': 'set', 'id': IQ_id})
.c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'to',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
// Check that the IQ set was acknowledged.
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
`<iq from="dummy@localhost/resource" id="${IQ_id}" type="result" xmlns="jabber:client"/>`
);
expect(_converse.roster.updateContact).toHaveBeenCalled();
// The contact should now be visible as an existing
// contact (but still offline).
return test_utils.waitUntil(function () {
var $header = $('a:contains("My contacts")');
var $contacts = $header.parent().find('li:visible');
return $contacts.length;
}, 600);
}).then(function () {
var $header = $('a:contains("My contacts")');
expect($header.length).toBe(1);
expect($header.is(":visible")).toBeTruthy();
var $contacts = $header.parent().find('li');
expect($contacts.length).toBe(1);
// Check that it has the right classes and text
expect($contacts.hasClass('to')).toBeTruthy();
expect($contacts.hasClass('both')).toBeFalsy();
expect($contacts.hasClass('current-xmpp-contact')).toBeTruthy();
expect($contacts.text().trim()).toBe('Contact');
expect(contact.presence.get('show')).toBe('offline');
/* <presence
* from='contact@example.org/resource'
* to='user@example.com/resource'/>
*/
stanza = $pres({'to': _converse.bare_jid, 'from': 'contact@example.org/resource'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
// Now the contact should also be online.
expect(contact.presence.get('show')).toBe('online');
/* Section 8.3. Creating a Mutual Subscription
*
* If the contact wants to create a mutual subscription,
* the contact MUST send a subscription request to the
* user.
*
* <presence from='contact@example.org' to='user@example.com' type='subscribe'/>
*/
spyOn(contact, 'authorize').and.callThrough();
spyOn(_converse.roster, 'handleIncomingSubscription').and.callThrough();
stanza = $pres({
'to': _converse.bare_jid,
'from': 'contact@example.org/resource',
'type': 'subscribe'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.handleIncomingSubscription).toHaveBeenCalled();
/* The user's client MUST send a presence stanza of type
* "subscribed" to the contact in order to approve the
* subscription request.
*
* <presence to='contact@example.org' type='subscribed'/>
*/
expect(contact.authorize).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
`<presence to="contact@example.org" type="subscribed" xmlns="jabber:client"/>`
);
/* As a result, the user's server MUST initiate a
* roster push containing a roster item for the
* contact with the 'subscription' attribute set to
* a value of "both".
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='both'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'both',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.updateContact).toHaveBeenCalled();
// The class on the contact will now have switched.
expect($contacts.hasClass('to')).toBeFalsy();
expect($contacts.hasClass('both')).toBeTruthy();
done();
}); });
sent_stanza = ""; // Reset
_converse.connection._dataRecv(test_utils.createRequest(stanza));
/* Upon receiving the presence stanza of type "subscribed",
* the user SHOULD acknowledge receipt of that
* subscription state notification by sending a presence
* stanza of type "subscribe".
*/
expect(contact.ackSubscribe).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
`<presence to="contact@example.org" type="subscribe" xmlns="jabber:client"/>`
);
/* The user's server MUST initiate a roster push to all of the user's
* available resources that have requested the roster,
* containing an updated roster item for the contact with
* the 'subscription' attribute set to a value of "to";
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='to'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
IQ_id = _converse.connection.getUniqueId('roster');
stanza = $iq({'type': 'set', 'id': IQ_id})
.c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'to',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
// Check that the IQ set was acknowledged.
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
`<iq from="dummy@localhost/resource" id="${IQ_id}" type="result" xmlns="jabber:client"/>`
);
expect(_converse.roster.updateContact).toHaveBeenCalled();
// The contact should now be visible as an existing
// contact (but still offline).
await test_utils.waitUntil(function () {
var $header = $('a:contains("My contacts")');
var $contacts = $header.parent().find('li:visible');
return $contacts.length;
}, 600);
$header = $('a:contains("My contacts")');
expect($header.length).toBe(1);
expect($header.is(":visible")).toBeTruthy();
$contacts = $header.parent().find('li');
expect($contacts.length).toBe(1);
// Check that it has the right classes and text
expect($contacts.hasClass('to')).toBeTruthy();
expect($contacts.hasClass('both')).toBeFalsy();
expect($contacts.hasClass('current-xmpp-contact')).toBeTruthy();
expect($contacts.text().trim()).toBe('Contact');
expect(contact.presence.get('show')).toBe('offline');
/* <presence
* from='contact@example.org/resource'
* to='user@example.com/resource'/>
*/
stanza = $pres({'to': _converse.bare_jid, 'from': 'contact@example.org/resource'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
// Now the contact should also be online.
expect(contact.presence.get('show')).toBe('online');
/* Section 8.3. Creating a Mutual Subscription
*
* If the contact wants to create a mutual subscription,
* the contact MUST send a subscription request to the
* user.
*
* <presence from='contact@example.org' to='user@example.com' type='subscribe'/>
*/
spyOn(contact, 'authorize').and.callThrough();
spyOn(_converse.roster, 'handleIncomingSubscription').and.callThrough();
stanza = $pres({
'to': _converse.bare_jid,
'from': 'contact@example.org/resource',
'type': 'subscribe'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.handleIncomingSubscription).toHaveBeenCalled();
/* The user's client MUST send a presence stanza of type
* "subscribed" to the contact in order to approve the
* subscription request.
*
* <presence to='contact@example.org' type='subscribed'/>
*/
expect(contact.authorize).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
`<presence to="contact@example.org" type="subscribed" xmlns="jabber:client"/>`
);
/* As a result, the user's server MUST initiate a
* roster push containing a roster item for the
* contact with the 'subscription' attribute set to
* a value of "both".
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='both'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'both',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.updateContact).toHaveBeenCalled();
// The class on the contact will now have switched.
expect($contacts.hasClass('to')).toBeFalsy();
expect($contacts.hasClass('both')).toBeTruthy();
done();
})); }));
it("Alternate Flow: Contact Declines Subscription Request", it("Alternate Flow: Contact Declines Subscription Request",
...@@ -466,7 +456,7 @@ ...@@ -466,7 +456,7 @@
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], null, ['rosterGroupsFetched'],
{ roster_groups: false }, { roster_groups: false },
function (done, _converse) { async function (done, _converse) {
var sent_IQ, IQ_id, jid = 'annegreet.gomez@localhost'; var sent_IQ, IQ_id, jid = 'annegreet.gomez@localhost';
test_utils.openControlBox(_converse); test_utils.openControlBox(_converse);
...@@ -480,51 +470,46 @@ ...@@ -480,51 +470,46 @@
sent_IQ = iq; sent_IQ = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback); IQ_id = sendIQ.bind(this)(iq, callback, errback);
}); });
return test_utils.waitUntil(function () { const $header = $('a:contains("My contacts")');
var $header = $('a:contains("My contacts")'); await test_utils.waitUntil(() => $header.parent().find('li').length);
var $contacts = $header.parent().find('li');
return $contacts.length; // remove the first user
}, 600).then(function () { $header.parent().find('li .remove-xmpp-contact').get(0).click();
expect(window.confirm).toHaveBeenCalled();
/* Section 8.6 Removing a Roster Item and Cancelling All
* Subscriptions
*
* First the user is removed from the roster
* Because there may be many steps involved in completely
* removing a roster item and cancelling subscriptions in
* both directions, the roster management protocol includes
* a "shortcut" method for doing so. The process may be
* initiated no matter what the current subscription state
* is by sending a roster set containing an item for the
* contact with the 'subscription' attribute set to a value
* of "remove":
*
* <iq type='set' id='remove1'>
* <query xmlns='jabber:iq:roster'>
* <item jid='contact@example.org' subscription='remove'/>
* </query>
* </iq>
*/
expect(sent_IQ.toLocaleString()).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster">`+
`<item jid="annegreet.gomez@localhost" subscription="remove"/>`+
`</query>`+
`</iq>`);
var $header = $('a:contains("My contacts")'); // Receive confirmation from the contact's server
// remove the first user // <iq type='result' id='remove1'/>
$header.parent().find('li .remove-xmpp-contact').get(0).click(); const stanza = $iq({'type': 'result', 'id':IQ_id});
expect(window.confirm).toHaveBeenCalled(); _converse.connection._dataRecv(test_utils.createRequest(stanza));
// Our contact has now been removed
/* Section 8.6 Removing a Roster Item and Cancelling All await test_utils.waitUntil(() => typeof _converse.roster.get(jid) === "undefined");
* Subscriptions done();
*
* First the user is removed from the roster
* Because there may be many steps involved in completely
* removing a roster item and cancelling subscriptions in
* both directions, the roster management protocol includes
* a "shortcut" method for doing so. The process may be
* initiated no matter what the current subscription state
* is by sending a roster set containing an item for the
* contact with the 'subscription' attribute set to a value
* of "remove":
*
* <iq type='set' id='remove1'>
* <query xmlns='jabber:iq:roster'>
* <item jid='contact@example.org' subscription='remove'/>
* </query>
* </iq>
*/
expect(sent_IQ.toLocaleString()).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster">`+
`<item jid="annegreet.gomez@localhost" subscription="remove"/>`+
`</query>`+
`</iq>`);
// Receive confirmation from the contact's server
// <iq type='result' id='remove1'/>
var stanza = $iq({'type': 'result', 'id':IQ_id});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
// Our contact has now been removed
expect(typeof _converse.roster.get(jid) === "undefined").toBeTruthy();
done();
});
})); }));
it("Receiving a subscription request", mock.initConverseWithPromises( it("Receiving a subscription request", mock.initConverseWithPromises(
......
(function (root, factory) { (function (root, factory) {
define(["jquery", "jasmine", "mock", "test-utils"], factory); define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) { } (this, function ($, jasmine, mock, test_utils) {
var _ = converse.env._; const $iq = converse.env.$iq;
var Strophe = converse.env.Strophe; const $msg = converse.env.$msg;
var $pres = converse.env.$pres; const $pres = converse.env.$pres;
var $msg = converse.env.$msg; const Strophe = converse.env.Strophe;
var $iq = converse.env.$iq; const _ = converse.env._;
var u = converse.env.utils; const sizzle = converse.env.sizzle;
const u = converse.env.utils;
var checkHeaderToggling = function (group) {
const checkHeaderToggling = function (group) {
var $group = $(group); var $group = $(group);
var toggle = group.querySelector('a.group-toggle'); var toggle = group.querySelector('a.group-toggle');
expect(u.isVisible($group[0])).toBeTruthy(); expect(u.isVisible($group[0])).toBeTruthy();
...@@ -38,57 +40,58 @@ ...@@ -38,57 +40,58 @@
it("supports roster versioning", it("supports roster versioning",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas; const IQ_stanzas = _converse.connection.IQ_stanzas;
test_utils.waitUntil( let node = await test_utils.waitUntil(
() => _.filter(IQ_stanzas, iq => iq.nodeTree.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop() () => _.filter(IQ_stanzas, iq => iq.nodeTree.querySelector('iq query[xmlns="jabber:iq:roster"]')).pop()
).then(node => { );
let stanza = node.nodeTree; let stanza = node.nodeTree;
expect(_converse.roster.data.get('version')).toBeUndefined(); expect(_converse.roster.data.get('version')).toBeUndefined();
expect(node.toLocaleString()).toBe( expect(node.toLocaleString()).toBe(
`<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+ `<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster"/>`+ `<query xmlns="jabber:iq:roster"/>`+
`</iq>`); `</iq>`);
let result = $iq({ let result = $iq({
'to': _converse.connection.jid, 'to': _converse.connection.jid,
'type': 'result', 'type': 'result',
'id': stanza.getAttribute('id') 'id': stanza.getAttribute('id')
}).c('query', { }).c('query', {
'xmlns': 'jabber:iq:roster', 'xmlns': 'jabber:iq:roster',
'ver': 'ver7' 'ver': 'ver7'
}).c('item', {'jid': 'nurse@example.com'}).up() }).c('item', {'jid': 'nurse@example.com'}).up()
.c('item', {'jid': 'romeo@example.com'}) .c('item', {'jid': 'romeo@example.com'})
_converse.connection._dataRecv(test_utils.createRequest(result)); _converse.connection._dataRecv(test_utils.createRequest(result));
expect(_converse.roster.data.get('version')).toBe('ver7');
expect(_converse.roster.models.length).toBe(2); await test_utils.waitUntil(() => _converse.roster.models.length > 1);
expect(_converse.roster.data.get('version')).toBe('ver7');
_converse.roster.fetchFromServer(); expect(_converse.roster.models.length).toBe(2);
node = _converse.connection.IQ_stanzas.pop();
stanza = node.nodeTree; _converse.roster.fetchFromServer();
expect(node.toLocaleString()).toBe( node = _converse.connection.IQ_stanzas.pop();
`<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+ stanza = node.nodeTree;
`<query ver="ver7" xmlns="jabber:iq:roster"/>`+ expect(node.toLocaleString()).toBe(
`</iq>`); `<iq id="${stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
`<query ver="ver7" xmlns="jabber:iq:roster"/>`+
result = $iq({ `</iq>`);
'to': _converse.connection.jid,
'type': 'result', result = $iq({
'id': stanza.getAttribute('id') 'to': _converse.connection.jid,
}); 'type': 'result',
_converse.connection._dataRecv(test_utils.createRequest(result)); 'id': stanza.getAttribute('id')
const roster_push = $iq({
'to': _converse.connection.jid,
'type': 'set',
}).c('query', {'xmlns': 'jabber:iq:roster', 'ver': 'ver34'})
.c('item', {'jid': 'romeo@example.com', 'subscription': 'remove'});
_converse.connection._dataRecv(test_utils.createRequest(roster_push));
expect(_converse.roster.data.get('version')).toBe('ver34');
expect(_converse.roster.models.length).toBe(1);
expect(_converse.roster.at(0).get('jid')).toBe('nurse@example.com');
done();
}); });
_converse.connection._dataRecv(test_utils.createRequest(result));
const roster_push = $iq({
'to': _converse.connection.jid,
'type': 'set',
}).c('query', {'xmlns': 'jabber:iq:roster', 'ver': 'ver34'})
.c('item', {'jid': 'romeo@example.com', 'subscription': 'remove'});
_converse.connection._dataRecv(test_utils.createRequest(roster_push));
expect(_converse.roster.data.get('version')).toBe('ver34');
expect(_converse.roster.models.length).toBe(1);
expect(_converse.roster.at(0).get('jid')).toBe('nurse@example.com');
done();
})); }));
describe("The live filter", function () { describe("The live filter", function () {
...@@ -549,17 +552,15 @@ ...@@ -549,17 +552,15 @@
it("can be collapsed under their own header", it("can be collapsed under their own header",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
_addContacts(_converse); _addContacts(_converse);
test_utils.waitUntil(function () { await test_utils.waitUntil(() => $(_converse.rosterview.el).find('.roster-group:visible li').length, 1000);
return $(_converse.rosterview.el).find('.roster-group:visible li').length; await checkHeaderToggling.apply(
}, 500).then(function () { _converse,
checkHeaderToggling.apply( [_converse.rosterview.get('Pending contacts').el]
_converse, );
[_converse.rosterview.get('Pending contacts').el] done();
).then(done);
});
})); }));
it("can be added to the roster", it("can be added to the roster",
...@@ -662,10 +663,10 @@ ...@@ -662,10 +663,10 @@
it("do not have a header if there aren't any", it("do not have a header if there aren't any",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.openControlBox(); test_utils.openControlBox();
var name = mock.pend_names[0]; const name = mock.pend_names[0];
_converse.roster.create({ _converse.roster.create({
jid: name.replace(/ /g,'.').toLowerCase() + '@localhost', jid: name.replace(/ /g,'.').toLowerCase() + '@localhost',
subscription: 'none', subscription: 'none',
...@@ -676,17 +677,18 @@ ...@@ -676,17 +677,18 @@
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) { spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
if (typeof callback === "function") { return callback(); } if (typeof callback === "function") { return callback(); }
}); });
test_utils.waitUntil(function () { await test_utils.waitUntil(function () {
var $pending_contacts = $(_converse.rosterview.get('Pending contacts').el); var $pending_contacts = $(_converse.rosterview.get('Pending contacts').el);
return $pending_contacts.is(':visible') && $pending_contacts.find('li:visible').length; return $pending_contacts.is(':visible') && $pending_contacts.find('li:visible').length;
}, 700).then(function () { }, 700)
$(_converse.rosterview.el).find(".pending-contact-name:contains('"+name+"')")
.parent().siblings('.remove-xmpp-contact')[0].click(); $(_converse.rosterview.el).find(".pending-contact-name:contains('"+name+"')")
expect(window.confirm).toHaveBeenCalled(); .parent().siblings('.remove-xmpp-contact')[0].click();
expect(_converse.connection.sendIQ).toHaveBeenCalled(); expect(window.confirm).toHaveBeenCalled();
expect(u.isVisible(_converse.rosterview.get('Pending contacts').el)).toEqual(false); expect(_converse.connection.sendIQ).toHaveBeenCalled();
done();
}); await test_utils.waitUntil(() => !u.isVisible(_converse.rosterview.get('Pending contacts').el));
done();
})); }));
it("is shown when a new private message is received", it("is shown when a new private message is received",
...@@ -822,42 +824,39 @@ ...@@ -822,42 +824,39 @@
it("can be removed by the user", it("can be removed by the user",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
var sent_IQ;
_addContacts(_converse); _addContacts(_converse);
test_utils.waitUntil(function () { await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('li').length);
return $(_converse.rosterview.el).find('li').length; const name = mock.cur_names[0];
}, 500).then(function () { const jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
var name = mock.cur_names[0]; const contact = _converse.roster.get(jid);
var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost'; spyOn(window, 'confirm').and.returnValue(true);
var contact = _converse.roster.get(jid); spyOn(contact, 'removeFromRoster').and.callThrough();
spyOn(window, 'confirm').and.returnValue(true);
spyOn(contact, 'removeFromRoster').and.callThrough();
var sendIQ = _converse.connection.sendIQ;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
sent_IQ = iq;
callback();
});
$(_converse.rosterview.el).find(".open-chat:contains('"+name+"')")
.parent().find('.remove-xmpp-contact')[0].click();
expect(window.confirm).toHaveBeenCalled(); const sendIQ = _converse.connection.sendIQ;
expect(sent_IQ.toLocaleString()).toBe( let sent_IQ;
`<iq type="set" xmlns="jabber:client">`+ spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
`<query xmlns="jabber:iq:roster"><item jid="max.frankfurter@localhost" subscription="remove"/></query>`+ sent_IQ = iq;
`</iq>`); callback();
expect(contact.removeFromRoster).toHaveBeenCalled();
expect($(_converse.rosterview.el).find(".open-chat:contains('"+name+"')").length).toEqual(0);
done();
}); });
$(_converse.rosterview.el).find(".open-chat:contains('"+name+"')")
.parent().find('.remove-xmpp-contact')[0].click();
expect(window.confirm).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
`<iq type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster"><item jid="max.frankfurter@localhost" subscription="remove"/></query>`+
`</iq>`);
expect(contact.removeFromRoster).toHaveBeenCalled();
await test_utils.waitUntil(() => $(_converse.rosterview.el).find(".open-chat:contains('"+name+"')").length === 0);
done();
})); }));
it("do not have a header if there aren't any", it("do not have a header if there aren't any",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { async function (done, _converse) {
test_utils.openControlBox(); test_utils.openControlBox();
var name = mock.cur_names[0]; var name = mock.cur_names[0];
...@@ -868,24 +867,20 @@ ...@@ -868,24 +867,20 @@
ask: null, ask: null,
fullname: name fullname: name
}); });
test_utils.waitUntil(function () { await test_utils.waitUntil(() => $(_converse.rosterview.el).find('.roster-group:visible li').length, 1000);
return $(_converse.rosterview.el).find('.roster-group:visible li').length; spyOn(window, 'confirm').and.returnValue(true);
}, 700).then(function () { spyOn(contact, 'removeFromRoster').and.callThrough();
spyOn(window, 'confirm').and.returnValue(true); spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
spyOn(contact, 'removeFromRoster').and.callThrough(); if (typeof callback === "function") { return callback(); }
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
if (typeof callback === "function") { return callback(); }
});
expect($(_converse.rosterview.el).find('.roster-group').css('display')).toEqual('block');
$(_converse.rosterview.el).find(".open-chat:contains('"+name+"')")
.parent().find('.remove-xmpp-contact')[0].click();
expect(window.confirm).toHaveBeenCalled();
expect(_converse.connection.sendIQ).toHaveBeenCalled();
expect(contact.removeFromRoster).toHaveBeenCalled();
expect($(_converse.rosterview.el).find('.roster-group').length).toEqual(0);
done();
}); });
expect($(_converse.rosterview.el).find('.roster-group').css('display')).toEqual('block');
$(_converse.rosterview.el).find(".open-chat:contains('"+name+"')")
.parent().find('.remove-xmpp-contact')[0].click();
expect(window.confirm).toHaveBeenCalled();
expect(_converse.connection.sendIQ).toHaveBeenCalled();
expect(contact.removeFromRoster).toHaveBeenCalled();
await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length === 0);
done();
})); }));
it("can change their status to online and be sorted alphabetically", it("can change their status to online and be sorted alphabetically",
......
...@@ -367,16 +367,14 @@ converse.plugins.add('converse-muc-views', { ...@@ -367,16 +367,14 @@ converse.plugins.add('converse-muc-views', {
updateRoomsList () { updateRoomsList () {
/* Send an IQ stanza to the server asking for all groupchats /* Send an IQ stanza to the server asking for all groupchats
*/ */
_converse.connection.sendIQ( const iq = $iq({
$iq({ 'to': this.model.get('muc_domain'),
'to': this.model.get('muc_domain'), 'from': _converse.connection.jid,
'from': _converse.connection.jid, 'type': "get"
'type': "get" }).c("query", {xmlns: Strophe.NS.DISCO_ITEMS});
}).c("query", {xmlns: Strophe.NS.DISCO_ITEMS}), _converse.api.sendIQ(iq)
this.onRoomsFound.bind(this), .then(iq => this.onRoomsFound(iq))
this.informNoRoomsFound.bind(this), .catch(iq => this.informNoRoomsFound())
5000
);
}, },
showRooms (ev) { showRooms (ev) {
...@@ -805,7 +803,7 @@ converse.plugins.add('converse-muc-views', { ...@@ -805,7 +803,7 @@ converse.plugins.add('converse-muc-views', {
const item = $build("item", {nick, role}); const item = $build("item", {nick, role});
const iq = $iq({to: groupchat, type: "set"}).c("query", {xmlns: Strophe.NS.MUC_ADMIN}).cnode(item.node); const iq = $iq({to: groupchat, type: "set"}).c("query", {xmlns: Strophe.NS.MUC_ADMIN}).cnode(item.node);
if (reason !== null) { iq.c("reason", reason); } if (reason !== null) { iq.c("reason", reason); }
return _converse.connection.sendIQ(iq, onSuccess, onError); return _converse.api.sendIQ(iq).then(onSuccess).catch(onError);
}, },
verifyRoles (roles) { verifyRoles (roles) {
......
...@@ -1651,9 +1651,9 @@ _converse.api = { ...@@ -1651,9 +1651,9 @@ _converse.api = {
* @returns {Promise} A promise which resolves when we receive a `result` stanza * @returns {Promise} A promise which resolves when we receive a `result` stanza
* or is rejected when we receive an `error` stanza. * or is rejected when we receive an `error` stanza.
*/ */
'sendIQ' (stanza) { 'sendIQ' (stanza, timeout) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
_converse.connection.sendIQ(stanza, resolve, reject, _converse.IQ_TIMEOUT); _converse.connection.sendIQ(stanza, resolve, reject, timeout || _converse.IQ_TIMEOUT);
_converse.emit('send', stanza); _converse.emit('send', stanza);
}); });
} }
......
...@@ -99,9 +99,8 @@ function queryForArchivedMessages (_converse, options, callback, errback) { ...@@ -99,9 +99,8 @@ function queryForArchivedMessages (_converse, options, callback, errback) {
return true; return true;
}, Strophe.NS.MAM); }, Strophe.NS.MAM);
_converse.connection.sendIQ( _converse.api.sendIQ(stanza, _converse.message_archiving_timeout)
stanza, .then(iq => {
function (iq) {
_converse.connection.deleteHandler(message_handler); _converse.connection.deleteHandler(message_handler);
if (_.isFunction(callback)) { if (_.isFunction(callback)) {
const set = iq.querySelector('set'); const set = iq.querySelector('set');
...@@ -112,13 +111,13 @@ function queryForArchivedMessages (_converse, options, callback, errback) { ...@@ -112,13 +111,13 @@ function queryForArchivedMessages (_converse, options, callback, errback) {
} }
callback(messages, rsm); callback(messages, rsm);
} }
}, }).catch(e => {
function () {
_converse.connection.deleteHandler(message_handler); _converse.connection.deleteHandler(message_handler);
if (_.isFunction(errback)) { errback.apply(this, arguments); } if (_.isFunction(errback)) {
}, errback.apply(this, arguments);
_converse.message_archiving_timeout }
); return;
});
} }
...@@ -333,7 +332,7 @@ converse.plugins.add('converse-mam', { ...@@ -333,7 +332,7 @@ converse.plugins.add('converse-mam', {
message_archiving_timeout: 8000, // Time (in milliseconds) to wait before aborting MAM request message_archiving_timeout: 8000, // Time (in milliseconds) to wait before aborting MAM request
}); });
_converse.onMAMError = function (model, iq) { _converse.onMAMError = function (iq) {
if (iq.querySelectorAll('feature-not-implemented').length) { if (iq.querySelectorAll('feature-not-implemented').length) {
_converse.log( _converse.log(
"Message Archive Management (XEP-0313) not supported by this server", "Message Archive Management (XEP-0313) not supported by this server",
...@@ -365,17 +364,14 @@ converse.plugins.add('converse-mam', { ...@@ -365,17 +364,14 @@ converse.plugins.add('converse-mam', {
'xmlns':Strophe.NS.MAM, 'xmlns':Strophe.NS.MAM,
'default':_converse.message_archiving 'default':_converse.message_archiving
}); });
_.each(preference.children, function (child) { _.each(preference.children, child => stanza.cnode(child).up());
stanza.cnode(child).up();
}); // XXX: Strictly speaking, the server should respond with the updated prefs
_converse.connection.sendIQ(stanza, _.partial(function (feature, iq) { // (see example 18: https://xmpp.org/extensions/xep-0313.html#config)
// XXX: Strictly speaking, the server should respond with the updated prefs // but Prosody doesn't do this, so we don't rely on it.
// (see example 18: https://xmpp.org/extensions/xep-0313.html#config) _converse.api.sendIQ(stanza)
// but Prosody doesn't do this, so we don't rely on it. .then(() => feature.save({'preferences': {'default':_converse.message_archiving}}))
feature.save({'preferences': {'default':_converse.message_archiving}}); .catch(_converse.onMAMError);
}, feature),
_converse.onMAMError
);
} else { } else {
feature.save({'preferences': {'default':_converse.message_archiving}}); feature.save({'preferences': {'default':_converse.message_archiving}});
} }
...@@ -388,11 +384,9 @@ converse.plugins.add('converse-mam', { ...@@ -388,11 +384,9 @@ converse.plugins.add('converse-mam', {
prefs['default'] !== _converse.message_archiving && // eslint-disable-line dot-notation prefs['default'] !== _converse.message_archiving && // eslint-disable-line dot-notation
!_.isUndefined(_converse.message_archiving) ) { !_.isUndefined(_converse.message_archiving) ) {
// Ask the server for archiving preferences // Ask the server for archiving preferences
_converse.connection.sendIQ( _converse.api.sendIQ($iq({'type': 'get'}).c('prefs', {'xmlns': Strophe.NS.MAM}))
$iq({'type': 'get'}).c('prefs', {'xmlns': Strophe.NS.MAM}), .then(_.partial(_converse.onMAMPreferences, feature))
_.partial(_converse.onMAMPreferences, feature), .catch(_converse.onMAMError);
_.partial(_converse.onMAMError, feature)
);
} }
}); });
......
...@@ -620,16 +620,10 @@ converse.plugins.add('converse-muc', { ...@@ -620,16 +620,10 @@ converse.plugins.add('converse-muc', {
* Returns a promise which resolves once the response IQ * Returns a promise which resolves once the response IQ
* has been received. * has been received.
*/ */
return new Promise((resolve, reject) => { return _converse.api.sendIQ(
_converse.connection.sendIQ( $iq({'to': this.get('jid'), 'type': "get"})
$iq({ .c("query", {xmlns: Strophe.NS.MUC_OWNER})
'to': this.get('jid'), );
'type': "get"
}).c("query", {xmlns: Strophe.NS.MUC_OWNER}),
resolve,
reject
);
});
}, },
sendConfiguration (config, callback, errback) { sendConfiguration (config, callback, errback) {
...@@ -652,7 +646,7 @@ converse.plugins.add('converse-muc', { ...@@ -652,7 +646,7 @@ converse.plugins.add('converse-muc', {
_.each(config || [], function (node) { iq.cnode(node).up(); }); _.each(config || [], function (node) { iq.cnode(node).up(); });
callback = _.isUndefined(callback) ? _.noop : _.partial(callback, iq.nodeTree); callback = _.isUndefined(callback) ? _.noop : _.partial(callback, iq.nodeTree);
errback = _.isUndefined(errback) ? _.noop : _.partial(errback, iq.nodeTree); errback = _.isUndefined(errback) ? _.noop : _.partial(errback, iq.nodeTree);
return _converse.connection.sendIQ(iq, callback, errback); return _converse.api.sendIQ(iq).then(callback).catch(errback);
}, },
saveAffiliationAndRole (pres) { saveAffiliationAndRole (pres) {
...@@ -685,19 +679,17 @@ converse.plugins.add('converse-muc', { ...@@ -685,19 +679,17 @@ converse.plugins.add('converse-muc', {
* (Object) member: Map containing the member's jid and * (Object) member: Map containing the member's jid and
* optionally a reason and affiliation. * optionally a reason and affiliation.
*/ */
return new Promise((resolve, reject) => { const iq = $iq({to: this.get('jid'), type: "set"})
const iq = $iq({to: this.get('jid'), type: "set"}) .c("query", {xmlns: Strophe.NS.MUC_ADMIN})
.c("query", {xmlns: Strophe.NS.MUC_ADMIN}) .c("item", {
.c("item", { 'affiliation': member.affiliation || affiliation,
'affiliation': member.affiliation || affiliation, 'nick': member.nick,
'nick': member.nick, 'jid': member.jid
'jid': member.jid });
}); if (!_.isUndefined(member.reason)) {
if (!_.isUndefined(member.reason)) { iq.c("reason", member.reason);
iq.c("reason", member.reason); }
} return _converse.api.sendIQ(iq);
_converse.connection.sendIQ(iq, resolve, reject);
});
}, },
setAffiliations (members) { setAffiliations (members) {
......
...@@ -329,7 +329,7 @@ converse.plugins.add('converse-roster', { ...@@ -329,7 +329,7 @@ converse.plugins.add('converse-roster', {
const iq = $iq({type: 'set'}) const iq = $iq({type: 'set'})
.c('query', {xmlns: Strophe.NS.ROSTER}) .c('query', {xmlns: Strophe.NS.ROSTER})
.c('item', {jid: this.get('jid'), subscription: "remove"}); .c('item', {jid: this.get('jid'), subscription: "remove"});
_converse.connection.sendIQ(iq, callback, errback); _converse.api.sendIQ(iq).then(callback).catch(errback);
return this; return this;
} }
}); });
...@@ -451,7 +451,7 @@ converse.plugins.add('converse-roster', { ...@@ -451,7 +451,7 @@ converse.plugins.add('converse-roster', {
this.addContactToRoster(jid, name, groups, attributes).then(handler, handler); this.addContactToRoster(jid, name, groups, attributes).then(handler, handler);
}, },
sendContactAddIQ (jid, name, groups, callback, errback) { sendContactAddIQ (jid, name, groups) {
/* Send an IQ stanza to the XMPP server to add a new roster contact. /* Send an IQ stanza to the XMPP server to add a new roster contact.
* *
* Parameters: * Parameters:
...@@ -462,14 +462,14 @@ converse.plugins.add('converse-roster', { ...@@ -462,14 +462,14 @@ converse.plugins.add('converse-roster', {
* (Function) errback - A function to call if an error occurred * (Function) errback - A function to call if an error occurred
*/ */
name = _.isEmpty(name)? jid: name; name = _.isEmpty(name)? jid: name;
const iq = $iq({type: 'set'}) const iq = $iq({'type': 'set'})
.c('query', {xmlns: Strophe.NS.ROSTER}) .c('query', {'xmlns': Strophe.NS.ROSTER})
.c('item', { jid, name }); .c('item', { jid, name });
_.each(groups, function (group) { iq.c('group').t(group).up(); }); _.each(groups, group => iq.c('group').t(group).up());
_converse.connection.sendIQ(iq, callback, errback); _converse.api.sendIQ(iq);
}, },
addContactToRoster (jid, name, groups, attributes) { async addContactToRoster (jid, name, groups, attributes) {
/* Adds a RosterContact instance to _converse.roster and /* Adds a RosterContact instance to _converse.roster and
* registers the contact on the XMPP server. * registers the contact on the XMPP server.
* Returns a promise which is resolved once the XMPP server has * Returns a promise which is resolved once the XMPP server has
...@@ -481,27 +481,22 @@ converse.plugins.add('converse-roster', { ...@@ -481,27 +481,22 @@ converse.plugins.add('converse-roster', {
* (Array of Strings) groups - Any roster groups the user might belong to * (Array of Strings) groups - Any roster groups the user might belong to
* (Object) attributes - Any additional attributes to be stored on the user's model. * (Object) attributes - Any additional attributes to be stored on the user's model.
*/ */
return new Promise((resolve, reject) => { groups = groups || [];
groups = groups || []; try {
this.sendContactAddIQ(jid, name, groups, await this.sendContactAddIQ(jid, name, groups);
() => { } catch (e) {
const contact = this.create(_.assignIn({ _converse.log(e, Strophe.LogLevel.ERROR);
'ask': undefined, alert(__('Sorry, there was an error while trying to add %1$s as a contact.', name));
'nickname': name, return e;
groups, }
jid, return this.create(_.assignIn({
'requesting': false, 'ask': undefined,
'subscription': 'none' 'nickname': name,
}, attributes), {sort: false}); groups,
resolve(contact); jid,
}, 'requesting': false,
function (err) { 'subscription': 'none'
alert(__('Sorry, there was an error while trying to add %1$s as a contact.', name)); }, attributes), {'sort': false});
_converse.log(err, Strophe.LogLevel.ERROR);
resolve(err);
}
);
});
}, },
subscribeBack (bare_jid, presence) { subscribeBack (bare_jid, presence) {
...@@ -570,24 +565,26 @@ converse.plugins.add('converse-roster', { ...@@ -570,24 +565,26 @@ converse.plugins.add('converse-roster', {
return _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version'); return _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version');
}, },
fetchFromServer () { async fetchFromServer () {
/* Fetch the roster from the XMPP server */ /* Fetch the roster from the XMPP server */
return new Promise((resolve, reject) => { const stanza = $iq({
const iq = $iq({ 'type': 'get',
'type': 'get', 'id': _converse.connection.getUniqueId('roster')
'id': _converse.connection.getUniqueId('roster') }).c('query', {xmlns: Strophe.NS.ROSTER});
}).c('query', {xmlns: Strophe.NS.ROSTER}); if (this.rosterVersioningSupported()) {
if (this.rosterVersioningSupported()) { stanza.attrs({'ver': this.data.get('version')});
iq.attrs({'ver': this.data.get('version')}); }
} let iq;
const callback = _.flow(this.onReceivedFromServer.bind(this), resolve); try {
const errback = function (iq) { iq = await _converse.api.sendIQ(stanza);
const errmsg = "Error while trying to fetch roster from the server"; } catch (e) {
_converse.log(errmsg, Strophe.LogLevel.ERROR); _converse.log(e, Strophe.LogLevel.ERROR);
reject(new Error(errmsg)); return _converse.log(
} "Error while trying to fetch roster from the server",
return _converse.connection.sendIQ(iq, callback, errback); Strophe.LogLevel.ERROR
}); );
}
return this.onReceivedFromServer(iq);
}, },
onReceivedFromServer (iq) { onReceivedFromServer (iq) {
...@@ -646,7 +643,7 @@ converse.plugins.add('converse-roster', { ...@@ -646,7 +643,7 @@ converse.plugins.add('converse-roster', {
createRequestingContact (presence) { createRequestingContact (presence) {
const bare_jid = Strophe.getBareJidFromJid(presence.getAttribute('from')), const bare_jid = Strophe.getBareJidFromJid(presence.getAttribute('from')),
nickname = _.get(sizzle(`nick[xmlns="${Strophe.NS.NICK}"]`, presence).pop(), 'textContent', null); nickname = _.get(sizzle(`nick[xmlns="${Strophe.NS.NICK}"]`, presence).pop(), 'textContent', null);
const user_data = { const user_data = {
'jid': bare_jid, 'jid': bare_jid,
'subscription': 'none', 'subscription': 'none',
......
...@@ -56,7 +56,7 @@ converse.plugins.add('converse-vcard', { ...@@ -56,7 +56,7 @@ converse.plugins.add('converse-vcard', {
}); });
function onVCardData (jid, iq, callback) { async function onVCardData (jid, iq) {
const vcard = iq.querySelector('vCard'); const vcard = iq.querySelector('vCard');
let result = {}; let result = {};
if (!_.isNull(vcard)) { if (!_.isNull(vcard)) {
...@@ -75,24 +75,10 @@ converse.plugins.add('converse-vcard', { ...@@ -75,24 +75,10 @@ converse.plugins.add('converse-vcard', {
} }
if (result.image) { if (result.image) {
const buffer = u.base64ToArrayBuffer(result['image']); const buffer = u.base64ToArrayBuffer(result['image']);
crypto.subtle.digest('SHA-1', buffer) const ab = await crypto.subtle.digest('SHA-1', buffer);
.then(ab => { result['image_hash'] = u.arrayBufferToHex(ab);
result['image_hash'] = u.arrayBufferToHex(ab);
if (callback) callback(result);
});
} else {
if (callback) callback(result);
}
}
function onVCardError (jid, iq, errback) {
if (errback) {
errback({
'stanza': iq,
'jid': jid,
'vcard_error': moment().format()
});
} }
return result;
} }
function createStanza (type, jid, vcard_el) { function createStanza (type, jid, vcard_el) {
...@@ -113,7 +99,7 @@ converse.plugins.add('converse-vcard', { ...@@ -113,7 +99,7 @@ converse.plugins.add('converse-vcard', {
return _converse.api.sendIQ(createStanza("set", jid, vcard_el)); return _converse.api.sendIQ(createStanza("set", jid, vcard_el));
} }
function getVCard (_converse, jid) { async function getVCard (_converse, jid) {
/* Request the VCard of another user. Returns a promise. /* Request the VCard of another user. Returns a promise.
* *
* Parameters: * Parameters:
...@@ -121,14 +107,17 @@ converse.plugins.add('converse-vcard', { ...@@ -121,14 +107,17 @@ converse.plugins.add('converse-vcard', {
* is being requested. * is being requested.
*/ */
const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid; const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid;
return new Promise((resolve, reject) => { let iq;
_converse.connection.sendIQ( try {
createStanza("get", to), iq = await _converse.api.sendIQ(createStanza("get", to))
_.partial(onVCardData, jid, _, resolve), } catch (iq) {
_.partial(onVCardError, jid, _, resolve), return {
_converse.IQ_TIMEOUT 'stanza': iq,
); 'jid': jid,
}); 'vcard_error': moment().format()
}
}
return onVCardData(jid, iq);
} }
/* Event handlers */ /* Event handlers */
......
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