Browse Source

Fix broken tests due to using ES2015 promises

JC Brand 8 năm trước cách đây
mục cha
commit
16d62bf9e4
18 tập tin đã thay đổi với 1086 bổ sung265 xóa
  1. 34 11
      spec/bookmarks.js
  2. 362 85
      spec/chatbox.js
  3. 276 52
      spec/chatroom.js
  4. 238 52
      spec/controlbox.js
  5. 31 25
      spec/converse.js
  6. 7 2
      spec/headline.js
  7. 2 3
      spec/mam.js
  8. 24 4
      spec/minchats.js
  9. 24 4
      spec/notification.js
  10. 14 5
      spec/otr.js
  11. 12 2
      spec/ping.js
  12. 6 1
      spec/presence.js
  13. 32 6
      spec/protocol.js
  14. 2 3
      spec/register.js
  15. 8 4
      spec/roomslist.js
  16. 2 2
      spec/transcripts.js
  17. 2 3
      spec/xmppstatus.js
  18. 10 1
      tests/mock.js

+ 34 - 11
spec/bookmarks.js

@@ -17,7 +17,9 @@
 
     describe("A chat room", function () {
 
-        it("can be bookmarked", mock.initConverse(function (_converse) {
+        it("can be bookmarked", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             var sent_stanza, IQ_id;
             var sendIQ = _converse.connection.sendIQ;
             spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -124,9 +126,12 @@
             _converse.connection._dataRecv(test_utils.createRequest(stanza));
             // We ignore this IQ stanza... (unless it's an error stanza), so
             // nothing to test for here.
+            done();
         }));
 
-        it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverse(function (_converse) {
+        it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             var jid = 'lounge@localhost';
             _converse.bookmarks.create({
                 'jid': jid,
@@ -144,11 +149,14 @@
                 'nick': ' Othello'
             });
             expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
+            done();
         }));
 
         describe("when bookmarked", function () {
 
-            it("displays that it's bookmarked through its bookmark icon", mock.initConverse(function (_converse) {
+            it("displays that it's bookmarked through its bookmark icon", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
                 var $bookmark_icon = view.$('.icon-pushpin');
@@ -157,9 +165,12 @@
                 expect($bookmark_icon.hasClass('button-on')).toBeTruthy();
                 view.model.set('bookmarked', false);
                 expect($bookmark_icon.hasClass('button-on')).toBeFalsy();
+                done();
             }));
 
-            it("can be unbookmarked", mock.initConverse(function (_converse) {
+            it("can be unbookmarked", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 var sent_stanza, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 test_utils.openChatRoom(_converse, 'theplay', 'conference.shakespeare.lit', 'JC');
@@ -216,6 +227,7 @@
                         "</pubsub>"+
                     "</iq>"
                 );
+                done();
             }));
         });
 
@@ -293,8 +305,9 @@
              */
         }));
 
