Parcourir la source

Fix failing tests

JC Brand il y a 7 ans
Parent
commit
ca141401a0

+ 1 - 8
css/converse.css

@@ -4574,7 +4574,6 @@ body.reset {
       position: absolute;
       bottom: 1em; }
   #conversejs .badge {
-    padding: 0.4em;
     line-height: 1;
     font-weight: normal;
     font-size: 90%; }
@@ -5442,13 +5441,7 @@ body.reset {
     #conversejs #controlbox form.search-xmpp-contact input {
       width: 8em; }
   #conversejs #controlbox .msgs-indicator {
-    border-radius: 3px;
-    background-color: #E77051;
-    color: white;
-    font-size: 12px;
-    font-weight: normal;
-    padding: 0 4px;
-    text-shadow: none; }
+    margin-right: 0.5em; }
   #conversejs #controlbox a.subscribe-to-user {
     padding-left: 2em;
     font-weight: bold; }

+ 1 - 8
css/inverse.css

@@ -4574,7 +4574,6 @@ body.reset {
       position: absolute;
       bottom: 1em; }
   #conversejs .badge {
-    padding: 0.4em;
     line-height: 1;
     font-weight: normal;
     font-size: 90%; }
@@ -5512,13 +5511,7 @@ body {
     #conversejs #controlbox form.search-xmpp-contact input {
       width: 8em; }
   #conversejs #controlbox .msgs-indicator {
-    border-radius: 3px;
-    background-color: #E77051;
-    color: white;
-    font-size: 14px;
-    font-weight: normal;
-    padding: 0 4px;
-    text-shadow: none; }
+    margin-right: 0.5em; }
   #conversejs #controlbox a.subscribe-to-user {
     padding-left: 2em;
     font-weight: bold; }

+ 1 - 0
mockup/user-panel.html

@@ -22,6 +22,7 @@
     <div class="list-container rooms-list-container">
         <div class="rooms-list">
             <div class="available-chatroom d-flex flex-row">
+                <span class="badge badge-info msgs-indicator">1</span>
                 <a class="open-room available-room w-100" data-room-jid="public@conference.test.com" title="Click to open this room" href="#">public</a>
                 <a href="#" class="room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
                     data-html="true" data-content="<b>Room Address (JID): </b><br>public@conference.test.com<br><b>Discussions: </b><br>Public discussions<br><b>Participants: </b>1<br><b>Features: </b><br>Non-anonymous">&nbsp;</a>

+ 1 - 7
sass/_controlbox.scss

@@ -74,13 +74,7 @@
         }
 
         .msgs-indicator {
-            border-radius: 3px;
-            background-color: $red;
-            color: white;
-            font-size: $font-size-small;
-            font-weight: normal;
-            padding: 0 4px;
-            text-shadow: none;
+            margin-right: 0.5em;
         }
 
         a.subscribe-to-user {

+ 0 - 1
sass/_core.scss

@@ -209,7 +209,6 @@ body.reset {
         }
     }
     .badge {
-        padding: 0.4em;
         line-height: 1;
         font-weight: normal;
         font-size: 90%;

+ 0 - 4
spec/bookmarks.js

@@ -320,7 +320,6 @@
                 [{'category': 'pubsub', 'type': 'pep'}],
                 ['http://jabber.org/protocol/pubsub#publish-options']
             ).then(function () {
-                test_utils.openControlBox().openRoomsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return _converse.bookmarks;
                 }, 300).then(function () {
@@ -465,7 +464,6 @@
                     ['http://jabber.org/protocol/pubsub#publish-options']
                 ).then(function () {
 
-                    test_utils.openControlBox().openRoomsPanel(_converse);
                     var IQ_id;
                     expect(_.filter(_converse.connection.send.calls.all(), function (call) {
                         var stanza = call.args[0];
@@ -575,7 +573,6 @@
                         'name':  'The Play',
                         'nick': ''
                     });
-                    test_utils.openControlBox().openRoomsPanel(_converse);
 
                     test_utils.waitUntil(function () {
                         return $('#chatrooms dl.bookmarks dd:visible').length;
@@ -609,7 +606,6 @@
                 [{'category': 'pubsub', 'type': 'pep'}],
                 ['http://jabber.org/protocol/pubsub#publish-options']
             ).then(function () {
-                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(

+ 0 - 54
spec/chatbox.js

@@ -27,7 +27,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 test_utils.openChatBoxFor(_converse, contact_jid);
@@ -65,7 +64,6 @@
                 }).then(function () {
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
                     expect(_converse.chatboxes.length).toEqual(1);
                     var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                     var message = '/me is tired';
@@ -97,7 +95,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 var i, $el, jid, chatboxview;
                 // openControlBox was called earlier, so the controlbox is
@@ -134,7 +131,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 var i, $el, jid, chatbox, chatboxview, trimmedview;
                 // openControlBox was called earlier, so the controlbox is
@@ -215,7 +211,6 @@
                 test_utils.createContacts(_converse, 'current');
                 _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
                 var $el, jid, chatbox;
@@ -243,7 +238,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 spyOn(_converse, 'emit');
                 spyOn(_converse.chatboxviews, 'trimChats');
@@ -279,7 +273,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                         return $(_converse.rosterview.el).find('.roster-group').length;
                     }, 300)
@@ -317,7 +310,6 @@
                 var chatview;
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                         return $(_converse.rosterview.el).find('.roster-group').length;
                     }, 300)
@@ -364,7 +356,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                         return $(_converse.rosterview.el).find('.roster-group').length;
                     }, 300)
@@ -409,7 +400,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
                     test_utils.openChatBoxFor(_converse, contact_jid);
@@ -430,7 +420,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
                     test_utils.openChatBoxFor(_converse, contact_jid);
@@ -478,7 +467,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
@@ -507,7 +495,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var view;
                     var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -540,7 +527,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var view, clearButton, $toolbar;
                     var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -581,7 +567,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                                 return $(_converse.rosterview.el).find('.roster-group').length;
                             }, 300)
@@ -749,7 +734,6 @@
 
                             test_utils.createContacts(_converse, 'current');
                             test_utils.openControlBox();
-                            test_utils.openContactsPanel(_converse);
 
                             // TODO: what could still be done for error
                             // messages... if the <error> element has type
@@ -853,7 +837,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                         test_utils.openChatBoxFor(_converse, sender_jid);
@@ -918,7 +901,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         test_utils.waitUntil(function () {
                                 return $(_converse.rosterview.el).find('.roster-group').length;
@@ -973,7 +955,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
@@ -1142,7 +1123,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     /* Ideally we wouldn't have to filter out headline
                      * messages, but Prosody gives them the wrong 'type' :(
@@ -1179,7 +1159,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     // Send a message from a different resource
                     spyOn(_converse, 'log');
@@ -1236,7 +1215,6 @@
                     }).then(function () {
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         // Send a message from a different resource
                         spyOn(_converse, 'log');
@@ -1285,7 +1263,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
                     /* <message from="mallory@evil.example" to="b@xmpp.example">
                      *    <received xmlns='urn:xmpp:carbons:2'>
                      *      <forwarded xmlns='urn:xmpp:forward:0'>
@@ -1334,7 +1311,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
                     test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
                         }, 300)
@@ -1394,7 +1370,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
                     test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
                         }, 300)
@@ -1483,7 +1458,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     spyOn(_converse, 'emit');
                     var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -1507,7 +1481,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                     test_utils.openChatBoxFor(_converse, contact_jid);
@@ -1529,7 +1502,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                     test_utils.openChatBoxFor(_converse, contact_jid);
@@ -1551,7 +1523,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     var message, msg;
                     var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -1680,7 +1651,6 @@
 
                     test_utils.createContacts(_converse, 'current');
                     test_utils.openControlBox();
-                    test_utils.openContactsPanel(_converse);
 
                     spyOn(_converse, 'emit');
                     var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -1705,7 +1675,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
                         }, 300).then(function () {
@@ -1731,7 +1700,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
                         test_utils.waitUntil(function () {
@@ -1772,7 +1740,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
                         }, 300).then(function () {
@@ -1812,7 +1779,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
                         spyOn(_converse, 'emit');
@@ -1896,7 +1862,6 @@
                         var view, contact_jid;
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                                 return $(_converse.rosterview.el).find('.roster-group li').length;
                         }, 700).then(function () {
@@ -1958,7 +1923,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                                 return $(_converse.rosterview.el).find('.roster-group').length;
                             }, 300)
@@ -2043,7 +2007,6 @@
                         var view, contact_jid;
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
                         }, 500).then(function () {
@@ -2111,7 +2074,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                         test_utils.openChatBoxFor(_converse, contact_jid);
@@ -2133,7 +2095,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
                         test_utils.waitUntil(function () {
                             return $(_converse.rosterview.el).find('.roster-group').length;
                         }, 300).then(function () {
@@ -2162,7 +2123,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
                         spyOn(_converse, 'emit');
@@ -2194,7 +2154,6 @@
 
                         test_utils.createContacts(_converse, 'current');
                         test_utils.openControlBox();
-                        test_utils.openContactsPanel(_converse);
 
                         spyOn(_converse, 'emit');
                         var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -2225,7 +2184,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 spyOn(_converse, 'emit');
                 var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
@@ -2266,7 +2224,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 spyOn(_converse, 'emit');
                 expect(_converse.msg_counter).toBe(0);
@@ -2300,7 +2257,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
                 _converse.windowState = 'hidden';
                 spyOn(_converse, 'clearMsgCounter').and.callThrough();
                 _converse.saveWindowState(null, 'focus');
@@ -2316,7 +2272,6 @@
 
                 test_utils.createContacts(_converse, 'current');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
 
                 expect(_converse.msg_counter).toBe(0);
                 spyOn(_converse, 'incrementMsgCounter').and.callThrough();
@@ -2392,7 +2347,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
 
                 var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
                     msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
@@ -2412,7 +2366,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
 
                 var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
                     msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be read');
@@ -2528,7 +2481,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return $(_converse.rosterview.el).find('.roster-group').length;
                 }, 500)
@@ -2561,7 +2513,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return $(_converse.rosterview.el).find('.roster-group').length;
                 }, 500)
@@ -2595,7 +2546,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return $(_converse.rosterview.el).find('.roster-group').length;
                 }, 500)
@@ -2630,7 +2580,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return $(_converse.rosterview.el).find('.roster-group').length;
                 }, 500)
@@ -2663,7 +2612,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return $(_converse.rosterview.el).find('.roster-group').length;
                 }, 500)
@@ -2698,7 +2646,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
 
                 var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 test_utils.openChatBoxFor(_converse, sender_jid);
@@ -2729,7 +2676,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'current');
-                test_utils.openContactsPanel(_converse);
 
                 var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
                 test_utils.openChatBoxFor(_converse, sender_jid);

+ 328 - 329
spec/chatroom.js

@@ -81,8 +81,10 @@
                         var jid = 'lounge@localhost';
                         var room = _converse.api.rooms.get(jid);
                         expect(room instanceof Object).toBeTruthy();
-                        expect(room.is_chatroom).toBeTruthy();
+
                         var chatroomview = _converse.chatboxviews.get(jid);
+                        expect(chatroomview.is_chatroom).toBeTruthy();
+
                         expect(u.isVisible(chatroomview.el)).toBeTruthy();
                         chatroomview.close();
 
@@ -140,15 +142,15 @@
                     var room = _converse.api.rooms.open(jid);
                     // Test on chat room that's not yet open
                     expect(room instanceof Object).toBeTruthy();
-                    expect(room.is_chatroom).toBeTruthy();
                     var chatroomview = _converse.chatboxviews.get(jid);
+                    expect(chatroomview.is_chatroom).toBeTruthy();
                     expect(u.isVisible(chatroomview.el)).toBeTruthy();
 
                     // Test again, now that the room exists.
                     room = _converse.api.rooms.open(jid);
                     expect(room instanceof Object).toBeTruthy();
-                    expect(room.is_chatroom).toBeTruthy();
                     chatroomview = _converse.chatboxviews.get(jid);
+                    expect(chatroomview.is_chatroom).toBeTruthy();
                     expect(u.isVisible(chatroomview.el)).toBeTruthy();
                     chatroomview.close();
 
@@ -358,7 +360,7 @@
                     .c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"});
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
 
-                    var input = view.el.querySelector('input.new-chatroom-nick');
+                    var input = view.el.querySelector('input[name="nick"]');
                     input.value = 'nicky';
                     view.el.querySelector('input[type=submit]').click();
                     expect(view.submitNickname).toHaveBeenCalled();
@@ -455,7 +457,7 @@
                     }).up()
                     .c('status', {code: '110'});
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
-                expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room.");
+                expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room");
 
                 presence = $pres({
                         to: 'dummy@localhost/_converse.js-29092160',
@@ -469,7 +471,7 @@
                     });
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(2);
-                expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room.");
+                expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room");
 
                 // Add another entrant, otherwise the above message will be
                 // collapsed if "newguy" leaves immediately again
@@ -485,7 +487,7 @@
                     });
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(3);
-                expect($chat_content.find('div.chat-info:last').html()).toBe("newgirl has entered the room.");
+                expect($chat_content.find('div.chat-info:last').html()).toBe("newgirl has entered the room");
 
                 // Don't show duplicate join messages
                 presence = $pres({
@@ -544,7 +546,7 @@
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content.find('div.chat-info').length).toBe(4);
                 var $msg_el = $chat_content.find('div.chat-info:last');
-                expect($msg_el.html()).toBe("newguy has left and re-entered the room.");
+                expect($msg_el.html()).toBe("newguy has left and re-entered the room");
                 expect($msg_el.data('leavejoin')).toBe('"newguy"');
 
                 presence = $pres({
@@ -561,7 +563,7 @@
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content.find('div.chat-info').length).toBe(4);
                 $msg_el = $chat_content.find('div.chat-info:last');
-                expect($msg_el.html()).toBe('newguy has left the room.');
+                expect($msg_el.html()).toBe('newguy has left the room');
                 expect($msg_el.data('leave')).toBe('"newguy"');
 
                 presence = $pres({
@@ -576,7 +578,7 @@
                     });
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(5);
-                expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room.");
+                expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room");
 
                 presence = $pres({
                         to: 'dummy@localhost/_converse.js-290918392',
@@ -590,7 +592,7 @@
                     });
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(5);
-                expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered and left the room.");
+                expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered and left the room");
 
                 presence = $pres({
                         to: 'dummy@localhost/_converse.js-29092160',
@@ -604,7 +606,7 @@
                     });
                 _converse.connection._dataRecv(test_utils.createRequest(presence));
                 expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(5);
-                expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room.");
+                expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room");
                 done();
             }));
 
@@ -639,10 +641,10 @@
 
                 var $time = $chat_content.find('time');
                 expect($time.length).toEqual(1);
-                expect($time.attr('class')).toEqual('message chat-info chat-date');
+                expect($time.attr('class')).toEqual('message chat-info chat-date badge badge-info');
                 expect($time.data('isodate')).toEqual(moment().startOf('day').format());
                 expect($time.text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
-                expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room.");
+                expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room");
 
                 // XXX: Hack. We clear the chat contents instead of mocking the date
                 $chat_content.html('');
@@ -664,7 +666,7 @@
 
                 $time = $chat_content.find('time');
                 expect($time.length).toEqual(1);
-                expect($time.attr('class')).toEqual('message chat-info chat-date');
+                expect($time.attr('class')).toEqual('message chat-info chat-date badge badge-info');
                 expect($time.data('isodate')).toEqual(moment().startOf('day').format());
                 expect($time.text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
                 expect($chat_content.find('div.chat-info').length).toBe(1);
@@ -700,11 +702,11 @@
                 expect($time.length).toEqual(2);
 
                 $time = $chat_content.find('time:eq(1)');
-                expect($time.attr('class')).toEqual('message chat-info chat-date');
+                expect($time.attr('class')).toEqual('message chat-info chat-date badge badge-info');
                 expect($time.data('isodate')).toEqual(moment().startOf('day').format());
                 expect($time.text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
                 expect($chat_content.find('div.chat-info').length).toBe(1);
-                expect($chat_content.find('div.chat-info:first').html()).toBe("newguy has entered the room.");
+                expect($chat_content.find('div.chat-info:first').html()).toBe("newguy has entered the room");
 
                 // XXX: Hack. We clear the chat contents instead of mocking the date
                 $chat_content.html('');
@@ -738,7 +740,7 @@
                 expect($time.length).toEqual(2);
 
                 $time = $chat_content.find('time:eq(1)');
-                expect($time.attr('class')).toEqual('message chat-info chat-date');
+                expect($time.attr('class')).toEqual('message chat-info chat-date badge badge-info');
                 expect($time.data('isodate')).toEqual(moment().startOf('day').format());
                 expect($time.text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
                 expect($chat_content.find('div.chat-info').length).toBe(1);
@@ -760,7 +762,8 @@
                     sent_IQ = iq;
                     IQ_id = sendIQ.bind(this)(iq, callback, errback);
                 });
-                var view = _converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
+                _converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
+                var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
 
                 var features_stanza = $iq({
                         from: 'coven@chat.shakespeare.lit',
@@ -863,18 +866,6 @@
                 });
             }));
 
-            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.initConverseWithPromises(
                     null, ['rosterGroupsFetched'], {},
@@ -1563,8 +1554,9 @@
                         '</message>').firstChild;
                     _converse.connection._dataRecv(test_utils.createRequest(stanza));
                     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);
+                    var chat_content = view.el.querySelector('.chat-content');
+                    expect($(chat_content).find('.chat-event:last').text()).toBe('Topic set by ralphm');
+                    expect($(chat_content).find('.chat-topic:last').text()).toBe(text);
                     done();
                 });
             }));
@@ -1579,8 +1571,9 @@
                     var subject = '<img src="x" onerror="alert(\'XSS\');"/>';
                     var view = _converse.chatboxviews.get('jdev@conference.jabber.org');
                     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);
+                    var chat_content = view.el.querySelector('.chat-content');
+                    expect($(chat_content).find('.chat-event:last').text()).toBe('Topic set by ralphm');
+                    expect($(chat_content).find('.chat-topic:last').text()).toBe(subject);
                     done();
                 });
             }));
@@ -1653,7 +1646,7 @@
                     expect($occupants.children().first(0).text()).toBe("oldnick");
 
                     expect($chat_content.find('div.chat-info').length).toBe(2);
-                    expect($chat_content.find('div.chat-info:first').html()).toBe("oldnick has entered the room.");
+                    expect($chat_content.find('div.chat-info:first').html()).toBe("oldnick has entered the room");
                     expect($chat_content.find('div.chat-info:last').html()).toBe(
                         __(_converse.muc.new_nickname_messages["210"], "oldnick")
                     );
@@ -1702,7 +1695,7 @@
                         __(_converse.muc.new_nickname_messages["303"], "newnick")
                     );
                     expect($chat_content.find('div.chat-info').last().html()).toBe(
-                        "newnick has entered the room.");
+                        "newnick has entered the room");
                     $occupants = $(view.el.querySelector('.occupant-list'));
                     expect($occupants.children().length).toBe(1);
                     expect($occupants.children().first(0).text()).toBe("newnick");
@@ -2366,7 +2359,7 @@
                             });
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
                     var info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
-                    expect(info_msgs.pop().textContent).toBe("trustworthyguy has entered the room.");
+                    expect(info_msgs.pop().textContent).toBe("trustworthyguy has entered the room");
 
                     var textarea = view.el.querySelector('.chat-textarea')
                     textarea.value = '/op';
@@ -2505,7 +2498,7 @@
                             });
                     _converse.connection._dataRecv(test_utils.createRequest(presence));
                     var info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
-                    expect(info_msgs.pop().textContent).toBe("annoyingGuy has entered the room.");
+                    expect(info_msgs.pop().textContent).toBe("annoyingGuy has entered the room");
 
                     var textarea = view.el.querySelector('.chat-textarea')
                     textarea.value = '/mute';
@@ -2606,50 +2599,39 @@
 
         describe("When attempting to enter a chatroom", function () {
 
-            var submitRoomForm = function (_converse) {
-                var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
-                var input = roomspanel.el.querySelector('input.new-chatroom-name');
-                var nick = roomspanel.el.querySelector('input.new-chatroom-nick');
-                var server = roomspanel.el.querySelector('input.new-chatroom-server');
-                input.value = 'problematic';
-                if (nick) {
-                    nick.value = 'dummy';
-                }
-                server.value = 'muc.localhost';
-                roomspanel.el.querySelector('form [type="submit"]').click();
-            };
-
             it("will show an error message if the room requires a password",
                 mock.initConverseWithPromises(
-                    null, ['rosterGroupsFetched'], {},
+                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'auth'})
-                    .c('not-authorized').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                spyOn(view, 'renderPasswordForm').and.callThrough();
-                view.onChatRoomPresence(presence);
-
-                var $chat_body = $(view.el).find('.chatroom-body');
-                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');
-
-                // Let's submit the form
-                spyOn(view, 'join');
-                var input_el = view.el.querySelector('[name="password"]');
-                input_el.value = 'secret';
-                view.el.querySelector('input[type=submit]').click();
-                expect(view.join).toHaveBeenCalledWith(undefined, "secret");
-                done();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'auth'})
+                        .c('not-authorized').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    spyOn(view, 'renderPasswordForm').and.callThrough();
+                    view.onChatRoomPresence(presence);
+
+                    var $chat_body = $(view.el).find('.chatroom-body');
+                    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');
+
+                    // Let's submit the form
+                    spyOn(view, 'join');
+                    var input_el = view.el.querySelector('[name="password"]');
+                    input_el.value = 'secret';
+                    view.el.querySelector('input[type=submit]').click();
+                    expect(view.join).toHaveBeenCalledWith('dummy', 'secret');
+                    done();
+                });
             }));
 
             it("will show an error message if the room is members-only and the user not included",
@@ -2657,20 +2639,22 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'auth'})
-                    .c('registration-required').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'auth'})
+                        .c('registration-required').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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",
@@ -2678,20 +2662,22 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'auth'})
-                    .c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'auth'})
+                        .c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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",
@@ -2699,24 +2685,26 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                    .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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');
-
-                var $input = $(view.el).find('.chatroom-body form.chatroom-form input:first');
-                $input.val('nicky');
-                view.el.querySelector('input[type=submit]').click();
-                done();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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');
+
+                    var $input = $(view.el).find('.chatroom-body form.chatroom-form input:first');
+                    $input.val('nicky');
+                    view.el.querySelector('input[type=submit]').click();
+                    done();
+                });
             }));
 
             it("will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true",
@@ -2724,57 +2712,59 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                /* <presence
-                 *      from='coven@chat.shakespeare.lit/thirdwitch'
-                 *      id='n13mt3l'
-                 *      to='hag66@shakespeare.lit/pda'
-                 *      type='error'>
-                 *  <x xmlns='http://jabber.org/protocol/muc'/>
-                 *  <error by='coven@chat.shakespeare.lit' type='cancel'>
-                 *      <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
-                 *  </error>
-                 *  </presence>
-                 */
-                submitRoomForm(_converse);
-                _converse.muc_nickname_from_jid = true;
-
-                var attrs = {
-                    from:'lounge@localhost/dummy',
-                    id:'n13mt3l',
-                    to:'dummy@localhost/pda',
-                    type:'error'
-                };
-                var presence = $pres().attrs(attrs)
-                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                spyOn(view, 'showErrorMessage').and.callThrough();
-                spyOn(view, 'join').and.callThrough();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    /* <presence
+                     *      from='coven@chat.shakespeare.lit/thirdwitch'
+                     *      id='n13mt3l'
+                     *      to='hag66@shakespeare.lit/pda'
+                     *      type='error'>
+                     *  <x xmlns='http://jabber.org/protocol/muc'/>
+                     *  <error by='coven@chat.shakespeare.lit' type='cancel'>
+                     *      <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
+                     *  </error>
+                     *  </presence>
+                     */
+                    _converse.muc_nickname_from_jid = true;
 
-                // Simulate repeatedly that there's already someone in the room
-                // with that nickname
-                view.onChatRoomPresence(presence);
-                expect(view.join).toHaveBeenCalledWith('dummy-2');
+                    var attrs = {
+                        from:'lounge@localhost/dummy',
+                        id:'n13mt3l',
+                        to:'dummy@localhost/pda',
+                        type:'error'
+                    };
+                    var presence = $pres().attrs(attrs)
+                        .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                        .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                            .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    spyOn(view, 'showErrorMessage').and.callThrough();
+                    spyOn(view, 'join').and.callThrough();
+
+                    // Simulate repeatedly that there's already someone in the room
+                    // with that nickname
+                    view.onChatRoomPresence(presence);
+                    expect(view.join).toHaveBeenCalledWith('dummy-2');
 
-                attrs.from = 'lounge@localhost/dummy-2';
-                presence = $pres().attrs(attrs)
-                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                view.onChatRoomPresence(presence);
+                    attrs.from = 'lounge@localhost/dummy-2';
+                    presence = $pres().attrs(attrs)
+                        .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                        .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                            .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    view.onChatRoomPresence(presence);
 
-                expect(view.join).toHaveBeenCalledWith('dummy-3');
+                    expect(view.join).toHaveBeenCalledWith('dummy-3');
 
-                attrs.from = 'lounge@localhost/dummy-3';
-                presence = $pres().attrs(attrs)
-                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                        .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                view.onChatRoomPresence(presence);
-                expect(view.join).toHaveBeenCalledWith('dummy-4');
-                done();
+                    attrs.from = 'lounge@localhost/dummy-3';
+                    presence = $pres().attrs(attrs)
+                        .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                        .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                            .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",
@@ -2782,20 +2772,22 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                    .c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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",
@@ -2803,20 +2795,22 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                    .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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",
@@ -2824,20 +2818,22 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                    .c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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",
@@ -2845,20 +2841,22 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                submitRoomForm(_converse);
-                var presence = $pres().attrs({
-                    from:'lounge@localhost/thirdwitch',
-                        id:'n13mt3l',
-                        to:'dummy@localhost/pda',
-                        type:'error'})
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({by:'lounge@localhost', type:'cancel'})
-                    .c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-                var view = _converse.chatboxviews.get('problematic@muc.localhost');
-                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();
+                test_utils.openChatRoomViaModal(_converse, 'problematic@muc.localhost', 'dummy')
+                .then(function () {
+                    var presence = $pres().attrs({
+                        from:'lounge@localhost/thirdwitch',
+                            id:'n13mt3l',
+                            to:'dummy@localhost/pda',
+                            type:'error'})
+                    .c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                    .c('error').attrs({by:'lounge@localhost', type:'cancel'})
+                        .c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+                    var view = _converse.chatboxviews.get('problematic@muc.localhost');
+                    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();
+                });
             }));
         });
 
