diff --git a/CHANGES.md b/CHANGES.md
index 698c04322..633618a19 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,7 @@
- If `auto_register_muc_nickname` is set, make sure to register when the user changes current nick.
- #1322: Display occupants’ avatars in the occupants list
- #1419: Clicking on avatar should show bigger version
+- #1426: Don't fetch member list if not affiliated
- #2647: Singleton mode doesn't work
- #2704: Send button doesn't work in a multi-user chat
- #2725: Send new presence status to all connected MUCs
diff --git a/karma.conf.js b/karma.conf.js
index e5210d5a0..076687a07 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -79,6 +79,7 @@ module.exports = function(config) {
{ 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' },
diff --git a/src/headless/plugins/muc/constants.js b/src/headless/plugins/muc/constants.js
new file mode 100644
index 000000000..fef4063dd
--- /dev/null
+++ b/src/headless/plugins/muc/constants.js
@@ -0,0 +1,6 @@
+export const MUC_ROLE_WEIGHTS = {
+ 'moderator': 1,
+ 'participant': 2,
+ 'visitor': 3,
+ 'none': 2
+};
diff --git a/src/headless/plugins/muc/muc.js b/src/headless/plugins/muc/muc.js
index 883c00719..8c96f5a61 100644
--- a/src/headless/plugins/muc/muc.js
+++ b/src/headless/plugins/muc/muc.js
@@ -1396,12 +1396,11 @@ const ChatRoomMixin = {
/**
* Get the {@link _converse.ChatRoomOccupant} instance which
* represents the current user.
- * @private
* @method _converse.ChatRoom#getOwnOccupant
* @returns { _converse.ChatRoomOccupant }
*/
getOwnOccupant () {
- return this.occupants.findWhere({ 'jid': _converse.bare_jid });
+ return this.occupants.getOwnOccupant();
},
async setNickname (nick) {
diff --git a/src/headless/plugins/muc/occupants.js b/src/headless/plugins/muc/occupants.js
index 9b178676a..e0cef559c 100644
--- a/src/headless/plugins/muc/occupants.js
+++ b/src/headless/plugins/muc/occupants.js
@@ -1,17 +1,11 @@
import ChatRoomOccupant from './occupant.js';
import u from '../../utils/form';
import { Collection } from '@converse/skeletor/src/collection';
+import { MUC_ROLE_WEIGHTS } from './constants.js';
import { Strophe } from 'strophe.js/src/strophe';
import { _converse, api } from '../../core.js';
import { getAffiliationList } from './affiliations/utils.js';
-const MUC_ROLE_WEIGHTS = {
- 'moderator': 1,
- 'participant': 2,
- 'visitor': 3,
- 'none': 2
-};
-
/**
* A list of {@link _converse.ChatRoomOccupant} instances, representing participants in a MUC.
@@ -34,12 +28,26 @@ const ChatRoomOccupants = Collection.extend({
}
},
+ /**
+ * Get the {@link _converse.ChatRoomOccupant} instance which
+ * represents the current user.
+ * @method _converse.ChatRoomOccupants#getOwnOccupant
+ * @returns { _converse.ChatRoomOccupant }
+ */
+ getOwnOccupant () {
+ return this.findWhere({ 'jid': _converse.bare_jid });
+ },
+
getAutoFetchedAffiliationLists () {
const affs = api.settings.get('muc_fetch_members');
return Array.isArray(affs) ? affs : affs ? ['member', 'admin', 'owner'] : [];
},
async fetchMembers () {
+ if (!['member', 'admin', 'owner'].includes(this.getOwnOccupant()?.get('affiliation'))) {
+ // https://xmpp.org/extensions/xep-0045.html#affil-priv
+ return;
+ }
const affiliations = this.getAutoFetchedAffiliationLists();
if (affiliations.length === 0) {
return;
@@ -59,26 +67,18 @@ const ChatRoomOccupants = Collection.extend({
!new_jids.includes(m.get('jid'))
);
});
-
removed_members.forEach(occupant => {
if (occupant.get('jid') === _converse.bare_jid) {
return;
- }
- if (occupant.get('show') === 'offline') {
+ } else if (occupant.get('show') === 'offline') {
occupant.destroy();
} else {
occupant.save('affiliation', null);
}
});
new_members.forEach(attrs => {
- const occupant = attrs.jid
- ? this.findOccupant({ 'jid': attrs.jid })
- : this.findOccupant({ 'nick': attrs.nick });
- if (occupant) {
- occupant.save(attrs);
- } else {
- this.create(attrs);
- }
+ const occupant = this.findOccupant(attrs);
+ occupant ? occupant.save(attrs) : this.create(attrs);
});
/**
* Triggered once the member lists for this MUC have been fetched and processed.
@@ -101,7 +101,6 @@ const ChatRoomOccupants = Collection.extend({
* If we have a JID, we use that as lookup variable,
* otherwise we use the nick. We don't always have both,
* but should have at least one or the other.
- * @private
* @method _converse.ChatRoomOccupants#findOccupant
* @param { OccupantData } data
*/
diff --git a/src/plugins/muc-views/tests/member-lists.js b/src/plugins/muc-views/tests/member-lists.js
new file mode 100644
index 000000000..fc0b9d169
--- /dev/null
+++ b/src/plugins/muc-views/tests/member-lists.js
@@ -0,0 +1,301 @@
+/*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)
+ ``+
+ ``+
+ ``
+ );
+ }));
+});
diff --git a/src/plugins/muc-views/tests/muc.js b/src/plugins/muc-views/tests/muc.js
index d90e91637..515acf9d5 100644
--- a/src/plugins/muc-views/tests/muc.js
+++ b/src/plugins/muc-views/tests/muc.js
@@ -1,12 +1,6 @@
/*global mock, converse */
-const $pres = converse.env.$pres;
-const $iq = converse.env.$iq;
-const $msg = converse.env.$msg;
-const Strophe = converse.env.Strophe;
-const Promise = converse.env.Promise;
-const sizzle = converse.env.sizzle;
-const u = converse.env.utils;
+const { $pres, $iq, $msg, Strophe, Promise, sizzle, u } = converse.env;
describe("Groupchats", function () {
@@ -284,145 +278,6 @@ describe("Groupchats", 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();
- }));
-
- 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("topic", function () {
it("is shown the header", mock.initConverse([], {}, async function (_converse) {
@@ -3826,150 +3681,6 @@ describe("Groupchats", function () {
}));
});
- 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)
- ``+
- ``+
- ``
- );
- }));
- });
describe("The affiliations delta", function () {
diff --git a/src/shared/tests/mock.js b/src/shared/tests/mock.js
index 221af8454..5accac5f7 100644
--- a/src/shared/tests/mock.js
+++ b/src/shared/tests/mock.js
@@ -283,7 +283,7 @@ async function returnMemberLists (_converse, muc_jid, members=[], affiliations=[
return new Promise(resolve => _converse.api.listen.on('membersFetched', resolve));
}
-async function receiveOwnMUCPresence (_converse, muc_jid, nick) {
+async function receiveOwnMUCPresence (_converse, muc_jid, nick, affiliation='owner', role='moderator') {
const sent_stanzas = _converse.connection.sent_stanzas;
await u.waitUntil(() => sent_stanzas.filter(iq => sizzle('presence history', iq).length).pop());
const presence = $pres({
@@ -291,17 +291,23 @@ async function receiveOwnMUCPresence (_converse, muc_jid, nick) {
from: `${muc_jid}/${nick}`,
id: u.getUniqueId()
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
- .c('item').attrs({
- affiliation: 'owner',
- jid: _converse.bare_jid,
- role: 'moderator'
- }).up()
+ .c('item').attrs({ affiliation, role, 'jid': _converse.bare_jid }).up()
.c('status').attrs({code:'110'});
_converse.connection._dataRecv(createRequest(presence));
}
-async function openAndEnterChatRoom (_converse, muc_jid, nick, features=[], members=[], force_open=true, settings={}) {
+async function openAndEnterChatRoom (
+ _converse,
+ muc_jid,
+ nick,
+ features=[],
+ members=[],
+ force_open=true,
+ settings={},
+ own_affiliation='owner',
+ own_role='moderator',
+ ) {
const { api } = _converse;
muc_jid = muc_jid.toLowerCase();
const room_creation_promise = api.rooms.open(muc_jid, settings, force_open);
@@ -310,7 +316,7 @@ async function openAndEnterChatRoom (_converse, muc_jid, nick, features=[], memb
// The user has just entered the room (because join was called)
// and receives their own presence from the server.
// See example 24: https://xmpp.org/extensions/xep-0045.html#enter-pres
- await receiveOwnMUCPresence(_converse, muc_jid, nick);
+ await receiveOwnMUCPresence(_converse, muc_jid, nick, own_affiliation, own_role);
await room_creation_promise;
const model = _converse.chatboxes.get(muc_jid);
@@ -318,7 +324,10 @@ async function openAndEnterChatRoom (_converse, muc_jid, nick, features=[], memb
const affs = api.settings.get('muc_fetch_members');
const all_affiliations = Array.isArray(affs) ? affs : (affs ? ['member', 'admin', 'owner'] : []);
- await returnMemberLists(_converse, muc_jid, members, all_affiliations);
+
+ if (['member', 'admin', 'owner'].includes(own_affiliation)) {
+ await returnMemberLists(_converse, muc_jid, members, all_affiliations);
+ }
await model.messages.fetched;
return model;
}