瀏覽代碼

`createMessage` now returns a promise.

Also, fix all broken tests, mostly related to this.
JC Brand 7 年之前
父節點
當前提交
ca9229a906
共有 11 個文件被更改,包括 649 次插入530 次删除
  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 {
                 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) {
               // 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;
             }
 
-            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')) {

+ 153 - 113
spec/chatbox.js

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

+ 120 - 116
spec/chatroom.js

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

+ 16 - 11
spec/controlbox.js

@@ -1,11 +1,12 @@
 (function (root, factory) {
     define(["jquery", "jasmine", "mock", "test-utils"], factory);
 } (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 () {
@@ -72,18 +73,18 @@
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 _converse.emit('rosterContactsFetched');
 
+                let chatview;
                 const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
                 test_utils.openChatBoxFor(_converse, sender_jid);
                 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});
 
                     expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                     expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
 
-                    var msg = $msg({
+                    const msg = $msg({
                             from: sender_jid,
                             to: _converse.connection.jid,
                             type: 'chat',
@@ -91,10 +92,13 @@
                         }).c('body').t('hello').up()
                         .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                     _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.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
 
-                    msg = $msg({
+                    const msg = $msg({
                             from: sender_jid,
                             to: _converse.connection.jid,
                             type: 'chat',
@@ -102,14 +106,15 @@
                         }).c('body').t('hello again').up()
                         .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
                     _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.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
-
                     chatview.model.set({'minimized': false});
                     expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
                     expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
                     done();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             }));
         });
 

+ 23 - 7
spec/mam.js

@@ -20,15 +20,27 @@
                     null, ['discoInitialized'], {},
                     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)">
                             <body>negan</body>
                             <stanza-id xmlns="urn:xmpp:sid:0" id="45fbbf2a-1059-479d-9283-c8effaf05621" by="trek-radio@conference.lightwitch.org"/>
                          </message>`).firstElementChild;
                     _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(
                         `<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">
@@ -39,10 +51,14 @@
                                 </forwarded>
                             </result>
                         </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();
-                });
+                }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
             }))
         });
 

文件差異過大導致無法顯示
+ 292 - 252
spec/messages.js


+ 19 - 15
spec/minchats.js

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

+ 13 - 7
spec/roomslist.js

@@ -229,14 +229,16 @@
                                          // have to mock stanza traffic.
                 }, function (done, _converse) {
 
+            let view, nick;
+            const room_jid = 'kitchen@conference.shakespeare.lit';
+
             test_utils.waitUntil(() => !_.isUndefined(_converse.rooms_list_view), 500)
             .then(() => test_utils.openAndEnterChatRoom(_converse, 'kitchen', 'conference.shakespeare.lit', 'romeo'))
             .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});
                 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(
                     $msg({
                         from: room_jid+'/'+nick,
@@ -260,9 +262,11 @@
                         type: 'groupchat'
                     }).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');
-
                 view.model.onMessage(
                     $msg({
                         from: room_jid+'/'+nick,
@@ -271,14 +275,16 @@
                         type: 'groupchat'
                     }).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');
 
                 // When the chat gets maximized again, the unread indicators are removed
                 view.model.set({'minimized': false});
                 indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
                 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();
                 done();
             }).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.GONE).length && _converse.GONE;
 
-
-
                     const attrs = {
                         'chat_state': chat_state,
                         'is_archived': !_.isNil(archive),
@@ -552,7 +550,7 @@
                             } else {
                                 resolve(this.messages.create(attrs));
                             }
-                        });
+                        }).catch(e => reject(e))
                     });
                 },
 
@@ -729,8 +727,11 @@
                     if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
                         const msgid = stanza.getAttribute('id'),
                               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});

+ 1 - 0
src/converse-mam.js

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

+ 3 - 1
src/converse-muc.js

@@ -926,7 +926,9 @@
                         if (sender === '') {
                             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')) {
                         // We only emit an event if it's not our own message

部分文件因文件數量過多而無法顯示