-        it("can be retrieved from the XMPP server",
-                mock.initConverseWithConnectionSpies(['send'], function (_converse) {
+        it("can be retrieved from the XMPP server", mock.initConverseWithPromises(
+            ['send'], ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             /* Client requests all items
              * -------------------------
              *
@@ -366,11 +379,14 @@
             expect(_converse.bookmarks.models.length).toBe(2);
             expect(_converse.bookmarks.findWhere({'jid': 'theplay@conference.shakespeare.lit'}).get('autojoin')).toBe(true);
             expect(_converse.bookmarks.findWhere({'jid': 'another@conference.shakespeare.lit'}).get('autojoin')).toBe(false);
+            done();
         }));
 
         describe("The rooms panel", function () {
 
-            it("shows a list of bookmarks", mock.initConverseWithConnectionSpies(['send'], function (_converse) {
+            it("shows a list of bookmarks", mock.initConverseWithPromises(
+                ['send'], ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 var IQ_id;
                 expect(_.filter(_converse.connection.send.calls.all(), function (call) {
                     var stanza = call.args[0];
@@ -415,10 +431,12 @@
                                     }).c('nick').t('JC').up().up();
                 _converse.connection._dataRecv(test_utils.createRequest(stanza));
                 expect($('#chatrooms dl.bookmarks dd').length).toBe(3);
+                done();
             }));
 
-            it("remembers the toggle state of the bookmarks list",
-                    mock.initConverseWithConnectionSpies(['send'], function (_converse) {
+            it("remembers the toggle state of the bookmarks list", mock.initConverseWithPromises(
+                ['send'], ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 var IQ_id;
                 expect(_.filter(_converse.connection.send.calls.all(), function (call) {
                     var stanza = call.args[0];
@@ -462,15 +480,19 @@
                 $('#chatrooms .bookmarks-toggle').click();
                 expect($('#chatrooms dl.bookmarks dd:visible').length).toBe(1);
                 expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
+                done();
             }));
         });
     });
 
     describe("When hide_open_bookmarks is true and a bookmarked room is opened", function () {
 
-        it("can be closed", mock.initConverse({ hide_open_bookmarks: true }, function (_converse) {
-            test_utils.openControlBox().openRoomsPanel(_converse);
+        it("can be closed", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'],
+            { hide_open_bookmarks: true },
+            function (done, _converse) {
 
+            test_utils.openControlBox().openRoomsPanel(_converse);
             // XXX Create bookmarks view here, otherwise we need to mock stanza
             // traffic for it to get created.
             _converse.bookmarksview = new _converse.BookmarksView(
@@ -502,6 +524,7 @@
             view.close();
             room_els = _converse.bookmarksview.el.querySelectorAll(".open-room");
             expect(room_els.length).toBe(1);
+            done();
         }));
     });
 }));

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 362 - 85
spec/chatbox.js


+ 276 - 52
spec/chatroom.js

@@ -1,8 +1,7 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils", "utils" ], factory);
-} (this, function (jasmine, mock, converse, test_utils, utils) {
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils", "utils" ], factory);
+} (this, function ($, jasmine, mock, converse, test_utils, utils) {
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
     var $pres = converse.env.$pres;
     var $iq = converse.env.$iq;
     var $msg = converse.env.$msg;
@@ -11,7 +10,11 @@
     return describe("ChatRooms", function () {
         describe("The \"rooms\" API", function () {
 
-            it("has a method 'close' which closes rooms by JID or all rooms when called with no arguments", mock.initConverse(function (_converse) {
+            it("has a method 'close' which closes rooms by JID or all rooms when called with no arguments",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 test_utils.openAndEnterChatRoom(_converse, 'leisure', 'localhost', 'dummy');
@@ -43,6 +46,7 @@
                 _converse.api.rooms.close();
                 expect(_converse.chatboxviews.get('lounge@localhost')).toBeUndefined();
                 expect(_converse.chatboxviews.get('leisure@localhost')).toBeUndefined();
+                done();
             }));
 
             it("has a method 'get' which returns a wrapped chat room (if it exists)", mock.initConverseWithAsync(function (done, _converse) {
@@ -242,7 +246,11 @@
         });
 
         describe("An instant chat room", function () {
-            it("will be created when muc_instant_rooms is set to true", mock.initConverse(function (_converse) {
+            it("will be created when muc_instant_rooms is set to true",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -269,7 +277,6 @@
                         .c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"});
                 _converse.connection._dataRecv(test_utils.createRequest(features_stanza));
 
-
                 var view = _converse.chatboxviews.get('lounge@localhost');
                 spyOn(view, 'join').and.callThrough();
 
@@ -346,12 +353,17 @@
                     "<iq to='lounge@localhost' type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
                         "<query xmlns='http://jabber.org/protocol/muc#owner'><x xmlns='jabber:x:data' type='submit'/>"+
                     "</query></iq>");
+                done();
             }));
         });
 
         describe("A Chat Room", function () {
 
-            it("shows join/leave messages when users enter or exit a room", mock.initConverse(function (_converse) {
+            it("shows join/leave messages when users enter or exit a room",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, "coven", 'chat.shakespeare.lit', 'some1');
                 var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
                 var $chat_content = view.$el.find('.chat-content');
@@ -432,9 +444,14 @@
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content.find('div.chat-info').length).toBe(3);
                 expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has left the room");
+                done();
             }));
 
-            it("shows its description in the chat heading",  mock.initConverse(function (_converse) {
+            it("shows its description in the chat heading",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -473,9 +490,14 @@
                 _converse.connection._dataRecv(test_utils.createRequest(features_stanza));
                 expect(view.generateHeadingHTML).toHaveBeenCalled();
                 expect(view.$('.chatroom-description').text()).toBe('This is the description');
+                done();
             }));
 
-            it("will specially mark messages in which you are mentioned", mock.initConverse(function (_converse) {
+            it("will specially mark messages in which you are mentioned",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
@@ -490,9 +512,14 @@
                     }).c('body').t(message).tree();
                 view.handleMUCMessage(msg);
                 expect(view.$el.find('.chat-message').hasClass('mentioned')).toBeTruthy();
+                done();
             }));
 
-            it("supports the /me command", mock.initConverse(function (_converse) {
+            it("supports the /me command",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
@@ -519,16 +546,26 @@
                 view.handleMUCMessage(msg);
                 expect(_.includes(view.$el.find('.chat-msg-author:last').text(), '**Max Mustermann')).toBeTruthy();
                 expect(view.$el.find('.chat-msg-content:last').text()).toBe(' is as well');
+                done();
             }));
 
-            it("can have spaces and special characters in its name", mock.initConverse(function (_converse) {
+            it("can have spaces and special characters in its name",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge & leisure', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get(
                         Strophe.escapeNode('lounge & leisure')+'@localhost');
                 expect(view instanceof _converse.ChatRoomView).toBe(true);
+                done();
             }));
 
-            it("can be configured if you're its owner", mock.initConverse(function (_converse) {
+            it("can be configured if you're its owner",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var view;
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
@@ -593,8 +630,8 @@
                     "</iq>");
 
                 /* Server responds with the configuration form.
-                    * See: // http://xmpp.org/extensions/xep-0045.html#example-165
-                    */
+                 * See: // http://xmpp.org/extensions/xep-0045.html#example-165
+                 */
                     var config_stanza = $iq({from: 'coven@chat.shakespeare.lit',
                         'id': IQ_id,
                         'to': 'dummy@localhost/desktop',
@@ -740,9 +777,14 @@
                 expect($sent_stanza.find('field[var="muc#roomconfig_moderatedroom"] value').text()).toBe('1');
                 expect($sent_stanza.find('field[var="muc#roomconfig_allowpm"] value').text()).toBe('moderators');
                 expect($sent_stanza.find('field[var="muc#roomconfig_presencebroadcast"] value').text()).toBe('moderator');
+                done();
             }));
 
-            it("shows users currently present in the room", mock.initConverse(function (_converse) {
+            it("shows users currently present in the room",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var name;
                 var view = _converse.chatboxviews.get('lounge@localhost'),
@@ -787,9 +829,14 @@
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
                     expect($occupants.find('li').length).toBe(i+1);
                 }
+                done();
             }));
 
-            it("escapes occupant nicknames when rendering them, to avoid JS-injection attacks", mock.initConverse(function (_converse) {
+            it("escapes occupant nicknames when rendering them, to avoid JS-injection attacks",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 /* <presence xmlns="jabber:client" to="jc@chat.example.org/converse.js-17184538"
                  *      from="oo@conference.chat.example.org/&lt;img src=&quot;x&quot; onerror=&quot;alert(123)&quot;/&gt;">
@@ -814,9 +861,14 @@
                 var occupant = view.$el.find('.occupant-list').find('li');
                 expect(occupant.length).toBe(2);
                 expect($(occupant).last().text()).toBe("&lt;img src=&quot;x&quot; onerror=&quot;alert(123)&quot;/&gt;");
+                done();
             }));
 
-            it("indicates moderators by means of a special css class and tooltip", mock.initConverse(function (_converse) {
+            it("indicates moderators by means of a special css class and tooltip",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
                 var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -838,9 +890,14 @@
                 expect($(occupant).last().text()).toBe("moderatorman");
                 expect($(occupant).last().attr('class').indexOf('moderator')).not.toBe(-1);
                 expect($(occupant).last().attr('title')).toBe(contact_jid + ' This user is a moderator. Click to mention moderatorman in your message.');
+                done();
             }));
 
-            it("will use the user's reserved nickname, if it exists", mock.initConverse(function (_converse) {
+            it("will use the user's reserved nickname, if it exists",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -920,9 +977,14 @@
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 var info_text = view.$el.find('.chat-content .chat-info').text();
                 expect(info_text).toBe('Your nickname has been automatically set to: thirdwitch');
+                done();
             }));
 
-            it("allows the user to invite their roster contacts to enter the chat room", mock.initConverseWithAsync(function (done, _converse) {
+            it("allows the user to invite their roster contacts to enter the chat room",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 test_utils.createContacts(_converse, 'current'); // We need roster contacts, so that we have someone to invite
                 // Since we don't actually fetch roster contacts, we need to
@@ -989,7 +1051,11 @@
                 });
             }));
 
-            it("can be joined automatically, based upon a received invite", mock.initConverse(function (_converse) {
+            it("can be joined automatically, based upon a received invite",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'current'); // We need roster contacts, who can invite us
                 spyOn(window, 'confirm').and.callFake(function () {
                     return true;
@@ -1018,9 +1084,14 @@
                 expect(_converse.chatboxes.models.length).toBe(2);
                 expect(_converse.chatboxes.models[0].id).toBe('controlbox');
                 expect(_converse.chatboxes.models[1].id).toBe(room_jid);
+                done();
             }));
 
-            it("shows received groupchat messages", mock.initConverse(function (_converse) {
+            it("shows received groupchat messages",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 spyOn(_converse, 'emit');
                 var view = _converse.chatboxviews.get('lounge@localhost');
@@ -1038,9 +1109,14 @@
                 expect($chat_content.find('.chat-message').length).toBe(1);
                 expect($chat_content.find('.chat-msg-content').text()).toBe(text);
                 expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
+                done();
             }));
 
-            it("shows sent groupchat messages", mock.initConverse(function (_converse) {
+            it("shows sent groupchat messages",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 spyOn(_converse, 'emit');
                 var view = _converse.chatboxviews.get('lounge@localhost');
@@ -1065,9 +1141,14 @@
                 expect($chat_content.find('.chat-msg-content').last().text()).toBe(text);
                 // We don't emit an event if it's our own message
                 expect(_converse.emit.calls.count(), 1);
+                done();
             }));
 
-            it("will cause the chat area to be scrolled down only if it was at the bottom already", mock.initConverseWithAsync(function (done, _converse) {
+            it("will cause the chat area to be scrolled down only if it was at the bottom already",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var message = 'This message is received while the chat area is scrolled up';
                 test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
@@ -1104,7 +1185,11 @@
                 }, 500);
             }));
 
-            it("shows received chatroom subject messages", mock.initConverse(function (_converse) {
+            it("shows received chatroom subject messages",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openAndEnterChatRoom(_converse, 'jdev', 'conference.jabber.org', 'jc');
 
                 var text = 'Jabber/XMPP Development | RFCs and Extensions: http://xmpp.org/ | Protocol and XSF discussions: xsf@muc.xmpp.org';
@@ -1118,9 +1203,14 @@
                 var view = _converse.chatboxviews.get('jdev@conference.jabber.org');
                 var $chat_content = view.$el.find('.chat-content');
                 expect($chat_content.find('.chat-info:last').text()).toBe('Topic set by ralphm to: '+text);
+                done();
             }));
 
-            it("escapes the subject before rendering it, to avoid JS-injection attacks", mock.initConverse(function (_converse) {
+            it("escapes the subject before rendering it, to avoid JS-injection attacks",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openAndEnterChatRoom(_converse, 'jdev', 'conference.jabber.org', 'jc');
                 spyOn(window, 'alert');
                 var subject = '<img src="x" onerror="alert(\'XSS\');"/>';
@@ -1128,9 +1218,14 @@
                 view.setChatRoomSubject('ralphm', subject);
                 var $chat_content = view.$el.find('.chat-content');
                 expect($chat_content.find('.chat-info:last').text()).toBe('Topic set by ralphm to: '+subject);
+                done();
             }));
 
-            it("informs users if their nicknames has been changed.", mock.initConverse(function (_converse) {
+            it("informs users if their nicknames has been changed.",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 /* The service then sends two presence stanzas to the full JID
                  * of each occupant (including the occupant who is changing his
                  * or her room nickname), one of type "unavailable" for the old
@@ -1240,9 +1335,14 @@
                 $occupants = view.$('.occupant-list');
                 expect($occupants.children().length).toBe(1);
                 expect($occupants.children().first(0).text()).toBe("newnick");
+                done();
             }));
 
-            it("queries for the room information before attempting to join the user",  mock.initConverse(function (_converse) {
+            it("queries for the room information before attempting to join the user",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -1306,9 +1406,14 @@
                 expect(view.model.get('open')).toBe(true);
                 expect(view.model.get('unmoderated')).toBe(true);
                 expect(view.model.get('nonanonymous')).toBe(true);
+                done();
             }));
 
-            it("updates the shown features when the room configuration has changed", mock.initConverse(function (_converse) {
+            it("updates the shown features when the room configuration has changed",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 test_utils.openAndEnterChatRoom(_converse, 'room', 'conference.example.org', 'dummy');
@@ -1356,9 +1461,14 @@
                 view.model.set({'membersonly': true});
                 expect(view.model.get('open')).toBe(false);
                 expect(view.model.get('membersonly')).toBe(true);
+                done();
             }));
 
-            it("indicates when a room is no longer anonymous", mock.initConverse(function (_converse) {
+            it("indicates when a room is no longer anonymous",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -1400,9 +1510,14 @@
                 expect($chat_body.html().trim().indexOf(
                     '<div class="chat-info">This room is now no longer anonymous</div>'
                 )).not.toBe(-1);
+                done();
             }));
 
-            it("informs users if they have been kicked out of the chat room", mock.initConverse(function (_converse) {
+            it("informs users if they have been kicked out of the chat room",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 /*  <presence
                  *      from='harfleur@chat.shakespeare.lit/pistol'
                  *      to='pistol@shakespeare.lit/harfleur'
@@ -1445,9 +1560,14 @@
                     'This action was done by Fluellen.'+
                     'The reason given is: "Avaunt, you cullion!".'
                 );
+                done();
             }));
 
-            it("can be saved to, and retrieved from, browserStorage", mock.initConverse(function (_converse) {
+            it("can be saved to, and retrieved from, browserStorage",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 // We instantiate a new ChatBoxes collection, which by default
                 // will be empty.
@@ -1472,9 +1592,13 @@
                     expect(_.isEqual(new_attrs.sort(), old_attrs.sort())).toEqual(true);
                 }
                 _converse.rosterview.render();
+                done();
             }));
 
-            it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost'),
                     trimmed_chatboxes = _converse.minimized_chats;
@@ -1505,7 +1629,11 @@
                 });
             }));
 
-            it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", mock.initConverse(function (_converse) {
+            it("can be closed again by clicking a DOM element with class 'close-chatbox-button'",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
                 spyOn(view, 'close').and.callThrough();
@@ -1520,13 +1648,18 @@
                 // we would have to mock the returned presence stanza.
                 // See the "leave" method on the ChatRoomView.
                 // expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
+                done();
             }));
         });
 
 
         describe("Each chat room can take special commands", function () {
 
-            it("to set the room topic", mock.initConverse(function (_converse) {
+            it("to set the room topic",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_stanza;
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
@@ -1558,9 +1691,14 @@
                 $textarea.val('/Subject This is yet another subject');
                 $textarea.trigger($.Event('keypress', {keyCode: 13}));
                 expect(sent_stanza.textContent).toBe('This is yet another subject');
+                done();
             }));
 
-            it("to clear messages", mock.initConverse(function (_converse) {
+            it("to clear messages",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                 var view = _converse.chatboxviews.get('lounge@localhost');
                 spyOn(view, 'onMessageSubmitted').and.callThrough();
@@ -1569,9 +1707,14 @@
                 view.$el.find('textarea.chat-textarea').trigger($.Event('keypress', {keyCode: 13}));
                 expect(view.onMessageSubmitted).toHaveBeenCalled();
                 expect(view.clearChatRoomMessages).toHaveBeenCalled();
+                done();
             }));
 
-            it("to make a user an owner", mock.initConverse(function (_converse) {
+            it("to make a user an owner",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -1611,9 +1754,14 @@
                             "</item>"+
                         "</query>"+
                     "</iq>");
+                done();
             }));
 
-            it("to ban a user", mock.initConverse(function (_converse) {
+            it("to ban a user",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id;
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -1652,6 +1800,7 @@
                             "</item>"+
                         "</query>"+
                     "</iq>");
+                done();
             }));
         });
 
@@ -1668,7 +1817,11 @@
                 roomspanel.$el.find('form').submit();
             };
 
-            it("will show an error message if the room requires a password", mock.initConverse(function (_converse) {
+            it("will show an error message if the room requires a password",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1687,9 +1840,14 @@
                 expect(view.renderPasswordForm).toHaveBeenCalled();
                 expect($chat_body.find('form.chatroom-form').length).toBe(1);
                 expect($chat_body.find('legend').text()).toBe('This chatroom requires a password');
+                done();
             }));
 
-            it("will show an error message if the room is members-only and the user not included", mock.initConverse(function (_converse) {
+            it("will show an error message if the room is members-only and the user not included",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1703,9 +1861,14 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe('You are not on the member list of this room.');
+                done();
             }));
 
-            it("will show an error message if the user has been banned", mock.initConverse(function (_converse) {
+            it("will show an error message if the user has been banned",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1719,9 +1882,14 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe('You have been banned from this room.');
+                done();
             }));
 
-            it("will render a nickname form if a nickname conflict happens and muc_nickname_from_jid=false", mock.initConverse(function (_converse) {
+            it("will render a nickname form if a nickname conflict happens and muc_nickname_from_jid=false",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1735,9 +1903,14 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body form.chatroom-form label:first').text()).toBe('Please choose your nickname');
+                done();
             }));
 
-            it("will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true", mock.initConverse(function (_converse) {
+            it("will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 /* <presence
                  *      from='coven@chat.shakespeare.lit/thirdwitch'
                  *      id='n13mt3l'
@@ -1788,9 +1961,14 @@
                         .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 view.onChatRoomPresence(presence);
                 expect(view.join).toHaveBeenCalledWith('dummy-4');
+                done();
             }));
 
-            it("will show an error message if the user is not allowed to have created the room", mock.initConverse(function (_converse) {
+            it("will show an error message if the user is not allowed to have created the room",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1804,9 +1982,14 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe('You are not allowed to create new rooms.');
+                done();
             }));
 
-            it("will show an error message if the user's nickname doesn't conform to room policy", mock.initConverse(function (_converse) {
+            it("will show an error message if the user's nickname doesn't conform to room policy",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1820,9 +2003,14 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe("Your nickname doesn't conform to this room's policies.");
+                done();
             }));
 
-            it("will show an error message if the room doesn't yet exist", mock.initConverse(function (_converse) {
+            it("will show an error message if the room doesn't yet exist",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1836,9 +2024,14 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe("This room does not (yet) exist.");
+                done();
             }));
 
-            it("will show an error message if the room has reached its maximum number of occupants", mock.initConverse(function (_converse) {
+            it("will show an error message if the room has reached its maximum number of occupants",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 submitRoomForm(_converse);
                 var presence = $pres().attrs({
                     from:'lounge@localhost/thirdwitch',
@@ -1852,12 +2045,17 @@
                 spyOn(view, 'showErrorMessage').and.callThrough();
                 view.onChatRoomPresence(presence);
                 expect(view.$el.find('.chatroom-body p:last').text()).toBe("This room has reached its maximum number of occupants.");
+                done();
             }));
         });
 
         describe("Someone being invited to a chat room", function () {
 
-            it("will first be added to the member list if the chat room is members only", mock.initConverse(function (_converse) {
+            it("will first be added to the member list if the chat room is members only",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var sent_IQs = [], IQ_ids = [];
                 var sendIQ = _converse.connection.sendIQ;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -1998,12 +2196,17 @@
                         "<x xmlns='jabber:x:conference' jid='coven@chat.shakespeare.lit' reason='Please join this chat room'/>"+
                     "</message>"
                 );
+                done();
             }));
         });
 
         describe("The affiliations delta", function () {
 
-            it("can be computed in various ways", mock.initConverse(function (_converse) {
+            it("can be computed in various ways",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openChatRoom(_converse, 'coven', 'chat.shakespeare.lit', 'dummy');
                 var roomview = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
 
@@ -2058,12 +2261,17 @@
                 old_list = [{'jid': 'wiccarocks@shakespeare.lit', 'affiliation': 'owner'}];
                 delta = roomview.computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list);
                 expect(delta.length).toBe(0);
+                done();
             }));
         });
 
         describe("The \"Rooms\" Panel", function () {
 
-            it("is opened by clicking the 'Chatrooms' tab", mock.initConverse(function (_converse) {
+            it("is opened by clicking the 'Chatrooms' tab",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var cbview = _converse.chatboxviews.get('controlbox');
                 var $tabs = cbview.$el.find('#controlbox-tabs');
@@ -2076,9 +2284,14 @@
                 expect($contacts.is(':visible')).toBe(false);
                 expect($chatrooms.is(':visible')).toBe(true);
                 expect(cbview.switchTab).toHaveBeenCalled();
+                done();
             }));
 
-            it("contains a form through which a new chatroom can be created", mock.initConverse(function (_converse) {
+            it("contains a form through which a new chatroom can be created",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
                 var $input = roomspanel.$el.find('input.new-chatroom-name');
@@ -2101,9 +2314,14 @@
                 roomspanel.$el.find('form').submit();
                 expect(roomspanel.openChatRoom).toHaveBeenCalled();
                 expect($('.chatroom:visible').length).toBe(1); // There should now be an open chatroom
+                done();
             }));
 
-            it("can list rooms publically available on the server", mock.initConverse(function (_converse) {
+            it("can list rooms publically available on the server",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var panel = _converse.chatboxviews.get('controlbox').roomspanel;
                 $(panel.tabs).find('li').last().find('a').click(); // Click the chatrooms tab
@@ -2126,9 +2344,14 @@
                 expect(panel.$('#available-chatrooms').children('dt').length).toBe(1);
                 expect(panel.$('#available-chatrooms').children('dt').first().text()).toBe("Rooms on muc.localhost");
                 expect(panel.$('#available-chatrooms').children('dd').length).toBe(4);
+                done();
             }));
 
-            it("shows the number of unread mentions received", mock.initConverse(function (_converse) {
+            it("shows the number of unread mentions received",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var room_jid = 'kitchen@conference.shakespeare.lit';
                 test_utils.openAndEnterChatRoom(
                     _converse, 'kitchen', 'conference.shakespeare.lit', 'fires');
@@ -2168,6 +2391,7 @@
                 view.model.set({'minimized': false});
                 expect(_.includes(roomspanel.tab_el.firstChild.classList, 'unread-msgs')).toBeFalsy();
                 expect(_.isNull(roomspanel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+                done();
             }));
         });
     });

+ 238 - 52
spec/controlbox.js

@@ -1,8 +1,7 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
     var $pres = converse.env.$pres;
     var $msg = converse.env.$msg;
     var $iq = converse.env.$iq;
@@ -25,7 +24,11 @@
 
     describe("The Control Box", function () {
 
-        it("can be opened by clicking a DOM element with class 'toggle-controlbox'", mock.initConverse(function (_converse) {
+        it("can be opened by clicking a DOM element with class 'toggle-controlbox'",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             // This spec will only pass if the controlbox is not currently
             // open yet.
             expect($("div#controlbox").is(':visible')).toBe(false);
@@ -39,18 +42,28 @@
             expect(_converse.controlboxtoggle.showControlBox).toHaveBeenCalled();
             expect(_converse.emit).toHaveBeenCalledWith('controlBoxOpened', jasmine.any(Object));
             expect($("div#controlbox").is(':visible')).toBe(true);
+            done();
         }));
 
         describe("The Status Widget", function () {
 
-            it("shows the user's chat status, which is online by default", mock.initConverse(function (_converse) {
+            it("shows the user's chat status, which is online by default",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var view = _converse.xmppstatusview;
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('online')).toBe(true);
                 expect(view.$el.find('a.choose-xmpp-status').attr('data-value')).toBe('I am online');
+                done();
             }));
 
-            it("can be used to set the current user's chat status", mock.initConverse(function (_converse) {
+            it("can be used to set the current user's chat status",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var view = _converse.xmppstatusview;
                 spyOn(view, 'toggleOptions').and.callThrough();
@@ -68,9 +81,14 @@
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('online')).toBe(false);
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('dnd')).toBe(true);
                 expect(view.$el.find('a.choose-xmpp-status').attr('data-value')).toBe('I am busy');
+                done();
             }));
 
-            it("can be used to set a custom status message", mock.initConverse(function (_converse) {
+            it("can be used to set a custom status message",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var view = _converse.xmppstatusview;
                 _converse.xmppstatus.save({'status': 'online'});
@@ -87,6 +105,7 @@
                 expect(_converse.emit).toHaveBeenCalledWith('statusMessageChanged', msg);
                 expect(view.$el.find('a.choose-xmpp-status').hasClass('online')).toBe(true);
                 expect(view.$el.find('a.choose-xmpp-status').attr('data-value')).toBe(msg);
+                done();
             }));
         });
     });
@@ -95,7 +114,11 @@
 
         describe("The live filter", function () {
 
-            it("will only appear when roster contacts flow over the visible area", mock.initConverseWithAsync(function (done, _converse) {
+            it("will only appear when roster contacts flow over the visible area",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter = _converse.rosterview.$('.roster-filter');
                 var names = mock.cur_names;
                 test_utils.openControlBox();
@@ -120,13 +143,17 @@
                         } else {
                             return !$filter.is(':visible');
                         }
-                }).then(function () {
-                    done();
-                });
+                    }).then(function () {
+                        done();
+                    });
                 });
             }));
 
-            it("can be used to filter the contacts shown", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to filter the contacts shown",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter;
                 var $roster;
                 _converse.roster_groups = true;
@@ -180,7 +207,11 @@
                 });
             }));
 
-            it("can be used to filter the groups shown", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to filter the groups shown",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter;
                 var $roster;
                 var $type;
@@ -226,7 +257,11 @@
                 });
             }));
 
-            it("has a button with which its contents can be cleared", mock.initConverseWithAsync(function (done, _converse) {
+            it("has a button with which its contents can be cleared",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 test_utils.openControlBox();
                 test_utils.createGroupedContacts(_converse);
@@ -250,7 +285,11 @@
                 });
             }));
 
-            it("can be used to filter contacts by their chat state", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to filter contacts by their chat state",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var $filter;
                 var $roster;
                 _converse.roster_groups = true;
@@ -287,7 +326,11 @@
 
         describe("A Roster Group", function () {
 
-            it("can be used to organize existing contacts", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be used to organize existing contacts",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
@@ -321,7 +364,11 @@
                 });
             }));
 
-            it("can share contacts with other roster groups", mock.initConverseWithAsync(function (done, _converse) {
+            it("can share contacts with other roster groups", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 var groups = ['colleagues', 'friends'];
                 spyOn(_converse, 'emit');
@@ -351,7 +398,11 @@
                 });
             }));
 
-            it("remembers whether it is closed or opened", mock.initConverse(function (_converse) {
+            it("remembers whether it is closed or opened",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = true;
                 var i=0, j=0;
                 var groups = {
@@ -378,6 +429,7 @@
                 expect(view.model.get('state')).toBe('closed');
                 $toggle.click();
                 expect(view.model.get('state')).toBe('opened');
+                done();
             }));
         });
 
@@ -388,7 +440,11 @@
                 test_utils.createContacts(_converse, 'pending').openControlBox().openContactsPanel(_converse);
             }
 
-            it("can be collapsed under their own header", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be collapsed under their own header", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dd').length;
@@ -399,7 +455,11 @@
                 });
             }));
 
-            it("can be added to the roster", mock.initConverse(function (_converse) {
+            it("can be added to the roster",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 test_utils.openControlBox();
@@ -410,9 +470,14 @@
                     fullname: mock.pend_names[0]
                 });
                 expect(_converse.rosterview.update).toHaveBeenCalled();
+                done();
             }));
 
-            it("are shown in the roster when show_only_online_users", mock.initConverseWithAsync(function (done, _converse) {
+            it("are shown in the roster when show_only_online_users", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.show_only_online_users = true;
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 _addContacts(_converse);
@@ -428,7 +493,11 @@
                 });
             }));
 
-            it("are shown in the roster when hide_offline_users", mock.initConverseWithAsync(function (done, _converse) {
+            it("are shown in the roster when hide_offline_users", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.hide_offline_users = true;
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 _addContacts(_converse);
@@ -444,7 +513,11 @@
                 });
             }));
 
-            it("can be removed by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be removed by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 var name = mock.pend_names[0];
                 var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -474,7 +547,11 @@
                 });
             }));
 
-            it("do not have a header if there aren't any", mock.initConverseWithAsync(function (done, _converse) {
+            it("do not have a header if there aren't any", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 var name = mock.pend_names[0];
                 _converse.roster.create({
@@ -500,7 +577,11 @@
                 });
             }));
 
-            it("will lose their own header once the last one has been removed", mock.initConverse(function (_converse) {
+            it("is shown when a new private message is received",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 var name;
                 spyOn(window, 'confirm').and.returnValue(true);
@@ -510,9 +591,14 @@
                         .parent().siblings('.remove-xmpp-contact').click();
                 }
                 expect(_converse.rosterview.$el.find('dt#pending-xmpp-contacts').is(':visible')).toBeFalsy();
+                done();
             }));
 
-            it("can be added to the roster and they will be sorted alphabetically", mock.initConverse(function (_converse) {
+            it("can be added to the roster and they will be sorted alphabetically",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var i, t;
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
@@ -530,6 +616,7 @@
                     return result + _.trim(value.textContent);
                 }, '');
                 expect(t).toEqual(mock.pend_names.slice(0,i+1).sort().join(''));
+                done();
             }));
         });
 
@@ -538,7 +625,11 @@
                 test_utils.createContacts(_converse, 'current').openControlBox().openContactsPanel(_converse);
             };
 
-            it("can be collapsed under their own header", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be collapsed under their own header", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dd:visible').length;
@@ -549,7 +640,11 @@
                 });
             }));
 
-            it("will be hidden when appearing under a collapsed group", mock.initConverseWithAsync(function (done, _converse) {
+            it("will be hidden when appearing under a collapsed group", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _converse.roster_groups = false;
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
@@ -572,7 +667,11 @@
                 });
             }));
 
-            it("can be added to the roster and they will be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be added to the roster and they will be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 for (var i=0; i<mock.cur_names.length; i++) {
                     _converse.roster.create({
@@ -595,7 +694,11 @@
                 });
             }));
 
-            it("can be removed by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be removed by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dd').length;
@@ -620,7 +723,11 @@
                 });
             }));
 
-            it("do not have a header if there aren't any", mock.initConverseWithAsync(function (done, _converse) {
+            it("do not have a header if there aren't any", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var name = mock.cur_names[0];
                 var contact;
                 contact = _converse.roster.create({
@@ -650,7 +757,11 @@
                 });
             }));
 
-            it("can change their status to online and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to online and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -674,7 +785,11 @@
                 });
             }));
 
-            it("can change their status to busy and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to busy and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -698,7 +813,11 @@
                 });
             }));
 
-            it("can change their status to away and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to away and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -722,7 +841,11 @@
                 });
             }));
 
-            it("can change their status to xa and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to xa and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -746,7 +869,11 @@
                 });
             }));
 
-            it("can change their status to unavailable and be sorted alphabetically", mock.initConverseWithAsync(function (done, _converse) {
+            it("can change their status to unavailable and be sorted alphabetically", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -770,7 +897,11 @@
                 });
             }));
 
-            it("are ordered according to status: online, busy, away, xa, unavailable, offline", mock.initConverseWithAsync(function (done, _converse) {
+            it("are ordered according to status: online, busy, away, xa, unavailable, offline", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 _addContacts(_converse);
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -860,7 +991,11 @@
 
         describe("Requesting Contacts", function () {
 
-            it("can be added to the roster and they will be sorted alphabetically", mock.initConverse(function (_converse) {
+            it("can be added to the roster and they will be sorted alphabetically",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 var i, children;
                 var names = [];
                 var addName = function (idx, item) {
@@ -887,9 +1022,14 @@
                 names = [];
                 children.each(addName);
                 expect(names.join('')).toEqual(mock.req_names.slice(0,mock.req_names.length+1).sort().join(''));
+                done();
             }));
 
-            it("do not have a header if there aren't any", mock.initConverseWithAsync(function (done, _converse) {
+            it("do not have a header if there aren't any", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openContactsPanel(_converse);
                 var name = mock.req_names[0];
                 spyOn(window, 'confirm').and.returnValue(true);
@@ -914,7 +1054,11 @@
                 });
             }));
 
-            it("can be collapsed under their own header", mock.initConverseWithAsync(function (done, _converse) {
+            it("can be collapsed under their own header", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'requesting').openControlBox();
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -925,7 +1069,11 @@
                 });
             }));
 
-            it("can have their requests accepted by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can have their requests accepted by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'requesting').openControlBox();
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -950,7 +1098,11 @@
                 });
             }));
 
-            it("can have their requests denied by the user", mock.initConverseWithAsync(function (done, _converse) {
+            it("can have their requests denied by the user", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'requesting').openControlBox();
                 test_utils.waitUntil(function () {
                         return _converse.rosterview.$el.find('dt').length;
@@ -973,7 +1125,9 @@
                 });
             }));
 
-            it("are persisted even if other contacts' change their presence ", mock.initConverse(function (_converse) {
+            it("are persisted even if other contacts' change their presence ", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
                 /* This is a regression test.
                  * https://github.com/jcbrand/_converse.js/issues/262
                  */
@@ -1009,12 +1163,17 @@
                 }).c('group').t('Friends');
                 _converse.roster.onReceivedFromServer(stanza.tree());
                 expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
+                done();
             }));
         });
 
         describe("All Contacts", function () {
 
-            it("are saved to, and can be retrieved from browserStorage", mock.initConverse(function (_converse) {
+            it("are saved to, and can be retrieved from browserStorage",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.openContactsPanel(_converse);
                 var new_attrs, old_attrs, attrs;
@@ -1036,9 +1195,14 @@
                     // comparison
                     expect(_.isEqual(new_attrs.sort(), old_attrs.sort())).toEqual(true);
                 }
+                done();
             }));
 
-            it("will show fullname and jid properties on tooltip", mock.initConverseWithAsync(function (done, _converse) {
+            it("will show fullname and jid properties on tooltip", 
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
@@ -1064,7 +1228,11 @@
 
     describe("The 'Add Contact' widget", function () {
 
-        it("opens up an add form when you click on it", mock.initConverse(function (_converse) {
+        it("opens up an add form when you click on it",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             var panel = _converse.chatboxviews.get('controlbox').contactspanel;
             spyOn(panel, 'toggleContactForm').and.callThrough();
             panel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
@@ -1072,9 +1240,14 @@
             expect(panel.toggleContactForm).toHaveBeenCalled();
             // XXX: Awaiting more tests, close it again for now...
             panel.$el.find('a.toggle-xmpp-contact-form').click();
+            done();
         }));
 
-        it("can be used to add contact and it checks for case-sensivity", mock.initConverseWithAsync(function (done, _converse) {
+        it("can be used to add contact and it checks for case-sensivity", 
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             spyOn(_converse, 'emit');
             spyOn(_converse.rosterview, 'update').and.callThrough();
             test_utils.openControlBox();
@@ -1101,12 +1274,15 @@
                 done();
             });
         }));
-
     });
 
     describe("The Controlbox Tabs", function () {
 
-        it("contains two tabs, 'Contacts' and 'ChatRooms'", mock.initConverse(function (_converse) {
+        it("contains two tabs, 'Contacts' and 'ChatRooms'",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             var cbview = _converse.chatboxviews.get('controlbox');
             var $panels = cbview.$el.find('.controlbox-panes');
@@ -1115,9 +1291,14 @@
             expect($panels.children().first().is(':visible')).toBe(true);
             expect($panels.children().last().attr('id')).toBe('chatrooms');
             expect($panels.children().last().is(':visible')).toBe(false);
+            done();
         }));
 
-        it("remembers which tab was open last", mock.initConverse(function (_converse) {
+        it("remembers which tab was open last",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             var cbview = _converse.chatboxviews.get('controlbox');
             var $tabs = cbview.$el.find('#controlbox-tabs');
@@ -1126,11 +1307,16 @@
             expect(cbview.model.get('active-panel')).toBe('chatrooms');
             $tabs.find('li').first().find('a').click();
             expect(cbview.model.get('active-panel')).toBe('users');
+            done();
         }));
 
         describe("The \"Contacts\" Panel", function () {
 
-            it("shows the number of unread mentions received", mock.initConverse(function (_converse) {
+            it("shows the number of unread mentions received",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.createContacts(_converse, 'all').openControlBox();
                 test_utils.openContactsPanel(_converse);
 
@@ -1168,8 +1354,8 @@
                 chatview.model.set({'minimized': false});
                 expect(_.includes(contacts_panel.tab_el.firstChild.classList, 'unread-msgs')).toBeFalsy();
                 expect(_.isNull(contacts_panel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+                done();
             }));
-
         });
     });
 }));

+ 31 - 25
spec/converse.js

@@ -7,7 +7,6 @@
 } (this, function (jasmine, converse, mock, test_utils) {
     var b64_sha1 = converse.env.b64_sha1;
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
 
     describe("Converse", function() {
         
@@ -272,32 +271,38 @@
 
         describe("The \"chats\" API", function() {
 
-            it("has a method 'get' which returns a wrapped chat box", mock.initConverse(function (_converse) {
-                test_utils.createContacts(_converse, 'current');
-                // Test on chat that doesn't exist.
-                expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
-                var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                // Test on chat that's not open
-                var box = _converse.api.chats.get(jid);
-                expect(typeof box === 'undefined').toBeTruthy();
-                var chatboxview = _converse.chatboxviews.get(jid);
-                // Test for single JID
-                test_utils.openChatBoxFor(_converse, jid);
-                box = _converse.api.chats.get(jid);
-                expect(box instanceof Object).toBeTruthy();
-                expect(box.model.get('box_id')).toBe(b64_sha1(jid));
-                chatboxview = _converse.chatboxviews.get(jid);
-                expect(chatboxview.$el.is(':visible')).toBeTruthy();
-                // Test for multiple JIDs
-                var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
-                test_utils.openChatBoxFor(_converse, jid2);
-                var list = _converse.api.chats.get([jid, jid2]);
-                expect(_.isArray(list)).toBeTruthy();
-                expect(list[0].model.get('box_id')).toBe(b64_sha1(jid));
-                expect(list[1].model.get('box_id')).toBe(b64_sha1(jid2));
+            it("has a method 'get' which returns a wrapped chat box", mock.initConverseWithPromises(
+                null, ['rosterInitialized'], {}, function (done, _converse) {
+                    test_utils.openControlBox();
+                    test_utils.createContacts(_converse, 'current');
+                    // Test on chat that doesn't exist.
+                    expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
+                    var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+                    // Test on chat that's not open
+                    var box = _converse.api.chats.get(jid);
+                    expect(typeof box === 'undefined').toBeTruthy();
+                    var chatboxview = _converse.chatboxviews.get(jid);
+                    // Test for single JID
+                    test_utils.openChatBoxFor(_converse, jid);
+                    box = _converse.api.chats.get(jid);
+                    expect(box instanceof Object).toBeTruthy();
+                    expect(box.model.get('box_id')).toBe(b64_sha1(jid));
+                    chatboxview = _converse.chatboxviews.get(jid);
+                    expect(chatboxview.$el.is(':visible')).toBeTruthy();
+                    // Test for multiple JIDs
+                    var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
+                    test_utils.openChatBoxFor(_converse, jid2);
+                    var list = _converse.api.chats.get([jid, jid2]);
+                    expect(_.isArray(list)).toBeTruthy();
+                    expect(list[0].model.get('box_id')).toBe(b64_sha1(jid));
+                    expect(list[1].model.get('box_id')).toBe(b64_sha1(jid2));
+                    done();
             }));
 
-            it("has a method 'open' which opens and returns a wrapped chat box", mock.initConverse(function (_converse) {
+            it("has a method 'open' which opens and returns a wrapped chat box", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
+                test_utils.openControlBox();
                 test_utils.createContacts(_converse, 'current');
                 var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 var chatboxview;
@@ -318,6 +323,7 @@
                 expect(_.isArray(list)).toBeTruthy();
                 expect(list[0].model.get('box_id')).toBe(b64_sha1(jid));
                 expect(list[1].model.get('box_id')).toBe(b64_sha1(jid2));
+                done();
             }));
         });
 

+ 7 - 2
spec/headline.js

@@ -40,7 +40,9 @@
             utils.isHeadlineMessage.restore();
         }));
 
-        it("will open and display headline messages", mock.initConverse(function (_converse) {
+        it("will open and display headline messages", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'], {}, function (done, _converse) {
+
             /* <message from='notify.example.com'
              *          to='romeo@im.example.com'
              *          type='headline'
@@ -74,9 +76,12 @@
             expect(utils.isHeadlineMessage.called).toBeTruthy();
             expect(utils.isHeadlineMessage.returned(true)).toBeTruthy();
             utils.isHeadlineMessage.restore(); // unwraps
+            done();
         }));
 
-        it("will not show a headline messages from a full JID if allow_non_roster_messaging is false", mock.initConverse(function (_converse) {
+        it("will not show a headline messages from a full JID if allow_non_roster_messaging is false",
+            mock.initConverse(function (_converse) {
+
             _converse.allow_non_roster_messaging = false;
             sinon.spy(utils, 'isHeadlineMessage');
             var stanza = $msg({

+ 2 - 3
spec/mam.js

@@ -1,9 +1,8 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     "use strict";
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
     var Strophe = converse.env.Strophe;
     var $iq = converse.env.$iq;
     var $msg = converse.env.$msg;

+ 24 - 4
spec/minchats.js

@@ -6,7 +6,11 @@
 
     describe("The Minimized Chats Widget", function () {
 
-        it("shows chats that have been minimized",  mock.initConverse(function (_converse) {
+        it("shows chats that have been minimized",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
@@ -34,9 +38,14 @@
             expect(_converse.minimized_chats.$el.is(':visible')).toBeTruthy();
             expect(_converse.minimized_chats.keys().length).toBe(2);
             expect(_.includes(_converse.minimized_chats.keys(), contact_jid)).toBeTruthy();
+            done();
         }));
 
-        it("can be toggled to hide or show minimized chats", mock.initConverse(function (_converse) {
+        it("can be toggled to hide or show minimized chats",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
@@ -56,9 +65,14 @@
             _converse.minimized_chats.$('#toggle-minimized-chats').click();
             expect(_converse.minimized_chats.$('.minimized-chats-flyout').is(':visible')).toBeFalsy();
             expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeTruthy();
+            done();
         }));
 
-        it("shows the number messages received to minimized chats", mock.initConverse(function (_converse) {
+        it("shows the number messages received to minimized chats",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
@@ -120,9 +134,14 @@
                 id: (new Date()).getTime()
             }).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
             expect(_converse.minimized_chats.toggleview.$('.unread-message-count').text()).toBe((i).toString());
+            done();
         }));
 
-        it("shows the number messages received to minimized groupchats", mock.initConverse(function (_converse) {
+        it("shows the number messages received to minimized groupchats",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             var room_jid = 'kitchen@conference.shakespeare.lit';
             test_utils.openAndEnterChatRoom(
                 _converse, 'kitchen', 'conference.shakespeare.lit', 'fires');
@@ -142,6 +161,7 @@
 
             expect(_converse.minimized_chats.toggleview.$('.unread-message-count').is(':visible')).toBeTruthy();
             expect(_converse.minimized_chats.toggleview.$('.unread-message-count').text()).toBe('1');
+            done();
         }));
     });
 }));

+ 24 - 4
spec/notification.js

@@ -12,7 +12,11 @@
             describe("And the desktop is not focused", function () {
                 describe("an HTML5 Notification", function () {
 
-                    it("is shown when a new private message is received", mock.initConverse(function (_converse) {
+                    it("is shown when a new private message is received",
+                        mock.initConverseWithPromises(
+                            null, ['rosterGroupsFetched'], {},
+                            function (done, _converse) {
+
                         // TODO: not yet testing show_desktop_notifications setting
                         test_utils.createContacts(_converse, 'current');
                         spyOn(_converse, 'showMessageNotification');
@@ -30,9 +34,14 @@
                         _converse.chatboxes.onMessage(msg); // This will emit 'message'
                         expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
                         expect(_converse.showMessageNotification).toHaveBeenCalled();
+                        done();
                     }));
 
-                    it("is shown when you are mentioned in a chat room", mock.initConverse(function (_converse) {
+                    it("is shown when you are mentioned in a chat room",
+                        mock.initConverseWithPromises(
+                            null, ['rosterGroupsFetched'], {},
+                            function (done, _converse) {
+
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                         var view = _converse.chatboxviews.get('lounge@localhost');
@@ -63,9 +72,14 @@
                         if (no_notification) {
                             delete window.Notification;
                         }
+                        done();
                     }));
 
-                    it("is shown for headline messages", mock.initConverse(function (_converse) {
+                    it("is shown for headline messages",
+                        mock.initConverseWithPromises(
+                            null, ['rosterGroupsFetched'], {},
+                            function (done, _converse) {
+
                         spyOn(_converse, 'showMessageNotification').and.callThrough();
                         spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
                         var stanza = $msg({
@@ -84,6 +98,7 @@
                                 'notify.example.com')
                             ).toBeTruthy();
                         expect(_converse.showMessageNotification).toHaveBeenCalled();
+                        done();
                     }));
 
                     it("is not shown for full JID headline messages if allow_non_roster_messaging is false", mock.initConverse(function (_converse) {
@@ -137,7 +152,11 @@
         describe("When play_sounds is set to true", function () {
             describe("A notification sound", function () {
 
-                it("is played when the current user is mentioned in a chat room", mock.initConverse(function (_converse) {
+                it("is played when the current user is mentioned in a chat room",
+                    mock.initConverseWithPromises(
+                        null, ['rosterGroupsFetched'], {},
+                        function (done, _converse) {
+
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
                     _converse.play_sounds = true;
@@ -175,6 +194,7 @@
                     view.onChatRoomMessage(message.nodeTree);
                     expect(_converse.playSoundNotification, 1);
                     _converse.play_sounds = false;
+                    done();
                 }));
             });
         });

+ 14 - 5
spec/otr.js

@@ -1,13 +1,16 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
-    var $ = converse.env.jQuery;
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     var Strophe = converse.env.Strophe;
     var b64_sha1 = converse.env.b64_sha1;
 
     return describe("The OTR module", function() {
 
-        it("will add processing hints to sent out encrypted <message> stanzas", mock.initConverse(function (_converse) {
+        it("will add processing hints to sent out encrypted <message> stanzas",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             test_utils.openContactsPanel(_converse);
             test_utils.createContacts(_converse, 'current');
@@ -25,11 +28,16 @@
             expect($hints.get(1).tagName).toBe('no-permanent-store');
             expect($hints.get(2).tagName).toBe('no-copy');
             chatview.model.set('otr_status', UNENCRYPTED); // Reset again to UNENCRYPTED
+            done();
         }));
 
         describe("An OTR Chat Message", function () {
 
-            it("will not be carbon copied when it's sent out", mock.initConverse(function (_converse) {
+            it("will not be carbon copied when it's sent out",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 test_utils.openControlBox();
                 test_utils.openContactsPanel(_converse);
                 test_utils.createContacts(_converse, 'current');
@@ -46,6 +54,7 @@
                 expect($sent.find('private').length).toBe(1);
                 expect($sent.find('private').attr('xmlns')).toBe('urn:xmpp:carbons:2');
                 chatbox.set('otr_status', 0); // Reset again to UNENCRYPTED
+                done();
             }));
         });
     });

+ 12 - 2
spec/ping.js

@@ -6,20 +6,30 @@
     describe("XMPP Ping", function () {
         describe("Ping and pong handlers", function () {
 
-            it("are registered when _converse.js is connected", mock.initConverse(function (_converse) {
+            it("are registered when _converse.js is connected",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'registerPingHandler').and.callThrough();
                 spyOn(_converse, 'registerPongHandler').and.callThrough();
                 _converse.emit('connected');
                 expect(_converse.registerPingHandler).toHaveBeenCalled();
                 expect(_converse.registerPongHandler).toHaveBeenCalled();
+                done();
             }));
 
-            it("are registered when _converse.js reconnected", mock.initConverse(function (_converse) {
+            it("are registered when _converse.js reconnected",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 spyOn(_converse, 'registerPingHandler').and.callThrough();
                 spyOn(_converse, 'registerPongHandler').and.callThrough();
                 _converse.emit('reconnected');
                 expect(_converse.registerPingHandler).toHaveBeenCalled();
                 expect(_converse.registerPongHandler).toHaveBeenCalled();
+                done();
             }));
         });
 

+ 6 - 1
spec/presence.js

@@ -49,7 +49,11 @@
 
     describe("A received presence stanza", function () {
 
-        it("has its priority taken into account", mock.initConverse(function (_converse) {
+        it("has its priority taken into account",
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
             test_utils.openControlBox();
             test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
             var contact_jid = mock.cur_names[8].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -218,6 +222,7 @@
             _converse.connection._dataRecv(test_utils.createRequest(stanza[0]));
             expect(_converse.roster.get(contact_jid).get('chat_status')).toBe('offline');
             expect(_.keys(contact.get('resources')).length).toBe(0);
+            done();
         }));
     });
 }));

+ 32 - 6
spec/protocol.js

@@ -10,6 +10,7 @@
     var Strophe = converse.env.Strophe;
     var $iq = converse.env.$iq;
     var $pres = converse.env.$pres;
+    var _ = converse.env._;
     // See:
     // https://xmpp.org/rfcs/rfc3921.html
 
@@ -47,11 +48,15 @@
              * that session. A client MUST acknowledge each roster push with an IQ
              * stanza of type "result".
              */
-            it("Subscribe to contact, contact accepts and subscribes back", mock.initConverseWithAsync(function (done, _converse) {
+            it("Subscribe to contact, contact accepts and subscribes back",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'],
+                    { roster_groups: false },
+                    function (done, _converse) {
+
                 /* The process by which a user subscribes to a contact, including
                 * the interaction between roster items and subscription states.
                 */
-                _converse.roster_groups = false;
                 var contact, stanza, sent_stanza, IQ_id;
                 test_utils.openControlBox(_converse);
                 var panel = _converse.chatboxviews.get('controlbox').contactspanel;
@@ -134,8 +139,10 @@
                 * </iq>
                 */
                 var create = _converse.roster.create;
+                var sent_stanzas = [];
                 spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
                     sent_stanza = stanza;
+                    sent_stanzas.push(stanza.toLocaleString());
                 });
                 spyOn(_converse.roster, 'create').and.callFake(function () {
                     contact = create.apply(_converse.roster, arguments);
@@ -165,6 +172,11 @@
                 *
                 *  <presence to='contact@example.org' type='subscribe'/>
                 */
+
+                test_utils.waitUntil(function () {
+                    return sent_stanzas.length == 1;
+                }).then(function () {
+
                 expect(contact.subscribe).toHaveBeenCalled();
                 expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
                     "<presence to='contact@example.org' type='subscribe' xmlns='jabber:client'>"+
@@ -347,9 +359,14 @@
                     expect($contacts.hasClass('both')).toBeTruthy();
                     done();
                 });
+                });
             }));
 
-            it("Alternate Flow: Contact Declines Subscription Request", mock.initConverse(function (_converse) {
+            it("Alternate Flow: Contact Declines Subscription Request",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
                 /* The process by which a user subscribes to a contact, including
                 * the interaction between roster items and subscription states.
                 */
@@ -429,11 +446,16 @@
                         "</query>"+
                     "</iq>"
                 );
+                done();
             }));
 
-            it("Unsubscribe to a contact when subscription is mutual", mock.initConverseWithAsync(function (done, _converse) {
+            it("Unsubscribe to a contact when subscription is mutual",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'],
+                    { roster_groups: false },
+                    function (done, _converse) {
+
                 var sent_IQ, IQ_id, jid = 'annegreet.gomez@localhost';
-                _converse.roster_groups = false;
                 test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current');
                 spyOn(window, 'confirm').and.returnValue(true);
@@ -490,7 +512,10 @@
                 });
             }));
 
-            it("Receiving a subscription request", mock.initConverse(function (_converse) {
+            it("Receiving a subscription request", mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched'], {},
+                function (done, _converse) {
+
                 test_utils.openControlBox(_converse);
                 test_utils.createContacts(_converse, 'current'); // Create some contacts so that we can test positioning
                 spyOn(_converse, "emit");
@@ -516,6 +541,7 @@
                     expect($header.is(":visible")).toBeTruthy();
                     var $contacts = $header.parent().nextUntil('dt', 'dd');
                     expect($contacts.length).toBe(1);
+                    done();
                 });
             }));
         });

+ 2 - 3
spec/register.js

@@ -1,7 +1,6 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
-    var $ = converse.env.jQuery;
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
     var Strophe = converse.env.Strophe;
     var $iq = converse.env.$iq;
 

+ 8 - 4
spec/roomslist.js

@@ -7,12 +7,13 @@
 
     describe("The converse-roomslist plugin", function () {
 
-        it("is shown under a list of open rooms in the \"Rooms\" panel", mock.initConverse(
+        it("is shown under a list of open rooms in the \"Rooms\" panel", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'],
             { whitelisted_plugins: ['converse-roomslist'],
               allow_bookmarks: false // Makes testing easier, otherwise we
                                      // have to mock stanza traffic.
             },
-            function (_converse) {
+            function (done, _converse) {
                 test_utils.openControlBox().openRoomsPanel(_converse);
                 var controlbox = _converse.chatboxviews.get('controlbox');
 
@@ -46,18 +47,20 @@
 
                 list = controlbox.el.querySelector('div.rooms-list-container');
                 expect(_.includes(list.classList, 'hidden')).toBeTruthy();
+                done();
             }
         ));
     });
 
     describe("An room shown in the rooms list", function () {
 
-        it("can be closed", mock.initConverse(
+        it("can be closed", mock.initConverseWithPromises(
+            null, ['rosterGroupsFetched'],
             { whitelisted_plugins: ['converse-roomslist'],
               allow_bookmarks: false // Makes testing easier, otherwise we
                                      // have to mock stanza traffic.
             },
-            function (_converse) {
+            function (done, _converse) {
 
             spyOn(window, 'confirm').and.callFake(function () {
                 return true;
@@ -76,6 +79,7 @@
             room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
             expect(room_els.length).toBe(0);
             expect(_converse.chatboxes.length).toBe(1);
+            done();
         }));
 
         it("shows unread messages directed at the user", mock.initConverseWithAsync(

+ 2 - 2
spec/transcripts.js

@@ -1,5 +1,6 @@
 (function (root, factory) {
     define([
+        "jquery.noconflict",
         "converse-core",
         "mock",
         "test_utils",
@@ -7,10 +8,9 @@
         "transcripts"
         ], factory
     );
-} (this, function (converse, mock, test_utils, utils, transcripts) {
+} (this, function ($, converse, mock, test_utils, utils, transcripts) {
     var Strophe = converse.env.Strophe;
     var _ = converse.env._;
-    var $ = converse.env.jQuery;
     var IGNORED_TAGS = [
         'stream:features',
         'auth',

+ 2 - 3
spec/xmppstatus.js

@@ -1,7 +1,6 @@
 (function (root, factory) {
-    define(["jasmine", "mock", "converse-core", "test-utils"], factory);
-} (this, function (jasmine, mock, converse, test_utils) {
-    var $ = converse.env.jQuery;
+    define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
+} (this, function ($, jasmine, mock, converse, test_utils) {
 
     return describe("The XMPPStatus model", function() {
 

+ 10 - 1
tests/mock.js

@@ -2,6 +2,7 @@
     define("mock", ['jquery.noconflict', 'converse'], factory);
 }(this, function ($, converse_api) {
     var _ = converse_api.env._;
+    var Promise = converse_api.env.Promise;
     var Strophe = converse_api.env.Strophe;
     var $iq = converse_api.env.$iq;
     var mock = {};
@@ -76,7 +77,7 @@
         };
     }();
 
-    function initConverse (settings, spies) {
+    function initConverse (settings, spies, promises) {
         window.localStorage.clear();
         window.sessionStorage.clear();
 
@@ -105,6 +106,14 @@
         return converse;
     }
 
+    mock.initConverseWithPromises = function (spies, promise_names, settings, func) {
+        return function (done) {
+            var _converse = initConverse(settings, spies);
+            var promises = _.map(promise_names, _converse.api.waitUntil);
+            Promise.all(promises).then(_.partial(func, done, _converse));
+        }
+    };
+
     mock.initConverseWithConnectionSpies = function (spies, settings, func) {
         if (_.isFunction(settings)) {
             var _func = settings;

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác