Ver código fonte

Move nickname tests into a new file

JC Brand 3 anos atrás
pai
commit
ba52defdae
3 arquivos alterados com 400 adições e 390 exclusões
  1. 2 1
      karma.conf.js
  2. 0 389
      src/plugins/muc-views/tests/muc.js
  3. 398 0
      src/plugins/muc-views/tests/nickname.js

+ 2 - 1
karma.conf.js

@@ -76,16 +76,17 @@ module.exports = function(config) {
       { pattern: "src/plugins/muc-views/tests/mam.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/markers.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/me-messages.js", type: 'module' },
+      { pattern: "src/plugins/muc-views/tests/member-lists.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/mentions.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/mep.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/modtools.js", type: 'module' },
-      { pattern: "src/plugins/muc-views/tests/member-lists.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc-api.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc-mentions.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc-messages.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc-registration.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muclist.js", type: 'module' },
+      { pattern: "src/plugins/muc-views/tests/nickname.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/occupants.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/retractions.js", type: 'module' },

+ 0 - 389
src/plugins/muc-views/tests/muc.js

@@ -1302,95 +1302,6 @@ describe("Groupchats", function () {
                 .toBe(`other-room@chat.jabberfr.org`);
         }));
 
-        it("will use the user's reserved nickname, if it exists",
-                mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
-
-            const IQ_stanzas = _converse.connection.IQ_stanzas;
-            const muc_jid = 'lounge@montague.lit';
-            await mock.openChatRoom(_converse, 'lounge', 'montague.lit', 'romeo');
-
-            let stanza = await u.waitUntil(() => IQ_stanzas.filter(
-                iq => iq.querySelector(
-                    `iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
-                )).pop()
-            );
-            // We pretend this is a new room, so no disco info is returned.
-            const features_stanza = $iq({
-                    from: 'lounge@montague.lit',
-                    'id': stanza.getAttribute('id'),
-                    'to': 'romeo@montague.lit/desktop',
-                    'type': 'error'
-                }).c('error', {'type': 'cancel'})
-                    .c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"});
-            _converse.connection._dataRecv(mock.createRequest(features_stanza));
-
-
-            /* <iq from='hag66@shakespeare.lit/pda'
-             *     id='getnick1'
-             *     to='coven@chat.shakespeare.lit'
-             *     type='get'>
-             * <query xmlns='http://jabber.org/protocol/disco#info'
-             *         node='x-roomuser-item'/>
-             * </iq>
-             */
-            const iq = await u.waitUntil(() => IQ_stanzas.filter(
-                    s => sizzle(`iq[to="${muc_jid}"] query[node="x-roomuser-item"]`, s).length
-                ).pop());
-
-            expect(Strophe.serialize(iq)).toBe(
-                `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="lounge@montague.lit" `+
-                    `type="get" xmlns="jabber:client">`+
-                        `<query node="x-roomuser-item" xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
-
-            /* <iq from='coven@chat.shakespeare.lit'
-             *     id='getnick1'
-             *     to='hag66@shakespeare.lit/pda'
-             *     type='result'>
-             *     <query xmlns='http://jabber.org/protocol/disco#info'
-             *             node='x-roomuser-item'>
-             *         <identity
-             *             category='conference'
-             *             name='thirdwitch'
-             *             type='text'/>
-             *     </query>
-             * </iq>
-             */
-            const view = _converse.chatboxviews.get('lounge@montague.lit');
-            stanza = $iq({
-                'type': 'result',
-                'id': iq.getAttribute('id'),
-                'from': view.model.get('jid'),
-                'to': _converse.connection.jid
-            }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info', 'node': 'x-roomuser-item'})
-            .c('identity', {'category': 'conference', 'name': 'thirdwitch', 'type': 'text'});
-            _converse.connection._dataRecv(mock.createRequest(stanza));
-
-            // The user has just entered the groupchat (because join was called)
-            // and receives their own presence from the server.
-            // See example 24:
-            // https://xmpp.org/extensions/xep-0045.html#enter-pres
-            const presence = $pres({
-                    to:'romeo@montague.lit/orchard',
-                    from:'lounge@montague.lit/thirdwitch',
-                    id:'DC352437-C019-40EC-B590-AF29E879AF97'
-            }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
-                .c('item').attrs({
-                    affiliation: 'member',
-                    jid: 'romeo@montague.lit/orchard',
-                    role: 'participant'
-                }).up()
-                .c('status').attrs({code:'110'}).up()
-                .c('status').attrs({code:'210'}).nodeTree;
-
-            _converse.connection._dataRecv(mock.createRequest(presence));
-
-            await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED));
-            await mock.returnMemberLists(_converse, muc_jid, [], ['member', 'admin', 'owner']);
-            await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-info').length);
-            const info_text = sizzle('.chat-content .chat-info:first', view).pop().textContent.trim();
-            expect(info_text).toBe('Your nickname has been automatically set to thirdwitch');
-        }));
-
         it("allows the user to invite their roster contacts to enter the groupchat",
                 mock.initConverse(['chatBoxesFetched'], {'view_mode': 'fullscreen'}, async function (_converse) {
 
@@ -1632,109 +1543,6 @@ describe("Groupchats", function () {
             expect(info_messages[0].textContent.trim()).toBe('Groupchat logging is now enabled');
         }));
 
-
-        it("informs users if their nicknames have been changed.",
-                mock.initConverse([], {}, async function (_converse) {
-
-            /* The service then sends two presence stanzas to the full JID
-             * of each occupant (including the occupant who is changing his
-             * or her room nickname), one of type "unavailable" for the old
-             * nickname and one indicating availability for the new
-             * nickname.
-             *
-             * See: https://xmpp.org/extensions/xep-0045.html#changenick
-             *
-             *  <presence
-             *      from='coven@montague.lit/thirdwitch'
-             *      id='DC352437-C019-40EC-B590-AF29E879AF98'
-             *      to='hag66@shakespeare.lit/pda'
-             *      type='unavailable'>
-             *  <x xmlns='http://jabber.org/protocol/muc#user'>
-             *      <item affiliation='member'
-             *          jid='hag66@shakespeare.lit/pda'
-             *          nick='oldhag'
-             *          role='participant'/>
-             *      <status code='303'/>
-             *      <status code='110'/>
-             *  </x>
-             *  </presence>
-             *
-             *  <presence
-             *      from='coven@montague.lit/oldhag'
-             *      id='5B4F27A4-25ED-43F7-A699-382C6B4AFC67'
-             *      to='hag66@shakespeare.lit/pda'>
-             *  <x xmlns='http://jabber.org/protocol/muc#user'>
-             *      <item affiliation='member'
-             *          jid='hag66@shakespeare.lit/pda'
-             *          role='participant'/>
-             *      <status code='110'/>
-             *  </x>
-             *  </presence>
-             */
-            const __ = _converse.__;
-            await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'oldnick');
-            const view = _converse.chatboxviews.get('lounge@montague.lit');
-            expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
-
-            await u.waitUntil(() => view.querySelectorAll('li .occupant-nick').length, 500);
-            let occupants = view.querySelector('.occupant-list');
-            expect(occupants.childElementCount).toBe(1);
-            expect(occupants.firstElementChild.querySelector('.occupant-nick').textContent.trim()).toBe("oldnick");
-
-            const csntext = await u.waitUntil(() => view.querySelector('.chat-content__notifications').textContent);
-            expect(csntext.trim()).toEqual("oldnick has entered the groupchat");
-
-            let presence = $pres().attrs({
-                    from:'lounge@montague.lit/oldnick',
-                    id:'DC352437-C019-40EC-B590-AF29E879AF98',
-                    to:'romeo@montague.lit/pda',
-                    type:'unavailable'
-                })
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
-                .c('item').attrs({
-                    affiliation: 'owner',
-                    jid: 'romeo@montague.lit/pda',
-                    nick: 'newnick',
-                    role: 'moderator'
-                }).up()
-                .c('status').attrs({code:'303'}).up()
-                .c('status').attrs({code:'110'}).nodeTree;
-
-            _converse.connection._dataRecv(mock.createRequest(presence));
-            await u.waitUntil(() => view.querySelectorAll('.chat-info').length);
-
-            expect(sizzle('div.chat-info:last').pop().textContent.trim()).toBe(
-                __(_converse.muc.new_nickname_messages["303"], "newnick")
-            );
-            expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
-
-            occupants = view.querySelector('.occupant-list');
-            expect(occupants.childElementCount).toBe(1);
-
-            presence = $pres().attrs({
-                    from:'lounge@montague.lit/newnick',
-                    id:'5B4F27A4-25ED-43F7-A699-382C6B4AFC67',
-                    to:'romeo@montague.lit/pda'
-                })
-                .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
-                .c('item').attrs({
-                    affiliation: 'owner',
-                    jid: 'romeo@montague.lit/pda',
-                    role: 'moderator'
-                }).up()
-                .c('status').attrs({code:'110'}).nodeTree;
-
-            _converse.connection._dataRecv(mock.createRequest(presence));
-            expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
-            expect(view.querySelectorAll('div.chat-info').length).toBe(1);
-            expect(sizzle('div.chat-info', view)[0].textContent.trim()).toBe(
-                __(_converse.muc.new_nickname_messages["303"], "newnick")
-            );
-            occupants = view.querySelector('.occupant-list');
-            expect(occupants.childElementCount).toBe(1);
-            expect(sizzle('.occupant-nick:first', occupants).pop().textContent.trim()).toBe("newnick");
-        }));
-
         it("queries for the groupchat information before attempting to join the user",
                 mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
 
@@ -3307,15 +3115,6 @@ describe("Groupchats", function () {
 
     describe("When attempting to enter a groupchat", function () {
 
-        it("will use the nickname set in the global settings if the user doesn't have a VCard nickname",
-                mock.initConverse(['chatBoxesFetched'], {'nickname': 'Benedict-Cucumberpatch'},
-                async function (_converse) {
-
-            await mock.openChatRoomViaModal(_converse, 'roomy@muc.montague.lit');
-            const view = _converse.chatboxviews.get('roomy@muc.montague.lit');
-            expect(view.model.get('nick')).toBe('Benedict-Cucumberpatch');
-        }));
-
         it("will show an error message if the groupchat requires a password",
                 mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
 
@@ -3434,105 +3233,6 @@ describe("Groupchats", function () {
             expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.BANNED);
         }));
 
-        it("will render a nickname form if a nickname conflict happens and muc_nickname_from_jid=false",
-                mock.initConverse([], {}, async function (_converse) {
-
-            const muc_jid = 'conflicted@muc.montague.lit';
-            await mock.openChatRoomViaModal(_converse, muc_jid, 'romeo');
-            const iq = await u.waitUntil(() => _converse.connection.IQ_stanzas.filter(
-                iq => iq.querySelector(
-                    `iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
-                )).pop());
-
-            const features_stanza = $iq({
-                    'from': muc_jid,
-                    'id': iq.getAttribute('id'),
-                    'to': 'romeo@montague.lit/desktop',
-                    'type': 'result'
-                })
-                .c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'})
-                    .c('identity', {'category': 'conference', 'name': 'A Dark Cave', 'type': 'text'}).up()
-                    .c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
-                    .c('feature', {'var': 'muc_hidden'}).up()
-                    .c('feature', {'var': 'muc_temporary'}).up()
-            _converse.connection._dataRecv(mock.createRequest(features_stanza));
-
-            const view = _converse.chatboxviews.get(muc_jid);
-            await u.waitUntil(() => view.model.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING);
-
-            const presence = $pres().attrs({
-                    from: `${muc_jid}/romeo`,
-                    id: u.getUniqueId(),
-                    to: 'romeo@montague.lit/pda',
-                    type: 'error'
-                }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                  .c('error').attrs({by: muc_jid, type:'cancel'})
-                      .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-            _converse.connection._dataRecv(mock.createRequest(presence));
-
-            const el = await u.waitUntil(() => view.querySelector('.muc-nickname-form .validation-message'));
-            expect(el.textContent.trim()).toBe('The nickname you chose is reserved or currently in use, please choose a different one.');
-        }));
-
-
-        it("will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true",
-                mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
-
-            const { api } = _converse;
-            const muc_jid = 'conflicting@muc.montague.lit'
-            await mock.openChatRoomViaModal(_converse, muc_jid, 'romeo');
-            /* <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>
-             */
-            api.settings.set('muc_nickname_from_jid', true);
-
-            const attrs = {
-                'from': `${muc_jid}/romeo`,
-                'id': u.getUniqueId(),
-                'to': 'romeo@montague.lit/pda',
-                'type': 'error'
-            };
-            let presence = $pres().attrs(attrs)
-                .c('x').attrs({'xmlns':'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({'by': muc_jid, 'type':'cancel'})
-                    .c('conflict').attrs({'xmlns':'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-
-            const view = _converse.chatboxviews.get(muc_jid);
-            spyOn(view.model, 'join').and.callThrough();
-
-            // Simulate repeatedly that there's already someone in the groupchat
-            // with that nickname
-            _converse.connection._dataRecv(mock.createRequest(presence));
-            expect(view.model.join).toHaveBeenCalledWith('romeo-2');
-
-            attrs.from = `${muc_jid}/romeo-2`;
-            attrs.id = u.getUniqueId();
-            presence = $pres().attrs(attrs)
-                .c('x').attrs({'xmlns':'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({'by': muc_jid, type:'cancel'})
-                    .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-            _converse.connection._dataRecv(mock.createRequest(presence));
-
-            expect(view.model.join).toHaveBeenCalledWith('romeo-3');
-
-            attrs.from = `${muc_jid}/romeo-3`;
-            attrs.id = new Date().getTime();
-            presence = $pres().attrs(attrs)
-                .c('x').attrs({'xmlns': 'http://jabber.org/protocol/muc'}).up()
-                .c('error').attrs({'by': muc_jid, 'type': 'cancel'})
-                    .c('conflict').attrs({'xmlns':'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-            _converse.connection._dataRecv(mock.createRequest(presence));
-            expect(view.model.join).toHaveBeenCalledWith('romeo-4');
-        }));
-
         it("will show an error message if the user is not allowed to have created the groupchat",
                 mock.initConverse([], {}, async function (_converse) {
 
@@ -3569,43 +3269,6 @@ describe("Groupchats", function () {
             expect(el.textContent.trim()).toBe('You are not allowed to create new groupchats.');
         }));
 
-        it("will show an error message if the user's nickname doesn't conform to groupchat policy",
-                mock.initConverse([], {}, async function (_converse) {
-
-            const muc_jid = 'conformist@muc.montague.lit'
-            await mock.openChatRoomViaModal(_converse, muc_jid, 'romeo');
-
-            const iq = await u.waitUntil(() => _converse.connection.IQ_stanzas.filter(
-                iq => iq.querySelector(
-                    `iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
-                )).pop());
-            const features_stanza = $iq({
-                    'from': muc_jid,
-                    'id': iq.getAttribute('id'),
-                    'to': 'romeo@montague.lit/desktop',
-                    'type': 'result'
-                }).c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'})
-                    .c('identity', {'category': 'conference', 'name': 'A Dark Cave', 'type': 'text'}).up()
-                    .c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
-            _converse.connection._dataRecv(mock.createRequest(features_stanza));
-
-            const view = _converse.chatboxviews.get(muc_jid);
-            await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
-
-            const presence = $pres().attrs({
-                    from: `${muc_jid}/romeo`,
-                    id: u.getUniqueId(),
-                    to:'romeo@montague.lit/pda',
-                    type:'error'
-                }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
-                  .c('error').attrs({by:'lounge@montague.lit', type:'cancel'})
-                      .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
-
-            _converse.connection._dataRecv(mock.createRequest(presence));
-            const el = await u.waitUntil(() => view.querySelector('.chatroom-body converse-muc-disconnected .disconnect-msg:last-child'));
-            expect(el.textContent.trim()).toBe("Your nickname doesn't conform to this groupchat's policies.");
-        }));
-
         it("will show an error message if the groupchat doesn't yet exist",
                 mock.initConverse([], {}, async function (_converse) {
 
@@ -3785,58 +3448,6 @@ describe("Groupchats", function () {
             expect(name_input.placeholder).toBe('name@muc.example.org');
         }));
 
-        it("doesn't show the nickname field if locked_muc_nickname is true",
-                mock.initConverse(['chatBoxesFetched'], {'locked_muc_nickname': true, 'muc_nickname_from_jid': true}, async function (_converse) {
-
-            await mock.openControlBox(_converse);
-            await mock.waitForRoster(_converse, 'current', 0);
-            const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
-            roomspanel.querySelector('.show-add-muc-modal').click();
-            mock.closeControlBox(_converse);
-            const modal = _converse.api.modal.get('add-chatroom-modal');
-            await u.waitUntil(() => u.isVisible(modal.el), 1000)
-            const name_input = modal.el.querySelector('input[name="chatroom"]');
-            name_input.value = 'lounge@montague.lit';
-            expect(modal.el.querySelector('label[for="nickname"]')).toBe(null);
-            expect(modal.el.querySelector('input[name="nickname"]')).toBe(null);
-            modal.el.querySelector('form input[type="submit"]').click();
-            await u.waitUntil(() => _converse.chatboxes.length > 1);
-            const chatroom = _converse.chatboxes.get('lounge@montague.lit');
-            expect(chatroom.get('nick')).toBe('romeo');
-        }));
-
-        it("uses the JID node if muc_nickname_from_jid is set to true",
-                mock.initConverse(['chatBoxesFetched'], {'muc_nickname_from_jid': true}, async function (_converse) {
-
-            await mock.openControlBox(_converse);
-            await mock.waitForRoster(_converse, 'current', 0);
-            const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
-            roomspanel.querySelector('.show-add-muc-modal').click();
-            mock.closeControlBox(_converse);
-            const modal = _converse.api.modal.get('add-chatroom-modal');
-            await u.waitUntil(() => u.isVisible(modal.el), 1000)
-            const label_nick = modal.el.querySelector('label[for="nickname"]');
-            expect(label_nick.textContent.trim()).toBe('Nickname:');
-            const nick_input = modal.el.querySelector('input[name="nickname"]');
-            expect(nick_input.value).toBe('romeo');
-        }));
-
-        it("uses the nickname passed in to converse.initialize",
-                mock.initConverse(['chatBoxesFetched'], {'nickname': 'st.nick'}, async function (_converse) {
-
-            await mock.openControlBox(_converse);
-            await mock.waitForRoster(_converse, 'current', 0);
-            const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
-            roomspanel.querySelector('.show-add-muc-modal').click();
-            mock.closeControlBox(_converse);
-            const modal = _converse.api.modal.get('add-chatroom-modal');
-            await u.waitUntil(() => u.isVisible(modal.el), 1000)
-            const label_nick = modal.el.querySelector('label[for="nickname"]');
-            expect(label_nick.textContent.trim()).toBe('Nickname:');
-            const nick_input = modal.el.querySelector('input[name="nickname"]');
-            expect(nick_input.value).toBe('st.nick');
-        }));
-
         it("doesn't require the domain when muc_domain is set",
                 mock.initConverse(['chatBoxesFetched'], {'muc_domain': 'muc.example.org'}, async function (_converse) {
 

+ 398 - 0
src/plugins/muc-views/tests/nickname.js

@@ -0,0 +1,398 @@
+/*global mock, converse */
+
+const { $pres, $iq, Strophe, sizzle, u }  = converse.env;
+
+fdescribe("A MUC", function () {
+
+    it("informs users if their nicknames have been changed.",
+            mock.initConverse([], {}, async function (_converse) {
+
+        /* The service then sends two presence stanzas to the full JID
+         * of each occupant (including the occupant who is changing his
+         * or her room nickname), one of type "unavailable" for the old
+         * nickname and one indicating availability for the new
+         * nickname.
+         *
+         * See: https://xmpp.org/extensions/xep-0045.html#changenick
+         *
+         *  <presence
+         *      from='coven@montague.lit/thirdwitch'
+         *      id='DC352437-C019-40EC-B590-AF29E879AF98'
+         *      to='hag66@shakespeare.lit/pda'
+         *      type='unavailable'>
+         *  <x xmlns='http://jabber.org/protocol/muc#user'>
+         *      <item affiliation='member'
+         *          jid='hag66@shakespeare.lit/pda'
+         *          nick='oldhag'
+         *          role='participant'/>
+         *      <status code='303'/>
+         *      <status code='110'/>
+         *  </x>
+         *  </presence>
+         *
+         *  <presence
+         *      from='coven@montague.lit/oldhag'
+         *      id='5B4F27A4-25ED-43F7-A699-382C6B4AFC67'
+         *      to='hag66@shakespeare.lit/pda'>
+         *  <x xmlns='http://jabber.org/protocol/muc#user'>
+         *      <item affiliation='member'
+         *          jid='hag66@shakespeare.lit/pda'
+         *          role='participant'/>
+         *      <status code='110'/>
+         *  </x>
+         *  </presence>
+         */
+        const __ = _converse.__;
+        await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'oldnick');
+        const view = _converse.chatboxviews.get('lounge@montague.lit');
+        expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
+
+        await u.waitUntil(() => view.querySelectorAll('li .occupant-nick').length, 500);
+        let occupants = view.querySelector('.occupant-list');
+        expect(occupants.childElementCount).toBe(1);
+        expect(occupants.firstElementChild.querySelector('.occupant-nick').textContent.trim()).toBe("oldnick");
+
+        const csntext = await u.waitUntil(() => view.querySelector('.chat-content__notifications').textContent);
+        expect(csntext.trim()).toEqual("oldnick has entered the groupchat");
+
+        let presence = $pres().attrs({
+                from:'lounge@montague.lit/oldnick',
+                id:'DC352437-C019-40EC-B590-AF29E879AF98',
+                to:'romeo@montague.lit/pda',
+                type:'unavailable'
+            })
+            .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
+            .c('item').attrs({
+                affiliation: 'owner',
+                jid: 'romeo@montague.lit/pda',
+                nick: 'newnick',
+                role: 'moderator'
+            }).up()
+            .c('status').attrs({code:'303'}).up()
+            .c('status').attrs({code:'110'}).nodeTree;
+
+        _converse.connection._dataRecv(mock.createRequest(presence));
+        await u.waitUntil(() => view.querySelectorAll('.chat-info').length);
+
+        expect(sizzle('div.chat-info:last').pop().textContent.trim()).toBe(
+            __(_converse.muc.new_nickname_messages["303"], "newnick")
+        );
+        expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
+
+        occupants = view.querySelector('.occupant-list');
+        expect(occupants.childElementCount).toBe(1);
+
+        presence = $pres().attrs({
+                from:'lounge@montague.lit/newnick',
+                id:'5B4F27A4-25ED-43F7-A699-382C6B4AFC67',
+                to:'romeo@montague.lit/pda'
+            })
+            .c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
+            .c('item').attrs({
+                affiliation: 'owner',
+                jid: 'romeo@montague.lit/pda',
+                role: 'moderator'
+            }).up()
+            .c('status').attrs({code:'110'}).nodeTree;
+
+        _converse.connection._dataRecv(mock.createRequest(presence));
+        expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.ENTERED);
+        expect(view.querySelectorAll('div.chat-info').length).toBe(1);
+        expect(sizzle('div.chat-info', view)[0].textContent.trim()).toBe(
+            __(_converse.muc.new_nickname_messages["303"], "newnick")
+        );
+        occupants = view.querySelector('.occupant-list');
+        expect(occupants.childElementCount).toBe(1);
+        expect(sizzle('.occupant-nick:first', occupants).pop().textContent.trim()).toBe("newnick");
+    }));
+
+    describe("when being entered", function () {
+
+        it("will use the user's reserved nickname, if it exists",
+                mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+
+            const IQ_stanzas = _converse.connection.IQ_stanzas;
+            const muc_jid = 'lounge@montague.lit';
+            await mock.openChatRoom(_converse, 'lounge', 'montague.lit', 'romeo');
+
+            let stanza = await u.waitUntil(() => IQ_stanzas.filter(
+                iq => iq.querySelector(
+                    `iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
+                )).pop()
+            );
+            // We pretend this is a new room, so no disco info is returned.
+            const features_stanza = $iq({
+                    from: 'lounge@montague.lit',
+                    'id': stanza.getAttribute('id'),
+                    'to': 'romeo@montague.lit/desktop',
+                    'type': 'error'
+                }).c('error', {'type': 'cancel'})
+                    .c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"});
+            _converse.connection._dataRecv(mock.createRequest(features_stanza));
+
+
+            /* <iq from='hag66@shakespeare.lit/pda'
+             *     id='getnick1'
+             *     to='coven@chat.shakespeare.lit'
+             *     type='get'>
+             * <query xmlns='http://jabber.org/protocol/disco#info'
+             *         node='x-roomuser-item'/>
+             * </iq>
+             */
+            const iq = await u.waitUntil(() => IQ_stanzas.filter(
+                    s => sizzle(`iq[to="${muc_jid}"] query[node="x-roomuser-item"]`, s).length
+                ).pop());
+
+            expect(Strophe.serialize(iq)).toBe(
+                `<iq from="romeo@montague.lit/orchard" id="${iq.getAttribute('id')}" to="lounge@montague.lit" `+
+                    `type="get" xmlns="jabber:client">`+
+                        `<query node="x-roomuser-item" xmlns="http://jabber.org/protocol/disco#info"/></iq>`);
+
+            /* <iq from='coven@chat.shakespeare.lit'
+             *     id='getnick1'
+             *     to='hag66@shakespeare.lit/pda'
+             *     type='result'>
+             *     <query xmlns='http://jabber.org/protocol/disco#info'
+             *             node='x-roomuser-item'>
+             *         <identity
+             *             category='conference'
+             *             name='thirdwitch'
+             *             type='text'/>
+             *     </query>
+             * </iq>
+             */
+            const view = _converse.chatboxviews.get('lounge@montague.lit');
+            stanza = $iq({
+                'type': 'result',
+                'id': iq.getAttribute('id'),
+                'from': view.model.get('jid'),
+                'to': _converse.connection.jid
+            }).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info', 'node': 'x-roomuser-item'})
+            .c('identity', {'category': 'conference', 'name': 'thirdwitch', 'type': 'text'});
+            _converse.connection._dataRecv(mock.createRequest(stanza));
+
+            // The user has just entered the groupchat (because join was called)
+            // and receives their own presence from the server.
+            // See example 24:
+            // https://xmpp.org/extensions/xep-0045.html#enter-pres
+            const presence = $pres({
+                    to:'romeo@montague.lit/orchard',
+                    from:'lounge@montague.lit/thirdwitch',
+                    id:'DC352437-C019-40EC-B590-AF29E879AF97'
+            }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
+                .c('item').attrs({
+                    affiliation: 'member',
+                    jid: 'romeo@montague.lit/orchard',
+                    role: 'participant'
+                }).up()
+                .c('status').attrs({code:'110'}).up()
+                .c('status').attrs({code:'210'}).nodeTree;
+
+            _converse.connection._dataRecv(mock.createRequest(presence));
+
+            await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED));
+            await mock.returnMemberLists(_converse, muc_jid, [], ['member', 'admin', 'owner']);
+            await u.waitUntil(() => view.querySelectorAll('.chat-content .chat-info').length);
+            const info_text = sizzle('.chat-content .chat-info:first', view).pop().textContent.trim();
+            expect(info_text).toBe('Your nickname has been automatically set to thirdwitch');
+        }));
+
+        it("will use the nickname set in the global settings if the user doesn't have a VCard nickname",
+                mock.initConverse(['chatBoxesFetched'], {'nickname': 'Benedict-Cucumberpatch'},
+                async function (_converse) {
+
+            await mock.openChatRoomViaModal(_converse, 'roomy@muc.montague.lit');
+            const view = _converse.chatboxviews.get('roomy@muc.montague.lit');
+            expect(view.model.get('nick')).toBe('Benedict-Cucumberpatch');
+        }));
+
+        it("will render a nickname form if a nickname conflict happens and muc_nickname_from_jid=false",
+                mock.initConverse([], {}, async function (_converse) {
+
+            const muc_jid = 'conflicted@muc.montague.lit';
+            await mock.openChatRoomViaModal(_converse, muc_jid, 'romeo');
+            const iq = await u.waitUntil(() => _converse.connection.IQ_stanzas.filter(
+                iq => iq.querySelector(
+                    `iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
+                )).pop());
+
+            const features_stanza = $iq({
+                    'from': muc_jid,
+                    'id': iq.getAttribute('id'),
+                    'to': 'romeo@montague.lit/desktop',
+                    'type': 'result'
+                })
+                .c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'})
+                    .c('identity', {'category': 'conference', 'name': 'A Dark Cave', 'type': 'text'}).up()
+                    .c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
+                    .c('feature', {'var': 'muc_hidden'}).up()
+                    .c('feature', {'var': 'muc_temporary'}).up()
+            _converse.connection._dataRecv(mock.createRequest(features_stanza));
+
+            const view = _converse.chatboxviews.get(muc_jid);
+            await u.waitUntil(() => view.model.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING);
+
+            const presence = $pres().attrs({
+                    from: `${muc_jid}/romeo`,
+                    id: u.getUniqueId(),
+                    to: 'romeo@montague.lit/pda',
+                    type: 'error'
+                }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                  .c('error').attrs({by: muc_jid, type:'cancel'})
+                      .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+            _converse.connection._dataRecv(mock.createRequest(presence));
+
+            const el = await u.waitUntil(() => view.querySelector('.muc-nickname-form .validation-message'));
+            expect(el.textContent.trim()).toBe('The nickname you chose is reserved or currently in use, please choose a different one.');
+        }));
+
+
+        it("will automatically choose a new nickname if a nickname conflict happens and muc_nickname_from_jid=true",
+                mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+
+            const { api } = _converse;
+            const muc_jid = 'conflicting@muc.montague.lit'
+            await mock.openChatRoomViaModal(_converse, muc_jid, 'romeo');
+            /* <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>
+             */
+            api.settings.set('muc_nickname_from_jid', true);
+
+            const attrs = {
+                'from': `${muc_jid}/romeo`,
+                'id': u.getUniqueId(),
+                'to': 'romeo@montague.lit/pda',
+                'type': 'error'
+            };
+            let presence = $pres().attrs(attrs)
+                .c('x').attrs({'xmlns':'http://jabber.org/protocol/muc'}).up()
+                .c('error').attrs({'by': muc_jid, 'type':'cancel'})
+                    .c('conflict').attrs({'xmlns':'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+
+            const view = _converse.chatboxviews.get(muc_jid);
+            spyOn(view.model, 'join').and.callThrough();
+
+            // Simulate repeatedly that there's already someone in the groupchat
+            // with that nickname
+            _converse.connection._dataRecv(mock.createRequest(presence));
+            expect(view.model.join).toHaveBeenCalledWith('romeo-2');
+
+            attrs.from = `${muc_jid}/romeo-2`;
+            attrs.id = u.getUniqueId();
+            presence = $pres().attrs(attrs)
+                .c('x').attrs({'xmlns':'http://jabber.org/protocol/muc'}).up()
+                .c('error').attrs({'by': muc_jid, type:'cancel'})
+                    .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+            _converse.connection._dataRecv(mock.createRequest(presence));
+
+            expect(view.model.join).toHaveBeenCalledWith('romeo-3');
+
+            attrs.from = `${muc_jid}/romeo-3`;
+            attrs.id = new Date().getTime();
+            presence = $pres().attrs(attrs)
+                .c('x').attrs({'xmlns': 'http://jabber.org/protocol/muc'}).up()
+                .c('error').attrs({'by': muc_jid, 'type': 'cancel'})
+                    .c('conflict').attrs({'xmlns':'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+            _converse.connection._dataRecv(mock.createRequest(presence));
+            expect(view.model.join).toHaveBeenCalledWith('romeo-4');
+        }));
+
+        it("will show an error message if the user's nickname doesn't conform to groupchat policy",
+                mock.initConverse([], {}, async function (_converse) {
+
+            const muc_jid = 'conformist@muc.montague.lit'
+            await mock.openChatRoomViaModal(_converse, muc_jid, 'romeo');
+
+            const iq = await u.waitUntil(() => _converse.connection.IQ_stanzas.filter(
+                iq => iq.querySelector(
+                    `iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`
+                )).pop());
+            const features_stanza = $iq({
+                    'from': muc_jid,
+                    'id': iq.getAttribute('id'),
+                    'to': 'romeo@montague.lit/desktop',
+                    'type': 'result'
+                }).c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'})
+                    .c('identity', {'category': 'conference', 'name': 'A Dark Cave', 'type': 'text'}).up()
+                    .c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
+            _converse.connection._dataRecv(mock.createRequest(features_stanza));
+
+            const view = _converse.chatboxviews.get(muc_jid);
+            await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING));
+
+            const presence = $pres().attrs({
+                    from: `${muc_jid}/romeo`,
+                    id: u.getUniqueId(),
+                    to:'romeo@montague.lit/pda',
+                    type:'error'
+                }).c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
+                  .c('error').attrs({by:'lounge@montague.lit', type:'cancel'})
+                      .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
+
+            _converse.connection._dataRecv(mock.createRequest(presence));
+            const el = await u.waitUntil(() => view.querySelector('.chatroom-body converse-muc-disconnected .disconnect-msg:last-child'));
+            expect(el.textContent.trim()).toBe("Your nickname doesn't conform to this groupchat's policies.");
+        }));
+
+        it("doesn't show the nickname field if locked_muc_nickname is true",
+                mock.initConverse(['chatBoxesFetched'], {'locked_muc_nickname': true, 'muc_nickname_from_jid': true}, async function (_converse) {
+
+            await mock.openControlBox(_converse);
+            await mock.waitForRoster(_converse, 'current', 0);
+            const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
+            roomspanel.querySelector('.show-add-muc-modal').click();
+            mock.closeControlBox(_converse);
+            const modal = _converse.api.modal.get('add-chatroom-modal');
+            await u.waitUntil(() => u.isVisible(modal.el), 1000)
+            const name_input = modal.el.querySelector('input[name="chatroom"]');
+            name_input.value = 'lounge@montague.lit';
+            expect(modal.el.querySelector('label[for="nickname"]')).toBe(null);
+            expect(modal.el.querySelector('input[name="nickname"]')).toBe(null);
+            modal.el.querySelector('form input[type="submit"]').click();
+            await u.waitUntil(() => _converse.chatboxes.length > 1);
+            const chatroom = _converse.chatboxes.get('lounge@montague.lit');
+            expect(chatroom.get('nick')).toBe('romeo');
+        }));
+
+        it("uses the JID node if muc_nickname_from_jid is set to true",
+                mock.initConverse(['chatBoxesFetched'], {'muc_nickname_from_jid': true}, async function (_converse) {
+
+            await mock.openControlBox(_converse);
+            await mock.waitForRoster(_converse, 'current', 0);
+            const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
+            roomspanel.querySelector('.show-add-muc-modal').click();
+            mock.closeControlBox(_converse);
+            const modal = _converse.api.modal.get('add-chatroom-modal');
+            await u.waitUntil(() => u.isVisible(modal.el), 1000)
+            const label_nick = modal.el.querySelector('label[for="nickname"]');
+            expect(label_nick.textContent.trim()).toBe('Nickname:');
+            const nick_input = modal.el.querySelector('input[name="nickname"]');
+            expect(nick_input.value).toBe('romeo');
+        }));
+
+        it("uses the nickname passed in to converse.initialize",
+                mock.initConverse(['chatBoxesFetched'], {'nickname': 'st.nick'}, async function (_converse) {
+
+            await mock.openControlBox(_converse);
+            await mock.waitForRoster(_converse, 'current', 0);
+            const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
+            roomspanel.querySelector('.show-add-muc-modal').click();
+            mock.closeControlBox(_converse);
+            const modal = _converse.api.modal.get('add-chatroom-modal');
+            await u.waitUntil(() => u.isVisible(modal.el), 1000)
+            const label_nick = modal.el.querySelector('label[for="nickname"]');
+            expect(label_nick.textContent.trim()).toBe('Nickname:');
+            const nick_input = modal.el.querySelector('input[name="nickname"]');
+            expect(nick_input.value).toBe('st.nick');
+        }));
+    });
+});
+