UNPKG

converse.js

Version:
469 lines (396 loc) 24.8 kB
/*global mock, converse, _ */ const { stx, sizzle, Strophe, u } = converse.env; async function openModtools (_converse, view) { const textarea = await u.waitUntil(() => view.querySelector('.chat-textarea')); textarea.value = '/modtools'; const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, key: "Enter" }; const message_form = view.querySelector('converse-muc-message-form'); message_form.onKeyDown(enter); const modal = await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal')); await u.waitUntil(() => u.isVisible(modal), 1000); return modal; } describe("The groupchat moderator tool", function () { it("allows you to set affiliations and roles", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; let members = [ {'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'}, {'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'}, {'jid': 'wiccarocks@shakespeare.lit', 'nick': 'wiccan', 'affiliation': 'admin'}, {'jid': 'crone1@shakespeare.lit', 'nick': 'thirdwitch', 'affiliation': 'owner'}, {'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'}, ]; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', [], members); const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => (view.model.occupants.length === 5), 1000); const modal = await openModtools(_converse, view); const tab = modal.querySelector('#affiliations-tab'); // Clear so that we don't match older stanzas _converse.api.connection.get().IQ_stanzas = []; tab.click(); let select = modal.querySelector('.select-affiliation'); expect(select.value).toBe('owner'); select.value = 'admin'; let button = modal.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length); let user_els = modal.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(1); expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: wiccarocks@shakespeare.lit'); expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: wiccan'); expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: admin'); _converse.api.connection.get().IQ_stanzas = []; select.value = 'owner'; button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 2); user_els = modal.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(2); expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit'); expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo'); expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner'); expect(user_els[1].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: crone1@shakespeare.lit'); expect(user_els[1].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: thirdwitch'); expect(user_els[1].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner'); const toggle = user_els[1].querySelector('.list-group-item:nth-child(3n) .toggle-form'); const component = user_els[1].querySelector('.list-group-item:nth-child(3n) converse-muc-affiliation-form'); expect(u.hasClass('hidden', component)).toBeTruthy(); toggle.click(); expect(u.hasClass('hidden', component)).toBeFalsy(); const form = user_els[1].querySelector('.list-group-item:nth-child(3n) .affiliation-form'); select = form.querySelector('.select-affiliation'); expect(select.value).toBe('owner'); select.value = 'admin'; const input = form.querySelector('input[name="reason"]'); input.value = "You're an admin now"; const submit = form.querySelector('.btn-primary'); submit.click(); spyOn(_converse.MUCOccupants.prototype, 'fetchMembers').and.callThrough(); const sent_IQ = _converse.api.connection.get().IQ_stanzas.pop(); expect(Strophe.serialize(sent_IQ)).toBe( `<iq id="${sent_IQ.getAttribute('id')}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+ `<query xmlns="http://jabber.org/protocol/muc#admin">`+ `<item affiliation="admin" jid="crone1@shakespeare.lit">`+ `<reason>You&apos;re an admin now</reason>`+ `</item>`+ `</query>`+ `</iq>`); _converse.api.connection.get().IQ_stanzas = []; const stanza = $iq({ 'type': 'result', 'id': sent_IQ.getAttribute('id'), 'from': view.model.get('jid'), 'to': _converse.api.connection.get().jid, 'xmlns': 'jabber:client' }); _converse.api.connection.get()._dataRecv(mock.createRequest(stanza)); await u.waitUntil(() => view.model.occupants.fetchMembers.calls.count()); members = [ {'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'}, {'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'}, {'jid': 'wiccarocks@shakespeare.lit', 'nick': 'wiccan', 'affiliation': 'admin'}, {'jid': 'crone1@shakespeare.lit', 'nick': 'thirdwitch', 'affiliation': 'admin'}, {'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'}, ]; await mock.returnMemberLists(_converse, muc_jid, members); await u.waitUntil(() => view.model.occupants.pluck('affiliation').filter(o => o === 'owner').length === 1); const alert = modal.querySelector('.alert-primary'); expect(alert.textContent.trim()).toBe('Affiliation changed'); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 1); user_els = modal.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(1); expect(user_els[0].querySelector('.list-group-item.active').textContent.trim()).toBe('JID: romeo@montague.lit'); expect(user_els[0].querySelector('.list-group-item:nth-child(2n)').textContent.trim()).toBe('Nickname: romeo'); expect(user_els[0].querySelector('.list-group-item:nth-child(3n) div').textContent.trim()).toBe('Affiliation: owner'); modal.querySelector('#roles-tab').click(); select = modal.querySelector('.select-role'); await u.waitUntil(() => u.isVisible(select)); expect(select.value).toBe('moderator'); button = modal.querySelector('.btn-primary[name="users_with_role"]'); button.click(); const roles_panel = modal.querySelector('#roles-tabpanel'); await u.waitUntil(() => roles_panel.querySelectorAll('.list-group--users > li').length === 1); select.value = 'participant'; button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); await u.waitUntil(() => roles_panel.querySelectorAll('.list-group--users > li')[0]?.textContent.trim() === 'No users with that role found.'); })); it("allows you to filter affiliation search results", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; const members = [ {'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'}, {'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'}, {'jid': 'wiccarocks@shakespeare.lit', 'nick': 'wiccan', 'affiliation': 'member'}, {'jid': 'crone1@shakespeare.lit', 'nick': 'thirdwitch', 'affiliation': 'member'}, {'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'member'}, {'jid': 'juliet@capulet.lit', 'nick': 'juliet', 'affiliation': 'member'}, ]; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', [], members); const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => (view.model.occupants.length === 6), 1000); // Clear so that we don't match older stanzas _converse.api.connection.get().IQ_stanzas = []; const modal = await openModtools(_converse, view); const select = modal.querySelector('.select-affiliation'); expect(select.value).toBe('owner'); select.value = 'member'; const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 6); const nicks = Array.from(modal.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick')); expect(nicks.join(' ')).toBe('gower juliet romeo thirdwitch wiccan witch'); const filter = modal.querySelector('[name="filter"]'); expect(filter).not.toBe(null); filter.value = 'romeo'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1)); filter.value = 'r'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 3)); filter.value = 'gower'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1)); filter.value = 'RoMeO'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1)); })); it("allows you to filter role search results", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', []); const view = _converse.chatboxviews.get(muc_jid); _converse.api.connection.get()._dataRecv(mock.createRequest( stx`<presence to="${_converse.jid}" from="${muc_jid}/nomorenicks" xmlns="jabber:client"> <x xmlns="${Strophe.NS.MUC_USER}"> <item affiliation="none" jid="nomorenicks@montague.lit" role="participant"/> </x> </presence>` )); _converse.api.connection.get()._dataRecv(mock.createRequest( stx`<presence to="${_converse.jid}" from="${muc_jid}/newb" xmlns="jabber:client"> <x xmlns="${Strophe.NS.MUC_USER}"> <item affiliation="none" jid="newb@montague.lit" role="participant"/> </x> </presence>` )); _converse.api.connection.get()._dataRecv(mock.createRequest( stx`<presence to="${_converse.jid}" from="${muc_jid}/some1" xmlns="jabber:client"> <x xmlns="${Strophe.NS.MUC_USER}"> <item affiliation="none" jid="some1@montague.lit" role="participant"/> </x> </presence>` )); _converse.api.connection.get()._dataRecv(mock.createRequest( stx`<presence to="${_converse.jid}" from="${muc_jid}/oldhag" xmlns="jabber:client"> <x xmlns="${Strophe.NS.MUC_USER}"> <item affiliation="none" jid="oldhag@montague.lit" role="participant"/> </x> </presence>` )); _converse.api.connection.get()._dataRecv(mock.createRequest( stx`<presence to="${_converse.jid}" from="${muc_jid}/crone" xmlns="jabber:client"> <x xmlns="${Strophe.NS.MUC_USER}"> <item affiliation="none" jid="crone@montague.lit" role="participant"/> </x> </presence>` )); _converse.api.connection.get()._dataRecv(mock.createRequest( stx`<presence to="${_converse.jid}" from="${muc_jid}/tux" xmlns="jabber:client"> <x xmlns="${Strophe.NS.MUC_USER}"> <item affiliation="none" jid="tux@montague.lit" role="participant"/> </x> </presence>` )); await u.waitUntil(() => (view.model.occupants.length === 7), 1000); const textarea = await u.waitUntil(() => view.querySelector('.chat-textarea')); textarea.value = '/modtools'; const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, key: "Enter" }; const message_form = view.querySelector('converse-muc-message-form'); message_form.onKeyDown(enter); const modal = await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal')); await u.waitUntil(() => u.isVisible(modal), 1000); const tab = modal.querySelector('#roles-tab'); tab.click(); // Clear so that we don't match older stanzas _converse.api.connection.get().IQ_stanzas = []; const select = modal.querySelector('.select-role'); expect(select.value).toBe('moderator'); select.value = 'participant'; const button = modal.querySelector('.btn-primary[name="users_with_role"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_role); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 6); const nicks = Array.from(modal.querySelectorAll('.list-group--users > li')).map(el => el.getAttribute('data-nick')); expect(nicks.join(' ')).toBe('crone newb nomorenicks oldhag some1 tux'); const filter = modal.querySelector('[name="filter"]'); expect(filter).not.toBe(null); filter.value = 'tux'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1)); filter.value = 'r'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 2)); filter.value = 'crone'; u.triggerEvent(filter, "keyup", "KeyboardEvent"); await u.waitUntil(() => ( modal.querySelectorAll('.list-group--users > li').length === 1)); })); it("shows an error message if a particular affiliation list may not be retrieved", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; const members = [ {'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'}, {'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'}, {'jid': 'wiccarocks@shakespeare.lit', 'nick': 'wiccan', 'affiliation': 'admin'}, {'jid': 'crone1@shakespeare.lit', 'nick': 'thirdwitch', 'affiliation': 'owner'}, {'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'}, ]; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', [], members); const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => (view.model.occupants.length === 5)); const modal = await openModtools(_converse, view); const tab = modal.querySelector('#affiliations-tab'); // Clear so that we don't match older stanzas _converse.api.connection.get().IQ_stanzas = []; const IQ_stanzas = _converse.api.connection.get().IQ_stanzas; tab.click(); const select = modal.querySelector('.select-affiliation'); select.value = 'outcast'; const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); const iq_query = await u.waitUntil(() => IQ_stanzas.filter( s => sizzle( `iq[to="${muc_jid}"] query[xmlns="${Strophe.NS.MUC_ADMIN}"] item[affiliation="outcast"]`, s ).length ).pop()); const error = stx`<iq from="${muc_jid}" id="${iq_query.getAttribute('id')}" type="error" to="${_converse.jid}" xmlns="jabber:client"> <error type="auth"> <forbidden xmlns="${Strophe.NS.STANZAS}"/> </error> </iq>`; _converse.api.connection.get()._dataRecv(mock.createRequest(error)); await u.waitUntil(() => !modal.loading_users_with_affiliation); const alert = await u.waitUntil(() => modal.querySelector('.alert')); expect(alert.textContent.trim()).toBe('Error: not allowed to fetch outcast list for MUC lounge@montague.lit'); const user_els = modal.querySelectorAll('.list-group--users > li'); expect(user_els.length).toBe(1); expect(user_els[0].textContent.trim()).toBe('No users with that affiliation found.'); })); it("shows an error message if a particular affiliation may not be set", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; const members = [ {'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'}, {'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'}, ]; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', [], members); const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => (view.model.occupants.length === 2)); const modal = await openModtools(_converse, view); // Clear so that we don't match older stanzas _converse.api.connection.get().IQ_stanzas = []; const tab = modal.querySelector('#affiliations-tab'); tab.click(); const select = modal.querySelector('.select-affiliation'); select.value = 'member'; const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 1); const user_els = modal.querySelectorAll('.list-group--users > li'); const toggle = user_els[0].querySelector('.list-group-item:nth-child(3n) .toggle-form'); const component = user_els[0].querySelector('.list-group-item:nth-child(3n) converse-muc-affiliation-form'); expect(u.hasClass('hidden', component)).toBeTruthy(); toggle.click(); expect(u.hasClass('hidden', component)).toBeFalsy(); const form = user_els[0].querySelector('.list-group-item:nth-child(3n) .affiliation-form'); const change_affiliation_dropdown = form.querySelector('.select-affiliation'); expect(change_affiliation_dropdown.value).toBe('member'); change_affiliation_dropdown.value = 'admin'; const input = form.querySelector('input[name="reason"]'); input.value = "You're an admin now"; const submit = form.querySelector('.btn-primary'); submit.click(); const sent_IQ = _converse.api.connection.get().IQ_stanzas.pop(); expect(Strophe.serialize(sent_IQ)).toBe( `<iq id="${sent_IQ.getAttribute('id')}" to="lounge@montague.lit" type="set" xmlns="jabber:client">`+ `<query xmlns="http://jabber.org/protocol/muc#admin">`+ `<item affiliation="admin" jid="gower@shakespeare.lit">`+ `<reason>You&apos;re an admin now</reason>`+ `</item>`+ `</query>`+ `</iq>`); const error = stx`<iq from="${muc_jid}" id="${sent_IQ.getAttribute('id')}" type="error" to="${_converse.jid}" xmlns="jabber:client"> <error type="cancel"> <not-allowed xmlns="${Strophe.NS.STANZAS}"/> </error> </iq>`; _converse.api.connection.get()._dataRecv(mock.createRequest(error)); })); it("doesn't allow admins to make more admins", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; const members = [ {'jid': 'hag66@shakespeare.lit', 'nick': 'witch', 'affiliation': 'member'}, {'jid': 'gower@shakespeare.lit', 'nick': 'gower', 'affiliation': 'member'}, {'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'admin'}, ]; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', [], members); const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => (view.model.occupants.length === 3)); const modal = await openModtools(_converse, view); const tab = modal.querySelector('#affiliations-tab'); // Clear so that we don't match older stanzas _converse.api.connection.get().IQ_stanzas = []; tab.click(); const show_affiliation_dropdown = modal.querySelector('.select-affiliation'); show_affiliation_dropdown.value = 'member'; const button = modal.querySelector('.btn-primary[name="users_with_affiliation"]'); button.click(); await u.waitUntil(() => !modal.loading_users_with_affiliation); await u.waitUntil(() => modal.querySelectorAll('.list-group--users > li').length === 2); const user_els = modal.querySelectorAll('.list-group--users > li'); let change_affiliation_dropdown = user_els[0].querySelector('.select-affiliation'); expect(Array.from(change_affiliation_dropdown.options).map(o => o.value)).toEqual(['member', 'outcast', 'none']); change_affiliation_dropdown = user_els[1].querySelector('.select-affiliation'); expect(Array.from(change_affiliation_dropdown.options).map(o => o.value)).toEqual(['member', 'outcast', 'none']); })); it("lets the assignable affiliations and roles be configured via modtools_disable_assign", mock.initConverse([], {}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; const members = [{'jid': 'romeo@montague.lit', 'nick': 'romeo', 'affiliation': 'owner'}]; await mock.openAndEnterMUC(_converse, muc_jid, 'romeo', [], members); const view = _converse.chatboxviews.get(muc_jid); const textarea = await u.waitUntil(() => view.querySelector('.chat-textarea')); textarea.value = '/modtools'; const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, key: "Enter" }; const message_form = view.querySelector('converse-muc-message-form'); message_form.onKeyDown(enter); await u.waitUntil(() => _converse.api.modal.get('converse-modtools-modal')); const occupant = view.model.occupants.findWhere({'jid': _converse.bare_jid}); expect(occupant.getAssignableAffiliations()).toEqual(['owner', 'admin', 'member', 'outcast', 'none']); _converse.api.settings.set('modtools_disable_assign', ['owner']); expect(occupant.getAssignableAffiliations()).toEqual(['admin', 'member', 'outcast', 'none']); _converse.api.settings.set('modtools_disable_assign', ['owner', 'admin']); expect(occupant.getAssignableAffiliations()).toEqual(['member', 'outcast', 'none']); _converse.api.settings.set('modtools_disable_assign', ['owner', 'admin', 'outcast']); expect(occupant.getAssignableAffiliations()).toEqual(['member', 'none']); expect(occupant.getAssignableRoles()).toEqual(['moderator', 'participant', 'visitor']); _converse.api.settings.set('modtools_disable_assign', ['admin', 'moderator']); expect(occupant.getAssignableRoles()).toEqual(['participant', 'visitor']); })); });