Commit 9d77a4ef authored by JC Brand's avatar JC Brand

Fixes #129 Add support for XEP-0156.

Only XML is supported for now.
parent 54e9c51a
......@@ -2,11 +2,18 @@
## 6.0.0 (Unreleased)
- #129: Add support for XEP-0156: Disovering Alternative XMPP Connection Methods. Only XML is supported for now.
- #1691 Fix `collection.chatbox is undefined` errors
- Prevent editing of sent file uploads.
### Breaking changes
- In order to add support for XEP-0156, the XMPP connection needs to be created
only once we know the JID of the user that's logging in. This means that the
[connectionInitialized](https://conversejs.org/docs/html/api/-_converse.html#event:connectionInitialized)
event now fires much later than before. Plugins that rely on `connectionInitialized`
being triggered before the user's JID has been provided will need to be updated.
- The following API methods now return promises:
* `_converse.api.chats.get`
* `_converse.api.chats.create`
......
......@@ -639,6 +639,23 @@ The default chat status that the user wil have. If you for example set this to
``'chat'``, then Converse will send out a presence stanza with ``"show"``
set to ``'chat'`` as soon as you've been logged in.
discover_connection_methods
---------------------------
* Default: ``false``
Use `XEP-0156 <https://xmpp.org/extensions/xep-0156.html>`_ to discover whether
the XMPP host for the current user advertises any Websocket or BOSH connection
URLs that can be used.
If this is set to ``false``, then a `websocket_url`_ or `bosh_service_url`_ need to be
set.
Currently only the XML encoded host-meta resource is supported as shown in
`Example 2 under section 3.3 <https://xmpp.org/extensions/xep-0156.html#httpexamples>`_.
domain_placeholder
------------------
......@@ -647,8 +664,6 @@ domain_placeholder
The placeholder text shown in the domain input on the registration form.
emoji_image_path
----------------
......@@ -1624,6 +1639,7 @@ Allows you to show or hide buttons on the chatboxes' toolbars.
.. _`websocket-url`:
websocket_url
-------------
......
......@@ -404,8 +404,8 @@
it("can be retrieved from the XMPP server", mock.initConverse(
{'connection': ['send']}, ['chatBoxesFetched', 'roomsPanelRendered', 'rosterGroupsFetched'], {},
async function (done, _converse) {
null, ['chatBoxesFetched', 'roomsPanelRendered', 'rosterGroupsFetched'], {},
async function (done, _converse) {
await test_utils.waitUntilDiscoConfirmed(
_converse, _converse.bare_jid,
......@@ -421,25 +421,12 @@
* </pubsub>
* </iq>
*/
let IQ_id;
const call = await u.waitUntil(() =>
_.filter(
_converse.connection.send.calls.all(),
call => {
const stanza = call.args[0];
if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
return;
}
if (sizzle('items[node="storage:bookmarks"]', stanza).length) {
IQ_id = stanza.getAttribute('id');
return true;
}
}
).pop()
);
const IQ_stanzas = _converse.connection.IQ_stanzas;
const sent_stanza = await u.waitUntil(
() => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
expect(Strophe.serialize(call.args[0])).toBe(
`<iq from="romeo@montague.lit/orchard" id="${IQ_id}" type="get" xmlns="jabber:client">`+
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
'<items node="storage:bookmarks"/>'+
'</pubsub>'+
......@@ -469,7 +456,7 @@
expect(_converse.bookmarks.models.length).toBe(0);
spyOn(_converse.bookmarks, 'onBookmarksReceived').and.callThrough();
var stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
var stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':sent_stanza.getAttribute('id')})
.c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('items', {'node': 'storage:bookmarks'})
.c('item', {'id': 'current'})
......@@ -495,7 +482,7 @@
describe("The rooms panel", function () {
it("shows a list of bookmarks", mock.initConverse(
{'connection': ['send']}, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched'], {},
async function (done, _converse) {
await test_utils.waitUntilDiscoConfirmed(
......@@ -505,31 +492,19 @@
);
test_utils.openControlBox();
let IQ_id;
const call = await u.waitUntil(() =>
_.filter(
_converse.connection.send.calls.all(),
call => {
const stanza = call.args[0];
if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
return;
}
if (sizzle('items[node="storage:bookmarks"]', stanza).length) {
IQ_id = stanza.getAttribute('id');
return true;
}
}
).pop()
);
expect(Strophe.serialize(call.args[0])).toBe(
`<iq from="romeo@montague.lit/orchard" id="${IQ_id}" type="get" xmlns="jabber:client">`+
const IQ_stanzas = _converse.connection.IQ_stanzas;
const sent_stanza = await u.waitUntil(
() => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
'<items node="storage:bookmarks"/>'+
'</pubsub>'+
'</iq>'
);
const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':sent_stanza.getAttribute('id')})
.c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('items', {'node': 'storage:bookmarks'})
.c('item', {'id': 'current'})
......@@ -583,7 +558,7 @@
it("remembers the toggle state of the bookmarks list", mock.initConverse(
{'connection': ['send']}, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched'], {},
async function (done, _converse) {
test_utils.openControlBox();
......@@ -593,31 +568,19 @@
['http://jabber.org/protocol/pubsub#publish-options']
);
let IQ_id;
const call = await u.waitUntil(() =>
_.filter(
_converse.connection.send.calls.all(),
call => {
const stanza = call.args[0];
if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
return;
}
if (sizzle('items[node="storage:bookmarks"]', stanza).length) {
IQ_id = stanza.getAttribute('id');
return true;
}
}
).pop()
);
expect(Strophe.serialize(call.args[0])).toBe(
`<iq from="romeo@montague.lit/orchard" id="${IQ_id}" type="get" xmlns="jabber:client">`+
const IQ_stanzas = _converse.connection.IQ_stanzas;
const sent_stanza = await u.waitUntil(
() => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
expect(Strophe.serialize(sent_stanza)).toBe(
`<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
'<items node="storage:bookmarks"/>'+
'</pubsub>'+
'</iq>'
);
const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':sent_stanza.getAttribute('id')})
.c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('items', {'node': 'storage:bookmarks'})
.c('item', {'id': 'current'})
......
......@@ -8,7 +8,7 @@
it("contains a checkbox to indicate whether the computer is trusted or not",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: false },
async function (done, _converse) {
......@@ -42,7 +42,7 @@
it("checkbox can be set to false by default",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
trusted: false,
allow_registration: false },
......
......@@ -10,7 +10,7 @@
it("is not available unless allow_registration=true",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: false },
async function (done, _converse) {
......@@ -24,7 +24,7 @@
it("can be opened by clicking on the registration tab",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: true },
async function (done, _converse) {
......@@ -45,18 +45,18 @@
it("allows the user to choose an XMPP provider's domain",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: true },
async function (done, _converse) {
spyOn(Strophe.Connection.prototype, 'connect');
await u.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'));
test_utils.openControlBox();
const cbview = _converse.chatboxviews.get('controlbox');
const registerview = cbview.registerpanel;
spyOn(registerview, 'onProviderChosen').and.callThrough();
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
spyOn(_converse.connection, 'connect');
// Open the register panel
cbview.el.querySelector('.toggle-register-login').click();
......@@ -75,17 +75,18 @@
form.querySelector('input[name=domain]').value = 'conversejs.org';
submit_button.click();
expect(registerview.onProviderChosen).toHaveBeenCalled();
expect(_converse.connection.connect).toHaveBeenCalled();
await u.waitUntil(() => _converse.connection.connect.calls.count());
done();
}));
it("will render a registration form as received from the XMPP provider",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: true },
async function (done, _converse) {
spyOn(Strophe.Connection.prototype, 'connect');
await u.waitUntil(() => _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'));
test_utils.openControlBox();
const cbview = _converse.chatboxviews.get('controlbox');
......@@ -97,7 +98,6 @@
spyOn(registerview, 'onRegistrationFields').and.callThrough();
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
spyOn(_converse.connection, 'connect').and.callThrough();
expect(registerview._registering).toBeFalsy();
expect(_converse.connection.connected).toBeFalsy();
......@@ -105,7 +105,7 @@
registerview.el.querySelector('input[type=submit]').click();
expect(registerview.onProviderChosen).toHaveBeenCalled();
expect(registerview._registering).toBeTruthy();
expect(_converse.connection.connect).toHaveBeenCalled();
await u.waitUntil(() => _converse.connection.connect.calls.count());
let stanza = new Strophe.Builder("stream:features", {
'xmlns:stream': "http://etherx.jabber.org/streams",
......@@ -137,7 +137,7 @@
it("will set form_type to legacy and submit it as legacy",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: true },
async function (done, _converse) {
......@@ -194,7 +194,7 @@
it("will set form_type to xform and submit it as xform",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
allow_registration: true },
async function (done, _converse) {
......@@ -267,7 +267,7 @@
it("renders the account registration form",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ auto_login: false,
view_mode: 'fullscreen',
allow_registration: true },
......
......@@ -53,7 +53,7 @@
it("uses bookmarks to determine groupchat names",
mock.initConverse(
{'connection': ['send']},
null,
['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'],
{'view_mode': 'fullscreen'},
async function (done, _converse) {
......@@ -113,7 +113,7 @@
describe("A groupchat shown in the groupchats list", function () {
it("is highlighted if its currently open", mock.initConverse(
it("is highlighted if it's currently open", mock.initConverse(
null, ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'],
{ view_mode: 'fullscreen',
allow_bookmarks: false // Makes testing easier, otherwise we have to mock stanza traffic.
......@@ -137,8 +137,6 @@
expect(room_els.length).toBe(1);
item = room_els[0];
expect(item.textContent.trim()).toBe('balcony@chat.shakespeare.lit');
const conv_el = document.querySelector('#conversejs');
conv_el.parentElement.removeChild(conv_el);
done();
}));
......
......@@ -11,7 +11,7 @@
it("gets enabled with an <enable> stanza and resumed with a <resume> stanza",
mock.initConverse(
null, ['connectionInitialized', 'chatBoxesInitialized'],
null, ['chatBoxesInitialized'],
{ 'auto_login': false,
'enable_smacks': true,
'show_controlbox_by_default': true,
......
......@@ -1124,7 +1124,7 @@ converse.plugins.add('converse-chatview', {
if (Backbone.history.getFragment() === "converse/chat?jid="+this.model.get('jid')) {
_converse.router.navigate('');
}
if (_converse.connection.connected) {
if (_converse.api.connection.connected()) {
// Immediately sending the chat state, because the
// model is going to be destroyed afterwards.
this.model.setChatState(_converse.INACTIVE);
......
......@@ -154,6 +154,7 @@ converse.plugins.add('converse-controlbox', {
_converse.api.promises.add('controlBoxInitialized');
const addControlBox = () => _converse.chatboxes.add({'id': 'controlbox'});
_converse.ControlBox = _converse.ChatBox.extend({
......@@ -220,9 +221,9 @@ converse.plugins.add('converse-controlbox', {
} else {
this.hide();
}
if (!_converse.connection.connected ||
!_converse.connection.authenticated ||
_converse.connection.disconnecting) {
const connection = get(_converse, 'connection', {});
if (!connection.connected || !connection.authenticated || connection.disconnecting) {
this.renderLoginPanel();
} else if (this.model.get('connected')) {
this.renderControlBoxPane();
......@@ -296,7 +297,8 @@ converse.plugins.add('converse-controlbox', {
if (_converse.sticky_controlbox) {
return;
}
if (_converse.connection.connected && !_converse.connection.disconnecting) {
const connection = get(_converse, 'connection', {});
if (connection.connected && !connection.disconnecting) {
this.model.save({'closed': true});
} else {
this.model.trigger('hide');
......@@ -319,7 +321,8 @@ converse.plugins.add('converse-controlbox', {
}
u.addClass('hidden', this.el);
_converse.api.trigger('chatBoxClosed', this);
if (!_converse.connection.connected) {
if (!_converse.api.connection.connected()) {
_converse.controlboxtoggle.render();
}
_converse.controlboxtoggle.show(callback);
......@@ -464,7 +467,7 @@ converse.plugins.add('converse-controlbox', {
if (["converse/login", "converse/register"].includes(Backbone.history.getFragment())) {
_converse.router.navigate('', {'replace': true});
}
_converse.connection.reset();
_converse.connection && _converse.connection.reset();
_converse.api.user.login(jid, password);
}
});
......@@ -510,7 +513,7 @@ converse.plugins.add('converse-controlbox', {
// artifacts (i.e. on page load the toggle is shown only to then
// seconds later be hidden in favor of the controlbox).
this.el.innerHTML = tpl_controlbox_toggle({
'label_toggle': _converse.connection.connected ? __('Chat Contacts') : __('Toggle chat')
'label_toggle': _converse.api.connection.connected() ? __('Chat Contacts') : __('Toggle chat')
})
return this;
},
......@@ -529,7 +532,7 @@ converse.plugins.add('converse-controlbox', {
if (!controlbox) {
controlbox = addControlBox();
}
if (_converse.connection.connected) {
if (_converse.api.connection.connected()) {
controlbox.save({'closed': false});
} else {
controlbox.trigger('show');
......@@ -540,7 +543,7 @@ converse.plugins.add('converse-controlbox', {
e.preventDefault();
if (u.isVisible(_converse.root.querySelector("#controlbox"))) {
const controlbox = _converse.chatboxes.get('controlbox');
if (_converse.connection.connected) {
if (_converse.api.connection.connected) {
controlbox.save({closed: true});
} else {
controlbox.trigger('hide');
......@@ -582,10 +585,9 @@ converse.plugins.add('converse-controlbox', {
});
Promise.all([
_converse.api.waitUntil('connectionInitialized'),
_converse.api.waitUntil('chatBoxViewsInitialized')
]).then(addControlBox).catch(e => _converse.log(e, Strophe.LogLevel.FATAL));
_converse.api.waitUntil('chatBoxViewsInitialized')
.then(addControlBox)
.catch(e => _converse.log(e, Strophe.LogLevel.FATAL));
_converse.api.listen.on('chatBoxesFetched', () => {
const controlbox = _converse.chatboxes.get('controlbox') || addControlBox();
......
......@@ -351,7 +351,7 @@ converse.plugins.add('converse-dragresize', {
_converse.resizing.chatbox.width,
_converse.resizing.chatbox.model.get('default_width')
);
if (_converse.connection.connected) {
if (_converse.api.connection.connected()) {
_converse.resizing.chatbox.model.save({'height': height});
_converse.resizing.chatbox.model.save({'width': width});
} else {
......
......@@ -318,7 +318,7 @@ converse.plugins.add('converse-minimize', {
* @param { _converse.ChatBoxView|_converse.ChatRoomView|_converse.ControlBoxView|_converse.HeadlinesBoxView } [newchat]
*/
async trimChats (newchat) {
if (_converse.no_trimming || !_converse.connection.connected || _converse.view_mode !== 'overlayed') {
if (_converse.no_trimming || !_converse.api.connection.connected() || _converse.view_mode !== 'overlayed') {
return;
}
const shown_chats = this.getShownChats();
......@@ -556,10 +556,7 @@ converse.plugins.add('converse-minimize', {
});
/************************ BEGIN Event Handlers ************************/
Promise.all([
_converse.api.waitUntil('connectionInitialized'),
_converse.api.waitUntil('chatBoxViewsInitialized')
]).then(() => {
_converse.api.waitUntil('chatBoxViewsInitialized').then(() => {
_converse.minimized_chats = new _converse.MinimizedChats({
model: _converse.chatboxes
});
......
......@@ -240,8 +240,8 @@ converse.plugins.add('converse-omemo', {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
const { _converse } = this;
const { __ } = _converse;
_converse.api.settings.update({
'omemo_default': false,
......
......@@ -175,7 +175,7 @@ converse.plugins.add('converse-register', {
initialize () {
this.reset();
this.registerHooks();
_converse.api.listen.on('connectionInitialized', () => this.registerHooks());
},
render () {
......@@ -340,7 +340,7 @@ converse.plugins.add('converse-register', {
* @method _converse.RegisterPanel#fetchRegistrationForm
* @param { String } domain_name - XMPP server domain
*/
fetchRegistrationForm (domain_name) {
async fetchRegistrationForm (domain_name) {
if (!this.model.get('registration_form_rendered')) {
this.renderRegistrationRequest();
}
......@@ -348,7 +348,8 @@ converse.plugins.add('converse-register', {
'domain': Strophe.getDomainFromJid(domain_name),
'_registering': true
});
_converse.connection.connect(this.domain, "", this.onConnectStatusChanged.bind(this));
await _converse.initConnection(this.domain);
_converse.connection.connect(this.domain, "", status => this.onConnectStatusChanged(status));
return false;
},
......
......@@ -19,6 +19,10 @@ const BOSH_SESSION_ID = 'converse.bosh-session';
converse.plugins.add('converse-bosh', {
enabled () {
return true;
},
initialize () {
const { _converse } = this;
......@@ -35,9 +39,15 @@ converse.plugins.add('converse-bosh', {
_converse.bosh_session.browserStorage = new BrowserStorage.session(id);
await new Promise(resolve => _converse.bosh_session.fetch({'success': resolve, 'error': resolve}));
}
if (_converse.jid && _converse.bosh_session.get('jid') === _converse.jid) {
_converse.bosh_session.clear({'silent': true });
_converse.bosh_session.save({'jid': _converse.jid, id});
if (_converse.jid) {
if (_converse.bosh_session.get('jid') !== _converse.jid) {
const jid = await _converse.setUserJID(_converse.jid);
_converse.bosh_session.clear({'silent': true });
_converse.bosh_session.save({jid});
}
} else { // Keepalive
const jid = _converse.bosh_session.get('jid');
jid && await _converse.setUserJID();
}
return _converse.bosh_session;
}
......@@ -45,17 +55,17 @@ converse.plugins.add('converse-bosh', {
_converse.startNewPreboundBOSHSession = function () {
if (!_converse.prebind_url) {
throw new Error(
"attemptPreboundSession: If you use prebind then you MUST supply a prebind_url");
throw new Error("startNewPreboundBOSHSession: If you use prebind then you MUST supply a prebind_url");
}
const xhr = new XMLHttpRequest();
xhr.open('GET', _converse.prebind_url, true);
xhr.setRequestHeader('Accept', 'application/json, text/javascript');
xhr.onload = function () {
xhr.onload = async function () {
if (xhr.status >= 200 && xhr.status < 400) {
const data = JSON.parse(xhr.responseText);
const jid = await _converse.setUserJID(data.jid);
_converse.connection.attach(
data.jid,
jid,
data.sid,
data.rid,
_converse.onConnectStatusChanged
......@@ -79,9 +89,6 @@ converse.plugins.add('converse-bosh', {
_converse.restoreBOSHSession = async function () {
if (!_converse.api.connection.isType('bosh')) {
return false;
}
const jid = (await initBOSHSession()).get('jid');
if (jid) {
try {
......@@ -119,9 +126,7 @@ converse.plugins.add('converse-bosh', {
}
});
_converse.api.listen.on('addClientFeatures',
() => _converse.api.disco.own.features.add(Strophe.NS.BOSH)
);
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.BOSH));
/************************ END Event Handlers ************************/
......
......@@ -850,8 +850,8 @@ converse.plugins.add('converse-chatboxes', {
'to': this.get('jid'),
'type': 'chat'
}).c(this.get('chat_state'), {'xmlns': Strophe.NS.CHATSTATES}).up()
.c('no-store', {'xmlns': Strophe.NS.HINTS}).up()
.c('no-permanent-store', {'xmlns': Strophe.NS.HINTS})
.c('no-store', {'xmlns': Strophe.NS.HINTS}).up()
.c('no-permanent-store', {'xmlns': Strophe.NS.HINTS})
);
}
},
......
This diff is collapsed.
......@@ -633,7 +633,7 @@ converse.plugins.add('converse-muc', {
disco_entity.destroy();
}
}
if (_converse.connection.connected) {
if (_converse.api.connection.connected()) {
this.sendUnavailablePresence(exit_msg);
}
u.safeSave(this, {'connection_status': converse.ROOMSTATUS.DISCONNECTED});
......
......@@ -107,6 +107,15 @@ u.isSameBareJID = function (jid1, jid2) {
Strophe.getBareJidFromJid(jid2).toLowerCase();
};
u.isSameDomain = function (jid1, jid2) {
if (!_.isString(jid1) || !_.isString(jid2)) {
return false;
}
return Strophe.getDomainFromJid(jid1).toLowerCase() ===
Strophe.getDomainFromJid(jid2).toLowerCase();
};
u.isNewMessage = function (message) {
/* Given a stanza, determine whether it's a new
* message, i.e. not a MAM archived one.
......
......@@ -9,7 +9,6 @@
const Strophe = converse.env.Strophe;
const dayjs = converse.env.dayjs;
const $iq = converse.env.$iq;
const u = converse.env.utils;
window.libsignal = {
'SignalProtocolAddress': function (name, device_id) {
......@@ -32,7 +31,7 @@
return Promise.resolve(key_and_tag);
}
},
'SessionBuilder': function (storage, remote_address) {
'SessionBuilder': function (storage, remote_address) { // eslint-disable-line no-unused-vars
this.processPreKey = function () {
return Promise.resolve();
}
......@@ -116,95 +115,87 @@
'preventDefault': function () {}
};
mock.mock_connection = function () { // eslint-disable-line wrap-iife
return function () {
Strophe.Bosh.prototype._processRequest = function () {}; // Don't attempt to send out stanzas
const c = new Strophe.Connection('jasmine tests');
const sendIQ = c.sendIQ;
c.IQ_stanzas = [];
c.IQ_ids = [];
c.sendIQ = function (iq, callback, errback) {
if (!_.isElement(iq)) {
iq = iq.nodeTree;
}
this.IQ_stanzas.push(iq);
const id = sendIQ.bind(this)(iq, callback, errback);
this.IQ_ids.push(id);
return id;
const OriginalConnection = Strophe.Connection;
function MockConnection (service, options) {
OriginalConnection.call(this, service, options);
Strophe.Bosh.prototype._processRequest = function () {}; // Don't attempt to send out stanzas
const sendIQ = this.sendIQ;
this.IQ_stanzas = [];
this.IQ_ids = [];
this.sendIQ = function (iq, callback, errback) {
if (!_.isElement(iq)) {
iq = iq.nodeTree;
}
this.IQ_stanzas.push(iq);
const id = sendIQ.bind(this)(iq, callback, errback);
this.IQ_ids.push(id);
return id;
}
const send = c.send;
c.sent_stanzas = [];
c.send = function (stanza) {
if (_.isElement(stanza)) {
this.sent_stanzas.push(stanza);
} else {
this.sent_stanzas.push(stanza.nodeTree);
}
return send.apply(this, arguments);
const send = this.send;
this.sent_stanzas = [];
this.send = function (stanza) {
if (_.isElement(stanza)) {
this.sent_stanzas.push(stanza);
} else {
this.sent_stanzas.push(stanza.nodeTree);
}
return send.apply(this, arguments);
}
c.features = Strophe.xmlHtmlNode(
'<stream:features xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">'+
'<ver xmlns="urn:xmpp:features:rosterver"/>'+
'<csi xmlns="urn:xmpp:csi:0"/>'+
'<c xmlns="http://jabber.org/protocol/caps" ver="UwBpfJpEt3IoLYfWma/o/p3FFRo=" hash="sha-1" node="http://prosody.im"/>'+
'<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">'+
'<required/>'+
'</bind>'+
`<sm xmlns='urn:xmpp:sm:3'/>`+
'<session xmlns="urn:ietf:params:xml:ns:xmpp-session">'+
'<optional/>'+
'</session>'+
'</stream:features>').firstChild;
this.features = Strophe.xmlHtmlNode(
'<stream:features xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">'+
'<ver xmlns="urn:xmpp:features:rosterver"/>'+
'<csi xmlns="urn:xmpp:csi:0"/>'+
'<this xmlns="http://jabber.org/protocol/caps" ver="UwBpfJpEt3IoLYfWma/o/p3FFRo=" hash="sha-1" node="http://prosody.im"/>'+
'<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">'+
'<required/>'+
'</bind>'+
`<sm xmlns='urn:xmpp:sm:3'/>`+
'<session xmlns="urn:ietf:params:xml:ns:xmpp-session">'+
'<optional/>'+
'</session>'+
'</stream:features>').firstChild;
c._proto._connect = function () {
c.connected = true;
c.mock = true;
c.jid = 'romeo@montague.lit/orchard';
c._changeConnectStatus(Strophe.Status.BINDREQUIRED);
};
this._proto._connect = () => {
this.connected = true;
this.mock = true;
this.jid = 'romeo@montague.lit/orchard';
this._changeConnectStatus(Strophe.Status.BINDREQUIRED);
};
c.bind = function () {
c.authenticated = true;
this.authenticated = true;
c._changeConnectStatus(Strophe.Status.CONNECTED);
};
this.bind = () => {
this.authenticated = true;
this.authenticated = true;
this._changeConnectStatus(Strophe.Status.CONNECTED);
};
c._proto._disconnect = function () {
c._onDisconnectTimeout();
}
this._proto._disconnect = () => this._onDisconnectTimeout();
this._proto._onDisconnectTimeout = _.noop;
}
c._proto._onDisconnectTimeout = _.noop;
return c;
};
}();
MockConnection.prototype = Object.create(OriginalConnection.prototype);
Strophe.Connection = MockConnection;
async function initConverse (settings, spies={}, promises) {
async function initConverse (settings, spies={}) {
window.localStorage.clear();
window.sessionStorage.clear();
const el = document.querySelector('#conversejs');
if (el) {
el.parentElement.removeChild(el);
}
const connection = mock.mock_connection();
if (spies && spies.connection) {
spies.connection.forEach(method => spyOn(connection, method));
}
const _converse = await converse.initialize(Object.assign({
'i18n': 'en',
'animate': false,
'auto_subscribe': false,
'play_sounds': false,
'bosh_service_url': 'montague.lit/http-bind',
'connection': connection,
'animate': false,
'use_emojione': false,
'debug': false,
'i18n': 'en',
'no_trimming': true,
'play_sounds': false,
'use_emojione': false,
'view_mode': mock.view_mode,
'debug': false
}, settings || {}));
if (spies && spies._converse) {
......@@ -214,7 +205,7 @@
_converse.ChatBoxViews.prototype.trimChat = function () {};
_converse.api.vcard.get = function (model, force) {
return new Promise((resolve, reject) => {
return new Promise(resolve => {
let jid;
if (_.isString(model)) {
jid = model;
......@@ -263,9 +254,13 @@
return async done => {
const _converse = await initConverse(settings, spies);
async function _done () {
await _converse.api.user.logout();
if (_converse.api.connection.connected()) {
await _converse.api.user.logout();
}
const el = document.querySelector('#conversejs');
el.parentElement.removeChild(el);
if (el) {
el.parentElement.removeChild(el);
}
done();
}
await Promise.all((promise_names || []).map(_converse.api.waitUntil));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment