/*global mock, converse */ const { $iq, Strophe, u } = converse.env; describe("A Groupchat", function () { describe("upon being entered", function () { it("will fetch the member list if muc_fetch_members is true", mock.initConverse([], {'muc_fetch_members': true}, async function (_converse) { const { api } = _converse; let sent_IQs = _converse.connection.IQ_stanzas; const muc_jid = 'lounge@montague.lit'; await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo'); let view = _converse.chatboxviews.get(muc_jid); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation]')).length).toBe(3); // Check in reverse order that we requested all three lists const owner_iq = sent_IQs.pop(); expect(Strophe.serialize(owner_iq)).toBe( ``+ ``+ ``); const admin_iq = sent_IQs.pop(); expect(Strophe.serialize(admin_iq)).toBe( ``+ ``+ ``); const member_iq = sent_IQs.pop(); expect(Strophe.serialize(member_iq)).toBe( ``+ ``+ ``); view.close(); _converse.connection.IQ_stanzas = []; sent_IQs = _converse.connection.IQ_stanzas; api.settings.set('muc_fetch_members', false); await mock.openAndEnterChatRoom(_converse, 'orchard@montague.lit', 'romeo'); view = _converse.chatboxviews.get('orchard@montague.lit'); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation]')).length).toBe(0); await view.close(); _converse.connection.IQ_stanzas = []; sent_IQs = _converse.connection.IQ_stanzas; api.settings.set('muc_fetch_members', ['admin']); await mock.openAndEnterChatRoom(_converse, 'courtyard@montague.lit', 'romeo'); view = _converse.chatboxviews.get('courtyard@montague.lit'); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation]')).length).toBe(1); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation="admin"]')).length).toBe(1); view.close(); _converse.connection.IQ_stanzas = []; sent_IQs = _converse.connection.IQ_stanzas; api.settings.set('muc_fetch_members', ['owner']); await mock.openAndEnterChatRoom(_converse, 'garden@montague.lit', 'romeo'); view = _converse.chatboxviews.get('garden@montague.lit'); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation]')).length).toBe(1); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation="owner"]')).length).toBe(1); view.close(); })); it("will not fetch the member list if the user is not affiliated", mock.initConverse([], {'muc_fetch_members': true}, async function (_converse) { const muc_jid = 'lounge@montague.lit'; const sent_IQs = _converse.connection.IQ_stanzas; spyOn(_converse.ChatRoomOccupants.prototype, 'fetchMembers').and.callThrough(); // Join MUC without an affiliation const model = await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], [], true, {}, 'none', 'participant'); await u.waitUntil(() => model.occupants.fetchMembers.calls.count()); expect(sent_IQs.filter(iq => iq.querySelector('query item[affiliation]')).length).toBe(0); })); describe("when fetching the member lists", function () { it("gracefully handles being forbidden from fetching the lists for certain affiliations", mock.initConverse([], {'muc_fetch_members': true}, async function (_converse) { const sent_IQs = _converse.connection.IQ_stanzas; const muc_jid = 'lounge@montague.lit'; const features = [ 'http://jabber.org/protocol/muc', 'jabber:iq:register', 'muc_hidden', 'muc_membersonly', 'muc_passwordprotected', Strophe.NS.MAM, Strophe.NS.SID ]; const nick = 'romeo'; await _converse.api.rooms.open(muc_jid); await mock.getRoomFeatures(_converse, muc_jid, features); await mock.waitForReservedNick(_converse, muc_jid, nick); mock.receiveOwnMUCPresence(_converse, muc_jid, nick); const view = _converse.chatboxviews.get(muc_jid); await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED)); // Check in reverse order that we requested all three lists const owner_iq = sent_IQs.pop(); expect(Strophe.serialize(owner_iq)).toBe( ``+ ``+ ``); const admin_iq = sent_IQs.pop(); expect(Strophe.serialize(admin_iq)).toBe( ``+ ``+ ``); const member_iq = sent_IQs.pop(); expect(Strophe.serialize(member_iq)).toBe( ``+ ``+ ``); // It might be that the user is not allowed to fetch certain lists. let err_stanza = u.toStanza( ` `); _converse.connection._dataRecv(mock.createRequest(err_stanza)); err_stanza = u.toStanza( ` `); _converse.connection._dataRecv(mock.createRequest(err_stanza)); // Now the service sends the member lists to the user const member_list_stanza = $iq({ 'from': muc_jid, 'id': member_iq.getAttribute('id'), 'to': 'romeo@montague.lit/orchard', 'type': 'result' }).c('query', {'xmlns': Strophe.NS.MUC_ADMIN}) .c('item', { 'affiliation': 'member', 'jid': 'hag66@shakespeare.lit', 'nick': 'thirdwitch', 'role': 'participant' }); _converse.connection._dataRecv(mock.createRequest(member_list_stanza)); await u.waitUntil(() => view.model.occupants.length > 1); expect(view.model.occupants.length).toBe(2); // The existing owner occupant should not have their // affiliation removed due to the owner list // not being returned (forbidden err). expect(view.model.occupants.findWhere({'jid': _converse.bare_jid}).get('affiliation')).toBe('owner'); expect(view.model.occupants.findWhere({'jid': 'hag66@shakespeare.lit'}).get('affiliation')).toBe('member'); })); }); }); }); describe("Someone being invited to a groupchat", function () { it("will first be added to the member list if the groupchat is members only", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) { await mock.waitForRoster(_converse, 'current', 0); spyOn(_converse.ChatRoomOccupants.prototype, 'fetchMembers').and.callThrough(); const sent_IQs = _converse.connection.IQ_stanzas; const muc_jid = 'coven@chat.shakespeare.lit'; const nick = 'romeo'; const room_creation_promise = _converse.api.rooms.open(muc_jid, {nick}); // Check that the groupchat queried for the features. let stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/disco#info"]`)).pop()); expect(Strophe.serialize(stanza)).toBe( ``+ ``+ ``); // State that the chat is members-only via the features IQ const view = _converse.chatboxviews.get(muc_jid); const features_stanza = $iq({ from: 'coven@chat.shakespeare.lit', 'id': stanza.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() .c('feature', {'var': 'muc_membersonly'}).up(); _converse.connection._dataRecv(mock.createRequest(features_stanza)); const sent_stanzas = _converse.connection.sent_stanzas; await u.waitUntil(() => sent_stanzas.filter(s => s.matches(`presence[to="${muc_jid}/${nick}"]`)).pop()); expect(view.model.features.get('membersonly')).toBeTruthy(); await room_creation_promise; await mock.createContacts(_converse, 'current'); let sent_stanza, sent_id; spyOn(_converse.connection, 'send').and.callFake(function (stanza) { if (stanza.nodeName === 'message') { sent_id = stanza.getAttribute('id'); sent_stanza = stanza; } }); const invitee_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; const reason = "Please join this groupchat"; view.model.directInvite(invitee_jid, reason); // Check in reverse order that we requested all three lists const owner_iq = sent_IQs.pop(); expect(Strophe.serialize(owner_iq)).toBe( ``+ ``+ ``); const admin_iq = sent_IQs.pop(); expect(Strophe.serialize(admin_iq)).toBe( ``+ ``+ ``); const member_iq = sent_IQs.pop(); expect(Strophe.serialize(member_iq)).toBe( ``+ ``+ ``); // Now the service sends the member lists to the user const member_list_stanza = $iq({ 'from': 'coven@chat.shakespeare.lit', 'id': member_iq.getAttribute('id'), 'to': 'romeo@montague.lit/orchard', 'type': 'result' }).c('query', {'xmlns': Strophe.NS.MUC_ADMIN}) .c('item', { 'affiliation': 'member', 'jid': 'hag66@shakespeare.lit', 'nick': 'thirdwitch', 'role': 'participant' }); _converse.connection._dataRecv(mock.createRequest(member_list_stanza)); const admin_list_stanza = $iq({ 'from': 'coven@chat.shakespeare.lit', 'id': admin_iq.getAttribute('id'), 'to': 'romeo@montague.lit/orchard', 'type': 'result' }).c('query', {'xmlns': Strophe.NS.MUC_ADMIN}) .c('item', { 'affiliation': 'admin', 'jid': 'wiccarocks@shakespeare.lit', 'nick': 'secondwitch' }); _converse.connection._dataRecv(mock.createRequest(admin_list_stanza)); const owner_list_stanza = $iq({ 'from': 'coven@chat.shakespeare.lit', 'id': owner_iq.getAttribute('id'), 'to': 'romeo@montague.lit/orchard', 'type': 'result' }).c('query', {'xmlns': Strophe.NS.MUC_ADMIN}) .c('item', { 'affiliation': 'owner', 'jid': 'crone1@shakespeare.lit', }); _converse.connection._dataRecv(mock.createRequest(owner_list_stanza)); // Converse puts the user on the member list stanza = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq[to="${muc_jid}"] query[xmlns="http://jabber.org/protocol/muc#admin"]`)).pop()); expect(stanza.outerHTML, ``+ ``+ ``+ `Please join this groupchat`+ ``+ ``+ ``); const result = $iq({ 'from': 'coven@chat.shakespeare.lit', 'id': stanza.getAttribute('id'), 'to': 'romeo@montague.lit/orchard', 'type': 'result' }); _converse.connection._dataRecv(mock.createRequest(result)); await u.waitUntil(() => view.model.occupants.fetchMembers.calls.count()); // Finally check that the user gets invited. expect(Strophe.serialize(sent_stanza)).toBe( // Strophe adds the xmlns attr (although not in spec) ``+ ``+ `` ); })); });