Pārlūkot izejas kodu

`createMessage` now returns a promise.

Also, fix all broken tests, mostly related to this.
JC Brand 7 gadi atpakaļ
vecāks
revīzija
ca9229a906
11 mainītis faili ar 649 papildinājumiem un 530 dzēšanām
  1. 3 3
      dist/converse.js
  2. 153 113
      spec/chatbox.js
  3. 120 116
      spec/chatroom.js
  4. 16 11
      spec/controlbox.js
  5. 23 7
      spec/mam.js
  6. 292 252
      spec/messages.js
  7. 19 15
      spec/minchats.js
  8. 13 7
      spec/roomslist.js
  9. 6 5
      src/converse-chatboxes.js
  10. 1 0
      src/converse-mam.js
  11. 3 1
      src/converse-muc.js

+ 3 - 3
dist/converse.js

@@ -63132,7 +63132,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
               } else {
               } else {
                 resolve(this.messages.create(attrs));
                 resolve(this.messages.create(attrs));
               }
               }
-            });
+            }).catch(e => reject(e));
           });
           });
         },
         },
 
 
@@ -63323,7 +63323,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
 
 
             if (!message) {
             if (!message) {
               // Only create the message when we're sure it's not a duplicate
               // Only create the message when we're sure it's not a duplicate
-              chatbox.incrementUnreadMsgCounter(chatbox.createMessage(stanza, original_stanza));
+              chatbox.createMessage(stanza, original_stanza).then(msg => chatbox.incrementUnreadMsgCounter(msg)).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             }
             }
           }
           }
 
 
@@ -73120,7 +73120,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
               return;
               return;
             }
             }
 
 
