Bladeren bron

Various test fixes. updates #1105

Wait appropriately when testing

* views and chats are now asynchronously returned, so we need to use `await`
* Wait for the storage transaction to complete when creating and updating messages
* Wait for all chatboxes to close
    Otherwise we get sessionStorage inconsistencies due to the async nature of localforage.

In the process, remove the `closeAllChatBoxes` override in
converse-controlbox by letting the `close` method decide whether it
should be closed or not.
JC Brand 6 jaren geleden
bovenliggende
commit
76e95e68f6

+ 24 - 24
dist/converse.js

@@ -49020,10 +49020,9 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_3__["default"].plugins
         /* This method gets overridden in src/converse-controlbox.js if
          * the controlbox plugin is active.
          */
-        this.each(function (view) {
-          view.close();
-        });
-        return this;
+        return Promise.all(this.map(view => view.close({
+          'name': 'closeAllChatBoxes'
+        })));
       },
 
       chatBoxMayBeShown(chatbox) {
@@ -50322,8 +50321,6 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
         this.remove();
 
         _converse.emit('chatBoxClosed', this);
-
-        return this;
       },
 
       renderEmojiPicker() {
@@ -50688,18 +50685,6 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
 
     },
     ChatBoxViews: {
-      closeAllChatBoxes() {
-        const _converse = this.__super__._converse;
-        this.each(function (view) {
-          if (view.model.get('id') === 'controlbox' && (_converse.disconnection_cause !== _converse.LOGOUT || _converse.show_controlbox_by_default)) {
-            return;
-          }
-
-          view.close();
-        });
-        return this;
-      },
-
       getChatBoxWidth(view) {
         const _converse = this.__super__._converse;
         const controlbox = this.get('controlbox');
@@ -50926,18 +50911,27 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_5__["default"].plugins
         this.el.querySelector('.controlbox-panes').insertAdjacentElement('afterBegin', this.controlbox_pane.el);
       },
 
-      close(ev) {
+      async close(ev) {
         if (ev && ev.preventDefault) {
           ev.preventDefault();
         }
 
+        if (ev.name === 'closeAllChatBoxes' && (_converse.disconnection_cause !== _converse.LOGOUT || _converse.show_controlbox_by_default)) {
+          return;
+        }
+
         if (_converse.sticky_controlbox) {
           return;
         }
 
         if (_converse.connection.connected && !_converse.connection.disconnecting) {
-          this.model.save({
-            'closed': true
+          await new Promise((resolve, reject) => {
+            return this.model.save({
+              'closed': true
+            }, {
+              'success': resolve,
+              'error': reject
+            });
           });
         } else {
           this.model.trigger('hide');
@@ -61671,6 +61665,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
             'references': this.getReferencesFromStanza(stanza),
             'older_versions': older_versions,
             'edited': moment().format()
+          }, {
+            'wait': true
           });
           return true;
         }
@@ -62146,7 +62142,11 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
           // TODO: handle <subject> messages (currently being done by ChatRoom)
           return;
         } else {
-          return this.messages.create(attrs);
+          return new Promise((success, error) => this.messages.create(attrs, {
+            success,
+            error,
+            'wait': true
+          }));
         }
       },
 
@@ -62285,8 +62285,8 @@ _converse_core__WEBPACK_IMPORTED_MODULE_2__["default"].plugins.add('converse-cha
           _converse.log(message, Strophe.LogLevel.ERROR);
         }
 
-        const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
-        chatbox.messages.create(attrs);
+        await chatbox.createMessage(message, message);
+        return true;
       },
 
       getMessageBody(stanza) {

+ 8 - 5
spec/bookmarks.js

@@ -166,6 +166,7 @@
                 'name':  'The Play',
                 'nick': ' Othello'
             });
+            await test_utils.waitUntil(() => _converse.api.rooms.get().length);
             expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
             done();
         }));
@@ -216,11 +217,13 @@
                 spyOn(_converse.bookmarks, 'sendBookmarkStanza').and.callThrough();
                 view.delegateEvents();
 
-                _converse.bookmarks.create({
-                    'jid': view.model.get('jid'),
-                    'autojoin': false,
-                    'name':  'The Play',
-                    'nick': ' Othello'
+                await new Promise((success, error) => {
+                    _converse.bookmarks.create({
+                        'jid': view.model.get('jid'),
+                        'autojoin': false,
+                        'name':  'The Play',
+                        'nick': ' Othello'
+                    }, {success, error});
                 });
                 expect(_converse.bookmarks.length).toBe(1);
                 expect(view.model.get('bookmarked')).toBeTruthy();

+ 17 - 17
spec/controlbox.js

@@ -21,7 +21,7 @@
             expect($("div#controlbox").is(':visible')).toBe(false);
             spyOn(_converse.controlboxtoggle, 'onClick').and.callThrough();
             spyOn(_converse.controlboxtoggle, 'showControlBox').and.callThrough();
-            spyOn(_converse, 'emit');
+            spyOn(_converse, 'emit').and.callThrough();
             // Redelegate so that the spies are now registered as the event handlers (specifically for 'onClick')
             _converse.controlboxtoggle.delegateEvents();
             document.querySelector('.toggle-controlbox').click();
@@ -39,9 +39,9 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                spyOn(_converse, 'emit');
+                spyOn(_converse, 'emit').and.callThrough();
                 spyOn(_converse.rosterview, 'update').and.callThrough();
-                test_utils.openControlBox();
+                await test_utils.openControlBox(_converse);
                 // Adding two contacts one with Capital initials and one with small initials of same JID (Case sensitive check)
                 _converse.roster.create({
                     jid: mock.pend_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
@@ -67,14 +67,15 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'all').openControlBox();
+                await test_utils.createContacts(_converse, 'all')
+                await test_utils.openControlBox(_converse);
                 _converse.emit('rosterContactsFetched');
 
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 await test_utils.openChatBoxFor(_converse, sender_jid);
                 await test_utils.waitUntil(() => _converse.chatboxes.length);
-                const chatview = _converse.chatboxviews.get(sender_jid);
-                chatview.model.set({'minimized': true});
+                const view = _converse.api.chatviews.get(sender_jid);
+                view.model.set({'minimized': true});
 
                 expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                 expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
@@ -88,7 +89,7 @@
                     .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                 _converse.chatboxes.onMessage(msg);
                 await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll(".msgs-indicator").length);
-                spyOn(chatview.model, 'incrementUnreadMsgCounter').and.callThrough();
+                spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough();
                 expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
                 expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
 
@@ -100,10 +101,10 @@
                     }).c('body').t('hello again').up()
                     .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                 _converse.chatboxes.onMessage(msg);
-                await test_utils.waitUntil(() => chatview.model.incrementUnreadMsgCounter.calls.count());
+                await test_utils.waitUntil(() => view.model.incrementUnreadMsgCounter.calls.count());
                 expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
                 expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
-                chatview.model.set({'minimized': false});
+                view.model.set({'minimized': false});
                 expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                 expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
                 done();
@@ -117,7 +118,7 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                test_utils.openControlBox();
+                test_utils.openControlBox(_converse);
                 var view = _converse.xmppstatusview;
                 expect($(view.el).find('.xmpp-status span:first-child').hasClass('online')).toBe(true);
                 expect(view.el.querySelector('.xmpp-status span.online').textContent.trim()).toBe('I am online');
@@ -129,15 +130,14 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.openControlBox();
-
+                await test_utils.openControlBox(_converse);
                 var cbview = _converse.chatboxviews.get('controlbox');
                 cbview.el.querySelector('.change-status').click()
                 var modal = _converse.xmppstatusview.status_modal;
 
                 await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
                 const view = _converse.xmppstatusview;
