Don't throw timeout errors for sent IQ#result or IQ#error stanzas

This commit is contained in:
JC Brand 2020-09-25 13:17:56 +02:00
parent e82d6785c2
commit 5350cb107f
6 changed files with 78 additions and 59 deletions

View File

@ -1,5 +1,8 @@
/* global mock, converse */
const Strophe = converse.env.Strophe;
describe("A chat room", function () {
it("can be bookmarked", mock.initConverse(['rosterGroupsFetched'], {}, async function (done, _converse) {
@ -19,7 +22,7 @@ describe("A chat room", function () {
spyOn(_converse.connection, 'getUniqueId').and.callThrough();
await mock.openChatRoom(_converse, 'theplay', 'conference.shakespeare.lit', 'JC');
var jid = 'theplay@conference.shakespeare.lit';
const jid = 'theplay@conference.shakespeare.lit';
const view = _converse.chatboxviews.get(jid);
spyOn(view, 'renderBookmarkForm').and.callThrough();
spyOn(view, 'closeForm').and.callThrough();
@ -78,7 +81,7 @@ describe("A chat room", function () {
view.el.querySelector('.btn-primary').click();
await u.waitUntil(() => sent_stanza);
expect(sent_stanza.toLocaleString()).toBe(
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq from="romeo@montague.lit/orchard" id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
`<publish node="storage:bookmarks">`+

View File

@ -418,8 +418,8 @@ describe("Message Archive Management", function () {
});
_converse.api.archive.query();
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client"><query queryid="${queryid}" xmlns="urn:xmpp:mam:2"/></iq>`);
done();
}));
@ -436,8 +436,8 @@ describe("Message Archive Management", function () {
});
_converse.api.archive.query({'with':'juliet@capulet.lit'});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -564,8 +564,8 @@ describe("Message Archive Management", function () {
'end': end
});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -613,8 +613,8 @@ describe("Message Archive Management", function () {
const start = '2010-06-07T00:00:00Z';
_converse.api.archive.query({'start': start});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -644,8 +644,8 @@ describe("Message Archive Management", function () {
const start = '2010-06-07T00:00:00Z';
_converse.api.archive.query({'start': start, 'max':10});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -682,8 +682,8 @@ describe("Message Archive Management", function () {
'max':10
});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -715,8 +715,8 @@ describe("Message Archive Management", function () {
});
_converse.api.archive.query({'before': '', 'max':10});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -752,8 +752,8 @@ describe("Message Archive Management", function () {
rsm.start = '2010-06-07T00:00:00Z';
_converse.api.archive.query(rsm);
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+
@ -787,7 +787,7 @@ describe("Message Archive Management", function () {
});
const promise = _converse.api.archive.query({'with': 'romeo@capulet.lit', 'max':'10'});
await u.waitUntil(() => sent_stanza);
const queryid = sent_stanza.nodeTree.querySelector('query').getAttribute('queryid');
const queryid = sent_stanza.querySelector('query').getAttribute('queryid');
/* <message id='aeb213' to='juliet@capulet.lit/chamber'>
* <result xmlns='urn:xmpp:mam:2' queryid='f27' id='28482-98726-73623'>
@ -953,9 +953,9 @@ describe("Chatboxes", function () {
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
await u.waitUntil(() => sent_stanza);
const stanza_el = sent_stanza.root().nodeTree;
const stanza_el = sent_stanza;
const queryid = stanza_el.querySelector('query').getAttribute('queryid');
expect(sent_stanza.toString()).toBe(
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${stanza_el.getAttribute('id')}" type="set" xmlns="jabber:client">`+
`<query queryid="${queryid}" xmlns="urn:xmpp:mam:2">`+
`<x type="submit" xmlns="jabber:x:data">`+

View File

@ -1496,7 +1496,7 @@ describe("Groupchats", function () {
* <query xmlns='http://jabber.org/protocol/muc#owner'/>
* </iq>
*/
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="`+IQ_id+`" to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#owner"/>`+
`</iq>`);
@ -1645,11 +1645,10 @@ describe("Groupchats", function () {
view.el.querySelector('.chatroom-form input[type="submit"]').click();
const sent_stanza = sent_IQ.nodeTree;
expect(sent_stanza.querySelector('field[var="muc#roomconfig_membersonly"] value').textContent.trim()).toBe('1');
expect(sent_stanza.querySelector('field[var="muc#roomconfig_moderatedroom"] value').textContent.trim()).toBe('1');
expect(sent_stanza.querySelector('field[var="muc#roomconfig_allowpm"] value').textContent.trim()).toBe('moderators');
expect(sent_stanza.querySelector('field[var="muc#roomconfig_presencebroadcast"] value').textContent.trim()).toBe('moderator');
expect(sent_IQ.querySelector('field[var="muc#roomconfig_membersonly"] value').textContent.trim()).toBe('1');
expect(sent_IQ.querySelector('field[var="muc#roomconfig_moderatedroom"] value').textContent.trim()).toBe('1');
expect(sent_IQ.querySelector('field[var="muc#roomconfig_allowpm"] value').textContent.trim()).toBe('moderators');
expect(sent_IQ.querySelector('field[var="muc#roomconfig_presencebroadcast"] value').textContent.trim()).toBe('moderator');
done();
}));
@ -3433,7 +3432,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(3);
expect(view.model.setAffiliation).toHaveBeenCalled();
// Check that the member list now gets updated
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item affiliation="owner" jid="annoyingguy@montague.lit">`+
@ -3513,7 +3512,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.model.setAffiliation).toHaveBeenCalled();
// Check that the member list now gets updated
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item affiliation="outcast" jid="annoyingguy@montague.lit">`+
@ -3612,7 +3611,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item nick="annoying guy" role="none">`+
@ -3717,7 +3716,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item nick="trustworthyguy" role="moderator">`+
@ -3761,7 +3760,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(3);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item nick="trustworthyguy" role="participant">`+
@ -3857,7 +3856,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(2);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item nick="annoyingGuy" role="visitor">`+
@ -3898,7 +3897,7 @@ describe("Groupchats", function () {
expect(view.validateRoleOrAffiliationChangeArgs.calls.count()).toBe(3);
expect(view.model.setRole).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq id="${IQ_id}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item nick="annoyingGuy" role="participant">`+

View File

@ -1,7 +1,9 @@
/*global mock */
/*global mock, converse */
// See: https://xmpp.org/rfcs/rfc3921.html
const Strophe = converse.env.Strophe;
describe("The Protocol", function () {
describe("Integration of Roster Items and Presence Subscriptions", function () {
@ -103,7 +105,7 @@ describe("The Protocol", function () {
*/
await mock.waitForRoster(_converse, 'all', 0);
expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq id="${IQ_id}" type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster">`+
`<item jid="contact@example.org"/>`+
@ -430,7 +432,7 @@ describe("The Protocol", function () {
/* _converse.js will then also automatically remove the
* contact from the user's roster.
*/
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster">`+
`<item jid="contact@example.org" subscription="remove"/>`+

View File

@ -1,4 +1,4 @@
/*global mock */
/*global mock, converse */
const $iq = converse.env.$iq;
const $pres = converse.env.$pres;
@ -651,7 +651,7 @@ describe("The Contacts Roster", function () {
await u.waitUntil(() => (sizzle(".pending-contact-name:contains('"+name+"')", _converse.rosterview.el).length === 0), 1000);
expect(window.confirm).toHaveBeenCalled();
expect(contact.removeFromRoster).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster">`+
`<item jid="lord.capulet@montague.lit" subscription="remove"/>`+
@ -829,7 +829,7 @@ describe("The Contacts Roster", function () {
});
sizzle(`.remove-xmpp-contact[title="Click to remove ${name} as a contact"]`, _converse.rosterview.el).pop().click();
expect(window.confirm).toHaveBeenCalled();
expect(sent_IQ.toLocaleString()).toBe(
expect(Strophe.serialize(sent_IQ)).toBe(
`<iq type="set" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster"><item jid="mercutio@montague.lit" subscription="remove"/></query>`+
`</iq>`);

View File

@ -209,6 +209,10 @@ export const _converse = {
OPENED: 'opened',
PREBIND: 'prebind',
/**
* @constant
* @type { integer }
*/
STANZA_TIMEOUT: 10000,
SUCCESS: 'success',
@ -463,10 +467,10 @@ export const api = _converse.api = {
* A hook is a special kind of event which allows you to intercept a data
* structure in order to modify it, before passing it back.
* @async
* @method _converse.api.hook
* @param {string} name - The hook name
* @param {...any} context - The context to which the hook applies (could be for example, a {@link _converse.ChatBox)).
* @param {...any} data - The data structure to be intercepted and modified by the hook listeners.
* @returns {Promise<any>} - A promise that resolves with the modified data structure.
*/
hook (name, context, data) {
const events = _converse._events[name] || [];
@ -516,6 +520,7 @@ export const api = _converse.api = {
* initialized. It's used together with the `auto_login` configuration flag
* to determine whether Converse should try to log the user in if it
* fails to restore a previous auth'd session.
* @returns {void}
*/
async login (jid, password, automatic=false) {
jid = jid || _converse.jid;
@ -879,6 +884,8 @@ export const api = _converse.api = {
/**
* Allows you to send XML stanzas.
* @method _converse.api.send
* @param {XMLElement} stanza
* @return {void}
* @example
* const msg = converse.env.$msg({
* 'from': 'juliet@example.com/balcony',
@ -905,29 +912,37 @@ export const api = _converse.api = {
},
/**
* Send an IQ stanza and receive a promise
* Send an IQ stanza
* @method _converse.api.sendIQ
* @param { XMLElement } stanza
* @param { Integer } timeout
* @param { Boolean } reject - Whether an error IQ should cause the promise
* @param {XMLElement} stanza
* @param {Integer} [timeout=_converse.STANZA_TIMEOUT]
* @param {Boolean} [reject=true] - Whether an error IQ should cause the promise
* to be rejected. If `false`, the promise will resolve instead of being rejected.
* @returns {Promise} A promise which resolves when we receive a `result` stanza
* or is rejected when we receive an `error` stanza.
* @returns {Promise} A promise which resolves (or potentially rejected) once we
* receive a `result` or `error` stanza or once a timeout is reached.
* If the IQ stanza being sent is of type `result` or `error`, there's
* nothing to wait for, so an already resolved promise is returned.
*/
sendIQ (stanza, timeout, reject=true) {
timeout = timeout || _converse.STANZA_TIMEOUT;
sendIQ (stanza, timeout=_converse.STANZA_TIMEOUT, reject=true) {
let promise;
if (reject) {
promise = new Promise((resolve, reject) => _converse.connection.sendIQ(stanza, resolve, reject, timeout));
promise.catch(e => {
if (e === null) {
throw new TimeoutError(
`Timeout error after ${timeout}ms for the following IQ stanza: ${Strophe.serialize(stanza)}`
);
}
});
stanza = stanza?.nodeTree ?? stanza;
if (['get', 'set'].includes(stanza.getAttribute('type'))) {
timeout = timeout || _converse.STANZA_TIMEOUT;
if (reject) {
promise = new Promise((resolve, reject) => _converse.connection.sendIQ(stanza, resolve, reject, timeout));
promise.catch(e => {
if (e === null) {
throw new TimeoutError(
`Timeout error after ${timeout}ms for the following IQ stanza: ${Strophe.serialize(stanza)}`
);
}
});
} else {
promise = new Promise(resolve => _converse.connection.sendIQ(stanza, resolve, resolve, timeout));
}
} else {
promise = new Promise(resolve => _converse.connection.sendIQ(stanza, resolve, resolve, timeout));
_converse.connection.sendIQ(stanza);
promise = new Promise.resolve();
}
api.trigger('send', stanza);
return promise;