converse.js
Version:
Browser based XMPP chat client
623 lines (581 loc) • 32.3 kB
JavaScript
/* global mock, converse */
const { Strophe, sizzle, stx, u, errors } = converse.env;
describe('The pubsub API', function () {
beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza }));
describe('fetching a nodes config settings', function () {
it(
"can be used to fetch a nodes's configuration settings",
mock.initConverse([], {}, async function (_converse) {
await mock.waitForRoster(_converse, 'current', 0);
const { api } = _converse;
const sent_stanzas = api.connection.get().sent_stanzas;
const own_jid = _converse.session.get('jid');
const node = 'princely_musings';
const pubsub_jid = 'pubsub.shakespeare.lit';
const promise = api.pubsub.config.get(pubsub_jid, node);
const sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => sizzle('pubsub configure', iq)).pop()
);
const response = stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
<configure node='${node}'>
<x xmlns='jabber:x:data' type='form'>
<field var='FORM_TYPE' type='hidden'>
<value>http://jabber.org/protocol/pubsub#node_config</value>
</field>
<field var='pubsub#title' type='text-single'
label='A friendly name for the node'/>
<field var='pubsub#deliver_notifications' type='boolean'
label='Whether to deliver event notifications'>
<value>true</value>
</field>
<field var='pubsub#deliver_payloads' type='boolean'
label='Whether to deliver payloads with event notifications'>
<value>true</value>
</field>
<field var='pubsub#notify_config' type='boolean'
label='Notify subscribers when the node configuration changes'>
<value>0</value>
</field>
<field var='pubsub#notify_delete' type='boolean'
label='Notify subscribers when the node is deleted'>
<value>false</value>
</field>
<field var='pubsub#notify_retract' type='boolean'
label='Notify subscribers when items are removed from the node'>
<value>false</value>
</field>
<field var='pubsub#notify_sub' type='boolean'
label='Notify owners about new subscribers and unsubscribes'>
<value>0</value>
</field>
<field var='pubsub#persist_items' type='boolean'
label='Persist items to storage'>
<value>1</value>
</field>
<field var='pubsub#max_items' type='text-single'
label='Max # of items to persist. \`max\` for no specific limit other than a server imposed maximum.'>
<value>10</value>
</field>
<field var='pubsub#item_expire' type='text-single'
label='Time after which to automatically purge items. \`max\` for no specific limit other than a server imposed maximum.'>
<value>604800</value>
</field>
<field var='pubsub#subscribe' type='boolean'
label='Whether to allow subscriptions'>
<value>1</value>
</field>
<field var='pubsub#access_model' type='list-single'
label='Specify the subscriber model'>
<option><value>authorize</value></option>
<option><value>open</value></option>
<option><value>presence</value></option>
<option><value>roster</value></option>
<option><value>whitelist</value></option>
<value>open</value>
</field>
<field var='pubsub#roster_groups_allowed' type='list-multi'
label='Roster groups allowed to subscribe'>
<option><value>friends</value></option>
<option><value>courtiers</value></option>
<option><value>servants</value></option>
<option><value>enemies</value></option>
</field>
<field var='pubsub#publish_model' type='list-single'
label='Specify the publisher model'>
<option><value>publishers</value></option>
<option><value>subscribers</value></option>
<option><value>open</value></option>
<value>publishers</value>
</field>
<field var='pubsub#purge_offline' type='boolean'
label='Purge all items when the relevant publisher goes offline?'>
<value>0</value>
</field>
<field var='pubsub#max_payload_size' type='text-single'
label='Max Payload size in bytes'>
<value>1028</value>
</field>
<field var='pubsub#send_last_published_item' type='list-single'
label='When to send the last published item'>
<option label='Never'><value>never</value></option>
<option label='When a new subscription is processed'><value>on_sub</value></option>
<option label='When a new subscription is processed and whenever a subscriber comes online'>
<value>on_sub_and_presence</value>
</option>
<value>never</value>
</field>
<field var='pubsub#presence_based_delivery' type='boolean'
label='Deliver event notifications only to available users'>
<value>0</value>
</field>
<field var='pubsub#notification_type' type='list-single'
label='Specify the delivery style for event notifications'>
<option><value>normal</value></option>
<option><value>headline</value></option>
<value>headline</value>
</field>
<field var='pubsub#type' type='text-single'
label='Specify the semantic type of payload data to be provided at this node.'>
<value>urn:example:e2ee:bundle</value>
</field>
<field var='pubsub#dataform_xslt' type='text-single' label='Payload XSLT'/>
</x>
</configure>
</pubsub>
</iq>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(response));
const result = await promise;
expect(result).toEqual({
access_model: null,
dataform_xslt: null,
deliver_notifications: true,
deliver_payloads: true,
item_expire: '604800',
max_items: '10',
max_payload_size: '1028',
notification_type: null,
notify_config: false,
notify_delete: false,
notify_retract: false,
notify_sub: false,
persist_items: true,
presence_based_delivery: false,
publish_model: null,
purge_offline: false,
roster_groups_allowed: null,
send_last_published_item: null,
subscribe: true,
title: null,
type: 'urn:example:e2ee:bundle',
});
})
);
it(
'handles error cases',
mock.initConverse([], {}, async function (_converse) {
await mock.waitForRoster(_converse, 'current', 0);
const { api } = _converse;
const sent_stanzas = api.connection.get().sent_stanzas;
const own_jid = _converse.session.get('jid');
const node = 'princely_musings';
const pubsub_jid = 'pubsub.shakespeare.lit';
let promise = api.pubsub.config.get(pubsub_jid, node);
let sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => sizzle('pubsub configure', iq)).pop()
);
let response = stx`<iq type='error'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<error type='cancel'>
<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='config-node'/>
</error>
</iq>`;
let first_error_thrown = false;
promise
.catch((e) => {
expect(e instanceof errors.FeatureNotImplementedError).toBe(true);
first_error_thrown = true;
})
.finally(() => {
expect(first_error_thrown).toBe(true);
});
_converse.api.connection.get()._dataRecv(mock.createRequest(response));
promise = api.pubsub.config.get(pubsub_jid, node);
sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => sizzle('pubsub configure', iq)).pop()
);
response = stx`<iq type='error'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<error type='auth'><forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>
</iq>`;
let second_error_thrown = false;
promise
.catch((e) => {
expect(e instanceof errors.ForbiddenError).toBe(true);
second_error_thrown = true;
})
.finally(() => {
expect(second_error_thrown).toBe(true);
});
_converse.api.connection.get()._dataRecv(mock.createRequest(response));
promise = api.pubsub.config.get(pubsub_jid, node);
sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => sizzle('pubsub configure', iq)).pop()
);
response = stx`<iq type='error'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<error type='cancel'><item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>
</iq>`;
let third_error_thrown = false;
promise
.catch((e) => {
expect(e instanceof errors.ItemNotFoundError).toBe(true);
third_error_thrown = true;
})
.finally(() => {
expect(third_error_thrown).toBe(true);
});
_converse.api.connection.get()._dataRecv(mock.createRequest(response));
})
);
});
describe('setting a nodes config settings', function () {
it(
'first fetches the config, and then changes the specified values',
mock.initConverse([], {}, async function (_converse) {
await mock.waitForRoster(_converse, 'current', 0);
const { api } = _converse;
const sent_stanzas = api.connection.get().sent_stanzas;
const own_jid = _converse.session.get('jid');
const node = 'princely_musings';
const pubsub_jid = 'pubsub.shakespeare.lit';
const promise = api.pubsub.config.set(pubsub_jid, node, { access_model: 'whitelist' });
let sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => sizzle('pubsub configure', iq)).pop()
);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
<configure node='${node}'>
<x xmlns='jabber:x:data' type='form'>
<field var='FORM_TYPE' type='hidden'>
<value>http://jabber.org/protocol/pubsub#node_config</value>
</field>
<field var='pubsub#title' type='text-single'
label='A friendly name for the node'/>
<field var='pubsub#deliver_notifications' type='boolean'
label='Whether to deliver event notifications'>
<value>true</value>
</field>
<field var='pubsub#deliver_payloads' type='boolean'
label='Whether to deliver payloads with event notifications'>
<value>true</value>
</field>
<field var='pubsub#notify_config' type='boolean'
label='Notify subscribers when the node configuration changes'>
<value>0</value>
</field>
<field var='pubsub#notify_delete' type='boolean'
label='Notify subscribers when the node is deleted'>
<value>false</value>
</field>
<field var='pubsub#notify_retract' type='boolean'
label='Notify subscribers when items are removed from the node'>
<value>false</value>
</field>
<field var='pubsub#notify_sub' type='boolean'
label='Notify owners about new subscribers and unsubscribes'>
<value>0</value>
</field>
<field var='pubsub#persist_items' type='boolean'
label='Persist items to storage'>
<value>1</value>
</field>
<field var='pubsub#max_items' type='text-single'
label='Max # of items to persist. \`max\` for no specific limit other than a server imposed maximum.'>
<value>10</value>
</field>
<field var='pubsub#item_expire' type='text-single'
label='Time after which to automatically purge items. \`max\` for no specific limit other than a server imposed maximum.'>
<value>604800</value>
</field>
<field var='pubsub#subscribe' type='boolean'
label='Whether to allow subscriptions'>
<value>1</value>
</field>
<field var='pubsub#publish_model' type='list-single'
label='Specify the publisher model'>
<option><value>publishers</value></option>
<option><value>subscribers</value></option>
<option><value>open</value></option>
<value>publishers</value>
</field>
<field var='pubsub#purge_offline' type='boolean'
label='Purge all items when the relevant publisher goes offline?'>
<value>0</value>
</field>
</x>
</configure>
</pubsub>
</iq>`)
);
sent_stanza = await u.waitUntil(() =>
sent_stanzas
.filter((iq) => iq.getAttribute('type') === 'set' && sizzle('pubsub configure', iq))
.pop()
);
expect(sent_stanza).toEqualStanza(stx`<iq xmlns="jabber:client"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
type="set"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
<configure node="princely_musings">
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#nodeconfig</value></field>
<field var="title"><value/></field>
<field var="deliver_notifications"><value>true</value></field>
<field var="deliver_payloads"><value>true</value></field>
<field var="notify_config"><value>false</value></field>
<field var="notify_delete"><value>false</value></field>
<field var="notify_retract"><value>false</value></field>
<field var="notify_sub"><value>false</value></field>
<field var="persist_items"><value>true</value></field>
<field var="max_items"><value>10</value></field>
<field var="item_expire"><value>604800</value></field>
<field var="subscribe"><value>true</value></field>
<field var="publish_model"><value/></field>
<field var="purge_offline"><value>false</value></field>
<field var="access_model"><value>whitelist</value></field>
</x>
</configure>
</pubsub>
</iq>`);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}"></iq>`)
);
const result = await promise;
expect(result).toEqual({
access_model: 'whitelist',
deliver_notifications: true,
deliver_payloads: true,
item_expire: '604800',
max_items: '10',
notify_config: false,
notify_delete: false,
notify_retract: false,
notify_sub: false,
persist_items: true,
publish_model: null,
purge_offline: false,
subscribe: true,
title: null,
});
})
);
it(
'handles error cases',
mock.initConverse([], {}, async function (_converse) {
await mock.waitForRoster(_converse, 'current', 0);
const { api } = _converse;
const sent_stanzas = api.connection.get().sent_stanzas;
const own_jid = _converse.session.get('jid');
const node = 'princely_musings';
const pubsub_jid = 'pubsub.shakespeare.lit';
const promise = api.pubsub.config.set(pubsub_jid, node, { access_model: 'whitelist' });
let sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => sizzle('pubsub configure', iq)).pop()
);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
<configure node='${node}'>
<x xmlns='jabber:x:data' type='form'>
<field var='FORM_TYPE' type='hidden'>
<value>http://jabber.org/protocol/pubsub#node_config</value>
</field>
<field var='pubsub#title' type='text-single'
label='A friendly name for the node'/>
<field var='pubsub#deliver_notifications' type='boolean'
label='Whether to deliver event notifications'>
<value>true</value>
</field>
</x>
</configure>
</pubsub>
</iq>`)
);
sent_stanza = await u.waitUntil(() =>
sent_stanzas
.filter((iq) => iq.getAttribute('type') === 'set' && sizzle('pubsub configure', iq))
.pop()
);
const response = stx`
<iq type='error'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<error type='modify'><not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error>
</iq>`;
let first_error_thrown = false;
promise
.catch((e) => {
expect(e instanceof errors.NotAcceptableError).toBe(true);
first_error_thrown = true;
})
.finally(() => {
expect(first_error_thrown).toBe(true);
});
_converse.api.connection.get()._dataRecv(mock.createRequest(response));
})
);
});
describe('publishing to a node', function () {
it(
"will try to manually configure the node if publish-options aren't supported",
mock.initConverse([], {}, async function (_converse) {
await mock.waitForRoster(_converse, 'current', 0);
const pubsub_jid = 'pubsub.shakespeare.lit';
const { api } = _converse;
const sent_stanzas = api.connection.get().sent_stanzas;
const own_jid = _converse.session.get('jid');
const node = 'princely_musings';
const promise = api.pubsub.publish(pubsub_jid, node, stx`<item></item>`, { access_model: 'whitelist' });
await mock.waitUntilDiscoConfirmed(
_converse,
pubsub_jid,
[{ 'category': 'pubsub', 'type': 'pep' }],
['http://jabber.org/protocol/pubsub#publish-options']
);
let sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => iq.querySelector('pubsub publish')).pop()
);
expect(sent_stanza).toEqualStanza(stx`
<iq type="set"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
xmlns="jabber:client"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<publish node="princely_musings"><item/></publish>
<publish-options>
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden">
<value>http://jabber.org/protocol/pubsub#publish-options</value>
</field>
<field var="pubsub#access_model"><value>whitelist</value></field>
</x>
</publish-options>
</pubsub>
</iq>`);
let response = stx`<iq type='error'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<error type='modify'>
<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<precondition-not-met xmlns='http://jabber.org/protocol/pubsub#errors'/>
</error>
</iq>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(response));
sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => iq.querySelector('pubsub configure')).pop()
);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
<configure node='${node}'>
<x xmlns='jabber:x:data' type='form'>
<field var='pubsub#access_model' type='list-single' label='Specify the subscriber model'>
<option><value>authorize</value></option>
<option><value>open</value></option>
<option><value>presence</value></option>
<option><value>roster</value></option>
<option><value>whitelist</value></option>
<value>open</value>
</field>
</x>
</configure>
</pubsub>
</iq>`)
);
sent_stanza = await u.waitUntil(() =>
sent_stanzas
.filter((iq) => iq.getAttribute('type') === 'set' && iq.querySelector('pubsub configure'))
.pop()
);
expect(sent_stanza).toEqualStanza(stx`<iq xmlns="jabber:client"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
type="set"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
<configure node="princely_musings">
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#nodeconfig</value></field>
<field var="access_model"><value>whitelist</value></field>
</x>
</configure>
</pubsub>
</iq>`);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}"></iq>`)
);
// Clear old stanzas
while (sent_stanzas.length) sent_stanzas.pop();
sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => iq.querySelector('pubsub publish')).pop()
);
expect(sent_stanza).toEqualStanza(stx`
<iq type="set"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
xmlns="jabber:client"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<publish node="princely_musings"><item/></publish>
<publish-options>
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden">
<value>http://jabber.org/protocol/pubsub#publish-options</value>
</field>
<field var="pubsub#access_model"><value>whitelist</value></field>
</x>
</publish-options>
</pubsub>
</iq>`);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}"></iq>`)
);
await promise;
})
);
});
});