-                spyOn(_converse, 'emit');
+                spyOn(_converse, 'emit').and.callThrough();
                 modal.el.querySelector('label[for="radio-busy"]').click(); // Change status to "dnd"
                 modal.el.querySelector('[type="submit"]').click();
 
@@ -153,15 +153,14 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.openControlBox();
-
+                await test_utils.openControlBox(_converse);
                 const cbview = _converse.chatboxviews.get('controlbox');
                 cbview.el.querySelector('.change-status').click()
                 const modal = _converse.xmppstatusview.status_modal;
 
                 await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
                 const view = _converse.xmppstatusview;
-                spyOn(_converse, 'emit');
+                spyOn(_converse, 'emit').and.callThrough();
 
                 const msg = 'I am happy';
                 modal.el.querySelector('input[name="status_message"]').value = msg;
@@ -182,7 +181,8 @@
                 null, ['rosterGroupsFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'all').openControlBox();
+            await test_utils.createContacts(_converse, 'all')
+            await test_utils.openControlBox(_converse);
 
             const panel = _converse.chatboxviews.get('controlbox').contactspanel;
             const cbview = _converse.chatboxviews.get('controlbox');

+ 8 - 8
spec/converse.js

@@ -286,32 +286,32 @@
                 null, ['rosterInitialized', 'chatBoxesInitialized'], {},
                 async (done, _converse) => {
 
-                test_utils.openControlBox();
+                test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current', 2);
                 _converse.emit('rosterContactsFetched');
 
                 // Test on chat that doesn't exist.
-                expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
+                let box = _converse.api.chats.get('non-existing@jabber.org')
+                expect(box).toBeFalsy();
                 const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
 
                 // Test on chat that's not open
-                let box = _converse.api.chats.get(jid);
+                box = _converse.api.chats.get(jid);
                 expect(typeof box === 'undefined').toBeTruthy();
                 expect(_converse.chatboxes.length).toBe(1);
 
                 // Test for one JID
                 test_utils.openChatBoxFor(_converse, jid);
-                await test_utils.waitUntil(() => _converse.chatboxes.length == 1);
+                await test_utils.waitUntil(() => _converse.chatboxes.length > 1);
                 box = _converse.api.chats.get(jid);
                 expect(box instanceof Object).toBeTruthy();
                 expect(box.get('box_id')).toBe(b64_sha1(jid));
 
-                const chatboxview = _converse.chatboxviews.get(jid);
+                const chatboxview = _converse.api.chatviews.get(jid);
                 expect(u.isVisible(chatboxview.el)).toBeTruthy();
                 // Test for multiple JIDs
-                test_utils.openChatBoxFor(_converse, jid2);
-                await test_utils.waitUntil(() => _converse.chatboxes.length == 2);
+                await test_utils.openChatBoxFor(_converse, jid2);
                 const list = _converse.api.chats.get([jid, jid2]);
                 expect(_.isArray(list)).toBeTruthy();
                 expect(list[0].get('box_id')).toBe(b64_sha1(jid));
@@ -323,7 +323,7 @@
                 null, ['rosterGroupsFetched', 'chatBoxesInitialized'], {},
                 async (done, _converse) => {
 
-                test_utils.openControlBox();
+                test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current', 2);
                 _converse.emit('rosterContactsFetched');
 

+ 17 - 11
spec/headline.js

@@ -1,19 +1,22 @@
 (function (root, factory) {
     define([
-        "jasmine",
         "jquery",
         "mock",
         "test-utils"
         ], factory);
-} (this, function (jasmine, $, mock, test_utils) {
+} (this, function (mock, test_utils) {
     "use strict";
-    var $msg = converse.env.$msg,
-        _ = converse.env._,
-        utils = converse.env.utils;
+    const $msg = converse.env.$msg,
+          _ = converse.env._,
+          utils = converse.env.utils;
 
     describe("A headlines box", function () {
 
-        it("will not open nor display non-headline messages", mock.initConverse((done, _converse) => {
+        it("will not open nor display non-headline messages",
+            mock.initConverse(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                async function (done, _converse) {
+        
             /* XMPP spam message:
              *
              *  <message xmlns="jabber:client"
@@ -25,7 +28,7 @@
              *  </message
              */
             sinon.spy(utils, 'isHeadlineMessage');
-            var stanza = $msg({
+            const stanza = $msg({
                     'xmlns': 'jabber:client',
                     'to': 'dummy@localhost',
                     'type': 'chat',
@@ -34,6 +37,7 @@
                 .c('nick', {'xmlns': "http://jabber.org/protocol/nick"}).t("-wwdmz").up()
                 .c('body').t('SORRY FOR THIS ADVERT');
             _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => _converse.api.chats.get().length);
             expect(utils.isHeadlineMessage.called).toBeTruthy();
             expect(utils.isHeadlineMessage.returned(false)).toBeTruthy();
             utils.isHeadlineMessage.restore();
@@ -57,7 +61,7 @@
              *  </message>
              */
             sinon.spy(utils, 'isHeadlineMessage');
-            var stanza = $msg({
+            const stanza = $msg({
                     'type': 'headline',
                     'from': 'notify.example.com',
                     'to': 'dummy@localhost',
@@ -78,18 +82,20 @@
             expect(utils.isHeadlineMessage.returned(true)).toBeTruthy();
             utils.isHeadlineMessage.restore(); // unwraps
             // Headlines boxes don't show an avatar
-            var view = _converse.chatboxviews.get('notify.example.com');
+            const view = _converse.chatboxviews.get('notify.example.com');
             expect(view.model.get('show_avatar')).toBeFalsy();
             expect(view.el.querySelector('img.avatar')).toBe(null);
             done();
         }));
 
         it("will not show a headline messages from a full JID if allow_non_roster_messaging is false",
-            mock.initConverse((done, _converse) => {
+            mock.initConverse(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                function (done, _converse) {
 
             _converse.allow_non_roster_messaging = false;
             sinon.spy(utils, 'isHeadlineMessage');
-            var stanza = $msg({
+            const stanza = $msg({
                     'type': 'headline',
                     'from': 'andre5114@jabber.snc.ru/Spark',
                     'to': 'dummy@localhost',

+ 84 - 81
spec/messages.js

@@ -18,12 +18,12 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current', 1);
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid);
-            const view = await _converse.api.chatviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             const textarea = view.el.querySelector('textarea.chat-textarea');
 
             textarea.value = 'But soft, what light through yonder airlock breaks?';
@@ -122,12 +122,12 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current', 1);
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid)
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             const textarea = view.el.querySelector('textarea.chat-textarea');
             expect(textarea.value).toBe('');
             view.keyPressed({
@@ -279,7 +279,7 @@
                 null, ['rosterGroupsFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
 
             let message, msg;
@@ -466,7 +466,7 @@
             null, ['rosterGroupsFetched'], {},
             async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
 
             /* Ideally we wouldn't have to filter out headline
@@ -502,7 +502,7 @@
                 null, ['rosterGroupsFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current', 2);
             test_utils.openControlBox();
 
             // Send a message from a different resource
@@ -526,8 +526,8 @@
 
             await _converse.chatboxes.onMessage(msg);
             await test_utils.waitUntil(() => (_converse.api.chats.get().length > 1))
-            const chatbox = _converse.chatboxes.get(sender_jid);
-            const view = _converse.chatboxviews.get(sender_jid);
+            const chatbox = _converse.api.chats.get(sender_jid);
+            const view = _converse.api.chatviews.get(sender_jid);
                 
             expect(chatbox).toBeDefined();
             expect(view).toBeDefined();
@@ -543,7 +543,6 @@
             await new Promise((resolve, reject) => view.once('messageInserted', resolve));
             expect(chat_content.querySelector('.chat-msg .chat-msg__text').textContent).toEqual(msgtext);
             expect(chat_content.querySelector('.chat-msg__time').textContent.match(/^[0-9][0-9]:[0-9][0-9]/)).toBeTruthy();
-            await test_utils.waitUntil(() => chatbox.vcard.get('fullname') === 'Candice van der Knijff')
             expect(chat_content.querySelector('span.chat-msg__author').textContent.trim()).toBe('Candice van der Knijff');
             done();
         }));
@@ -554,7 +553,7 @@
                 async function (done, _converse) {
 
             await test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp']);
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
 
@@ -579,8 +578,9 @@
             await _converse.chatboxes.onMessage(msg);
             await test_utils.waitUntil(() => _converse.api.chats.get().length);
             // Check that the chatbox and its view now exist
-            const chatbox = _converse.chatboxes.get(recipient_jid);
-            const view = _converse.chatboxviews.get(recipient_jid);
+            const chatbox = _converse.api.chats.get(recipient_jid);
+            const view = _converse.api.chatviews.get(recipient_jid);
+            await new Promise((resolve, reject) => view.once('messageInserted', resolve));
             expect(chatbox).toBeDefined();
             expect(view).toBeDefined();
             await new Promise(resolve => view.once('messageInserted', resolve));
@@ -602,7 +602,7 @@
             null, ['rosterGroupsFetched'], {},
             async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             /* <message from="mallory@evil.example" to="b@xmpp.example">
              *    <received xmlns='urn:xmpp:carbons:2'>
@@ -635,11 +635,11 @@
             await _converse.chatboxes.onMessage(msg);
 
             // Check that chatbox for impersonated user is not created.
-            let chatbox = _converse.chatboxes.get(impersonated_jid);
+            let chatbox = _converse.api.chats.get(impersonated_jid);
             expect(chatbox).not.toBeDefined();
 
             // Check that the chatbox for the malicous user is not created
-            chatbox = _converse.chatboxes.get(sender_jid);
+            chatbox = _converse.api.chats.get(sender_jid);
             expect(chatbox).not.toBeDefined();
             done();
         }));
@@ -652,7 +652,7 @@
             if (_converse.view_mode === 'fullscreen') {
                 return done();
             }
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             const contact_name = mock.cur_names[0];
             const contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -712,10 +712,10 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
-            spyOn(_converse, 'emit');
+            spyOn(_converse, 'emit').and.callThrough();
             const contact_name = mock.cur_names[1];
             const contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost';
 
@@ -724,8 +724,8 @@
             test_utils.clearChatBoxMessages(_converse, contact_jid);
             const one_day_ago = moment();
             one_day_ago.subtract('days', 1);
-            const chatbox = _converse.chatboxes.get(contact_jid);
-            const view = _converse.chatboxviews.get(contact_jid);
+            const chatbox = _converse.api.chats.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
 
             let message = 'This is a day old message';
             let msg = $msg({
@@ -806,14 +806,14 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
-            spyOn(_converse, 'emit');
+            spyOn(_converse, 'emit').and.callThrough();
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid)
             expect(_converse.emit).toHaveBeenCalledWith('chatBoxFocused', jasmine.any(Object));
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             const message = 'This message is sent from this chatbox';
             spyOn(view.model, 'sendMessage').and.callThrough();
             await test_utils.sendMessage(view, message);
@@ -829,12 +829,12 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid)
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             const message = '<p>This message contains <em>some</em> <b>markup</b></p>';
             spyOn(view.model, 'sendMessage').and.callThrough();
             await test_utils.sendMessage(view, message);
@@ -850,12 +850,12 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid)
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             const message = 'This message contains a hyperlink: www.opkode.com';
             spyOn(view.model, 'sendMessage').and.callThrough();
             test_utils.sendMessage(view, message);
@@ -873,13 +873,13 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
 
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid)
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
 
             let message = "http://www.opkode.com/'onmouseover='alert(1)'whatever";
             await test_utils.sendMessage(view, message);
@@ -917,7 +917,7 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             const view = await test_utils.openChatBoxFor(_converse, contact_jid);
@@ -957,13 +957,13 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current', 1);
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
-            let base_url = 'https://conversejs.org';
+            const base_url = 'https://conversejs.org';
             let message = base_url+"/logo/conversejs-filled.svg";
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid);
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             spyOn(view.model, 'sendMessage').and.callThrough();
             test_utils.sendMessage(view, message);
             await test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length, 1000)
@@ -993,7 +993,6 @@
             expect(msg[0].querySelectorAll('img').length).toEqual(2);
 
             // Non-https images aren't rendered
-            base_url = document.URL.split(window.location.pathname)[0];
             message = base_url+"/logo/conversejs-filled.svg";
             expect(view.el.querySelectorAll('img').length).toBe(4);
             test_utils.sendMessage(view, message);
@@ -1002,22 +1001,20 @@
         }));
 
         it("will render the message time as configured",
-                mock.initConverse(
-                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                    async function (done, _converse) {
+            mock.initConverse(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {'time_format': 'hh:mm'},
+                async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
 
-            _converse.time_format = 'hh:mm';
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid)
-            const view = await _converse.api.chatviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
             const message = 'This message is sent from this chatbox';
             await test_utils.sendMessage(view, message);
-            await new Promise((resolve, reject) => view.model.messages.once('rendered', resolve));
 
-            const chatbox = await _converse.api.chats.get(contact_jid);
+            const chatbox = _converse.api.chats.get(contact_jid);
             expect(chatbox.messages.models.length, 1);
             const msg_object = chatbox.messages.models[0];
 
@@ -1035,7 +1032,7 @@
                 null, ['rosterGroupsFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             test_utils.openControlBox();
 
@@ -1205,8 +1202,10 @@
             mock.initConverse(
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
-            test_utils.createContacts(_converse, 'current', 1);
+
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
+
             const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             const msg_id = u.getUniqueId();
             const sent_stanzas = [];
@@ -1294,7 +1293,7 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current', 1);
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             await test_utils.openChatBoxFor(_converse, contact_jid);
@@ -1354,7 +1353,7 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 test_utils.openControlBox();
                 await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 300);
@@ -1373,9 +1372,9 @@
                     .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
                 );
                 await test_utils.waitUntil(() => (_converse.api.chats.get().length === 2));
-                const chatbox = _converse.chatboxes.get(sender_jid);
+                const chatbox = _converse.api.chats.get(sender_jid);
                 expect(chatbox).toBeDefined();
-                const view = await _converse.api.chatviews.get(sender_jid);
+                const view = _converse.api.chatviews.get(sender_jid);
                 expect(view).toBeDefined();
 
                 expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
@@ -1400,7 +1399,7 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current', 1);
+                await test_utils.createContacts(_converse, 'current', 1);
                 _converse.emit('rosterContactsFetched');
                 test_utils.openControlBox();
                 const message = 'This is a received message';
@@ -1413,6 +1412,7 @@
                         'type': 'chat',
                         'id': msg_id,
                     }).c('body').t('But soft, what light through yonder airlock breaks?').tree());
+
                 await new Promise((resolve, reject) => view.once('messageInserted', resolve));
                 expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
                 expect(view.el.querySelector('.chat-msg__text').textContent)
@@ -1439,6 +1439,7 @@
                         'id': u.getUniqueId(),
                     }).c('body').t('But soft, what light through yonder window breaks?').up()
                     .c('replace', {'id': msg_id, 'xmlns': 'urn:xmpp:message-correct:0'}).tree());
+
                 await new Promise((resolve, reject) => view.model.messages.once('rendered', resolve));
 
                 expect(view.el.querySelector('.chat-msg__text').textContent)
@@ -1490,12 +1491,12 @@
                     expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
 
                     await _converse.chatboxes.onMessage(msg);
-                    await test_utils.waitUntil(() => _converse.api.chats.get().length);
+                    await test_utils.waitUntil(() => (_converse.api.chats.get().length > 1));
                     expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
 
                     // Check that the chatbox and its view now exist
-                    const chatbox = _converse.chatboxes.get(sender_jid);
-                    const view = _converse.chatboxviews.get(sender_jid);
+                    const chatbox = _converse.api.chats.get(sender_jid);
+                    const view = _converse.api.chatviews.get(sender_jid);
                     await new Promise((resolve, reject) => view.once('messageInserted', resolve));
 
                     expect(chatbox).toBeDefined();
@@ -1526,7 +1527,7 @@
                     _converse.allow_non_roster_messaging = false;
                     _converse.emit('rosterContactsFetched');
 
-                    spyOn(_converse, 'emit');
+                    spyOn(_converse, 'emit').and.callThrough();
                     const message = 'This is a received message from someone not on the roster';
                     const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                     const msg = $msg({
@@ -1540,11 +1541,11 @@
                     // We don't already have an open chatbox for this user
                     expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
 
-                    let chatbox = await _converse.api.chats.get(sender_jid);
+                    let chatbox = _converse.api.chats.get(sender_jid);
                     expect(chatbox).not.toBeDefined();
                     // onMessage is a handler for received XMPP messages
                     await _converse.chatboxes.onMessage(msg);
-                    expect(_converse.api.chats.get().length).toBe(1);
+                    await test_utils.waitUntil(() => (_converse.api.chats.get().length > 1))
                     let view = _converse.chatboxviews.get(sender_jid);
                     expect(view).not.toBeDefined();
 
@@ -1555,7 +1556,7 @@
                     await new Promise((resolve, reject) => view.once('messageInserted', resolve));
                     expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
                     // Check that the chatbox and its view now exist
-                    chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.api.chats.get(sender_jid);
                     expect(chatbox).toBeDefined();
                     expect(view).toBeDefined();
                     // Check that the message was received and check the message parameters
@@ -1584,7 +1585,7 @@
                         null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                         async function (done, _converse) {
 
-                    test_utils.createContacts(_converse, 'current');
+                    await test_utils.createContacts(_converse, 'current');
                     _converse.emit('rosterContactsFetched');
                     test_utils.openControlBox();
 
@@ -1608,8 +1609,9 @@
                     let fullname = _converse.xmppstatus.get('fullname');
                     fullname = _.isEmpty(fullname)? _converse.bare_jid: fullname;
                     await _converse.api.chats.open(sender_jid)
-                    var msg_text = 'This message will not be sent, due to an error';
-                    const view = await _converse.api.chatviews.get(sender_jid);
+                    let msg_text = 'This message will not be sent, due to an error';
+                    const view = _converse.api.chatviews.get(sender_jid);
+                    await view.model.messagesFetchedPromise;
                     let message = view.model.messages.create({
                         'msgid': '82bc02ce-9651-4336-baf0-fa04762ed8d2',
                         'fullname': fullname,
@@ -1719,6 +1721,7 @@
                         .c('text', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" })
                             .t('Something else went wrong as well');
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
+                    await test_utils.waitUntil(() => view.model.messages.length > 3);
                     await new Promise((resolve, reject) => view.once('messageInserted', resolve));
                     expect(chat_content.querySelectorAll('.chat-error').length).toEqual(3);
                     done();
@@ -1731,7 +1734,7 @@
 
                     // See #1317
                     // https://github.com/conversejs/converse.js/issues/1317
-                    test_utils.createContacts(_converse, 'current');
+                    await test_utils.createContacts(_converse, 'current');
                     _converse.emit('rosterContactsFetched');
                     test_utils.openControlBox();
 
@@ -1769,14 +1772,14 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 test_utils.openControlBox();
 
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const message = 'This message is received while the chat area is scrolled up';
                 await test_utils.openChatBoxFor(_converse, sender_jid)
-                const view = await _converse.api.chatviews.get(sender_jid);
+                const view = _converse.api.chatviews.get(sender_jid);
                 spyOn(view, 'onScrolledDown').and.callThrough();
                 // Create enough messages so that there's a scrollbar.
                 const promises = [];
@@ -1823,7 +1826,7 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
 
                 await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length)
@@ -1854,10 +1857,10 @@
                         type: 'chat',
                         id: '134234623462346'
                     }).c('body').t(message).up()
-                    .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
+                        .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                 await _converse.chatboxes.onMessage(msg);
-                await test_utils.waitUntil(() => _converse.chatboxviews.keys().length > 1, 1000);
-                const view = _converse.chatboxviews.get(sender_jid);
+                await test_utils.waitUntil(() => _converse.api.chats.get().length > 1);
+                const view = _converse.api.chatviews.get(sender_jid);
                 await new Promise((resolve, reject) => view.once('messageInserted', resolve));
                 await test_utils.waitUntil(() => view.model.messages.length);
                 expect(_converse.chatboxes.getChatBox).toHaveBeenCalled();
@@ -1876,11 +1879,11 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current', 1);
+                await test_utils.createContacts(_converse, 'current', 1);
                 _converse.emit('rosterContactsFetched');
                 const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 await test_utils.openChatBoxFor(_converse, contact_jid);
-                const view = await _converse.api.chatviews.get(contact_jid);
+                const view = _converse.api.chatviews.get(contact_jid);
                 spyOn(view.model, 'sendMessage').and.callThrough();
 
                 let stanza = u.toStanza(`
@@ -1927,11 +1930,11 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 await test_utils.openChatBoxFor(_converse, contact_jid)
-                const view = await _converse.api.chatviews.get(contact_jid);
+                const view = _converse.api.chatviews.get(contact_jid);
                 spyOn(view.model, 'sendMessage').and.callThrough();
 
                 let stanza = u.toStanza(`
@@ -1976,11 +1979,11 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current', 1);
+                await test_utils.createContacts(_converse, 'current', 1);
                 _converse.emit('rosterContactsFetched');
                 const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 await test_utils.openChatBoxFor(_converse, contact_jid);
-                const view = await _converse.api.chatviews.get(contact_jid);
+                const view = _converse.api.chatviews.get(contact_jid);
                 spyOn(view.model, 'sendMessage').and.callThrough();
                 const stanza = u.toStanza(`
                     <message from="${contact_jid}"
@@ -2007,7 +2010,7 @@
                     async function (done, _converse) {
 
                 const base_url = 'https://conversejs.org';
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 await test_utils.openChatBoxFor(_converse, contact_jid)
@@ -2265,7 +2268,7 @@
                 null, ['rosterGroupsFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
             const view = _converse.api.chatviews.get('lounge@localhost');
             const msg = $msg({
@@ -2287,7 +2290,7 @@
             await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
             const jid = 'lounge@localhost';
             const room = _converse.api.rooms.get(jid);
-            const view = await _converse.api.chatviews.get(jid);
+            const view = _converse.api.chatviews.get(jid);
             const stanza = $pres({
                     to: 'dummy@localhost/_converse.js-29092160',
                     from: 'coven@chat.shakespeare.lit/newguy'
@@ -2453,7 +2456,7 @@
                 null, ['rosterGroupsFetched'], {},
                 async function (done, _converse) {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
             const view = _converse.chatboxviews.get('lounge@localhost');
             const textarea = view.el.querySelector('textarea.chat-textarea');
@@ -2636,7 +2639,7 @@
                     async function (done, _converse) {
 
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom');
-                const view = await _converse.chatviews.get('lounge@localhost');
+                const view = _converse.api.chatviews.get('lounge@localhost');
                 ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
                     _converse.connection._dataRecv(test_utils.createRequest(
                         $pres({
@@ -2679,7 +2682,7 @@
                     async function (done, _converse) {
 
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom');
-                const view = await _converse.api.chatviews.get('lounge@localhost');
+                const view = _converse.api.chatviews.get('lounge@localhost');
                 ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh', 'Link Mauve'].forEach((nick) => {
                     _converse.connection._dataRecv(test_utils.createRequest(
                         $pres({
@@ -2740,7 +2743,7 @@
                     async function (done, _converse) {
 
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom');
-                const view = await _converse.api.chatviews.get('lounge@localhost');
+                const view = _converse.api.chatviews.get('lounge@localhost');
                 ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
                     _converse.connection._dataRecv(test_utils.createRequest(
                         $pres({
@@ -2814,7 +2817,7 @@
                         async function (done, _converse) {
 
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom');
-                const view = await _converse.api.chatviews.get('lounge@localhost');
+                const view = _converse.api.chatviews.get('lounge@localhost');
                 ['z3r0', 'mr.robot', 'gibson', 'sw0rdf1sh'].forEach((nick) => {
                     _converse.connection._dataRecv(test_utils.createRequest(
                         $pres({

+ 47 - 35
spec/muc.js

@@ -46,7 +46,7 @@
                 await test_utils.openAndEnterChatRoom(_converse, 'leisure', 'localhost', 'dummy');
                 expect(u.isVisible(_converse.chatboxviews.get('lounge@localhost').el)).toBeTruthy();
                 expect(u.isVisible(_converse.chatboxviews.get('leisure@localhost').el)).toBeTruthy();
-                _converse.api.roomviews.close();
+                await _converse.api.roomviews.close();
                 expect(_converse.chatboxviews.get('lounge@localhost')).toBeUndefined();
                 expect(_converse.chatboxviews.get('leisure@localhost')).toBeUndefined();
                 done();
@@ -57,43 +57,43 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group .group-toggle').length, 300);
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 let jid = 'lounge@localhost';
-                let room = _converse.api.rooms.get(jid);
+                let room = await _converse.api.rooms.get(jid);
                 expect(room instanceof Object).toBeTruthy();
 
-                let chatroomview = _converse.chatboxviews.get(jid);
-                expect(chatroomview.is_chatroom).toBeTruthy();
+                let view = _converse.chatboxviews.get(jid);
+                expect(view.is_chatroom).toBeTruthy();
 
-                expect(u.isVisible(chatroomview.el)).toBeTruthy();
-                chatroomview.close();
+                expect(u.isVisible(view.el)).toBeTruthy();
+                await view.close();
 
                 // Test with mixed case
                 await test_utils.openAndEnterChatRoom(_converse, 'Leisure', 'localhost', 'dummy');
                 jid = 'Leisure@localhost';
-                room = _converse.api.rooms.get(jid);
+                room = await _converse.api.rooms.get(jid);
                 expect(room instanceof Object).toBeTruthy();
-                chatroomview = _converse.chatboxviews.get(jid.toLowerCase());
-                expect(u.isVisible(chatroomview.el)).toBeTruthy();
+                view = _converse.chatboxviews.get(jid.toLowerCase());
+                expect(u.isVisible(view.el)).toBeTruthy();
 
                 jid = 'leisure@localhost';
-                room = _converse.api.rooms.get(jid);
+                room = await _converse.api.rooms.get(jid);
                 expect(room instanceof Object).toBeTruthy();
-                chatroomview = _converse.chatboxviews.get(jid.toLowerCase());
-                expect(u.isVisible(chatroomview.el)).toBeTruthy();
+                view = _converse.chatboxviews.get(jid.toLowerCase());
+                expect(u.isVisible(view.el)).toBeTruthy();
 
                 jid = 'leiSure@localhost';
-                room = _converse.api.rooms.get(jid);
+                room = await _converse.api.rooms.get(jid);
                 expect(room instanceof Object).toBeTruthy();
-                chatroomview = _converse.chatboxviews.get(jid.toLowerCase());
-                expect(u.isVisible(chatroomview.el)).toBeTruthy();
-                chatroomview.close();
+                view = _converse.chatboxviews.get(jid.toLowerCase());
+                expect(u.isVisible(view.el)).toBeTruthy();
+                view.close();
 
                 // Non-existing room
                 jid = 'lounge2@localhost';
-                room = _converse.api.rooms.get(jid);
+                room = await _converse.api.rooms.get(jid);
                 expect(typeof room === 'undefined').toBeTruthy();
                 done();
             }));
@@ -112,7 +112,7 @@
                 let jid = 'lounge@localhost';
                 let chatroomview, sent_IQ, IQ_id;
                 test_utils.openControlBox();
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 await test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group .group-toggle').length);
                 let room = await _converse.api.rooms.open(jid);
                 // Test on groupchat that's not yet open
@@ -395,7 +395,7 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 await test_utils.waitUntil(() => test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy'));
                 const view = _converse.chatboxviews.get('lounge@localhost');
                 if (!view.el.querySelectorAll('.chat-area').length) {
@@ -447,8 +447,9 @@
                     }).up()
                     .c('status', {code: '110'}).up()
                     .c('status', {code: '100'});
-
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
+                await test_utils.waitUntil(() => sizzle('div.chat-info', chat_content).length > 1);
+
                 expect(chat_content.querySelectorAll('.chat-info').length).toBe(2);
                 expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
                     .toBe("This groupchat is not anonymous");
@@ -793,6 +794,8 @@
                         </x>
                     </presence>`);
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
+                await test_utils.waitUntil(() => sizzle('div.chat-info', chat_content).length > 3);
+
                 expect(sizzle('div.chat-info', chat_content).length).toBe(4);
                 expect(sizzle('div.chat-info:last', chat_content).pop().textContent).toBe("jcbrand has entered the groupchat");
 
@@ -1134,7 +1137,7 @@
 
                 await test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp']);
                 await test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'));
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 const view = _converse.chatboxviews.get('lounge@localhost');
                 if (!view.el.querySelectorAll('.chat-area').length) {
@@ -1200,7 +1203,7 @@
                  *      </x>
                  *  </presence></body>
                  */
-                var presence = $pres({
+                const presence = $pres({
                         to: 'dummy@localhost/_converse.js-29092160',
                         from: 'coven@chat.shakespeare.lit/some1'
                     }).c('x', {xmlns: Strophe.NS.MUC_USER})
@@ -1212,8 +1215,10 @@
                     .c('status', {code: '110'});
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect(view.model.saveAffiliationAndRole).toHaveBeenCalled();
+
                 expect(u.isVisible(view.el.querySelector('.toggle-chatbox-button'))).toBeTruthy();
                 await test_utils.waitUntil(() => !_.isNull(view.el.querySelector('.configure-chatroom-button')))
+
                 expect(u.isVisible(view.el.querySelector('.configure-chatroom-button'))).toBeTruthy();
                 view.el.querySelector('.configure-chatroom-button').click();
 
@@ -1720,7 +1725,7 @@
                     null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current'); // We need roster contacts, so that we have someone to invite
+                await test_utils.createContacts(_converse, 'current'); // We need roster contacts, so that we have someone to invite
                 // Since we don't actually fetch roster contacts, we need to
                 // cheat here and emit the event.
                 _converse.emit('rosterContactsFetched');
@@ -1787,13 +1792,11 @@
                     null, ['rosterGroupsFetched'], {},
                     async function (done, _converse) {
 
-                test_utils.createContacts(_converse, 'current'); // We need roster contacts, who can invite us
-                spyOn(window, 'confirm').and.callFake(function () {
-                    return true;
-                });
+                const result = await test_utils.createContacts(_converse, 'current', 1);
+                spyOn(window, 'confirm').and.callFake(() => true);
                 await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 const view = _converse.chatboxviews.get('lounge@localhost');
-                view.close(); // Hack, otherwise we have to mock stanzas.
+                await view.close(); // Hack, otherwise we have to mock stanzas.
 
                 const name = mock.cur_names[0];
                 const from_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -1803,11 +1806,13 @@
                 expect(_converse.chatboxes.models.length).toBe(1);
                 expect(_converse.chatboxes.models[0].id).toBe("controlbox");
 
+                await test_utils.waitUntil(() => _converse.roster.get(from_jid).get('fullname'));
                 const stanza = u.toStanza(`
                     <message xmlns="jabber:client" to="${_converse.bare_jid}" from="${from_jid}" id="9bceb415-f34b-4fa4-80d5-c0d076a24231">
                        <x xmlns="jabber:x:conference" jid="${room_jid}" reason="${reason}"/>
                     </message>`);
-                _converse.onDirectMUCInvitation(stanza);
+                await _converse.onDirectMUCInvitation(stanza);
+
                 expect(window.confirm).toHaveBeenCalledWith(
                     name + ' has invited you to join a groupchat: '+ room_jid +
                     ', and left the following reason: "'+reason+'"');
@@ -2481,6 +2486,8 @@
                 // The chatboxes will then be fetched from browserStorage inside the
                 // onConnected method
                 newchatboxes.onConnected();
+                await new Promise((resolve, reject) => _converse.api.listen.once('chatBoxesFetched', resolve));
+
                 expect(newchatboxes.length).toEqual(2);
                 // Check that the chatrooms retrieved from browserStorage
                 // have the same attributes values as the original ones.
@@ -2544,6 +2551,7 @@
                 view.el.querySelector('.close-chatbox-button').click();
                 expect(view.close).toHaveBeenCalled();
                 expect(view.model.leave).toHaveBeenCalled();
+                await test_utils.waitUntil(() => _converse.emit.calls.count());
                 expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
                 done();
             }));
@@ -3770,7 +3778,7 @@
                 await test_utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
                 expect(view.model.features.get('membersonly')).toBeTruthy();
 
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
 
                 let sent_stanza, sent_id;
                 spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
@@ -3970,8 +3978,9 @@
                 roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
                 modal.el.querySelector('input[name="chatroom"]').value = 'lounce@muc.localhost';
                 modal.el.querySelector('form input[type="submit"]').click();
-                await test_utils.waitUntil(() => _converse.chatboxes.length);
+                await test_utils.waitUntil(() => _converse.chatboxes.length > 1);
                 await test_utils.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
+                expect(sizzle('.chatroom', _converse.el).filter(u.isVisible).length).toBe(1); // There should now be an open chatroom
                 done();
             }));
 
@@ -4039,7 +4048,7 @@
 
             it("shows the number of unread mentions received",
                 mock.initConverse(
-                    null, ['rosterGroupsFetched'], {'allow_bookmarks': false},
+                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {'allow_bookmarks': false},
                     async function (done, _converse) {
                 // XXX: we set `allow_bookmarks` to false, so that the groupchats
                 // list gets rendered. Otherwise we would have to mock
@@ -4058,7 +4067,7 @@
                 const view = _converse.chatboxviews.get(room_jid);
                 view.model.set({'minimized': true});
 
-                var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
+                const contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const nick = mock.chatroom_names[0];
 
                 await view.model.onMessage($msg({
@@ -4067,6 +4076,7 @@
                         to: 'dummy@localhost',
                         type: 'groupchat'
                     }).c('body').t(message).tree());
+                await new Promise((resolve, reject) => view.once('messageInserted', resolve));
                 await test_utils.waitUntil(() => view.model.messages.length);
                 expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                 expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
@@ -4250,7 +4260,7 @@
                 describe("A paused notification", function () {
                     it("will be shown if received",
                         mock.initConverse(
-                            null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                            null, ['rosterGroupsFetched', 'chatBoxViewsInitialized'], {},
                             async function (done, _converse) {
 
                         await test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
@@ -4277,6 +4287,8 @@
                             }).up()
                             .c('status', {code: '110'});
                         _converse.connection._dataRecv(test_utils.createRequest(presence));
+                        await test_utils.waitUntil(() => sizzle('div.chat-info:first', chat_content).length);
+
                         expect(sizzle('div.chat-info:first', chat_content).pop().textContent)
                             .toBe("some1 has entered the groupchat");
 

+ 1 - 1
spec/notification.js

@@ -49,6 +49,7 @@
                         await test_utils.createContacts(_converse, 'current');
                         await test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                         const view = _converse.api.chatviews.get('lounge@localhost');
+
                         if (!view.el.querySelectorAll('.chat-area').length) {
                             view.renderChatArea();
                         }
@@ -72,7 +73,6 @@
                                 to: 'dummy@localhost',
                                 type: 'groupchat'
                             }).c('body').t(message).tree();
-
                         _converse.connection._dataRecv(test_utils.createRequest(msg));
                         await new Promise((resolve, reject) => view.once('messageInserted', resolve));
                         expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();

+ 15 - 20
spec/protocol.js

@@ -6,9 +6,9 @@
         "test-utils"], factory);
 } (this, function (jasmine, $, mock, test_utils) {
     "use strict";
-    const Strophe = converse.env.Strophe;
     const $iq = converse.env.$iq;
     const $pres = converse.env.$pres;
+    const Strophe = converse.env.Strophe;
     const _ = converse.env._;
     const sizzle = converse.env.sizzle;
     const u = converse.env.utils;
@@ -461,7 +461,7 @@
 
                 var sent_IQ, IQ_id, jid = 'annegreet.gomez@localhost';
                 test_utils.openControlBox(_converse);
-                test_utils.createContacts(_converse, 'current');
+                await test_utils.createContacts(_converse, 'current');
                 spyOn(window, 'confirm').and.returnValue(true);
                 // We now have a contact we want to remove
                 expect(_converse.roster.get(jid) instanceof _converse.RosterContact).toBeTruthy();
@@ -515,17 +515,17 @@
 
             it("Receiving a subscription request", mock.initConverse(
                 null, ['rosterGroupsFetched'], {},
-                function (done, _converse) {
+                async function (done, _converse) {
 
-                spyOn(_converse, "emit");
-                test_utils.openControlBox(_converse);
-                test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
+                spyOn(_converse, "emit").and.callThrough();
+                await test_utils.openControlBox(_converse);
+                await test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
                 /* <presence
                  *     from='user@example.com'
                  *     to='contact@example.org'
                  *     type='subscribe'/>
                  */
-                var stanza = $pres({
+                const stanza = $pres({
                     'to': _converse.bare_jid,
                     'from': 'contact@example.org',
                     'type': 'subscribe'
@@ -533,19 +533,14 @@
                     'xmlns': Strophe.NS.NICK,
                 }).t('Clint Contact');
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(function () {
-                    var $header = $('a:contains("Contact requests")');
-                    var $contacts = $header.parent().find('li:visible');
-                    return $contacts.length;
-                }, 600).then(function () {
-                    expect(_converse.emit).toHaveBeenCalledWith('contactRequest', jasmine.any(Object));
-                    var $header = $('a:contains("Contact requests")');
-                    expect($header.length).toBe(1);
-                    expect($header.is(":visible")).toBeTruthy();
-                    var $contacts = $header.parent().find('li');
-                    expect($contacts.length).toBe(1);
-                    done();
-                });
+                await test_utils.waitUntil(() => $('a:contains("Contact requests")').parent().find('li:visible').length, 1000);
+                expect(_converse.emit).toHaveBeenCalledWith('contactRequest', jasmine.any(Object));
+                const $header = $('a:contains("Contact requests")');
+                expect($header.length).toBe(1);
+                expect($header.is(":visible")).toBeTruthy();
+                const $contacts = $header.parent().find('li');
+                expect($contacts.length).toBe(1);
+                done();
             }));
         });
     });

+ 7 - 8
spec/register.js

@@ -13,11 +13,10 @@
                 null, ['connectionInitialized', 'chatBoxesInitialized'],
                 { auto_login: false,
                   allow_registration: false },
-                async function (done, _converse) {
+                function (done, _converse) {
 
-            await test_utils.waitUntil(() => _converse.chatboxviews.get('controlbox'));
             test_utils.openControlBox();
-            const cbview = _converse.chatboxviews.get('controlbox');
+            const cbview = _converse.api.controlbox.get();
             expect(cbview.el.querySelectorAll('a.register-account').length).toBe(0);
             done();
         }));
@@ -30,7 +29,7 @@
                 async function (done, _converse) {
 
             await test_utils.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'), 300);
-            const cbview = _converse.chatboxviews.get('controlbox');
+            const cbview = _converse.api.controlbox.get();
             test_utils.openControlBox();
             const panels = cbview.el.querySelector('.controlbox-panes');
             const login = panels.firstElementChild;
@@ -52,7 +51,7 @@
 
             await test_utils.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'));
             test_utils.openControlBox();
-            const cbview = _converse.chatboxviews.get('controlbox');
+            const cbview = _converse.api.controlbox.get();
             const registerview = cbview.registerpanel;
             spyOn(registerview, 'onProviderChosen').and.callThrough();
             registerview.delegateEvents();  // We need to rebind all events otherwise our spy won't be called
@@ -88,7 +87,7 @@
 
             await test_utils.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'));
             test_utils.openControlBox();
-            const cbview = _converse.chatboxviews.get('controlbox');
+            const cbview = _converse.api.controlbox.get();
             cbview.el.querySelector('.toggle-register-login').click();
 
             const registerview = _converse.chatboxviews.get('controlbox').registerpanel;
@@ -144,7 +143,7 @@
 
             await test_utils.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'));
             test_utils.openControlBox();
-            const cbview = _converse.chatboxviews.get('controlbox');
+            const cbview = _converse.api.controlbox.get();
             cbview.el.querySelector('.toggle-register-login').click();
 
             const registerview = cbview.registerpanel;
@@ -201,7 +200,7 @@
 
             await test_utils.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'));
             test_utils.openControlBox();
-            const cbview = _converse.chatboxviews.get('controlbox');
+            const cbview = _converse.api.controlbox.get();
             cbview.el.querySelector('.toggle-register-login').click();
             const registerview = _converse.chatboxviews.get('controlbox').registerpanel;
             spyOn(registerview, 'onProviderChosen').and.callThrough();

+ 5 - 3
spec/roomslist.js

@@ -13,7 +13,7 @@
                                         // have to mock stanza traffic.
                 }, async function (done, _converse) {
 
-            test_utils.openControlBox();
+            await test_utils.openControlBox(_converse);
             const controlbox = _converse.chatboxviews.get('controlbox');
             let list = controlbox.el.querySelector('div.rooms-list-container');
             expect(_.includes(list.classList, 'hidden')).toBeTruthy();
@@ -123,7 +123,7 @@
             spyOn(_converse, 'isUniView').and.callFake(() => true);
 
             let room_els, item;
-            test_utils.openControlBox();
+            test_utils.openControlBox(_converse);
             await _converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
             room_els = _converse.rooms_list_view.el.querySelectorAll(".available-chatroom");
             expect(room_els.length).toBe(1);
@@ -151,7 +151,7 @@
 
             const IQ_stanzas = _converse.connection.IQ_stanzas;
             const room_jid = 'coven@chat.shakespeare.lit';
-            test_utils.openControlBox();
+            test_utils.openControlBox(_converse);
             await _converse.api.rooms.open(room_jid, {'nick': 'some1'});
             const last_stanza = await test_utils.waitUntil(() => _.get(_.filter(
                 IQ_stanzas,
@@ -266,6 +266,8 @@
             close_el.click();
             expect(window.confirm).toHaveBeenCalledWith(
                 'Are you sure you want to leave the groupchat lounge@conference.shakespeare.lit?');
+
+            await test_utils.waitUntil(() => !_converse.api.rooms.get().length);
             room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
             expect(room_els.length).toBe(0);
             expect(_converse.chatboxes.length).toBe(1);

+ 13 - 8
spec/spoilers.js

@@ -11,11 +11,13 @@
 
         it("can be received with a hint",
             mock.initConverse(
-                null, ['rosterGroupsFetched'], {},
+                null, ['rosterGroupsFetched', 'chatBoxViewsInitialized'], {},
                 async (done, _converse) => {
 
             test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
+
+            await test_utils.createContacts(_converse, 'current');
             const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
             /* <message to='romeo@montague.net/orchard' from='juliet@capulet.net/balcony' id='spoiler2'>
@@ -38,7 +40,8 @@
             _converse.chatboxes.onMessage(msg);
 
             await test_utils.waitUntil(() => _converse.api.chats.get().length === 2);
-            const view = _converse.chatboxviews.get(sender_jid);
+            const view = _converse.api.chatviews.get(sender_jid);
+            await new Promise((resolve, reject) => view.once('messageInserted', resolve));
             await test_utils.waitUntil(() => view.model.vcard.get('fullname') === 'Max Frankfurter')
             expect(view.el.querySelector('.chat-msg__author').textContent.trim()).toBe('Max Frankfurter');
             const message_content = view.el.querySelector('.chat-msg__text');
@@ -53,7 +56,7 @@
                 null, ['rosterGroupsFetched'], {},
                 async (done, _converse) => {
 
-            test_utils.createContacts(_converse, 'current');
+            await test_utils.createContacts(_converse, 'current');
             _converse.emit('rosterContactsFetched');
             const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
             /* <message to='romeo@montague.net/orchard' from='juliet@capulet.net/balcony' id='spoiler2'>
@@ -73,7 +76,8 @@
                     }).tree();
             _converse.chatboxes.onMessage(msg);
             await test_utils.waitUntil(() => _converse.api.chats.get().length === 2);
-            const view = _converse.chatboxviews.get(sender_jid);
+            const view = _converse.api.chatviews.get(sender_jid);
+            await new Promise((resolve, reject) => view.once('messageInserted', resolve));
             await test_utils.waitUntil(() => view.model.vcard.get('fullname') === 'Max Frankfurter')
             expect(_.includes(view.el.querySelector('.chat-msg__author').textContent, 'Max Frankfurter')).toBeTruthy();
             const message_content = view.el.querySelector('.chat-msg__text');
@@ -88,7 +92,7 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async (done, _converse) => {
 
-            test_utils.createContacts(_converse, 'current', 1);
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
 
             test_utils.openControlBox();
@@ -105,7 +109,8 @@
             _converse.connection._dataRecv(test_utils.createRequest(presence));
             await test_utils.openChatBoxFor(_converse, contact_jid);
             await test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]);
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
+            spyOn(view, 'onMessageSubmitted').and.callThrough();
             spyOn(_converse.connection, 'send');
 
             await test_utils.waitUntil(() => view.el.querySelector('.toggle-compose-spoiler'));
@@ -163,7 +168,7 @@
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                 async (done, _converse) => {
 
-            test_utils.createContacts(_converse, 'current', 1);
+            await test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
 
             test_utils.openControlBox();
@@ -180,7 +185,7 @@
             _converse.connection._dataRecv(test_utils.createRequest(presence));
             await test_utils.openChatBoxFor(_converse, contact_jid);
             await test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]);
-            const view = _converse.chatboxviews.get(contact_jid);
+            const view = _converse.api.chatviews.get(contact_jid);
 
             await test_utils.waitUntil(() => view.el.querySelector('.toggle-compose-spoiler'));
             let spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');

+ 1 - 2
src/converse-chatboxviews.js

@@ -139,8 +139,7 @@ converse.plugins.add('converse-chatboxviews', {
                 /* This method gets overridden in src/converse-controlbox.js if
                  * the controlbox plugin is active.
                  */
-                this.each(function (view) { view.close(); });
-                return this;
+                return Promise.all(this.map(view => view.close({'name': 'closeAllChatBoxes'})));
             },
 
             chatBoxMayBeShown (chatbox) {

+ 0 - 1
src/converse-chatview.js

@@ -1154,7 +1154,6 @@ converse.plugins.add('converse-chatview', {
                 }
                 this.remove();
                 _converse.emit('chatBoxClosed', this);
-                return this;
             },
 
             renderEmojiPicker () {

+ 12 - 14
src/converse-controlbox.js

@@ -101,18 +101,6 @@ converse.plugins.add('converse-controlbox', {
         },
 
         ChatBoxViews: {
-            closeAllChatBoxes () {
-                const { _converse } = this.__super__;
-                this.each(function (view) {
-                    if (view.model.get('id') === 'controlbox' &&
-                            (_converse.disconnection_cause !== _converse.LOGOUT || _converse.show_controlbox_by_default)) {
-                        return;
-                    }
-                    view.close();
-                });
-                return this;
-            },
-
             getChatBoxWidth (view) {
                 const { _converse } = this.__super__;
                 const controlbox = this.get('controlbox');
@@ -321,13 +309,23 @@ converse.plugins.add('converse-controlbox', {
                 )
             },
 
-            close (ev) {
+            async close (ev) {
                 if (ev && ev.preventDefault) { ev.preventDefault(); }
+                if (ev.name === 'closeAllChatBoxes' &&
+                        (_converse.disconnection_cause !== _converse.LOGOUT ||
+                         _converse.show_controlbox_by_default)) {
+                    return;
+                }
                 if (_converse.sticky_controlbox) {
                     return;
                 }
                 if (_converse.connection.connected && !_converse.connection.disconnecting) {
-                    this.model.save({'closed': true});
+                    await new Promise((resolve, reject) => {
+                        return this.model.save(
+                            {'closed': true},
+                            {'success': resolve, 'error': reject}
+                        );
+                    });
                 } else {
                     this.model.trigger('hide');
                 }

+ 4 - 4
src/headless/converse-chatboxes.js

@@ -317,7 +317,7 @@ converse.plugins.add('converse-chatboxes', {
                         'references': this.getReferencesFromStanza(stanza),
                         'older_versions': older_versions,
                         'edited': moment().format()
-                    });
+                    }, {'wait': true});
                     return true;
                 }
                 return false;
@@ -724,7 +724,7 @@ converse.plugins.add('converse-chatboxes', {
                     // TODO: handle <subject> messages (currently being done by ChatRoom)
                     return;
                 } else {
-                    return this.messages.create(attrs);
+                    return new Promise((success, error) => this.messages.create(attrs, {success, error, 'wait': true}));
                 }
             },
 
@@ -844,8 +844,8 @@ converse.plugins.add('converse-chatboxes', {
                     _converse.log('Received an error message without id attribute!', Strophe.LogLevel.ERROR);
                     _converse.log(message, Strophe.LogLevel.ERROR);
                 }
-                const attrs = await chatbox.getMessageAttributesFromStanza(message, message);
-                chatbox.messages.create(attrs);
+                await chatbox.createMessage(message, message);
+                return true;
             },
 
             getMessageBody (stanza) {

+ 0 - 2
tests/mock.js

@@ -159,8 +159,6 @@
     }();
 
     async function initConverse (settings, spies, promises) {
-        window.localStorage.clear();
-        window.sessionStorage.clear();
         const el = document.querySelector('#conversejs');
         if (el) {
             el.parentElement.removeChild(el);

+ 13 - 8
tests/utils.js

@@ -62,7 +62,7 @@
     };
 
     utils.openControlBox = function () {
-        var toggle = document.querySelector(".toggle-controlbox");
+        const toggle = document.querySelector(".toggle-controlbox");
         if (!u.isVisible(document.querySelector("#controlbox"))) {
             if (!u.isVisible(toggle)) {
                 u.removeClass('hidden', toggle);
@@ -92,15 +92,19 @@
         return views;
     };
 
-    utils.openChatBoxFor = function (_converse, jid) {
+    utils.openChatBoxFor = async function (_converse, jid) {
+        await _converse.api.waitUntil('rosterContactsFetched');
         _converse.roster.get(jid).trigger("open");
         return utils.waitUntil(() => _converse.chatboxviews.get(jid), 1000);
     };
 
     utils.openChatRoomViaModal = async function (_converse, jid, nick='') {
         // Opens a new chatroom
-        utils.openControlBox(_converse);
-        const roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+        const model = await _converse.api.chats.open('controlbox');
+        await utils.waitUntil(() => model.get('connected'));
+        utils.openControlBox();
+        const view = await _converse.chatboxviews.get('controlbox');
+        const roomspanel = view.roomspanel;
         roomspanel.el.querySelector('.show-add-muc-modal').click();
         utils.closeControlBox(_converse);
         const modal = roomspanel.add_room_modal;
@@ -121,6 +125,7 @@
         const stanzas = _converse.connection.IQ_stanzas;
         await _converse.api.rooms.open(room_jid);
         const view = _converse.chatboxviews.get(room_jid);
+
         let stanza = await utils.waitUntil(() => _.get(_.filter(
             stanzas,
             iq => iq.nodeTree.querySelector(
@@ -195,7 +200,7 @@
             }).up()
             .c('status').attrs({code:'110'});
         _converse.connection._dataRecv(utils.createRequest(presence));
-        await utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.ENTERED));
+        return utils.waitUntil(() => (view.model.get('connection_status') === converse.ROOMSTATUS.ENTERED));
     };
 
     utils.clearBrowserStorage = function () {
@@ -252,9 +257,9 @@
             requesting = false;
             ask = null;
         } else if (type === 'all') {
-            this.createContacts(_converse, 'current')
-                .createContacts(_converse, 'requesting')
-                .createContacts(_converse, 'pending');
+            await this.createContacts(_converse, 'current');
+            await this.createContacts(_converse, 'requesting')
+            await this.createContacts(_converse, 'pending');
             return this;
         } else {
             throw Error("Need to specify the type of contact to create");