diff --git a/spec/modtools.js b/spec/modtools.js
index cd53f236c..74e247fe6 100644
--- a/spec/modtools.js
+++ b/spec/modtools.js
@@ -7,6 +7,18 @@ const sizzle = converse.env.sizzle;
const Strophe = converse.env.Strophe;
const u = converse.env.utils;
+
+async function openModtools (view) {
+ const textarea = view.el.querySelector('.chat-textarea');
+ textarea.value = '/modtools';
+ const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
+ view.onKeyDown(enter);
+ await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
+ const modal = view.modtools_modal;
+ await u.waitUntil(() => u.isVisible(modal.el), 1000);
+ return modal;
+}
+
describe("The groupchat moderator tool", function () {
it("allows you to set affiliations and roles",
@@ -28,14 +40,7 @@ describe("The groupchat moderator tool", function () {
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 5), 1000);
- const textarea = view.el.querySelector('.chat-textarea');
- textarea.value = '/modtools';
- const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
- view.onKeyDown(enter);
- await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
-
- const modal = view.modtools_modal;
- await u.waitUntil(() => u.isVisible(modal.el), 1000);
+ const modal = await openModtools(view);
let tab = modal.el.querySelector('#affiliations-tab');
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
@@ -156,16 +161,9 @@ describe("The groupchat moderator tool", function () {
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 6), 1000);
- const textarea = view.el.querySelector('.chat-textarea');
- textarea.value = '/modtools';
- const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
- view.onKeyDown(enter);
- await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
-
- const modal = view.modtools_modal;
- await u.waitUntil(() => u.isVisible(modal.el), 1000);
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
+ const modal = await openModtools(view);
const select = modal.el.querySelector('.select-affiliation');
expect(select.value).toBe('owner');
select.value = 'member';
@@ -328,15 +326,7 @@ describe("The groupchat moderator tool", function () {
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 5));
-
- const textarea = view.el.querySelector('.chat-textarea');
- textarea.value = '/modtools';
- const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
- view.onKeyDown(enter);
- await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
-
- const modal = view.modtools_modal;
- await u.waitUntil(() => u.isVisible(modal.el), 1000);
+ const modal = await openModtools(view);
const tab = modal.el.querySelector('#affiliations-tab');
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
@@ -371,6 +361,73 @@ describe("The groupchat moderator tool", function () {
done();
}));
+ it("shows an error message if a particular affiliation may not be set",
+ mock.initConverse(
+ ['rosterGroupsFetched'], {},
+ async function (done, _converse) {
+
+ spyOn(_converse.ChatRoomView.prototype, 'showModeratorToolsModal').and.callThrough();
+ 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.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
+ const view = _converse.chatboxviews.get(muc_jid);
+ await u.waitUntil(() => (view.model.occupants.length === 2));
+ const modal = await openModtools(view);
+ // Clear so that we don't match older stanzas
+ _converse.connection.IQ_stanzas = [];
+
+ const tab = modal.el.querySelector('#affiliations-tab');
+ tab.click();
+ const select = modal.el.querySelector('.select-affiliation');
+ select.value = 'member';
+ const button = modal.el.querySelector('.btn-primary[name="users_with_affiliation"]');
+ button.click();
+ await u.waitUntil(() => !modal.loading_users_with_affiliation);
+ const user_els = modal.el.querySelectorAll('.list-group--users > li');
+ expect(user_els.length).toBe(1);
+
+ const toggle = user_els[0].querySelector('.list-group-item:nth-child(3n) .toggle-form');
+ const form = user_els[0].querySelector('.list-group-item:nth-child(3n) .affiliation-form');
+ expect(u.hasClass('hidden', form)).toBeTruthy();
+ toggle.click();
+ expect(u.hasClass('hidden', form)).toBeFalsy();
+ 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.connection.IQ_stanzas.pop();
+ expect(Strophe.serialize(sent_IQ)).toBe(
+ ``+
+ ``+
+ `- `+
+ `You're an admin now`+
+ `
`+
+ ``+
+ ``);
+
+ const error = u.toStanza(
+ `
+
+
+
+
+ `);
+ _converse.connection._dataRecv(mock.createRequest(error));
+
+ done();
+ }));
+
+
it("doesn't allow admins to make more admins",
mock.initConverse(
['rosterGroupsFetched'], {},
@@ -386,15 +443,7 @@ describe("The groupchat moderator tool", function () {
await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo', [], members);
const view = _converse.chatboxviews.get(muc_jid);
await u.waitUntil(() => (view.model.occupants.length === 3));
-
- const textarea = view.el.querySelector('.chat-textarea');
- textarea.value = '/modtools';
- const enter = { 'target': textarea, 'preventDefault': function preventDefault () {}, 'keyCode': 13 };
- view.onKeyDown(enter);
- await u.waitUntil(() => view.showModeratorToolsModal.calls.count());
-
- const modal = view.modtools_modal;
- await u.waitUntil(() => u.isVisible(modal.el), 1000);
+ const modal = await openModtools(view);
const tab = modal.el.querySelector('#affiliations-tab');
// Clear so that we don't match older stanzas
_converse.connection.IQ_stanzas = [];
diff --git a/src/modals/moderator-tools.js b/src/modals/moderator-tools.js
index 81c894649..06d462eff 100644
--- a/src/modals/moderator-tools.js
+++ b/src/modals/moderator-tools.js
@@ -147,7 +147,7 @@ export default BootstrapModal.extend({
this.model.set({'affiliation': affiliation});
},
- assignAffiliation (ev) {
+ async assignAffiliation (ev) {
ev.stopPropagation();
ev.preventDefault();
const data = new FormData(ev.target);
@@ -157,17 +157,23 @@ export default BootstrapModal.extend({
'reason': data.get('reason')
}
const current_affiliation = this.model.get('affiliation');
- this.chatroomview.model.setAffiliation(affiliation, [attrs])
- .then(async () => {
- this.alert(__('Affiliation changed'), 'primary');
- await this.chatroomview.model.occupants.fetchMembers()
- this.model.set({'affiliation': null}, {'silent': true});
- this.model.set({'affiliation': current_affiliation});
- })
- .catch(err => {
+ try {
+ await this.chatroomview.model.setAffiliation(affiliation, [attrs]);
+ } catch (e) {
+ if (e === null) {
+ this.alert(__('Timeout error while trying to set the affiliation'), 'danger');
+ } else if (sizzle(`not-allowed[xmlns="${Strophe.NS.STANZAS}"]`, e).length) {
+ this.alert(__('Sorry, you\'re not allowed to make that change'), 'danger');
+ } else {
this.alert(__('Sorry, something went wrong while trying to set the affiliation'), 'danger');
- log.error(err);
- });
+ }
+ log.error(e);
+ return;
+ }
+ this.alert(__('Affiliation changed'), 'primary');
+ await this.chatroomview.model.occupants.fetchMembers()
+ this.model.set({'affiliation': null}, {'silent': true});
+ this.model.set({'affiliation': current_affiliation});
},
assignRole (ev) {