converse.js
Version:
Browser based XMPP chat client
302 lines (259 loc) • 16.2 kB
JavaScript
/*global mock, converse */
const { $iq, Strophe, u, stx } = converse.env;
describe("A Groupchat", function () {
beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza }));
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.api.connection.get().IQ_stanzas;
const muc_jid = 'lounge@montague.lit';
await mock.openAndEnterMUC(_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(
`<iq id="${owner_iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="owner"/></query>`+
`</iq>`);
const admin_iq = sent_IQs.pop();
expect(Strophe.serialize(admin_iq)).toBe(
`<iq id="${admin_iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="admin"/></query>`+
`</iq>`);
const member_iq = sent_IQs.pop();
expect(Strophe.serialize(member_iq)).toBe(
`<iq id="${member_iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="member"/></query>`+
`</iq>`);
view.close();
_converse.api.connection.get().IQ_stanzas = [];
sent_IQs = _converse.api.connection.get().IQ_stanzas;
api.settings.set('muc_fetch_members', false);
await mock.openAndEnterMUC(_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.api.connection.get().IQ_stanzas = [];
sent_IQs = _converse.api.connection.get().IQ_stanzas;
api.settings.set('muc_fetch_members', ['admin']);
await mock.openAndEnterMUC(_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.api.connection.get().IQ_stanzas = [];
sent_IQs = _converse.api.connection.get().IQ_stanzas;
api.settings.set('muc_fetch_members', ['owner']);
await mock.openAndEnterMUC(_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.api.connection.get().IQ_stanzas;
spyOn(_converse.exports.MUCOccupants.prototype, 'fetchMembers').and.callThrough();
// Join MUC without an affiliation
const model = await mock.openAndEnterMUC(_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.api.connection.get().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';
_converse.api.rooms.open(muc_jid);
await mock.waitForMUCDiscoInfo(_converse, muc_jid, features);
await mock.waitForReservedNick(_converse, muc_jid, nick);
mock.receiveOwnMUCPresence(_converse, muc_jid, nick);
const view = await u.waitUntil(() => _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(
`<iq id="${owner_iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="owner"/></query>`+
`</iq>`);
const admin_iq = sent_IQs.pop();
expect(Strophe.serialize(admin_iq)).toBe(
`<iq id="${admin_iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="admin"/></query>`+
`</iq>`);
const member_iq = sent_IQs.pop();
expect(Strophe.serialize(member_iq)).toBe(
`<iq id="${member_iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="member"/></query>`+
`</iq>`);
// It might be that the user is not allowed to fetch certain lists.
let err_stanza = u.toStanza(
`<iq xmlns="jabber:client" type="error" to="${_converse.jid}" from="${muc_jid}" id="${admin_iq.getAttribute('id')}">
<error type="auth"><forbidden xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
</iq>`);
_converse.api.connection.get()._dataRecv(mock.createRequest(err_stanza));
err_stanza = u.toStanza(
`<iq xmlns="jabber:client" type="error" to="${_converse.jid}" from="${muc_jid}" id="${owner_iq.getAttribute('id')}">
<error type="auth"><forbidden xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
</iq>`);
_converse.api.connection.get()._dataRecv(mock.createRequest(err_stanza));
// Now the service sends the member lists to the user
const member_list_stanza = stx`
<iq from="${muc_jid}"
id="${member_iq.getAttribute('id')}"
to="romeo@montague.lit/orchard"
type="result"
xmlns="jabber:client">
<query xmlns="${Strophe.NS.MUC_ADMIN}">
<item affiliation="member" jid="hag66@shakespeare.lit" nick="thirdwitch" role="participant"/>
</query>
</iq>`;
_converse.api.connection.get()._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 () {
beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza }));
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.exports.MUCOccupants.prototype, 'fetchMembers').and.callThrough();
const sent_IQs = _converse.api.connection.get().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(
`<iq from="romeo@montague.lit/orchard" id="${stanza.getAttribute("id")}" to="${muc_jid}" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/disco#info"/>`+
`</iq>`);
const features_stanza = stx`
<iq from="coven@chat.shakespeare.lit"
id="${stanza.getAttribute('id')}"
to="romeo@montague.lit/desktop"
type="result"
xmlns="jabber:client">
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="conference" name="A Dark Cave" type="text"/>
<feature var="http://jabber.org/protocol/muc"/>
<feature var="muc_hidden"/>
<feature var="muc_temporary"/>
<feature var="muc_membersonly"/>
</query>
</iq>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(features_stanza));
const sent_stanzas = _converse.api.connection.get().sent_stanzas;
await u.waitUntil(() => sent_stanzas.filter(s => s.matches(`presence[to="${muc_jid}/${nick}"]`)).pop());
// State that the chat is members-only via the features IQ
const view = await u.waitUntil(() => _converse.chatboxviews.get(muc_jid));
expect(view.model.features.get('membersonly')).toBeTruthy();
await room_creation_promise;
await mock.createContacts(_converse, 'current');
let sent_stanza, sent_id;
spyOn(_converse.api.connection.get(), '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(
`<iq id="${owner_iq.getAttribute('id')}" to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="owner"/></query>`+
`</iq>`);
const admin_iq = sent_IQs.pop();
expect(Strophe.serialize(admin_iq)).toBe(
`<iq id="${admin_iq.getAttribute('id')}" to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="admin"/></query>`+
`</iq>`);
const member_iq = sent_IQs.pop();
expect(Strophe.serialize(member_iq)).toBe(
`<iq id="${member_iq.getAttribute('id')}" to="coven@chat.shakespeare.lit" type="get" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin"><item affiliation="member"/></query>`+
`</iq>`);
// Now the service sends the member lists to the user
const member_list_stanza = stx`
<iq from="coven@chat.shakespeare.lit"
id="${member_iq.getAttribute('id')}"
to="romeo@montague.lit/orchard"
type="result"
xmlns="jabber:client">
<query xmlns="${Strophe.NS.MUC_ADMIN}">
<item affiliation="member" jid="hag66@shakespeare.lit" nick="thirdwitch" role="participant"/>
</query>
</iq>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(member_list_stanza));
const admin_list_stanza = stx`
<iq from="coven@chat.shakespeare.lit"
id="${admin_iq.getAttribute('id')}"
to="romeo@montague.lit/orchard"
type="result"
xmlns="jabber:client">
<query xmlns="${Strophe.NS.MUC_ADMIN}">
<item affiliation="admin" jid="wiccarocks@shakespeare.lit" nick="secondwitch"/>
</query>
</iq>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(admin_list_stanza));
const owner_list_stanza = stx`
<iq from="coven@chat.shakespeare.lit"
id="${owner_iq.getAttribute('id')}"
to="romeo@montague.lit/orchard"
type="result"
xmlns="jabber:client">
<query xmlns="${Strophe.NS.MUC_ADMIN}">
<item affiliation="owner" jid="crone1@shakespeare.lit"/>
</query>
</iq>`;
_converse.api.connection.get()._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,
`<iq id="${stanza.getAttribute('id')}" to="coven@chat.shakespeare.lit" type="set" xmlns="jabber:client">`+
`<query xmlns="http://jabber.org/protocol/muc#admin">`+
`<item affiliation="member" jid="${invitee_jid}">`+
`<reason>Please join this groupchat</reason>`+
`</item>`+
`</query>`+
`</iq>`);
const result = stx`
<iq from="coven@chat.shakespeare.lit"
id="${stanza.getAttribute('id')}"
to="romeo@montague.lit/orchard"
type="result"
xmlns="jabber:client"/>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(result));
await u.waitUntil(() => view.model.occupants.fetchMembers.calls.count());
// Finally check that the user gets invited.
expect(sent_stanza).toEqualStanza(stx`
<message id="${sent_id}" to="${invitee_jid}" xmlns="jabber:client">
<x jid="coven@chat.shakespeare.lit" reason="Please join this groupchat" xmlns="jabber:x:conference"/>
</message>
`);
}));
});