converse.js
Version:
Browser based XMPP chat client
290 lines (242 loc) • 16.1 kB
JavaScript
/*global mock, converse */
const { sizzle, u } = converse.env;
describe('The "Groupchats" Add modal', function () {
beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza }));
it('can be opened from a link in the "Groupchats" section of the controlbox',
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
const modal = await mock.openAddMUCModal(_converse);
const muc_jid = 'lounge@muc.montague.lit';
let label_name = modal.querySelector('label[for="chatroom"]');
expect(modal.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
expect(label_name.textContent.trim()).toBe('Groupchat name or address:');
const label_nick = modal.querySelector('label[for="nickname"]');
expect(label_nick.textContent.trim()).toBe('Nickname:');
const nick_input = modal.querySelector('input[name="nickname"]');
expect(nick_input.value).toBe('Romeo');
nick_input.value = 'romeo';
modal.querySelector('input[name="chatroom"]').value = muc_jid;
modal.querySelector('form input[type="submit"]').click();
await mock.waitForMUCDiscoInfo(_converse, muc_jid);
await u.waitUntil(() => _converse.chatboxes.length);
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
})
);
it("doesn't require the domain when muc_domain is set",
mock.initConverse(['chatBoxesFetched'], { muc_domain: 'muc.example.org' }, async function (_converse) {
let modal = await mock.openAddMUCModal(_converse);
expect(modal.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
const label_name = modal.querySelector('label[for="chatroom"]');
expect(label_name.textContent.trim()).toBe('Groupchat name or address:');
let name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'lounge';
let nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
modal.querySelector('form input[type="submit"]').click();
await mock.waitForMUCDiscoInfo(_converse, 'lounge@muc.example.org');
await u.waitUntil(() => _converse.chatboxes.length);
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@muc.example.org')).toBe(true);
// However, you can still open MUCs with different domains
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
roomspanel.querySelector('.show-add-muc-modal').click();
modal = await u.waitUntil(() => document.querySelector('converse-add-muc-modal'), 1000);
name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'lounge@conference.example.org';
nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
modal.querySelector('form input[type="submit"]').click();
await mock.waitForMUCDiscoInfo(_converse, 'lounge@conference.example.org');
await u.waitUntil(() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2);
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 2);
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@conference.example.org')).toBe(
true
);
})
);
it('uses the muc_domain if locked_muc_domain is true', mock.initConverse(
['chatBoxesFetched'], { muc_domain: 'muc.example.org', locked_muc_domain: true },
async function (_converse) {
let modal = await mock.openAddMUCModal(_converse);
expect(modal.querySelector('.modal-title').textContent.trim()).toBe('Enter a new Groupchat');
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
const label_name = modal.querySelector('label[for="chatroom"]');
expect(label_name.textContent.trim()).toBe('Groupchat name:');
let name_input = modal.querySelector('input[name="chatroom"]');
expect(name_input.placeholder).toBe('');
name_input.value = 'lounge';
let nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
modal.querySelector('form input[type="submit"]').click();
await mock.waitForMUCDiscoInfo(_converse, 'lounge@muc.example.org');
await u.waitUntil(() => _converse.chatboxes.length);
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('lounge@muc.example.org')).toBe(true);
// However, you can still open MUCs with different domains
const roomspanel = _converse.chatboxviews.get('controlbox').querySelector('converse-rooms-list');
roomspanel.querySelector('.show-add-muc-modal').click();
modal = await u.waitUntil(() => document.querySelector('converse-add-muc-modal'));
name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'lounge@conference';
nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
modal.querySelector('form input[type="submit"]').click();
await mock.waitForMUCDiscoInfo(_converse, 'lounge-conference@muc.example.org');
await u.waitUntil(
() => _converse.chatboxes.models.filter(c => c.get('type') === 'chatroom').length === 2
);
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 2);
expect(
_converse.chatboxes.models.map(m => m.get('id')).includes('lounge-conference@muc.example.org')
).toBe(true);
})
);
it("lets you create a MUC with only the name",
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
const { domain } = _converse;
await mock.waitUntilDiscoConfirmed(
_converse,
domain,
[{ category: 'server', type: 'IM' }],
[ ],
);
const nick = 'max';
const muc_jid = 'the-lounge@muc.example.org';
const own_jid = _converse.session.get('jid');
const modal = await mock.openAddMUCModal(_converse);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
const name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'The Lounge';
const nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = nick;
modal.querySelector('form input[type="submit"]').click();
await mock.waitUntilDiscoConfirmed(_converse, domain, [], [], ['muc.example.org'], 'items');
await mock.waitUntilDiscoConfirmed(_converse, 'muc.example.org', [], [Strophe.NS.MUC]);
await mock.waitForMUCDiscoInfo(_converse, muc_jid);
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
expect(_converse.chatboxes.models.map(m => m.get('id')).includes(muc_jid)).toBe(true);
const muc = _converse.chatboxes.get(muc_jid);
expect(muc.get('name')).toBe('The Lounge');
await mock.waitForMUCDiscoInfo(_converse, muc_jid);
// Own presence which states that the room is locked and needs to
// be configured (code 201)
const presence =
stx`<presence
id="5025e055-036c-4bc5-a227-706e7e352053"
to="${own_jid}"
from="${muc_jid}/${nick}"
xmlns="jabber:client">
<x xmlns="http://jabber.org/protocol/muc#user">
<item affiliation="owner" jid="${own_jid}" role="moderator"/>
<status code="110"/>
<status code="201"/>
</x>
</presence>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(presence));
const IQ_stanzas = _converse.api.connection.get().IQ_stanzas;
const iq = await u.waitUntil(() => IQ_stanzas.filter((s) => sizzle(`query[xmlns="${Strophe.NS.MUC_OWNER}"]`, s).length).pop());
spyOn(muc, 'sendConfiguration').and.callThrough();
expect(iq).toEqualStanza(stx`
<iq id="${iq.getAttribute('id')}" to="${muc_jid}" type="get" xmlns="jabber:client">
<query xmlns="http://jabber.org/protocol/muc#owner"/>
</iq>`);
_converse.api.connection.get()._dataRecv(mock.createRequest(
stx`<iq xmlns="jabber:client"
type="result"
to="${own_jid}"
from="${muc_jid}" id="${iq.getAttribute('id')}">
<query xmlns="http://jabber.org/protocol/muc#owner">
<x xmlns="jabber:x:data" type="form">
<title>Configuration for ${muc_jid}</title>
<instructions>Complete and submit this form to configure the room.</instructions>
<field var="FORM_TYPE" type="hidden">
<value>http://jabber.org/protocol/muc#roomconfig</value>
</field>
<field type="text-single" var="muc#roomconfig_roomname" label="Name"><value></value></field>
<field type="text-single" var="muc#roomconfig_roomdesc" label="Description"><value/></field>
<field type="boolean" var="muc#roomconfig_persistentroom" label="Make Room Persistent?"/>
<field type="boolean" var="muc#roomconfig_publicroom" label="Make Room Publicly Searchable?"><value>1</value></field>
<field type="boolean" var="muc#roomconfig_changesubject" label="Allow Occupants to Change Subject?"/>
<field type="list-single" var="muc#roomconfig_whois" label="Who May Discover Real JIDs?"><option label="Moderators Only">
<value>moderators</value></option><option label="Anyone"><value>anyone</value></option>
</field>
<field label="Roles and Affiliations that May Retrieve Member List"
type="list-multi"
var="muc#roomconfig_getmemberlist">
<value>moderator</value>
<value>participant</value>
<value>visitor</value>
</field>
<field type="text-private" var="muc#roomconfig_roomsecret" label="Password"><value/></field>
<field type="boolean" var="muc#roomconfig_moderatedroom" label="Make Room Moderated?"/>
<field type="boolean" var="muc#roomconfig_membersonly" label="Make Room Members-Only?"/>
<field type="text-single" var="muc#roomconfig_historylength" label="Maximum Number of History Messages Returned by Room">
<value>20</value></field>
</x>
</query>
</iq>`));
await u.waitUntil(() => muc.sendConfiguration.calls.count() === 1);
const sent_stanza = IQ_stanzas.filter(s => s.getAttribute('type') === 'set').pop();
expect(sizzle('field[var="muc#roomconfig_roomname"] value', sent_stanza).pop().textContent.trim()).toBe('The Lounge');
})
);
it("shows a validation error when only the name was specified and there's no default MUC service",
mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
const { domain } = _converse;
await mock.waitUntilDiscoConfirmed(
_converse,
domain,
[{ category: 'server', type: 'IM' }],
[],
);
const nick = 'max';
const modal = await mock.openAddMUCModal(_converse);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
const name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'The Lounge';
const nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = nick;
modal.querySelector('form input[type="submit"]').click();
await u.waitUntil(() => name_input.classList.contains('error'));
expect(name_input.classList.contains('is-invalid')).toBe(true);
expect(modal.querySelector('.invalid-feedback')?.textContent).toBe(
"No default groupchat service found. "+
"You'll need to specify the full address, for example room@conference.example.org"
);
})
);
it("normalizes the MUC name when creating the corresponding JID",
mock.initConverse(['chatBoxesFetched'], {muc_domain: 'montague.lit'}, async function (_converse) {
const modal = await mock.openAddMUCModal(_converse);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
const name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'Into the Äther: A Journey';
const nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
modal.querySelector('form input[type="submit"]').click();
await mock.waitForMUCDiscoInfo(_converse, 'into-the-ather-a-journey@montague.lit');
await u.waitUntil(() => sizzle('.chatroom', _converse.el).filter(u.isVisible).length === 1);
expect(_converse.chatboxes.models.map(m => m.get('id')).includes('into-the-ather-a-journey@montague.lit')).toBe(true);
expect(modal.normalizeNode('Into the Äther: A Journey')).toBe('into-the-ather-a-journey');
expect(modal.normalizeNode(' A silly summer song ∷ ')).toBe('a-silly-summer-song');
})
);
it("applies a muc_roomid_policy",
mock.initConverse(['chatBoxesFetched'], {
muc_domain: 'montague.lit',
muc_roomid_policy: /^[a-z0-9._-]{5,40}$/,
muc_roomid_policy_hint: '<br><b>Policy for groupchat id:</b><br>- between 5 and 40 characters,<br>- lowercase from a to z (no special characters) or<br>- digits or<br>- dots (.) or<br>- underlines (_) or<br>- hyphens (-),<br>- no spaces<br>',
}, async function (_converse) {
const modal = await mock.openAddMUCModal(_converse);
spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
const name_input = modal.querySelector('input[name="chatroom"]');
name_input.value = 'lounge\ music@montague.lit';
const nick_input = modal.querySelector('input[name="nickname"]');
nick_input.value = 'max';
modal.querySelector('form input[type="submit"]').click();
await u.waitUntil(() => name_input.classList.contains('error'));
expect(name_input.classList.contains('is-invalid')).toBe(true);
expect(modal.querySelector('.invalid-feedback')?.textContent).toBe('Groupchat id is invalid.');
})
);
});