Pārlūkot izejas kodu

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

JC Brand 4 gadi atpakaļ
vecāks
revīzija
5350cb107f
6 mainītis faili ar 78 papildinājumiem un 59 dzēšanām
  1. 5 2
      spec/bookmarks.js
  2. 19 19
      spec/mam.js
  3. 12 13
      spec/muc.js
  4. 5 3
      spec/protocol.js
  5. 3 3
      spec/roster.js
  6. 34 19
      src/headless/converse-core.js

+ 5 - 2
spec/bookmarks.js

@@ -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">`+

+ 19 - 19
spec/mam.js

@@ -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">`+

+ 12 - 13
spec/muc.js

@@ -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">`+

+ 5 - 3
spec/protocol.js

@@ -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"/>`+

+ 3 - 3
spec/roster.js

@@ -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>`);

+ 34 - 19
src/headless/converse-core.js

@@ -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;