-            this.incrementUnreadMsgCounter(this.createMessage(stanza, original_stanza));
+            this.createMessage(stanza, original_stanza).then(msg => this.incrementUnreadMsgCounter(msg)).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
           }
           }
 
 
           if (sender !== this.get('nick')) {
           if (sender !== this.get('nick')) {

+ 153 - 113
spec/chatbox.js

@@ -27,10 +27,11 @@
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 test_utils.openControlBox();
                 test_utils.openControlBox();
 
 
+                let view;
                 const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                test_utils.openChatBoxFor(_converse, contact_jid);
-                test_utils.waitUntil(() => _converse.chatboxes.length == 2).then(() => {
-                    var view = _converse.chatboxviews.get(contact_jid);
+                test_utils.openChatBoxFor(_converse, contact_jid)
+                .then(() => {
+                    view = _converse.chatboxviews.get(contact_jid);
                     test_utils.sendMessage(view, '/help');
                     test_utils.sendMessage(view, '/help');
 
 
                     const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info:not(.chat-date)'), 0);
                     const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info:not(.chat-date)'), 0);
@@ -39,13 +40,15 @@
                     expect(info_messages.pop().textContent).toBe('/me: Write in the third person');
                     expect(info_messages.pop().textContent).toBe('/me: Write in the third person');
                     expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
                     expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
 
 
-                    var msg = $msg({
+                    const msg = $msg({
                             from: contact_jid,
                             from: contact_jid,
                             to: _converse.connection.jid,
                             to: _converse.connection.jid,
                             type: 'chat',
                             type: 'chat',
                             id: (new Date()).getTime()
                             id: (new Date()).getTime()
                         }).c('body').t('hello world').tree();
                         }).c('body').t('hello world').tree();
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
+                    return test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length);
+                }).then(() => {
                     expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
                     expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
                     done();
                     done();
                 });
                 });
@@ -741,7 +744,7 @@
                             spyOn(_converse, 'log');
                             spyOn(_converse, 'log');
                             recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
                             recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
                             return test_utils.openChatBoxFor(_converse, recipient_jid);
                             return test_utils.openChatBoxFor(_converse, recipient_jid);
-                        }).then(() => {
+                        }).then((view) => {
                             var msg = $msg({
                             var msg = $msg({
                                     'from': _converse.bare_jid,
                                     'from': _converse.bare_jid,
                                     'id': (new Date()).getTime(),
                                     'id': (new Date()).getTime(),
@@ -757,7 +760,8 @@
                                         'type': 'chat'
                                         'type': 'chat'
                                 }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             _converse.chatboxes.onMessage(msg);
                             _converse.chatboxes.onMessage(msg);
-
+                            return test_utils.waitUntil(() => view.model.messages.length);
+                        }).then(() => {
                             // Check that the chatbox and its view now exist
                             // Check that the chatbox and its view now exist
                             var chatbox = _converse.chatboxes.get(recipient_jid);
                             var chatbox = _converse.chatboxes.get(recipient_jid);
                             var chatboxview = _converse.chatboxviews.get(recipient_jid);
                             var chatboxview = _converse.chatboxviews.get(recipient_jid);
@@ -886,7 +890,7 @@
                             spyOn(_converse, 'log');
                             spyOn(_converse, 'log');
                             recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
                             recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
                             return test_utils.openChatBoxFor(_converse, recipient_jid);
                             return test_utils.openChatBoxFor(_converse, recipient_jid);
-                        }).then(() => {
+                        }).then((view) => {
                             var msg = $msg({
                             var msg = $msg({
                                     'from': _converse.bare_jid,
                                     'from': _converse.bare_jid,
                                     'id': (new Date()).getTime(),
                                     'id': (new Date()).getTime(),
@@ -902,7 +906,8 @@
                                         'type': 'chat'
                                         'type': 'chat'
                                 }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             _converse.chatboxes.onMessage(msg);
                             _converse.chatboxes.onMessage(msg);
-
+                            return test_utils.waitUntil(() => view.model.messages.length);
+                        }).then(() => {
                             // Check that the chatbox and its view now exist
                             // Check that the chatbox and its view now exist
                             var chatbox = _converse.chatboxes.get(recipient_jid);
                             var chatbox = _converse.chatboxes.get(recipient_jid);
                             var chatboxview = _converse.chatboxviews.get(recipient_jid);
                             var chatboxview = _converse.chatboxviews.get(recipient_jid);
@@ -1041,17 +1046,18 @@
                         _converse.emit('rosterContactsFetched');
                         _converse.emit('rosterContactsFetched');
                         test_utils.openControlBox();
                         test_utils.openControlBox();
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                         const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
+                        let view;
 
 
                         // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
                         // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
                         spyOn(_converse, 'emit');
                         spyOn(_converse, 'emit');
                         test_utils.openChatBoxFor(_converse, sender_jid)
                         test_utils.openChatBoxFor(_converse, sender_jid)
                         .then(() => {
                         .then(() => {
-                            var view = _converse.chatboxviews.get(sender_jid);
+                            view = _converse.chatboxviews.get(sender_jid);
                             expect(view.el.querySelectorAll('.chat-event').length).toBe(0);
                             expect(view.el.querySelectorAll('.chat-event').length).toBe(0);
                             // Insert <composing> message, to also check that
                             // Insert <composing> message, to also check that
                             // text messages are inserted correctly with
                             // text messages are inserted correctly with
                             // temporary chat events in the chat contents.
                             // temporary chat events in the chat contents.
-                            var msg = $msg({
+                            const msg = $msg({
                                     'to': _converse.bare_jid,
                                     'to': _converse.bare_jid,
                                     'xmlns': 'jabber:client',
                                     'xmlns': 'jabber:client',
                                     'from': sender_jid,
                                     'from': sender_jid,
@@ -1059,14 +1065,18 @@
                                 .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
                                 .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
                                 .tree();
                                 .tree();
                             _converse.chatboxes.onMessage(msg);
                             _converse.chatboxes.onMessage(msg);
+                            return test_utils.waitUntil(() => view.model.messages.length);
+                        }).then(() => {
                             expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
                             expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
-                            msg = $msg({
+                            const msg = $msg({
                                     from: sender_jid,
                                     from: sender_jid,
                                     to: _converse.connection.jid,
                                     to: _converse.connection.jid,
                                     type: 'chat',
                                     type: 'chat',
                                     id: (new Date()).getTime()
                                     id: (new Date()).getTime()
                                 }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             _converse.chatboxes.onMessage(msg);
                             _converse.chatboxes.onMessage(msg);
+                            return test_utils.waitUntil(() => (view.model.messages.length > 1));
+                        }).then(() => {
                             expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
                             expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
                             expect($(view.el).find('.chat-state-notification').length).toBe(0);
                             expect($(view.el).find('.chat-state-notification').length).toBe(0);
                             done();
                             done();
@@ -1178,12 +1188,15 @@
                       .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                       .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                 _converse.windowState = 'hidden';
                 _converse.windowState = 'hidden';
                 _converse.chatboxes.onMessage(msg);
                 _converse.chatboxes.onMessage(msg);
-                expect(_converse.incrementMsgCounter).toHaveBeenCalled();
-                expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
-                expect(_converse.msg_counter).toBe(1);
-                expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
-                _converse.windowSate = previous_state;
-                done();
+                return test_utils.waitUntil(() => _converse.api.chats.get().length)
+                .then(() => {
+                    expect(_converse.incrementMsgCounter).toHaveBeenCalled();
+                    expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
+                    expect(_converse.msg_counter).toBe(1);
+                    expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
+                    _converse.windowSate = previous_state;
+                    done();
+                });
             }));
             }));
 
 
             it("is cleared when the window is focused",
             it("is cleared when the window is focused",
@@ -1237,7 +1250,8 @@
                 // initial state
                 // initial state
                 expect(_converse.msg_counter).toBe(0);
                 expect(_converse.msg_counter).toBe(0);
 
 
-                var message = 'This message will always increment the message counter from zero',
+                let view;
+                const message = 'This message will always increment the message counter from zero',
                     sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
                     sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
                     msgFactory = function () {
                     msgFactory = function () {
                         return $msg({
                         return $msg({
@@ -1254,24 +1268,29 @@
                 // leave converse-chat page
                 // leave converse-chat page
                 _converse.windowState = 'hidden';
                 _converse.windowState = 'hidden';
                 _converse.chatboxes.onMessage(msgFactory());
                 _converse.chatboxes.onMessage(msgFactory());
-                expect(_converse.msg_counter).toBe(1);
+                return test_utils.waitUntil(() => _converse.api.chats.get().length)
+                .then(() => {
+                    expect(_converse.msg_counter).toBe(1);
 
 
-                // come back to converse-chat page
-                _converse.saveWindowState(null, 'focus');
-                var view = _converse.chatboxviews.get(sender_jid);
-                expect(u.isVisible(view.el)).toBeTruthy();
-                expect(_converse.msg_counter).toBe(0);
+                    // come back to converse-chat page
+                    _converse.saveWindowState(null, 'focus');
+                    view = _converse.chatboxviews.get(sender_jid);
+                    expect(u.isVisible(view.el)).toBeTruthy();
+                    expect(_converse.msg_counter).toBe(0);
 
 
-                // close chatbox and leave converse-chat page again
-                view.close();
-                _converse.windowState = 'hidden';
+                    // close chatbox and leave converse-chat page again
+                    view.close();
+                    _converse.windowState = 'hidden';
 
 
-                // check that msg_counter is incremented from zero again
-                _converse.chatboxes.onMessage(msgFactory());
-                view = _converse.chatboxviews.get(sender_jid);
-                expect(u.isVisible(view.el)).toBeTruthy();
-                expect(_converse.msg_counter).toBe(1);
-                done();
+                    // check that msg_counter is incremented from zero again
+                    _converse.chatboxes.onMessage(msgFactory());
+                    return test_utils.waitUntil(() => _converse.api.chats.get().length)
+                }).then(() => {
+                    view = _converse.chatboxviews.get(sender_jid);
+                    expect(u.isVisible(view.el)).toBeTruthy();
+                    expect(_converse.msg_counter).toBe(1);
+                    done();
+                });
             }));
             }));
         });
         });
 
 
@@ -1288,13 +1307,17 @@
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
                       msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                       msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
 
 
+                let view;
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
-                .then((view) => {
+                .then((v) => {
+                    view = v;
                     view.model.save('scrolled', true);
                     view.model.save('scrolled', true);
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
                     expect(view.model.get('num_unread')).toBe(1);
                     expect(view.model.get('num_unread')).toBe(1);
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("is not incremented when the message is received and ChatBoxView is scrolled down",
             it("is not incremented when the message is received and ChatBoxView is scrolled down",
@@ -1323,15 +1346,18 @@
                 test_utils.createContacts(_converse, 'current');
                 test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
 
 
-                var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                var msgFactory = function () {
+                let chatbox;
+                const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+                const msgFactory = function () {
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                 };
                 };
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
                     _converse.windowState = 'hidden';
                     _converse.windowState = 'hidden';
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     expect(chatbox.get('num_unread')).toBe(1);
                     expect(chatbox.get('num_unread')).toBe(1);
                     done();
                     done();
                 });
                 });
@@ -1344,17 +1370,19 @@
 
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
-
-                var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                var msgFactory = function () {
+                let chatbox;
+                const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+                const msgFactory = function () {
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                 };
                 };
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
                     chatbox.save('scrolled', true);
                     chatbox.save('scrolled', true);
                     _converse.windowState = 'hidden';
                     _converse.windowState = 'hidden';
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     expect(chatbox.get('num_unread')).toBe(1);
                     expect(chatbox.get('num_unread')).toBe(1);
                     done();
                     done();
                 });
                 });
@@ -1368,20 +1396,23 @@
                 test_utils.createContacts(_converse, 'current');
                 test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
 
 
+                let chatbox;
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                var msgFactory = function () {
+                const msgFactory = function () {
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                 };
                 };
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 .then(() => {
                 .then(() => {
-                    const chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
                     _converse.windowState = 'hidden';
                     _converse.windowState = 'hidden';
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     expect(chatbox.get('num_unread')).toBe(1);
                     expect(chatbox.get('num_unread')).toBe(1);
                     _converse.saveWindowState(null, 'focus');
                     _converse.saveWindowState(null, 'focus');
                     expect(chatbox.get('num_unread')).toBe(0);
                     expect(chatbox.get('num_unread')).toBe(0);
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
             it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
@@ -1391,21 +1422,24 @@
 
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
-                var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                var msgFactory = function () {
+                let chatbox;
+                const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+                const msgFactory = function () {
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                     return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                 };
                 };
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
                     chatbox.save('scrolled', true);
                     chatbox.save('scrolled', true);
                     _converse.windowState = 'hidden';
                     _converse.windowState = 'hidden';
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     expect(chatbox.get('num_unread')).toBe(1);
                     expect(chatbox.get('num_unread')).toBe(1);
                     _converse.saveWindowState(null, 'focus');
                     _converse.saveWindowState(null, 'focus');
                     expect(chatbox.get('num_unread')).toBe(1);
                     expect(chatbox.get('num_unread')).toBe(1);
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
         });
         });
 
 
@@ -1419,28 +1453,29 @@
                 test_utils.createContacts(_converse, 'current');
                 test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
 
 
+                let msg, chatbox, indicator_el, selector;
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
                     chatbox.save('scrolled', true);
                     chatbox.save('scrolled', true);
-
-                    var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
+                    msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
-
-                    var selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
-                        indicator_el = sizzle(selector, _converse.rosterview.el).pop();
-
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
+                    selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
+                    indicator_el = sizzle(selector, _converse.rosterview.el).pop();
                     expect(indicator_el.textContent).toBe('1');
                     expect(indicator_el.textContent).toBe('1');
 
 
                     msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
                     msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
-
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     indicator_el = sizzle(selector, _converse.rosterview.el).pop();
                     indicator_el = sizzle(selector, _converse.rosterview.el).pop();
                     expect(indicator_el.textContent).toBe('2');
                     expect(indicator_el.textContent).toBe('2');
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("is updated when message is received and chatbox is minimized",
             it("is updated when message is received and chatbox is minimized",
@@ -1452,28 +1487,30 @@
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
 
+                let chatbox, indicator_el, msg, selector;
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
                     var chatboxview = _converse.chatboxviews.get(sender_jid);
                     var chatboxview = _converse.chatboxviews.get(sender_jid);
                     chatboxview.minimize();
                     chatboxview.minimize();
 
 
-                    var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
+                    msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
-
-                    var selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
-                        indicator_el = sizzle(selector, _converse.rosterview.el).pop();
-
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
+                    selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
+                    indicator_el = sizzle(selector, _converse.rosterview.el).pop();
                     expect(indicator_el.textContent).toBe('1');
                     expect(indicator_el.textContent).toBe('1');
 
 
                     msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
                     msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
-
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     indicator_el = sizzle(selector, _converse.rosterview.el).pop();
                     indicator_el = sizzle(selector, _converse.rosterview.el).pop();
                     expect(indicator_el.textContent).toBe('2');
                     expect(indicator_el.textContent).toBe('2');
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
             it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
@@ -1484,27 +1521,28 @@
                 test_utils.createContacts(_converse, 'current');
                 test_utils.createContacts(_converse, 'current');
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-
+                let chatbox, view, select_msgs_indicator;
+                const msgFactory = function () {
+                    return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
+                };
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
-                    var chatboxview = _converse.chatboxviews.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
+                    view = _converse.chatboxviews.get(sender_jid);
                     var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
                     var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
-                    var selectMsgsIndicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
-                    var msgFactory = function () {
-                        return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
-                    };
-                    chatboxview.minimize();
-
+                    select_msgs_indicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
+                    view.minimize();
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
-                    expect(selectMsgsIndicator().text()).toBe('1');
-
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
+                    expect(select_msgs_indicator().text()).toBe('1');
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
-                    expect(selectMsgsIndicator().text()).toBe('2');
-
-                    chatboxview.maximize();
-                    expect(selectMsgsIndicator().length).toBe(0);
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
+                    expect(select_msgs_indicator().text()).toBe('2');
+                    view.maximize();
+                    expect(select_msgs_indicator().length).toBe(0);
                     done();
                     done();
                 });
                 });
             }));
             }));
@@ -1518,27 +1556,27 @@
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
 
+                let view, chatbox, select_msgs_indicator;
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
-                    var chatboxview = _converse.chatboxviews.get(sender_jid);
+                    chatbox = _converse.chatboxes.get(sender_jid);
+                    view = _converse.chatboxviews.get(sender_jid);
                     var msgFactory = function () {
                     var msgFactory = function () {
                         return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
                         return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
                     };
                     };
-                    var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
-                        selectMsgsIndicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
-
+                    var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
+                    select_msgs_indicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
                     chatbox.save('scrolled', true);
                     chatbox.save('scrolled', true);
-
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
-                    expect(selectMsgsIndicator().text()).toBe('1');
-
-                    chatboxview.viewUnreadMessages();
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
+                    expect(select_msgs_indicator().text()).toBe('1');
+                    view.viewUnreadMessages();
                     _converse.rosterview.render();
                     _converse.rosterview.render();
-                    expect(selectMsgsIndicator().length).toBe(0);
+                    expect(select_msgs_indicator().length).toBe(0);
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
             it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
@@ -1550,24 +1588,25 @@
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
 
+                let select_msgs_indicator, view;
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => test_utils.openChatBoxFor(_converse, sender_jid))
                 .then(() => {
                 .then(() => {
-                    var chatbox = _converse.chatboxes.get(sender_jid);
-                    var chatboxview = _converse.chatboxviews.get(sender_jid);
+                    const chatbox = _converse.chatboxes.get(sender_jid);
+                    view = _converse.chatboxviews.get(sender_jid);
                     var msgFactory = function () {
                     var msgFactory = function () {
                         return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
                         return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
                     };
                     };
-                    var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
-                        selectMsgsIndicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
-
+                    var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
+                    select_msgs_indicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
                     chatbox.save('scrolled', true);
                     chatbox.save('scrolled', true);
-
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
-                    expect(selectMsgsIndicator().text()).toBe('1');
-
-                    test_utils.openChatBoxFor(_converse, sender_jid);
-                    expect(selectMsgsIndicator().text()).toBe('1');
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
+                    expect(select_msgs_indicator().text()).toBe('1');
+                    return test_utils.openChatBoxFor(_converse, sender_jid);
+                }).then(() => {
+                    expect(select_msgs_indicator().text()).toBe('1');
                     done();
                     done();
                 });
                 });
             }));
             }));