@@ -3084,63 +3082,41 @@
             }));
         });
 
-        describe("The \"Rooms\" Panel", function () {
-
-            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');
-                var $panels = $(cbview.el).find('.controlbox-panes');
-                var $contacts = $panels.children().first();
-                var $chatrooms = $panels.children().last();
-                spyOn(cbview, 'switchTab').and.callThrough();
-                cbview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
-                $tabs.find('li').last().find('a')[0].click(); // Clicks the chatrooms tab
-                expect(u.isVisible($contacts[0])).toBe(false);
-                expect(u.isVisible($chatrooms[0])).toBe(true);
-                expect(cbview.switchTab).toHaveBeenCalled();
-                done();
-            }));
+        describe("The \"Chatrooms\" section", function () {
 
-            it("contains a form through which a new chatroom can be created",
+            it("contains a link to a modal 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');
-                var $nick = $(roomspanel.el).find('input.new-chatroom-nick');
-                var $server = $(roomspanel.el).find('input.new-chatroom-server');
-                expect($input.length).toBe(1);
-                expect($server.length).toBe(1);
-                expect($('.chatroom:visible').length).toBe(0); // There shouldn't be any chatrooms open currently
-                spyOn(roomspanel, 'openChatRoom').and.callThrough();
-                spyOn(_converse.ChatRoomView.prototype, 'getRoomFeatures').and.callFake(function () {
-                    var deferred = new $.Deferred();
-                    deferred.resolve();
-                    return deferred.promise();
-                });
+                roomspanel.el.querySelector('.trigger-add-chatrooms-modal').click();
+                test_utils.closeControlBox(_converse);
+                const modal = roomspanel.add_room_modal;
+                test_utils.waitUntil(function () {
+                    return u.isVisible(modal.el);
+                }, 1000).then(function () {
+                    spyOn(_converse.ChatRoomView.prototype, 'getRoomFeatures').and.callFake(function () {
+                        var deferred = new $.Deferred();
+                        deferred.resolve();
+                        return deferred.promise();
+                    });
+                    roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
 
-                roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
-                $input.val('Lounge');
-                $nick.val('dummy');
-                $server.val('muc.localhost');
-                roomspanel.el.querySelector('form [type="submit"]').click();
-                expect(roomspanel.openChatRoom).toHaveBeenCalled();
-                expect($('.chatroom:visible').length).toBe(1); // There should now be an open chatroom
-                done();
+                    modal.el.querySelector('input[name="chatroom"]').value = 'lounce@muc.localhost';
+                    modal.el.querySelector('form input[type="submit"]').click();
+                    expect($('.chatroom:visible').length).toBe(1); // There should now be an open chatroom
+                    done();
+                }).catch(_.partial(console.error, _));
             }));
 
-            it("can list rooms publically available on the server",
+            it("contains a link to a modal which can list rooms publically available on the server",
                 mock.initConverseWithPromises(
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
+
                 var sendIQ = _converse.connection.sendIQ;
                 var sent_stanza, IQ_id;
                 spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@@ -3149,53 +3125,79 @@
                 });
 
                 test_utils.openControlBox();
-                var panel = _converse.chatboxviews.get('controlbox').roomspanel;
-                $(panel.tabs).find('li').last().find('a')[0].click(); // Click the chatrooms tab
-                panel.model.set({'muc_domain': 'muc.localhost'}); // Make sure the domain is set
-                // See: http://xmpp.org/extensions/xep-0045.html#disco-rooms
-                expect(document.querySelectorAll('#available-chatrooms dt').length).toBe(0);
-                expect(document.querySelectorAll('#available-chatrooms dd').length).toBe(0);
-                document.querySelector('input#show-rooms').click();
-
-                expect(sent_stanza.toLocaleString()).toBe(
-                    "<iq to='muc.localhost' from='dummy@localhost/resource' type='get' xmlns='jabber:client' id='"+IQ_id+"'>"+
-                        "<query xmlns='http://jabber.org/protocol/disco#items'/>"+
-                    "</iq>"
-                );
-
-                var iq = $iq({
-                    from:'muc.localhost',
-                    to:'dummy@localhost/pda',
-                    id: IQ_id,
-                    type:'result'
-                }).c('query')
-                  .c('item', { jid:'heath@chat.shakespeare.lit', name:'A Lonely Heath'}).up()
-                  .c('item', { jid:'coven@chat.shakespeare.lit', name:'A Dark Cave'}).up()
-                  .c('item', { jid:'forres@chat.shakespeare.lit', name:'The Palace'}).up()
-                  .c('item', { jid:'inverness@chat.shakespeare.lit', name:'Macbeth&apos;s Castle'}).nodeTree;
-                _converse.connection._dataRecv(test_utils.createRequest(iq));
-
-                expect(document.querySelectorAll('#available-chatrooms dt').length).toBe(1);
-                expect($(panel.el.querySelector('#available-chatrooms')).children('dt').first().text()).toBe("Rooms found");
-                expect($(panel.el.querySelector('#available-chatrooms')).children('dd').length).toBe(4);
-                done();
-            }));
-        });
+                var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                roomspanel.el.querySelector('.trigger-list-chatrooms-modal').click();
+                test_utils.closeControlBox(_converse);
+                const modal = roomspanel.list_rooms_modal;
+                test_utils.waitUntil(function () {
+                    return u.isVisible(modal.el);
+                }, 1000).then(function () {
+                    spyOn(_converse.ChatRoomView.prototype, 'getRoomFeatures').and.callFake(function () {
+                        var deferred = new $.Deferred();
+                        deferred.resolve();
+                        return deferred.promise();
+                    });
+                    roomspanel.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
+
+                    // See: http://xmpp.org/extensions/xep-0045.html#disco-rooms
+                    expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(0);
 
-        describe("The \"Rooms\" Panel", function () {
+                    const input = modal.el.querySelector('input[name="server"]').value = 'chat.shakespear.lit';
+                    modal.el.querySelector('input[type="submit"]').click();
+
+                    expect(sent_stanza.toLocaleString()).toBe(
+                        "<iq to='chat.shakespear.lit' from='dummy@localhost/resource' type='get' xmlns='jabber:client' id='"+IQ_id+"'>"+
+                            "<query xmlns='http://jabber.org/protocol/disco#items'/>"+
+                        "</iq>"
+                    );
+
+                    var iq = $iq({
+                        from:'muc.localhost',
+                        to:'dummy@localhost/pda',
+                        id: IQ_id,
+                        type:'result'
+                    }).c('query')
+                    .c('item', { jid:'heath@chat.shakespeare.lit', name:'A Lonely Heath'}).up()
+                    .c('item', { jid:'coven@chat.shakespeare.lit', name:'A Dark Cave'}).up()
+                    .c('item', { jid:'forres@chat.shakespeare.lit', name:'The Palace'}).up()
+                    .c('item', { jid:'inverness@chat.shakespeare.lit', name:'Macbeth&apos;s Castle'}).nodeTree;
+                    _converse.connection._dataRecv(test_utils.createRequest(iq));
+
+                    expect(modal.el.querySelectorAll('.available-chatrooms li').length).toBe(5);
+
+                    const rooms = modal.el.querySelectorAll('.available-chatrooms li');
+                    expect(rooms[0].textContent.trim()).toBe("Rooms found:");
+                    expect(rooms[1].textContent.trim()).toBe("A Lonely Heath");
+                    expect(rooms[2].textContent.trim()).toBe("A Dark Cave");
+                    expect(rooms[3].textContent.trim()).toBe("The Palace");
+                    expect(rooms[4].textContent.trim()).toBe("Macbeth's Castle");
+
+                    rooms[4].querySelector('.open-room').click();
+                    expect($('.chatroom:visible').length).toBe(1); // There should now be an open chatroom
+                    var view = _converse.chatboxviews.get('inverness@chat.shakespeare.lit');
+                    expect(view.el.querySelector('.chat-head-chatroom').textContent.trim()).toBe("Macbeth's Castle");
+                    done();
+                }).catch(_.partial(console.error, _));
+            }));
 
             it("shows the number of unread mentions received",
                 mock.initConverseWithPromises(
-                    null, ['rosterGroupsFetched'], {},
+                    null, ['rosterGroupsFetched'], {'allow_bookmarks': false},
                     function (done, _converse) {
+                // XXX: we set `allow_bookmarks` to false, so that the rooms
+                // list gets rendered. Otherwise we would have to mock
+                // the bookmark stanza exchange.
+
+                test_utils.openControlBox();
+                var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(0);
 
                 var room_jid = 'kitchen@conference.shakespeare.lit';
                 test_utils.openAndEnterChatRoom(
                         _converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
 
-                    test_utils.openContactsPanel(_converse);
-                    var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
-                    expect(_.isNull(roomspanel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+                    expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
+                    expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
 
                     var view = _converse.chatboxviews.get(room_jid);
                     view.model.set({'minimized': true});
@@ -3209,32 +3211,29 @@
                             to: 'dummy@localhost',
                             type: 'groupchat'
                         }).c('body').t(message).tree();
-
                     view.handleMUCMessage(msg);
 
-                    test_utils.waitUntil(function () {
-                        return _.includes(roomspanel.tab_el.firstChild.classList, 'unread-msgs');
-                    }, 300).then(function () {
-                        expect(_.includes(roomspanel.tab_el.firstChild.classList, 'unread-msgs')).toBeTruthy();
-                        expect(roomspanel.tab_el.querySelector('.msgs-indicator').textContent).toBe('1');
+                    expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
+                    expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
+                    expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('1');
 
-                        msg = $msg({
-                            from: room_jid+'/'+nick,
-                            id: (new Date()).getTime(),
-                            to: 'dummy@localhost',
-                            type: 'groupchat'
-                        }).c('body').t(message).tree();
-                        view.handleMUCMessage(msg);
-                        expect(roomspanel.tab_el.querySelector('.msgs-indicator').textContent).toBe('2');
+                    msg = $msg({
+                        from: room_jid+'/'+nick,
+                        id: (new Date()).getTime(),
+                        to: 'dummy@localhost',
+                        type: 'groupchat'
+                    }).c('body').t(message).tree();
+                    view.handleMUCMessage(msg);
 
-                        var contacts_panel = _converse.chatboxviews.get('controlbox').contactspanel;
-                        expect(_.isNull(contacts_panel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+                    expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
+                    expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
+                    expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('2');
 
-                        view.model.set({'minimized': false});
-                        expect(_.includes(roomspanel.tab_el.firstChild.classList, 'unread-msgs')).toBeFalsy();
-                        expect(_.isNull(roomspanel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
-                        done();
-                    });
+                    view.model.set({'minimized': false});
+
+                    expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
+                    expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
+                    done();
                 });
             }));
         });

+ 48 - 84
spec/controlbox.js

@@ -8,7 +8,7 @@
     var u = converse.env.utils;
 
 
-    describe("The Control Box", function () {
+    describe("The Controlbox", function () {
 
         it("can be opened by clicking a DOM element with class 'toggle-controlbox'",
             mock.initConverseWithPromises(
@@ -31,6 +31,53 @@
             done();
         }));
 
+        describe("The \"Contacts\" section", function () {
+
+            it("shows the number of unread mentions received",
+                mock.initConverseWithPromises(
+                    null, ['rosterGroupsFetched'], {},
+                    function (done, _converse) {
+
+                test_utils.createContacts(_converse, 'all').openControlBox();
+
+                var contacts_panel = _converse.chatboxviews.get('controlbox').contactspanel;
+                expect(_.isNull(contacts_panel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+
+                var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+                test_utils.openChatBoxFor(_converse, sender_jid);
+                var chatview = _converse.chatboxviews.get(sender_jid);
+                chatview.model.set({'minimized': true});
+
+                var msg = $msg({
+                        from: sender_jid,
+                        to: _converse.connection.jid,
+                        type: 'chat',
+                        id: (new Date()).getTime()
+                    }).c('body').t('hello').up()
+                    .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
+                _converse.chatboxes.onMessage(msg);
+                expect(contacts_panel.tab_el.querySelector('.msgs-indicator').textContent).toBe('1');
+
+                msg = $msg({
+                        from: sender_jid,
+                        to: _converse.connection.jid,
+                        type: 'chat',
+                        id: (new Date()).getTime()
+                    }).c('body').t('hello again').up()
+                    .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
+                _converse.chatboxes.onMessage(msg);
+                expect(contacts_panel.tab_el.querySelector('.msgs-indicator').textContent).toBe('2');
+
+                var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+                expect(_.isNull(roomspanel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
+
+                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();
+            }));
+        });
+
         describe("The Status Widget", function () {
 
             it("shows the user's chat status, which is online by default",
@@ -144,87 +191,4 @@
             });
         }));
     });
-
-    describe("The Controlbox Tabs", function () {
-
-        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');
-            expect($panels.children().length).toBe(2);
-            expect($panels.children().first().attr('id')).toBe('users');
-            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.initConverseWithPromises(
-                null, ['rosterGroupsFetched'], {},
-                function (done, _converse) {
-
-            test_utils.openControlBox();
-            var cbview = _converse.chatboxviews.get('controlbox');
-            var $tabs = $(cbview.el).find('#controlbox-tabs');
-            expect(cbview.model.get('active-panel')).toBe('users');
-            $tabs.find('li').last().find('a')[0].click();
-            expect(cbview.model.get('active-panel')).toBe('chatrooms');
-            $tabs.find('li').first().find('a')[0].click();
-            expect(cbview.model.get('active-panel')).toBe('users');
-            done();
-        }));
-
-        describe("The \"Contacts\" Panel", function () {
-
-            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);
-
-                var contacts_panel = _converse.chatboxviews.get('controlbox').contactspanel;
-                expect(_.isNull(contacts_panel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
-
-                var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-                test_utils.openChatBoxFor(_converse, sender_jid);
-                var chatview = _converse.chatboxviews.get(sender_jid);
-                chatview.model.set({'minimized': true});
-
-                var msg = $msg({
-                        from: sender_jid,
-                        to: _converse.connection.jid,
-                        type: 'chat',
-                        id: (new Date()).getTime()
-                    }).c('body').t('hello').up()
-                    .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
-                _converse.chatboxes.onMessage(msg);
-                expect(contacts_panel.tab_el.querySelector('.msgs-indicator').textContent).toBe('1');
-
-                msg = $msg({
-                        from: sender_jid,
-                        to: _converse.connection.jid,
-                        type: 'chat',
-                        id: (new Date()).getTime()
-                    }).c('body').t('hello again').up()
-                    .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
-                _converse.chatboxes.onMessage(msg);
-                expect(contacts_panel.tab_el.querySelector('.msgs-indicator').textContent).toBe('2');
-
-                var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
-                expect(_.isNull(roomspanel.tab_el.querySelector('.msgs-indicator'))).toBeTruthy();
-
-                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();
-            }));
-        });
-    });
 }));

+ 8 - 8
spec/converse.js

@@ -276,7 +276,7 @@
 
         describe("The \"chats\" API", function() {
 
-            it("has a method 'get' which returns a wrapped chat box", mock.initConverseWithPromises(
+            it("has a method 'get' which returns the chatbox model", mock.initConverseWithPromises(
                 null, ['rosterInitialized'], {}, function (done, _converse) {
                     test_utils.openControlBox();
                     test_utils.createContacts(_converse, 'current');
@@ -291,7 +291,7 @@
                     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));
+                    expect(box.get('box_id')).toBe(b64_sha1(jid));
                     chatboxview = _converse.chatboxviews.get(jid);
                     expect($(chatboxview.el).is(':visible')).toBeTruthy();
                     // Test for multiple JIDs
@@ -299,12 +299,12 @@
                     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));
+                    expect(list[0].get('box_id')).toBe(b64_sha1(jid));
+                    expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
                     done();
             }));
 
-            it("has a method 'open' which opens and returns a wrapped chat box", mock.initConverseWithPromises(
+            it("has a method 'open' which opens and returns the chatbox model", mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched'], {}, function (done, _converse) {
 
                 test_utils.openControlBox();
@@ -315,7 +315,7 @@
                 expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
                 var box = _converse.api.chats.open(jid);
                 expect(box instanceof Object).toBeTruthy();
-                expect(box.model.get('box_id')).toBe(b64_sha1(jid));
+                expect(box.get('box_id')).toBe(b64_sha1(jid));
                 expect(
                     _.keys(box),
                     ['close', 'endOTR', 'focus', 'get', 'initiateOTR', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
@@ -326,8 +326,8 @@
                 var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
                 var list = _converse.api.chats.open([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));
+                expect(list[0].get('box_id')).toBe(b64_sha1(jid));
+                expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
                 done();
             }));
         });

+ 0 - 3
spec/minchats.js

@@ -13,7 +13,6 @@
 
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
-            test_utils.openContactsPanel(_converse);
             _converse.minimized_chats.toggleview.model.browserStorage._clear();
             _converse.minimized_chats.initToggle();
 
@@ -48,7 +47,6 @@
 
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
-            test_utils.openContactsPanel(_converse);
             _converse.minimized_chats.toggleview.model.browserStorage._clear();
             _converse.minimized_chats.initToggle();
 
@@ -79,7 +77,6 @@
 
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
-            test_utils.openContactsPanel(_converse);
             _converse.minimized_chats.toggleview.model.browserStorage._clear();
             _converse.minimized_chats.initToggle();
 

+ 0 - 2
spec/otr.js

@@ -77,7 +77,6 @@
                 function (done, _converse) {
 
             test_utils.openControlBox();
-            test_utils.openContactsPanel(_converse);
             test_utils.createContacts(_converse, 'current');
 
             var UNVERIFIED = 1, UNENCRYPTED = 0;
@@ -104,7 +103,6 @@
                     function (done, _converse) {
 
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
                 test_utils.createContacts(_converse, 'current');
 
                 var msgtext = "?OTR,1,3,?OTR:AAIDAAAAAAEAAAABAAAAwCQ8HKsag0y0DGKsneo0kzKu1ua5L93M4UKTkCf1I2kbm2RgS5kIxDTxrTj3wVRB+H5Si86E1fKtuBgsDf/bKkGTM0h/49vh5lOD9HkE8cnSrFEn5GN,";

+ 0 - 2
spec/roomslist.js

@@ -14,7 +14,6 @@
                                      // have to mock stanza traffic.
             },
             function (done, _converse) {
-                test_utils.openControlBox().openRoomsPanel(_converse);
                 var controlbox = _converse.chatboxviews.get('controlbox');
 
                 var list = controlbox.el.querySelector('div.rooms-list-container');
@@ -69,7 +68,6 @@
             test_utils.openChatRoom(
                 _converse, 'lounge', 'conference.shakespeare.lit', 'JC');
             expect(_converse.chatboxes.length).toBe(2);
-            test_utils.openControlBox().openRoomsPanel(_converse);
             var room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
             expect(room_els.length).toBe(1);
             var close_el = _converse.rooms_list_view.el.querySelector(".close-room");

+ 0 - 6
spec/roster.js

@@ -497,7 +497,6 @@
                 // Must be initialized, so that render is called and documentFragment set up.
                 test_utils.createContacts(_converse, 'pending');
                 test_utils.openControlBox();
-                test_utils.openContactsPanel(_converse);
             }
 
             it("can be collapsed under their own header", 
@@ -690,7 +689,6 @@
             var _addContacts = function (_converse) {
                 test_utils.createContacts(_converse, 'current')
                     .openControlBox()
-                    .openContactsPanel(_converse);
             };
 
             it("can be collapsed under their own header", 
@@ -1081,7 +1079,6 @@
                         names.push($(item).text().replace(/^\s+|\s+$/g, ''));
                     }
                 };
-                test_utils.openContactsPanel(_converse);
                 spyOn(_converse, 'emit');
                 spyOn(_converse.rosterview, 'update').and.callThrough();
                 spyOn(_converse.controlboxtoggle, 'showControlBox').and.callThrough();
@@ -1112,7 +1109,6 @@
                     null, ['rosterGroupsFetched'], {},
                     function (done, _converse) {
 
-                test_utils.openContactsPanel(_converse);
                 var name = mock.req_names[0];
                 spyOn(window, 'confirm').and.returnValue(true);
                 _converse.roster.create({
@@ -1258,7 +1254,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'all').openControlBox();
-                test_utils.openContactsPanel(_converse);
                 var new_attrs, old_attrs, attrs;
                 var num_contacts = _converse.roster.length;
                 var new_roster = new _converse.RosterContacts();
@@ -1287,7 +1282,6 @@
                     function (done, _converse) {
 
                 test_utils.createContacts(_converse, 'all').openControlBox();
-                test_utils.openContactsPanel(_converse);
                 test_utils.waitUntil(function () {
                     return $(_converse.rosterview.el).find('.roster-group li').length;
                 }, 700).then(function () {

+ 0 - 2
spec/spoilers.js

@@ -93,7 +93,6 @@
 
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
-            test_utils.openContactsPanel(_converse);
             var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
             // XXX: We need to send a presence from the contact, so that we
@@ -167,7 +166,6 @@
 
             test_utils.createContacts(_converse, 'current');
             test_utils.openControlBox();
-            test_utils.openContactsPanel(_converse);
             var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
             // XXX: We need to send a presence from the contact, so that we

+ 3 - 2
src/converse-chatboxes.js

@@ -366,10 +366,11 @@
                         create = attrs;
                         attrs = jid;
                         jid = attrs.jid;
-                    } else {
-                        attrs.jid = jid;
                     }
                     jid = jid.toLowerCase();
+                    attrs.jid = jid;
+                    attrs.id = jid;
+
                     let  chatbox = this.get(Strophe.getBareJidFromJid(jid));
                     if (!chatbox && create) {
                         chatbox = this.create(attrs, {

+ 1 - 0
src/converse-chatview.js

@@ -269,6 +269,7 @@
                 },
 
                 render () {
+                    // XXX: Is this still needed?
                     this.el.setAttribute('id', this.model.get('box_id'));
                     this.el.innerHTML = tpl_chatbox(
                         _.extend(this.model.toJSON(), {

+ 0 - 12
src/converse-fullscreen.js

@@ -44,18 +44,6 @@
                         this.createBrandHeadingHTML()
                     );
                 }
-            },
-
-            ChatRoomView: {
-                afterShown (focus) {
-                    /* Make sure chat rooms are scrolled down when opened
-                     */
-                    this.scrollDown();
-                    if (focus) {
-                        this.focus();
-                    }
-                    return this.__super__.afterShown.apply(this, arguments);
-                }
             }
         },
 

+ 3 - 2
src/converse-muc-views.js

@@ -242,8 +242,9 @@
                 openRoom (ev) {
                     ev.preventDefault();
                     const jid = ev.target.getAttribute('data-room-jid');
+                    const name = ev.target.getAttribute('data-room-name');
                     this.modal.hide();
-                    _converse.api.rooms.open(jid);
+                    _converse.api.rooms.open(jid, {'name': name});
                 },
 
                 toggleRoomInfo (ev) {
@@ -264,7 +265,7 @@
                     );
                     const div = document.createElement('div');
                     div.innerHTML = tpl_room_item({
-                        'name': name,
+                        'name': Strophe.xmlunescape(name),
                         'jid': room.getAttribute('jid'),
                         'open_title': __('Click to open this room'),
                         'info_title': __('Show more information on this room')

+ 3 - 2
src/converse-muc.js

@@ -482,6 +482,7 @@
                 'rooms': {
                     'close' (jids) {
                         if (_.isUndefined(jids)) {
+                            // FIXME: can't access views here
                             _converse.chatboxviews.each(function (view) {
                                 if (view.is_chatroom && view.model) {
                                     view.close();
@@ -543,9 +544,9 @@
                             attrs.nick = Strophe.getNodeFromJid(_converse.bare_jid);
                         }
                         if (_.isString(jids)) {
-                            return _converse.getChatRoom(jids, attrs);
+                            return getChatRoom(jids, attrs);
                         }
-                        return _.map(jids, _.partial(_converse.getChatRoom, _, attrs));
+                        return _.map(jids, _.partial(getChatRoom, _, attrs));
                     }
                 }
             });

+ 1 - 1
src/templates/chatroom_form.html

@@ -1,7 +1,7 @@
 <div class="chatroom-form-container">
     <form class="converse-form chatroom-form">
         <fieldset class="form-group">
-            <span class="spinner centered"/>
+            <span class="spinner fa fa-spinner centered"/>
         </fieldset>
     </form>
 </div>

+ 1 - 1
src/templates/login_panel.html

@@ -5,7 +5,7 @@
             <p class="feedback-message {[ if (!o.conn_feedback_message) { ]} hidden {[ } ]}">{{{o.conn_feedback_message}}}</p>
         </div>
         {[ if (o.auto_login || o._converse.CONNECTION_STATUS[o.connection_status] === 'CONNECTING') { ]}
-            <span class="spinner centered"/>
+            <span class="spinner fa fa-spinner centered"/>
         {[ } else { ]}
             {[ if (o.authentication == o.LOGIN || o.authentication == o.EXTERNAL) { ]}
                 <div class="form-group">

+ 1 - 1
src/templates/registration_request.html

@@ -1,4 +1,4 @@
-<span class="spinner login-submit"></span>
+<span class="spinner login-submit fa fa-spinner"></span>
 <p class="info">{{{o.__("Hold tight, we're fetching the registration form…")}}}</p>
 {[ if (o.cancel) { ]}
     <button class="btn btn-secondary button-cancel hor_centered">{{{o.__('Cancel')}}}</button>

+ 1 - 0
src/templates/room_item.html

@@ -2,6 +2,7 @@
   <div class="available-chatroom d-flex flex-row">
     <a class="open-room available-room w-100"
        data-room-jid="{{{o.jid}}}"
+       data-room-name="{{{o.name}}}"
        title="{{{o.open_title}}}"
        href="#">{{{o.name}}}</a>
     <a class="right room-info icon-room-info"

+ 2 - 2
src/templates/room_panel.html

@@ -1,8 +1,8 @@
 <!-- <div id="chatrooms"> -->
 <div class="d-flex">
     <span class="w-100">{{{o.heading_chatrooms}}}</span>
-    <a class="chatbox-btn fa fa-list-ul" title="{{{o.title_list_rooms}}}" data-toggle="modal" data-target="#list-chatrooms-modal"></a>
-    <a class="chatbox-btn fa fa-users" title="{{{o.title_new_room}}}" data-toggle="modal" data-target="#add-chatrooms-modal"></a>
+    <a class="chatbox-btn trigger-list-chatrooms-modal fa fa-list-ul" title="{{{o.title_list_rooms}}}" data-toggle="modal" data-target="#list-chatrooms-modal"></a>
+    <a class="chatbox-btn trigger-add-chatrooms-modal fa fa-users" title="{{{o.title_new_room}}}" data-toggle="modal" data-target="#add-chatrooms-modal"></a>
 </div>
 <div class="list-container open-rooms-list rooms-list-container"></div>
 <div class="list-container bookmarks-list rooms-list-container"></div>

+ 2 - 2
src/templates/rooms_list_item.html

@@ -1,11 +1,11 @@
 <div class="room-item">
 <div class="available-chatroom d-flex flex-row {[ if (o.num_unread_general) { ]} unread-msgs {[ } ]}" data-room-jid="{{{o.jid}}}">
 {[ if (o.num_unread) { ]}
-    <span class="msgs-indicator">{{{ o.num_unread }}}</span>
+    <span class="msgs-indicator badge badge-info">{{{ o.num_unread }}}</span>
 {[ } ]}
 <a class="open-room available-room w-100"
     data-room-jid="{{{o.jid}}}"
-    title="{{{o.open_title}}}" href="#">{{{o.name}}}</a>
+    title="{{{o.open_title}}}" href="#">{{{o.name || o.jid}}}</a>
 
 <a class="right close-room icon-leave"
    data-room-jid="{{{o.jid}}}"

+ 1 - 1
src/templates/trimmed_chat.html

@@ -2,6 +2,6 @@
     {[ if (o.num_unread) { ]} 
         <span class="badge badge-light">{{{o.num_unread}}}</span>
     {[ } ]}
-    {{{o. title }}}
+    {{{o.title || o.jid }}}
 </a>
 <a class="chatbox-btn close-chatbox-button fa fa-times"></a>

+ 0 - 1
src/utils.js

@@ -705,6 +705,5 @@
         evt.initEvent(name, bubbles, cancelable);
         el.dispatchEvent(evt);
     };
-
     return u;
 }));

+ 1 - 0
tests/index.html

@@ -7,6 +7,7 @@
     <link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png">
 
     <link rel="stylesheet" type="text/css" media="screen" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
+    <link type="text/css" rel="stylesheet" media="screen" href="../node_modules/font-awesome/css/font-awesome.min.css" />
     <link rel="stylesheet" type="text/css" media="screen" href="../css/jasmine.css">
     <link type="text/css" rel="stylesheet" media="screen" href="../css/theme.css" />
     <link type="text/css" rel="stylesheet" media="screen" href="../css/converse.css" />

+ 1 - 0
tests/transpiled.html

@@ -7,6 +7,7 @@
     <link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png">
 
     <link rel="stylesheet" type="text/css" media="screen" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
+    <link type="text/css" rel="stylesheet" media="screen" href="../node_modules/font-awesome/css/font-awesome.min.css" />
     <link rel="stylesheet" type="text/css" media="screen" href="../css/jasmine.css">
     <link type="text/css" rel="stylesheet" media="screen" href="../css/theme.css" />
     <link type="text/css" rel="stylesheet" media="screen" href="../css/converse.css" />

+ 35 - 36
tests/utils.js

@@ -83,18 +83,6 @@
         return this;
     };
 
-    utils.openContactsPanel = function (converse) {
-        this.openControlBox(converse);
-        var cbview = converse.chatboxviews.get('controlbox');
-        cbview.el.querySelector('#controlbox-tabs li:first-child a').click();
-    };
-
-    utils.openRoomsPanel = function (converse) {
-        utils.openControlBox();
-        var cbview = converse.chatboxviews.get('controlbox');
-        cbview.el.querySelector('#controlbox-tabs li:last-child a').click();
-    };
-
     utils.openChatBoxes = function (converse, amount) {
         var i = 0, jid, views = [];
         for (i; i<amount; i++) {
@@ -108,25 +96,36 @@
         return converse.roster.get(jid).trigger("open");
     };
 
-    utils.openChatRoom = function (_converse, room, server, nick) {
+    utils.openChatRoomViaModal = function (_converse, jid, nick) {
         // Opens a new chatroom
-        this.openControlBox(_converse);
-        this.openRoomsPanel(_converse);
-        var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
-        roomspanel.el.querySelector('input.new-chatroom-name').value = room;
-        roomspanel.el.querySelector('input.new-chatroom-server').value = server;
-        roomspanel.el.querySelector('form input[type="submit"]').click();
-        this.closeControlBox(_converse);
+        return new Promise(function (resolve, reject) {
+            utils.openControlBox(_converse);
+            var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
+            roomspanel.el.querySelector('.trigger-add-chatrooms-modal').click();
+            utils.closeControlBox(_converse);
+            const modal = roomspanel.add_room_modal;
+            utils.waitUntil(function () {
+                return u.isVisible(modal.el);
+            }, 1000).then(function () {
+                modal.el.querySelector('input[name="chatroom"]').value = jid;
+                modal.el.querySelector('form input[type="submit"]').click();
+                resolve();
+            }).catch(_.partial(console.error, _));
+        }).catch(_.partial(console.error, _));
+    };
+
+    utils.openChatRoom = function (_converse, room, server, nick) {
+        _converse.api.rooms.open(`${room}@${server}`);
     };
 
-    utils.openAndEnterChatRoom = function (converse, room, server, nick) {
+    utils.openAndEnterChatRoom = function (_converse, room, server, nick) {
         return new Promise(function (resolve, reject) {
-            sinon.spy(converse.connection, 'sendIQ');
-            utils.openChatRoom(converse, room, server);
-            var view = converse.chatboxviews.get((room+'@'+server).toLowerCase());
+            sinon.spy(_converse.connection, 'sendIQ');
+            _converse.api.rooms.open(`${room}@${server}`);
+            var view = _converse.chatboxviews.get((room+'@'+server).toLowerCase());
 
             // We pretend this is a new room, so no disco info is returned.
-            var IQ_id = converse.connection.sendIQ.firstCall.returnValue;
+            var IQ_id = _converse.connection.sendIQ.firstCall.returnValue;
             var features_stanza = $iq({
                     'from': room+'@'+server,
                     'id': IQ_id,
@@ -134,40 +133,40 @@
                     'type': 'error'
                 }).c('error', {'type': 'cancel'})
                     .c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"});
-            converse.connection._dataRecv(utils.createRequest(features_stanza));
+            _converse.connection._dataRecv(utils.createRequest(features_stanza));
 
             utils.waitUntil(function () {
-                return converse.connection.sendIQ.secondCall;
+                return _converse.connection.sendIQ.secondCall;
             }).then(function () {
                 // The XMPP server returns the reserved nick for this user.
-                IQ_id = converse.connection.sendIQ.secondCall.returnValue;
+                IQ_id = _converse.connection.sendIQ.secondCall.returnValue;
                 var stanza = $iq({
                     'type': 'result',
                     'id': IQ_id,
                     'from': view.model.get('jid'),
-                    'to': converse.connection.jid 
+                    'to': _converse.connection.jid 
                 }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info', 'node': 'x-roomuser-item'})
                     .c('identity', {'category': 'conference', 'name': nick, 'type': 'text'});
-                converse.connection._dataRecv(utils.createRequest(stanza));
+                _converse.connection._dataRecv(utils.createRequest(stanza));
                 // The user has just entered the room (because join was called)
                 // and receives their own presence from the server.
                 // See example 24: http://xmpp.org/extensions/xep-0045.html#enter-pres
                 var presence = $pres({
-                        to: converse.connection.jid,
+                        to: _converse.connection.jid,
                         from: room+'@'+server+'/'+nick,
                         id: 'DC352437-C019-40EC-B590-AF29E879AF97'
                 }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
                     .c('item').attrs({
                         affiliation: 'member',
-                        jid: converse.bare_jid,
+                        jid: _converse.bare_jid,
                         role: 'participant'
                     }).up()
                     .c('status').attrs({code:'110'});
-                converse.connection._dataRecv(utils.createRequest(presence));
-                converse.connection.sendIQ.restore();
+                _converse.connection._dataRecv(utils.createRequest(presence));
+                _converse.connection.sendIQ.restore();
                 resolve();
-            });
-        });
+            }).catch(_.partial(console.error, _));
+        }).catch(_.partial(console.error, _));
     };
 
     utils.clearBrowserStorage = function () {