Browse Source

Move nickname tests into a new file

JC Brand 3 years ago
parent
commit
ba52defdae
3 changed files with 400 additions and 390 deletions
  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/mam.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/markers.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/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/mentions.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/mep.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/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-api.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc-mentions.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-messages.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muc-registration.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/muc.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/muclist.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/occupants.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/rai.js", type: 'module' },
       { pattern: "src/plugins/muc-views/tests/retractions.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`);
                 .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",
         it("allows the user to invite their roster contacts to enter the groupchat",
                 mock.initConverse(['chatBoxesFetched'], {'view_mode': 'fullscreen'}, async function (_converse) {
                 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');
             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",
         it("queries for the groupchat information before attempting to join the user",
                 mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
                 mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
 
 
@@ -3307,15 +3115,6 @@ describe("Groupchats", function () {
 
 
     describe("When attempting to enter a groupchat", 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",
         it("will show an error message if the groupchat requires a password",
                 mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
                 mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
 
 
@@ -3434,105 +3233,6 @@ describe("Groupchats", function () {
             expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.BANNED);
             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",
         it("will show an error message if the user is not allowed to have created the groupchat",
                 mock.initConverse([], {}, async function (_converse) {
                 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.');
             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",
         it("will show an error message if the groupchat doesn't yet exist",
                 mock.initConverse([], {}, async function (_converse) {
                 mock.initConverse([], {}, async function (_converse) {
 
 
@@ -3785,58 +3448,6 @@ describe("Groupchats", function () {
             expect(name_input.placeholder).toBe('name@muc.example.org');
             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",
         it("doesn't require the domain when muc_domain is set",
                 mock.initConverse(['chatBoxesFetched'], {'muc_domain': 'muc.example.org'}, async function (_converse) {
                 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');
+        }));
+    });
+});
+