@@ -1584,20 +1623,21 @@
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
 
+                let selectUnreadMsgCount;
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 .then(() => {
                 .then(() => {
                     const msgFactory = function () {
                     const msgFactory = function () {
                         return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
                         return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
                     };
                     };
-                    const selectUnreadMsgCount = function () {
+                    selectUnreadMsgCount = function () {
                         const minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
                         const minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
                         return minimizedChatBoxView.el.querySelector('.message-count');
                         return minimizedChatBoxView.el.querySelector('.message-count');
                     };
                     };
-
                     const chatbox = _converse.chatboxes.get(sender_jid);
                     const chatbox = _converse.chatboxes.get(sender_jid);
                     chatbox.save('scrolled', true);
                     chatbox.save('scrolled', true);
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
-
+                    return test_utils.waitUntil(() => chatbox.messages.length);
+                }).then(() => {
                     const chatboxview = _converse.chatboxviews.get(sender_jid);
                     const chatboxview = _converse.chatboxviews.get(sender_jid);
                     chatboxview.minimize();
                     chatboxview.minimize();
 
 
@@ -1605,7 +1645,7 @@
                     expect(u.isVisible(unread_count)).toBeTruthy();
                     expect(u.isVisible(unread_count)).toBeTruthy();
                     expect(unread_count.innerHTML).toBe('1');
                     expect(unread_count.innerHTML).toBe('1');
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("is incremented when message is received and windows is not focused",
             it("is incremented when message is received and windows is not focused",
@@ -1617,27 +1657,27 @@
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
 
+                let selectUnreadMsgCount;
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 test_utils.openChatBoxFor(_converse, sender_jid)
                 .then(() => {
                 .then(() => {
                     const msgFactory = function () {
                     const msgFactory = function () {
                         return test_utils.createChatMessage(_converse, sender_jid,
                         return test_utils.createChatMessage(_converse, sender_jid,
                             'This message will be received as unread, but eventually will be read');
                             'This message will be received as unread, but eventually will be read');
                     };
                     };
-                    const selectUnreadMsgCount = function () {
+                    selectUnreadMsgCount = function () {
                         const minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
                         const minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
                         return minimizedChatBoxView.el.querySelector('.message-count');
                         return minimizedChatBoxView.el.querySelector('.message-count');
                     };
                     };
-
-                    const chatboxview = _converse.chatboxviews.get(sender_jid);
-                    chatboxview.minimize();
-
+                    const view = _converse.chatboxviews.get(sender_jid);
+                    view.minimize();
                     _converse.chatboxes.onMessage(msgFactory());
                     _converse.chatboxes.onMessage(msgFactory());
-
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
                     const unread_count = selectUnreadMsgCount();
                     const unread_count = selectUnreadMsgCount();
                     expect(u.isVisible(unread_count)).toBeTruthy();
                     expect(u.isVisible(unread_count)).toBeTruthy();
                     expect(unread_count.innerHTML).toBe('1');
                     expect(unread_count.innerHTML).toBe('1');
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("will render Openstreetmap-URL from geo-URI",
             it("will render Openstreetmap-URL from geo-URI",

+ 120 - 116
spec/chatroom.js

@@ -612,10 +612,14 @@
                     null, ['rosterGroupsFetched'], {},
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
                     function (done, _converse) {
 
 
-                test_utils.openAndEnterChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'dummy').then(function () {
-                    var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
-                    var chat_content = view.el.querySelector('.chat-content');
-                    var $chat_content = $(chat_content);
+                let view, chat_content, $chat_content;
+                const ONE_DAY_LATER = 86400000;
+                const baseTime = new Date();
+                test_utils.openAndEnterChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'dummy')
+                .then(() => {
+                    view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
+                    chat_content = view.el.querySelector('.chat-content');
+                    $chat_content = $(chat_content);
                     var indicator = chat_content.querySelector('.date-separator');
                     var indicator = chat_content.querySelector('.date-separator');
                     expect(indicator).not.toBe(null);
                     expect(indicator).not.toBe(null);
                     expect(indicator.getAttribute('class')).toEqual('message date-separator');
                     expect(indicator.getAttribute('class')).toEqual('message date-separator');
@@ -625,11 +629,8 @@
                     expect(chat_content.querySelector('div.chat-info').textContent).toBe(
                     expect(chat_content.querySelector('div.chat-info').textContent).toBe(
                         "dummy has entered the groupchat"
                         "dummy has entered the groupchat"
                     );
                     );
-
-                    var baseTime = new Date();
                     jasmine.clock().install();
                     jasmine.clock().install();
                     jasmine.clock().mockDate(baseTime);
                     jasmine.clock().mockDate(baseTime);
-                    var ONE_DAY_LATER = 86400000;
                     jasmine.clock().tick(ONE_DAY_LATER);
                     jasmine.clock().tick(ONE_DAY_LATER);
 
 
                     /* <presence to="dummy@localhost/_converse.js-29092160"
                     /* <presence to="dummy@localhost/_converse.js-29092160"
@@ -694,7 +695,7 @@
 
 
                     jasmine.clock().tick(ONE_DAY_LATER);
                     jasmine.clock().tick(ONE_DAY_LATER);
 
 
-                    var stanza = Strophe.xmlHtmlNode(
+                    const stanza = Strophe.xmlHtmlNode(
                         '<message xmlns="jabber:client"' +
                         '<message xmlns="jabber:client"' +
                         '   to="dummy@localhost/_converse.js-290929789"' +
                         '   to="dummy@localhost/_converse.js-290929789"' +
                         '   type="groupchat"' +
                         '   type="groupchat"' +
@@ -703,8 +704,15 @@
                         '       <delay xmlns="urn:xmpp:delay" stamp="'+moment().format()+'" from="some1@localhost"/>'+
                         '       <delay xmlns="urn:xmpp:delay" stamp="'+moment().format()+'" from="some1@localhost"/>'+
                         '</message>').firstChild;
                         '</message>').firstChild;
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                    presence = $pres({
+                    jasmine.clock().uninstall();
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
+                    jasmine.clock().install();
+                    jasmine.clock().mockDate(baseTime);
+                    jasmine.clock().tick(ONE_DAY_LATER);
+                    jasmine.clock().tick(ONE_DAY_LATER);
+                    jasmine.clock().tick(ONE_DAY_LATER);
+                    const presence = $pres({
                             to: 'dummy@localhost/_converse.js-29092160',
                             to: 'dummy@localhost/_converse.js-29092160',
                             from: 'coven@chat.shakespeare.lit/newguy'
                             from: 'coven@chat.shakespeare.lit/newguy'
                         }).c('x', {xmlns: Strophe.NS.MUC_USER})
                         }).c('x', {xmlns: Strophe.NS.MUC_USER})
@@ -715,7 +723,7 @@
                         });
                         });
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
 
 
-                    let time = chat_content.querySelectorAll('time.separator-text');
+                    const time = chat_content.querySelectorAll('time.separator-text');
                     expect(time.length).toEqual(4);
                     expect(time.length).toEqual(4);
 
 
                     var $indicator = $chat_content.find('.date-separator:eq(3)');
                     var $indicator = $chat_content.find('.date-separator:eq(3)');
@@ -727,7 +735,7 @@
 
 
                     jasmine.clock().tick(ONE_DAY_LATER);
                     jasmine.clock().tick(ONE_DAY_LATER);
 
 
-                    stanza = Strophe.xmlHtmlNode(
+                    const stanza = Strophe.xmlHtmlNode(
                         '<message xmlns="jabber:client"' +
                         '<message xmlns="jabber:client"' +
                         '   to="dummy@localhost/_converse.js-290929789"' +
                         '   to="dummy@localhost/_converse.js-290929789"' +
                         '   type="groupchat"' +
                         '   type="groupchat"' +
@@ -736,11 +744,18 @@
                         '       <delay xmlns="urn:xmpp:delay" stamp="'+moment().format()+'" from="some1@localhost"/>'+
                         '       <delay xmlns="urn:xmpp:delay" stamp="'+moment().format()+'" from="some1@localhost"/>'+
                         '</message>').firstChild;
                         '</message>').firstChild;
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
+                    jasmine.clock().uninstall();
+                    return test_utils.waitUntil(() => view.model.messages.length > 1);
+                }).then(() => {
+                    jasmine.clock().install();
+                    jasmine.clock().mockDate(baseTime);
+                    jasmine.clock().tick(ONE_DAY_LATER);
+                    jasmine.clock().tick(ONE_DAY_LATER);
+                    jasmine.clock().tick(ONE_DAY_LATER);
+                    jasmine.clock().tick(ONE_DAY_LATER);
                     jasmine.clock().tick(ONE_DAY_LATER);
                     jasmine.clock().tick(ONE_DAY_LATER);
-
                     // Test a user leaving a groupchat
                     // Test a user leaving a groupchat
-                    presence = $pres({
+                    const presence = $pres({
                             to: 'dummy@localhost/_converse.js-29092160',
                             to: 'dummy@localhost/_converse.js-29092160',
                             type: 'unavailable',
                             type: 'unavailable',
                             from: 'coven@chat.shakespeare.lit/newguy'
                             from: 'coven@chat.shakespeare.lit/newguy'
@@ -754,10 +769,10 @@
                             });
                             });
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
 
 
-                    time = chat_content.querySelectorAll('time.separator-text');
+                    const time = chat_content.querySelectorAll('time.separator-text');
                     expect(time.length).toEqual(6);
                     expect(time.length).toEqual(6);
 
 
-                    $indicator = $chat_content.find('.date-separator:eq(5)');
+                    const $indicator = $chat_content.find('.date-separator:eq(5)');
                     expect($indicator.attr('class')).toEqual('message date-separator');
                     expect($indicator.attr('class')).toEqual('message date-separator');
                     expect($indicator.data('isodate')).toEqual(moment().startOf('day').format());
                     expect($indicator.data('isodate')).toEqual(moment().startOf('day').format());
 
 
@@ -766,11 +781,9 @@
                     expect($chat_content.find('div.chat-info:last').html()).toBe(
                     expect($chat_content.find('div.chat-info:last').html()).toBe(
                         'newguy has left the groupchat. '+
                         'newguy has left the groupchat. '+
                         '"Disconnected: Replaced by new connection"');
                         '"Disconnected: Replaced by new connection"');
-
                     jasmine.clock().uninstall();
                     jasmine.clock().uninstall();
                     done();
                     done();
-                    return;
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             it("shows its description in the chat heading",
             it("shows its description in the chat heading",
@@ -827,16 +840,16 @@
                     null, ['rosterGroupsFetched'], {},
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
                     function (done, _converse) {
 
 
+                let view;
                 test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
                 test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
-                .then(function () {
-                    return test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'))
-                }).then(function () {
+                .then(() => test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname')))
+                .then(() => {
                     test_utils.createContacts(_converse, 'current');
                     test_utils.createContacts(_converse, 'current');
                     return test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                     return test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
-                }).then(function () {
-                    var view = _converse.chatboxviews.get('lounge@localhost');
+                }).then(() => {
+                    view = _converse.chatboxviews.get('lounge@localhost');
                     if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); }
                     if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); }
-                    var message = '/me is tired';
+                    const message = '/me is tired';
                     var nick = mock.chatroom_names[0],
                     var nick = mock.chatroom_names[0],
                         msg = $msg({
                         msg = $msg({
                             'from': 'lounge@localhost/'+nick,
                             'from': 'lounge@localhost/'+nick,
@@ -845,17 +858,21 @@
                             'type': 'groupchat'
                             'type': 'groupchat'
                         }).c('body').t(message).tree();
                         }).c('body').t(message).tree();
                     view.model.onMessage(msg);
                     view.model.onMessage(msg);
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
                     expect(_.includes($(view.el).find('.chat-msg__author').text(), '**Dyon van de Wege')).toBeTruthy();
                     expect(_.includes($(view.el).find('.chat-msg__author').text(), '**Dyon van de Wege')).toBeTruthy();
                     expect($(view.el).find('.chat-msg__text').text()).toBe(' is tired');
                     expect($(view.el).find('.chat-msg__text').text()).toBe(' is tired');
 
 
-                    message = '/me is as well';
-                    msg = $msg({
+                    const message = '/me is as well';
+                    const msg = $msg({
                         from: 'lounge@localhost/Max Mustermann',
                         from: 'lounge@localhost/Max Mustermann',
                         id: (new Date()).getTime(),
                         id: (new Date()).getTime(),
                         to: 'dummy@localhost',
                         to: 'dummy@localhost',
                         type: 'groupchat'
                         type: 'groupchat'
                     }).c('body').t(message).tree();
                     }).c('body').t(message).tree();
                     view.model.onMessage(msg);
                     view.model.onMessage(msg);
+                    return test_utils.waitUntil(() => view.model.messages.length > 1);
+                }).then(() => {
                     expect(_.includes($(view.el).find('.chat-msg__author:last').text(), '**Max Mustermann')).toBeTruthy();
                     expect(_.includes($(view.el).find('.chat-msg__author:last').text(), '**Max Mustermann')).toBeTruthy();
                     expect($(view.el).find('.chat-msg__text:last').text()).toBe(' is as well');
                     expect($(view.el).find('.chat-msg__text:last').text()).toBe(' is as well');
                     done();
                     done();
@@ -1495,20 +1512,18 @@
                     null, ['rosterGroupsFetched'], {},
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
                     function (done, _converse) {
 
 
+                let view;
+                const text = 'This is a received message';
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
                     spyOn(_converse, 'emit');
                     spyOn(_converse, 'emit');
-                    var view = _converse.chatboxviews.get('lounge@localhost');
-
-
+                    view = _converse.chatboxviews.get('lounge@localhost');
                     if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); }
                     if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); }
                     var nick = mock.chatroom_names[0];
                     var nick = mock.chatroom_names[0];
-
                     view.model.occupants.create({
                     view.model.occupants.create({
                         'nick': nick,
                         'nick': nick,
                         'muc_jid': `${view.model.get('jid')}/${nick}`
                         'muc_jid': `${view.model.get('jid')}/${nick}`
                     });
                     });
 
 
-                    var text = 'This is a received message';
                     var message = $msg({
                     var message = $msg({
                         from: 'lounge@localhost/'+nick,
                         from: 'lounge@localhost/'+nick,
                         id: '1',
                         id: '1',
@@ -1516,6 +1531,8 @@
                         type: 'groupchat'
                         type: 'groupchat'
                     }).c('body').t(text);
                     }).c('body').t(text);
                     view.model.onMessage(message.nodeTree);
                     view.model.onMessage(message.nodeTree);
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
                     var $chat_content = $(view.el).find('.chat-content');
                     var $chat_content = $(view.el).find('.chat-content');
                     expect($chat_content.find('.chat-msg').length).toBe(1);
                     expect($chat_content.find('.chat-msg').length).toBe(1);
                     expect($chat_content.find('.chat-msg__text').text()).toBe(text);
                     expect($chat_content.find('.chat-msg__text').text()).toBe(text);
@@ -1568,9 +1585,10 @@
                     null, ['rosterGroupsFetched'], {},
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
                     function (done, _converse) {
 
 
+                let view;
                 var message = 'This message is received while the chat area is scrolled up';
                 var message = 'This message is received while the chat area is scrolled up';
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
-                    var view = _converse.chatboxviews.get('lounge@localhost');
+                    view = _converse.chatboxviews.get('lounge@localhost');
                     spyOn(view, 'scrollDown').and.callThrough();
                     spyOn(view, 'scrollDown').and.callThrough();
                     /* Create enough messages so that there's a
                     /* Create enough messages so that there's a
                     * scrollbar.
                     * scrollbar.
@@ -1584,8 +1602,10 @@
                                 id: (new Date()).getTime(),
                                 id: (new Date()).getTime(),
                             }).c('body').t('Message: '+i).tree());
                             }).c('body').t('Message: '+i).tree());
                     }
                     }
+                    return test_utils.waitUntil(() => view.model.messages.length === 20);
+                }).then(() => {
                     // Give enough time for `markScrolled` to have been called
                     // Give enough time for `markScrolled` to have been called
-                    setTimeout(function () {
+                    setTimeout(() => {
                         view.content.scrollTop = 0;
                         view.content.scrollTop = 0;
                         view.model.onMessage(
                         view.model.onMessage(
                             $msg({
                             $msg({
@@ -1595,12 +1615,15 @@
                                 id: (new Date()).getTime(),
                                 id: (new Date()).getTime(),
                             }).c('body').t(message).tree());
                             }).c('body').t(message).tree());
 
 
-                        // Now check that the message appears inside the chatbox in the DOM
-                        var $chat_content = $(view.el).find('.chat-content');
-                        var msg_txt = $chat_content.find('.chat-msg:last').find('.chat-msg__text').text();
-                        expect(msg_txt).toEqual(message);
-                        expect(view.content.scrollTop).toBe(0);
-                        done();
+                        test_utils.waitUntil(() => view.model.messages.length === 21)
+                        .then(() => {
+                            // Now check that the message appears inside the chatbox in the DOM
+                            var $chat_content = $(view.el).find('.chat-content');
+                            var msg_txt = $chat_content.find('.chat-msg:last').find('.chat-msg__text').text();
+                            expect(msg_txt).toEqual(message);
+                            expect(view.content.scrollTop).toBe(0);
+                            done();
+                        }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
                     }, 500);
                     }, 500);
                 });
                 });
             }));
             }));
@@ -3435,19 +3458,19 @@
                 var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
                 var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
                 expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(0);
                 expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(0);
 
 
-                var room_jid = 'kitchen@conference.shakespeare.lit';
-                test_utils.openAndEnterChatRoom(
-                        _converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
-
+                let view, nick;
+                const room_jid = 'kitchen@conference.shakespeare.lit';
+                const message = 'fires: Your attention is required';
+                test_utils.openAndEnterChatRoom(_converse, 'kitchen', 'conference.shakespeare.lit', 'fires')
+                .then(() => {
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
 
 
-                    var view = _converse.chatboxviews.get(room_jid);
+                    view = _converse.chatboxviews.get(room_jid);
                     view.model.set({'minimized': true});
                     view.model.set({'minimized': true});
 
 
                     var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
                     var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
-                    var message = 'fires: Your attention is required';
-                    var nick = mock.chatroom_names[0];
+                    nick = mock.chatroom_names[0];
 
 
                     view.model.onMessage($msg({
                     view.model.onMessage($msg({
                             from: room_jid+'/'+nick,
                             from: room_jid+'/'+nick,
@@ -3455,7 +3478,8 @@
                             to: 'dummy@localhost',
                             to: 'dummy@localhost',
                             type: 'groupchat'
                             type: 'groupchat'
                         }).c('body').t(message).tree());
                         }).c('body').t(message).tree());
-
+                    return test_utils.waitUntil(() => view.model.messages.length);
+                }).then(() => {
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
                     expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('1');
                     expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('1');
@@ -3466,17 +3490,16 @@
                         'to': 'dummy@localhost',
                         'to': 'dummy@localhost',
                         'type': 'groupchat'
                         'type': 'groupchat'
                     }).c('body').t(message).tree());
                     }).c('body').t(message).tree());
-
+                    return test_utils.waitUntil(() => view.model.messages.length > 1);
+                }).then(() => {
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
                     expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('2');
                     expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('2');
-
                     view.model.set({'minimized': false});
                     view.model.set({'minimized': false});
-
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
                     expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
             }));
             }));
 
 
             describe("A Chat Status Notification", function () {
             describe("A Chat Status Notification", function () {
@@ -3488,11 +3511,11 @@
                             null, ['rosterGroupsFetched'], {},
                             null, ['rosterGroupsFetched'], {},
                             function (done, _converse) {
                             function (done, _converse) {
 
 
-                        test_utils.openAndEnterChatRoom(
-                                _converse, 'coven', 'chat.shakespeare.lit', 'some1').then(function () {
-
-                            var room_jid = 'coven@chat.shakespeare.lit';
-                            var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
+                        let view;
+                        const room_jid = 'coven@chat.shakespeare.lit';
+                        test_utils.openAndEnterChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'some1')
+                        .then(() => {
+                            view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
                             var $chat_content = $(view.el).find('.chat-content');
                             var $chat_content = $(view.el).find('.chat-content');
 
 
                             expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
                             expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
@@ -3534,9 +3557,9 @@
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
-
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
-
+                            return test_utils.waitUntil(() => view.model.messages.length);
+                        }).then(() => {
                             // Check that the notification appears inside the chatbox in the DOM
                             // Check that the notification appears inside the chatbox in the DOM
                             var events = view.el.querySelectorAll('.chat-event');
                             var events = view.el.querySelectorAll('.chat-event');
                             expect(events.length).toBe(3);
                             expect(events.length).toBe(3);
@@ -3548,91 +3571,63 @@
                             expect(notifications.length).toBe(1);
                             expect(notifications.length).toBe(1);
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[0].textContent).toEqual('newguy is typing');
 
 
-                            const timeout_functions = [];
-                            spyOn(window, 'setTimeout').and.callFake(function (func, delay) {
-                                timeout_functions.push(func);
-                            });
-
                             // Check that it doesn't appear twice
                             // Check that it doesn't appear twice
-                            msg = $msg({
+                            const msg = $msg({
                                     from: room_jid+'/newguy',
                                     from: room_jid+'/newguy',
                                     id: (new Date()).getTime(),
                                     id: (new Date()).getTime(),
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
-
-                            events = view.el.querySelectorAll('.chat-event');
+                            return test_utils.waitUntil(() => view.model.messages.length > 1);
+                        }).then(() => {
+                            const events = view.el.querySelectorAll('.chat-event');
                             expect(events.length).toBe(3);
                             expect(events.length).toBe(3);
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
 
 
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
+                            const notifications = view.el.querySelectorAll('.chat-state-notification');
                             expect(notifications.length).toBe(1);
                             expect(notifications.length).toBe(1);
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[0].textContent).toEqual('newguy is typing');
-
-                            expect(timeout_functions.length).toBe(1);
-
                             // <composing> state for a different occupant
                             // <composing> state for a different occupant
-                            msg = $msg({
+                            const msg = $msg({
                                     from: room_jid+'/nomorenicks',
                                     from: room_jid+'/nomorenicks',
                                     id: (new Date()).getTime(),
                                     id: (new Date()).getTime(),
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
-                            events = view.el.querySelectorAll('.chat-event');
+                            return test_utils.waitUntil(() => view.model.messages.length > 2);
+                        }).then(() => {
+                            const events = view.el.querySelectorAll('.chat-event');
                             expect(events.length).toBe(3);
                             expect(events.length).toBe(3);
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
 
 
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
+                            const notifications = view.el.querySelectorAll('.chat-state-notification');
                             expect(notifications.length).toBe(2);
                             expect(notifications.length).toBe(2);
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[1].textContent).toEqual('nomorenicks is typing');
                             expect(notifications[1].textContent).toEqual('nomorenicks is typing');
-                            expect(timeout_functions.length).toBe(2);
 
 
                             // Check that new messages appear under the chat state
                             // Check that new messages appear under the chat state
                             // notifications
                             // notifications
-                            msg = $msg({
+                            const msg = $msg({
                                 from: 'lounge@localhost/some1',
                                 from: 'lounge@localhost/some1',
                                 id: (new Date()).getTime(),
                                 id: (new Date()).getTime(),
                                 to: 'dummy@localhost',
                                 to: 'dummy@localhost',
                                 type: 'groupchat'
                                 type: 'groupchat'
                             }).c('body').t('hello world').tree();
                             }).c('body').t('hello world').tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
-
-                            var messages = view.el.querySelectorAll('.message');
+                            return test_utils.waitUntil(() => view.model.messages.length);
+                        }).then(() => {
+                            const messages = view.el.querySelectorAll('.message');
                             expect(messages.length).toBe(7);
                             expect(messages.length).toBe(7);
                             expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
                             expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
                             expect(view.el.querySelector('.chat-msg .chat-msg__text').textContent).toBe('hello world');
                             expect(view.el.querySelector('.chat-msg .chat-msg__text').textContent).toBe('hello world');
-
-                            // Test that the composing notifications get removed
-                            // via timeout.
-                            timeout_functions[0]();
-                            events = view.el.querySelectorAll('.chat-event');
-                            expect(events.length).toBe(3);
-                            expect(events[0].textContent).toEqual('some1 has entered the groupchat');
-                            expect(events[1].textContent).toEqual('newguy has entered the groupchat');
-                            expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
-
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
-                            expect(notifications.length).toBe(1);
-                            expect(notifications[0].textContent).toEqual('nomorenicks is typing');
-
-                            timeout_functions[1]();
-                            events = view.el.querySelectorAll('.chat-event');
-                            expect(events.length).toBe(3);
-                            expect(events[0].textContent).toEqual('some1 has entered the groupchat');
-                            expect(events[1].textContent).toEqual('newguy has entered the groupchat');
-                            expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
-
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
-                            expect(notifications.length).toBe(0);
                             done();
                             done();
-                        });
+                        }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
                     }));
                     }));
                 });
                 });
 
 
@@ -3642,10 +3637,11 @@
                                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                                 function (done, _converse) {
                                 function (done, _converse) {
 
 
+                        let view, msg
+                        const room_jid = 'coven@chat.shakespeare.lit';
                         test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1')
                         test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1')
                         .then(() => {
                         .then(() => {
-                            var room_jid = 'coven@chat.shakespeare.lit';
-                            var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
+                            view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
                             var $chat_content = $(view.el).find('.chat-content');
                             var $chat_content = $(view.el).find('.chat-content');
 
 
                             /* <presence to="dummy@localhost/_converse.js-29092160"
                             /* <presence to="dummy@localhost/_converse.js-29092160"
@@ -3700,13 +3696,15 @@
                             // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
                             // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
 
 
                             // <composing> state
                             // <composing> state
-                            var msg = $msg({
+                            const msg = $msg({
                                     from: room_jid+'/newguy',
                                     from: room_jid+'/newguy',
                                     id: (new Date()).getTime(),
                                     id: (new Date()).getTime(),
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
+                            return test_utils.waitUntil(() => view.model.messages.length);
+                        }).then(() => {
 
 
                             // Check that the notification appears inside the chatbox in the DOM
                             // Check that the notification appears inside the chatbox in the DOM
                             var events = view.el.querySelectorAll('.chat-event');
                             var events = view.el.querySelectorAll('.chat-event');
@@ -3720,63 +3718,69 @@
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[0].textContent).toEqual('newguy is typing');
 
 
                             // Check that it doesn't appear twice
                             // Check that it doesn't appear twice
-                            msg = $msg({
+                            const msg = $msg({
                                     from: room_jid+'/newguy',
                                     from: room_jid+'/newguy',
                                     id: (new Date()).getTime(),
                                     id: (new Date()).getTime(),
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
+                            return test_utils.waitUntil(() => view.model.messages.length > 1);
+                        }).then(() => {
 
 
-                            events = view.el.querySelectorAll('.chat-event');
+                            const events = view.el.querySelectorAll('.chat-event');
                             expect(events.length).toBe(3);
                             expect(events.length).toBe(3);
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
 
 
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
+                            const notifications = view.el.querySelectorAll('.chat-state-notification');
                             expect(notifications.length).toBe(1);
                             expect(notifications.length).toBe(1);
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[0].textContent).toEqual('newguy is typing');
 
 
                             // <composing> state for a different occupant
                             // <composing> state for a different occupant
-                            msg = $msg({
+                            const msg = $msg({
                                     from: room_jid+'/nomorenicks',
                                     from: room_jid+'/nomorenicks',
                                     id: (new Date()).getTime(),
                                     id: (new Date()).getTime(),
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
-                            events = view.el.querySelectorAll('.chat-event');
+                            return test_utils.waitUntil(() => view.model.messages.length > 2);
+                        }).then(() => {
+                            const events = view.el.querySelectorAll('.chat-event');
                             expect(events.length).toBe(3);
                             expect(events.length).toBe(3);
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
 
 
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
+                            const notifications = view.el.querySelectorAll('.chat-state-notification');
                             expect(notifications.length).toBe(2);
                             expect(notifications.length).toBe(2);
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[0].textContent).toEqual('newguy is typing');
                             expect(notifications[1].textContent).toEqual('nomorenicks is typing');
                             expect(notifications[1].textContent).toEqual('nomorenicks is typing');
 
 
                             // <paused> state from occupant who typed first
                             // <paused> state from occupant who typed first
-                            msg = $msg({
+                            const msg = $msg({
                                     from: room_jid+'/newguy',
                                     from: room_jid+'/newguy',
                                     id: (new Date()).getTime(),
                                     id: (new Date()).getTime(),
                                     to: 'dummy@localhost',
                                     to: 'dummy@localhost',
                                     type: 'groupchat'
                                     type: 'groupchat'
                                 }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                                 }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
                             view.model.onMessage(msg);
                             view.model.onMessage(msg);
-                            events = view.el.querySelectorAll('.chat-event');
+                            return test_utils.waitUntil(() => view.model.messages.length > 3);
+                        }).then(() => {
+                            const events = view.el.querySelectorAll('.chat-event');
                             expect(events.length).toBe(3);
                             expect(events.length).toBe(3);
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[0].textContent).toEqual('some1 has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[1].textContent).toEqual('newguy has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
                             expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
 
 
-                            notifications = view.el.querySelectorAll('.chat-state-notification');
+                            const notifications = view.el.querySelectorAll('.chat-state-notification');
                             expect(notifications.length).toBe(2);
                             expect(notifications.length).toBe(2);
                             expect(notifications[0].textContent).toEqual('nomorenicks is typing');
                             expect(notifications[0].textContent).toEqual('nomorenicks is typing');
                             expect(notifications[1].textContent).toEqual('newguy has stopped typing');
                             expect(notifications[1].textContent).toEqual('newguy has stopped typing');
                             done();
                             done();
-                        });
+                        }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
                     }));
                     }));
                 });
                 });
             });
             });

+ 16 - 11
spec/controlbox.js

@@ -1,11 +1,12 @@
 (function (root, factory) {
 (function (root, factory) {
     define(["jquery", "jasmine", "mock", "test-utils"], factory);
     define(["jquery", "jasmine", "mock", "test-utils"], factory);
 } (this, function ($, jasmine, mock, test_utils) {
 } (this, function ($, jasmine, mock, test_utils) {
-    var _ = converse.env._;
-    var $pres = converse.env.$pres;
-    var $msg = converse.env.$msg;
-    var $iq = converse.env.$iq;
-    var u = converse.env.utils;
+    const _ = converse.env._,
+          $pres = converse.env.$pres,
+          $msg = converse.env.$msg,
+          $iq = converse.env.$iq,
+          u = converse.env.utils,
+          Strophe = converse.env.Strophe;
 
 
 
 
     describe("The Controlbox", function () {
     describe("The Controlbox", function () {
@@ -72,18 +73,18 @@
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 _converse.emit('rosterContactsFetched');
                 _converse.emit('rosterContactsFetched');
 
 
+                let chatview;
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
 
                 test_utils.openChatBoxFor(_converse, sender_jid);
                 test_utils.openChatBoxFor(_converse, sender_jid);
                 return test_utils.waitUntil(() => _converse.chatboxes.length).then(() => {
                 return test_utils.waitUntil(() => _converse.chatboxes.length).then(() => {
-
-                    const chatview = _converse.chatboxviews.get(sender_jid);
+                    chatview = _converse.chatboxviews.get(sender_jid);
                     chatview.model.set({'minimized': true});
                     chatview.model.set({'minimized': true});
 
 
                     expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                     expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                     expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
                     expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
 
 
-                    var msg = $msg({
+                    const msg = $msg({
                             from: sender_jid,
                             from: sender_jid,
                             to: _converse.connection.jid,
                             to: _converse.connection.jid,
                             type: 'chat',
                             type: 'chat',
@@ -91,10 +92,13 @@
                         }).c('body').t('hello').up()
                         }).c('body').t('hello').up()
                         .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                         .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
+                    return test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll(".msgs-indicator"));
+                }).then(() => {
+                    spyOn(chatview.model, 'incrementUnreadMsgCounter').and.callThrough();
                     expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
                     expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
                     expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
                     expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
 
 
-                    msg = $msg({
+                    const msg = $msg({
                             from: sender_jid,
                             from: sender_jid,
                             to: _converse.connection.jid,
                             to: _converse.connection.jid,
                             type: 'chat',
                             type: 'chat',
@@ -102,14 +106,15 @@
                         }).c('body').t('hello again').up()
                         }).c('body').t('hello again').up()
                         .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                         .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
+                return test_utils.waitUntil(() => chatview.model.incrementUnreadMsgCounter.calls.count());
+            }).then(() => {
                     expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
                     expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
                     expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
                     expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
-
                     chatview.model.set({'minimized': false});
                     chatview.model.set({'minimized': false});
                     expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                     expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                     expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
                     expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             }));
             }));
         });
         });
 
 

+ 23 - 7
spec/mam.js

@@ -20,15 +20,27 @@
                     null, ['discoInitialized'], {},
                     null, ['discoInitialized'], {},
                     function (done, _converse) {
                     function (done, _converse) {
 
 
-                test_utils.openAndEnterChatRoom(_converse, 'trek-radio', 'conference.lightwitch.org', 'jcbrand').then(function () {
-                    var chatroomview = _converse.chatboxviews.get('trek-radio@conference.lightwitch.org');
-                    var stanza = Strophe.xmlHtmlNode(
+                let view, stanza;
+
+                test_utils.openAndEnterChatRoom(_converse, 'trek-radio', 'conference.lightwitch.org', 'jcbrand')
+                .then(() => {
+                    view = _converse.chatboxviews.get('trek-radio@conference.lightwitch.org');
+                    stanza = Strophe.xmlHtmlNode(
                         `<message xmlns="jabber:client" to="jcbrand@lightwitch.org/converse.js-73057452" type="groupchat" from="trek-radio@conference.lightwitch.org/comndrdukath#0805 (STO)">
                         `<message xmlns="jabber:client" to="jcbrand@lightwitch.org/converse.js-73057452" type="groupchat" from="trek-radio@conference.lightwitch.org/comndrdukath#0805 (STO)">
                             <body>negan</body>
                             <body>negan</body>
                             <stanza-id xmlns="urn:xmpp:sid:0" id="45fbbf2a-1059-479d-9283-c8effaf05621" by="trek-radio@conference.lightwitch.org"/>
                             <stanza-id xmlns="urn:xmpp:sid:0" id="45fbbf2a-1059-479d-9283-c8effaf05621" by="trek-radio@conference.lightwitch.org"/>
                          </message>`).firstElementChild;
                          </message>`).firstElementChild;
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
+                    return test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length)
+                }).then(() => {
+                    // XXX: we wait here until the first message appears before
+                    // sending the duplicate. If we don't do that, then the
+                    // duplicate appears before the promise for `createMessage`
+                    // has been resolved, which means that the `isDuplicate`
+                    // check fails because the first message doesn't exist yet.
+                    //
+                    // Not sure whether such a race-condition might pose a problem
+                    // in "real-world" situations.
                     stanza = Strophe.xmlHtmlNode(
                     stanza = Strophe.xmlHtmlNode(
                         `<message xmlns="jabber:client" to="jcbrand@lightwitch.org/converse.js-73057452">
                         `<message xmlns="jabber:client" to="jcbrand@lightwitch.org/converse.js-73057452">
                             <result xmlns="urn:xmpp:mam:2" queryid="82d9db27-6cf8-4787-8c2c-5a560263d823" id="45fbbf2a-1059-479d-9283-c8effaf05621">
                             <result xmlns="urn:xmpp:mam:2" queryid="82d9db27-6cf8-4787-8c2c-5a560263d823" id="45fbbf2a-1059-479d-9283-c8effaf05621">
@@ -39,10 +51,14 @@
                                 </forwarded>
                                 </forwarded>
                             </result>
                             </result>
                         </message>`).firstElementChild;
                         </message>`).firstElementChild;
-                    chatroomview.model.onMessage(stanza);
-                    expect(chatroomview.content.querySelectorAll('.chat-msg').length).toBe(1);
+
+                    spyOn(view.model, 'isDuplicate').and.callThrough();
+                    view.model.onMessage(stanza);
+                    return test_utils.waitUntil(() => view.model.isDuplicate.calls.count());
+                }).then(() => {
+                    expect(view.content.querySelectorAll('.chat-msg').length).toBe(1);
                     done();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             }))
             }))
         });
         });
 
 

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 292 - 252
spec/messages.js


+ 19 - 15
spec/minchats.js

@@ -4,6 +4,7 @@
     const _ = converse.env._;
     const _ = converse.env._;
     const  $msg = converse.env.$msg;
     const  $msg = converse.env.$msg;
     const u = converse.env.utils;
     const u = converse.env.utils;
+    const Strophe = converse.env.Strophe;
 
 
     describe("The Minimized Chats Widget", function () {
     describe("The Minimized Chats Widget", function () {
 
 
@@ -43,7 +44,7 @@
                 expect(_converse.minimized_chats.keys().length).toBe(2);
                 expect(_converse.minimized_chats.keys().length).toBe(2);
                 expect(_.includes(_converse.minimized_chats.keys(), contact_jid)).toBeTruthy();
                 expect(_.includes(_converse.minimized_chats.keys(), contact_jid)).toBeTruthy();
                 done();
                 done();
-            });
+            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
         }));
         }));
 
 
         it("can be toggled to hide or show minimized chats",
         it("can be toggled to hide or show minimized chats",
@@ -74,7 +75,7 @@
             }).then(() => {
             }).then(() => {
                 expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeTruthy();
                 expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeTruthy();
                 done();
                 done();
-            });
+            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
         }));
         }));
 
 
         it("shows the number messages received to minimized chats",
         it("shows the number messages received to minimized chats",
@@ -99,7 +100,7 @@
                 contact_jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
                 contact_jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
                 test_utils.openChatBoxFor(_converse, contact_jid);
                 test_utils.openChatBoxFor(_converse, contact_jid);
             }
             }
-            return test_utils.waitUntil(() => _converse.chatboxes.length == 4).then(() => {
+            test_utils.waitUntil(() => _converse.chatboxes.length == 4).then(() => {
                 for (i=0; i<3; i++) {
                 for (i=0; i<3; i++) {
                     chatview = _converse.chatboxviews.get(contact_jid);
                     chatview = _converse.chatboxviews.get(contact_jid);
                     chatview.model.set({'minimized': true});
                     chatview.model.set({'minimized': true});
@@ -111,9 +112,11 @@
                     }).c('body').t('This message is sent to a minimized chatbox').up()
                     }).c('body').t('This message is sent to a minimized chatbox').up()
                     .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                     .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                     _converse.chatboxes.onMessage(msg);
                     _converse.chatboxes.onMessage(msg);
-                    expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeTruthy();
-                    expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i+1).toString());
                 }
                 }
+                return test_utils.waitUntil(() => chatview.model.messages.length);
+            }).then(() => {
+                expect(u.isVisible(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count'))).toBeTruthy();
+                expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((3).toString());
                 // Chat state notifications don't increment the unread messages counter
                 // Chat state notifications don't increment the unread messages counter
                 // <composing> state
                 // <composing> state
                 _converse.chatboxes.onMessage($msg({
                 _converse.chatboxes.onMessage($msg({
@@ -122,7 +125,7 @@
                     type: 'chat',
                     type: 'chat',
                     id: (new Date()).getTime()
                     id: (new Date()).getTime()
                 }).c('composing', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
                 }).c('composing', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
-                expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
+                expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
 
 
                 // <paused> state
                 // <paused> state
                 _converse.chatboxes.onMessage($msg({
                 _converse.chatboxes.onMessage($msg({
@@ -131,7 +134,7 @@
                     type: 'chat',
                     type: 'chat',
                     id: (new Date()).getTime()
                     id: (new Date()).getTime()
                 }).c('paused', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
                 }).c('paused', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
-                expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
+                expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
 
 
                 // <gone> state
                 // <gone> state
                 _converse.chatboxes.onMessage($msg({
                 _converse.chatboxes.onMessage($msg({
@@ -140,7 +143,7 @@
                     type: 'chat',
                     type: 'chat',
                     id: (new Date()).getTime()
                     id: (new Date()).getTime()
                 }).c('gone', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
                 }).c('gone', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
-                expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
+                expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
 
 
                 // <inactive> state
                 // <inactive> state
                 _converse.chatboxes.onMessage($msg({
                 _converse.chatboxes.onMessage($msg({
@@ -149,9 +152,9 @@
                     type: 'chat',
                     type: 'chat',
                     id: (new Date()).getTime()
                     id: (new Date()).getTime()
                 }).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
                 }).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
-                expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
+                expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
                 done();
                 done();
-            });
+            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
         }));
         }));
 
 
         it("shows the number messages received to minimized groupchats",
         it("shows the number messages received to minimized groupchats",
@@ -159,7 +162,7 @@
                 null, ['rosterGroupsFetched'], {},
                 null, ['rosterGroupsFetched'], {},
                 function (done, _converse) {
                 function (done, _converse) {
 
 
-            var room_jid = 'kitchen@conference.shakespeare.lit';
+            const room_jid = 'kitchen@conference.shakespeare.lit';
             test_utils.openAndEnterChatRoom(
             test_utils.openAndEnterChatRoom(
                 _converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
                 _converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
                 var view = _converse.chatboxviews.get(room_jid);
                 var view = _converse.chatboxviews.get(room_jid);
@@ -175,11 +178,12 @@
                         type: 'groupchat'
                         type: 'groupchat'
                     }).c('body').t(message).tree();
                     }).c('body').t(message).tree();
                 view.model.onMessage(msg);
                 view.model.onMessage(msg);
-
-                expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeTruthy();
-                expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe('1');
+                return test_utils.waitUntil(() => view.model.messages.length);
+            }).then(() => {
+                expect(u.isVisible(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count'))).toBeTruthy();
+                expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe('1');
                 done();
                 done();
-            });
+            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
         }));
         }));
     });
     });
 }));
 }));

+ 13 - 7
spec/roomslist.js

@@ -229,14 +229,16 @@
                                          // have to mock stanza traffic.
                                          // have to mock stanza traffic.
                 }, function (done, _converse) {
                 }, function (done, _converse) {
 
 
+            let view, nick;
+            const room_jid = 'kitchen@conference.shakespeare.lit';
+
             test_utils.waitUntil(() => !_.isUndefined(_converse.rooms_list_view), 500)
             test_utils.waitUntil(() => !_.isUndefined(_converse.rooms_list_view), 500)
             .then(() => test_utils.openAndEnterChatRoom(_converse, 'kitchen', 'conference.shakespeare.lit', 'romeo'))
             .then(() => test_utils.openAndEnterChatRoom(_converse, 'kitchen', 'conference.shakespeare.lit', 'romeo'))
             .then(() => {
             .then(() => {
-                const room_jid = 'kitchen@conference.shakespeare.lit';
-                const view = _converse.chatboxviews.get(room_jid);
+                view = _converse.chatboxviews.get(room_jid);
                 view.model.set({'minimized': true});
                 view.model.set({'minimized': true});
                 const 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];
+                nick = mock.chatroom_names[0];
                 view.model.onMessage(
                 view.model.onMessage(
                     $msg({
                     $msg({
                         from: room_jid+'/'+nick,
                         from: room_jid+'/'+nick,
@@ -260,9 +262,11 @@
                         type: 'groupchat'
                         type: 'groupchat'
                     }).c('body').t('romeo: Your attention is required').tree()
                     }).c('body').t('romeo: Your attention is required').tree()
                 );
                 );
-                var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                return test_utils.waitUntil(() => _converse.rooms_list_view.el.querySelectorAll(".msgs-indicator"));
+            }).then(() => {
+                spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough();
+                const indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
                 expect(indicator_el.textContent).toBe('1');
                 expect(indicator_el.textContent).toBe('1');
-
                 view.model.onMessage(
                 view.model.onMessage(
                     $msg({
                     $msg({
                         from: room_jid+'/'+nick,
                         from: room_jid+'/'+nick,
@@ -271,14 +275,16 @@
                         type: 'groupchat'
                         type: 'groupchat'
                     }).c('body').t('romeo: and another thing...').tree()
                     }).c('body').t('romeo: and another thing...').tree()
                 );
                 );
-                indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                return test_utils.waitUntil(() => view.model.incrementUnreadMsgCounter.calls.count());
+            }).then(() => {
+                let indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
                 expect(indicator_el.textContent).toBe('2');
                 expect(indicator_el.textContent).toBe('2');
 
 
                 // When the chat gets maximized again, the unread indicators are removed
                 // When the chat gets maximized again, the unread indicators are removed
                 view.model.set({'minimized': false});
                 view.model.set({'minimized': false});
                 indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
                 indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
                 expect(_.isNull(indicator_el));
                 expect(_.isNull(indicator_el));
-                room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
+                const room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
                 expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
                 expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
                 done();
                 done();
             }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));

+ 6 - 5
src/converse-chatboxes.js

@@ -497,8 +497,6 @@
                                 stanza.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE ||
                                 stanza.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE ||
                                 stanza.getElementsByTagName(_converse.GONE).length && _converse.GONE;
                                 stanza.getElementsByTagName(_converse.GONE).length && _converse.GONE;
 
 
-
-
                     const attrs = {
                     const attrs = {
                         'chat_state': chat_state,
                         'chat_state': chat_state,
                         'is_archived': !_.isNil(archive),
                         'is_archived': !_.isNil(archive),
@@ -552,7 +550,7 @@
                             } else {
                             } else {
                                 resolve(this.messages.create(attrs));
                                 resolve(this.messages.create(attrs));
                             }
                             }
-                        });
+                        }).catch(e => reject(e))
                     });
                     });
                 },
                 },
 
 
@@ -729,8 +727,11 @@
                     if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
                     if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
                         const msgid = stanza.getAttribute('id'),
                         const msgid = stanza.getAttribute('id'),
                               message = msgid && chatbox.messages.findWhere({msgid});
                               message = msgid && chatbox.messages.findWhere({msgid});
-                        if (!message) { // Only create the message when we're sure it's not a duplicate
-                            chatbox.incrementUnreadMsgCounter(chatbox.createMessage(stanza, original_stanza));
+                        if (!message) {
+                            // Only create the message when we're sure it's not a duplicate
+                            chatbox.createMessage(stanza, original_stanza)
+                                .then(msg => chatbox.incrementUnreadMsgCounter(msg))
+                                .catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
                         }
                         }
                     }
                     }
                     _converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
                     _converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});

+ 1 - 0
src/converse-mam.js

@@ -128,6 +128,7 @@
             //
             //
             // New functions which don't exist yet can also be added.
             // New functions which don't exist yet can also be added.
             ChatBox: {
             ChatBox: {
+
                 getMessageAttributesFromStanza (message, original_stanza) {
                 getMessageAttributesFromStanza (message, original_stanza) {
                     return new Promise((resolve, reject) => {
                     return new Promise((resolve, reject) => {
                         this.__super__.getMessageAttributesFromStanza.apply(this, arguments)
                         this.__super__.getMessageAttributesFromStanza.apply(this, arguments)

+ 3 - 1
src/converse-muc.js

@@ -926,7 +926,9 @@
                         if (sender === '') {
                         if (sender === '') {
                             return;
                             return;
                         }
                         }
-                        this.incrementUnreadMsgCounter(this.createMessage(stanza, original_stanza));
+                        this.createMessage(stanza, original_stanza)
+                            .then(msg => this.incrementUnreadMsgCounter(msg))
+                            .catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
                     }
                     }
                     if (sender !== this.get('nick')) {
                     if (sender !== this.get('nick')) {
                         // We only emit an event if it's not our own message
                         // We only emit an event if it's not our own message

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels