Add test that the OMEMO toolbar button renders

Fix and improve accordingly. updates #497
This commit is contained in:
JC Brand 2018-05-12 19:37:44 +02:00
parent 2f149a0ea2
commit 6042c233bc
12 changed files with 231 additions and 74 deletions

View File

@ -9,10 +9,11 @@
"plugins": ["lodash"],
"extends": ["eslint:recommended", "plugin:lodash/canonical"],
"globals": {
"window": true,
"sinon": true,
"Promise": true,
"define": true,
"require": true
"require": true,
"sinon": true,
"window": true
},
"rules": {
"lodash/prefer-lodash-method": [2, {

View File

@ -7331,8 +7331,8 @@ body.reset {
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
flex: 0 0 33.3333333333%;
max-width: 33.3333333333%;
flex: 0 0 25%;
max-width: 25%;
padding: 0; }
#conversejs .chat-head .user-custom-message {
color: white;

View File

@ -7373,8 +7373,8 @@ body {
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
flex: 0 0 33.3333333333%;
max-width: 33.3333333333%;
flex: 0 0 25%;
max-width: 25%;
padding: 0; }
#conversejs .chat-head .user-custom-message {
color: white;

View File

@ -56,7 +56,7 @@
.chatbox-buttons {
flex-direction: row-reverse;
@include make-col-ready();
@include make-col(4);
@include make-col(3);
padding: 0;
}

View File

@ -431,7 +431,7 @@
expect(view).toBeDefined();
var $toolbar = $(view.el).find('ul.chat-toolbar');
expect($toolbar.length).toBe(1);
expect($toolbar.children('li').length).toBe(2);
expect($toolbar.children('li').length).toBe(1);
done();
}));
@ -496,39 +496,6 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("contains a button for starting an encrypted chat session",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
var timeout = true, $toolbar, view;
test_utils.createContacts(_converse, 'current');
test_utils.openControlBox();
test_utils.waitUntil(function () {
return $(_converse.rosterview.el).find('.roster-group').length;
}, 300).then(function () {
// TODO: More tests can be added here...
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
view = _converse.chatboxviews.get(contact_jid);
$toolbar = $(view.el).find('ul.chat-toolbar');
expect($toolbar.find('.toggle-otr').length).toBe(1);
// Register spies
spyOn(view, 'toggleOTRMenu').and.callThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
timeout = false;
$toolbar[0].querySelector('.toggle-otr').click();
return test_utils.waitUntil(function () {
return view.el.querySelector('.otr-menu').offsetHeight;
}, 300)
}).then(function () {
expect(view.toggleOTRMenu).toHaveBeenCalled();
done();
});
}));
it("can contain a button for starting a call",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},

View File

@ -2886,6 +2886,7 @@
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
var muc_iq;
var sent_IQs = [], IQ_ids = [];
var sendIQ = _converse.connection.sendIQ;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
@ -3008,30 +3009,31 @@
'jid': 'crone1@shakespeare.lit',
});
_converse.connection._dataRecv(test_utils.createRequest(owner_list_stanza));
test_utils.waitUntil(function () {
return IQ_ids.length;
}, 300).then(function () {
test_utils.waitUntil(() => {
return _.filter(
_converse.connection.IQ_stanzas,
(iq) => {
if (iq.nodeTree.getAttribute('to') === 'coven@chat.shakespeare.lit' && iq.nodeTree.getAttribute('type') === 'set') {
muc_iq = iq;
return true;
}
return false;
}).length;
}).then(function () {
// Check that the member list now gets updated
var iq = "<iq to='coven@chat.shakespeare.lit' type='set' xmlns='jabber:client' id='"+IQ_ids.pop()+"'>"+
expect(muc_iq.toLocaleString()).toBe("<iq to='coven@chat.shakespeare.lit' type='set' xmlns='jabber:client' id='"+muc_iq.nodeTree.getAttribute('id')+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='member' jid='"+invitee_jid+"'>"+
"<reason>Please join this chat room</reason>"+
"</item>"+
"</query>"+
"</iq>";
test_utils.waitUntil(function () {
return _.includes(_.invokeMap(sent_IQs, Object.prototype.toLocaleString), iq);
}, 300).then(function () {
// Finally check that the user gets invited.
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
"<message from='dummy@localhost/resource' to='"+invitee_jid+"' id='"+sent_id+"' xmlns='jabber:client'>"+
"<x xmlns='jabber:x:conference' jid='coven@chat.shakespeare.lit' reason='Please join this chat room'/>"+
"</message>"
);
done();
});
"</iq>");
// Finally check that the user gets invited.
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
"<message from='dummy@localhost/resource' to='"+invitee_jid+"' id='"+sent_id+"' xmlns='jabber:client'>"+
"<x xmlns='jabber:x:conference' jid='coven@chat.shakespeare.lit' reason='Please join this chat room'/>"+
"</message>");
done();
});
}));
});

View File

@ -317,7 +317,7 @@
expect(box.get('box_id')).toBe(b64_sha1(jid));
expect(
_.keys(box),
['close', 'endOTR', 'focus', 'get', 'initiateOTR', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
['close', 'focus', 'get', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
);
chatboxview = _converse.chatboxviews.get(jid);
expect($(chatboxview.el).is(':visible')).toBeTruthy();

156
spec/omemo.js Normal file
View File

@ -0,0 +1,156 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function (jasmine, mock, converse, test_utils) {
var Strophe = converse.env.Strophe;
var b64_sha1 = converse.env.b64_sha1;
var $iq = converse.env.$iq;
var _ = converse.env._;
window.libsignal = {
'KeyHelper': {
'generateIdentityKeyPair': function () {
return Promise.resolve({
'pubKey': 1234,
'privKey': 4321
});
},
'generateRegistrationId': function () {
return 1234;
}
}
};
describe("The OMEMO module", function() {
it("will add processing hints to sent out encrypted <message> stanzas",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
// TODO
done();
}));
it("adds a toolbar button for starting an encrypted chat session",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
let devicelist_iq,
disco_info_iq;
test_utils.createContacts(_converse, 'current');
const contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.waitUntil(function () {
return _.filter(_converse.connection.IQ_stanzas, function (iq) {
const node = iq.nodeTree.querySelector('iq[to="dummy@localhost"] query[xmlns="http://jabber.org/protocol/disco#info"]');
if (node) { disco_info_iq = iq; }
return node;
}).length > 0;
}, 1000).then(function () {
/* PEP support is prerequisite for OMEMO */
const stanza = $iq({
'type': 'result',
'from': 'dummy@localhost',
'to': 'dummy@localhost/resource',
'id': disco_info_iq.nodeTree.getAttribute('id'),
}).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
.c('identity', {
'category': 'pubsub',
'type': 'pep'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
return test_utils.waitUntil(() => {
return _.filter(
_converse.connection.IQ_stanzas,
(iq) => {
const node = iq.nodeTree.querySelector('iq[to="'+_converse.bare_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
if (node) {
devicelist_iq = iq;
}
return node;
}).length;
});
}).then(function () {
expect(devicelist_iq.toLocaleString()).toBe(
"<iq type='get' from='dummy@localhost' to='dummy@localhost' xmlns='jabber:client' id='"+devicelist_iq.nodeTree.getAttribute('id')+"'>"+
"<query xmlns='http://jabber.org/protocol/disco#items' "+
"node='eu.siacs.conversations.axolotl.devicelist'/>"+
"</iq>");
const stanza = $iq({
'from': contact_jid,
'id': devicelist_iq.nodeTree.getAttribute('id'),
'to': _converse.bare_jid,
'type': 'result',
}).c('query', {
'xmlns': 'http://jabber.org/protocol/disco#items',
'node': 'eu.siacs.conversations.axolotl.devicelist'
}).c('device', {'id': '482886413b977930064a5888b92134fe'}).up()
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.devicelists.length).toBe(1);
const devicelist = _converse.devicelists.get(_converse.bare_jid);
expect(devicelist.devices.length).toBe(1);
expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
test_utils.openChatBoxFor(_converse, contact_jid);
return test_utils.waitUntil(() => {
return _.filter(
_converse.connection.IQ_stanzas,
(iq) => {
const node = iq.nodeTree.querySelector('iq[to="'+contact_jid+'"] query[node="eu.siacs.conversations.axolotl.devicelist"]');
if (node) {
devicelist_iq = iq;
}
return node;
}).length;});
}).then(function () {
expect(devicelist_iq.toLocaleString()).toBe(
"<iq type='get' from='dummy@localhost' to='"+contact_jid+"' xmlns='jabber:client' id='"+devicelist_iq.nodeTree.getAttribute('id')+"'>"+
"<query xmlns='http://jabber.org/protocol/disco#items' "+
"node='eu.siacs.conversations.axolotl.devicelist'/>"+
"</iq>");
const stanza = $iq({
'from': contact_jid,
'id': devicelist_iq.nodeTree.getAttribute('id'),
'to': _converse.bare_jid,
'type': 'result',
}).c('query', {
'xmlns': 'http://jabber.org/protocol/disco#items',
'node': 'eu.siacs.conversations.axolotl.devicelist'
}).c('device', {'id': '368866411b877c30064a5f62b917cffe'}).up()
.c('device', {'id': '3300659945416e274474e469a1f0154c'}).up()
.c('device', {'id': '4e30f35051b7b8b42abe083742187228'}).up()
.c('device', {'id': 'ae890ac52d0df67ed7cfdf51b644e901'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.devicelists.length).toBe(2);
const devicelist = _converse.devicelists.get(contact_jid);
expect(devicelist.devices.length).toBe(4);
expect(devicelist.devices.at(0).get('id')).toBe('368866411b877c30064a5f62b917cffe');
expect(devicelist.devices.at(1).get('id')).toBe('3300659945416e274474e469a1f0154c');
expect(devicelist.devices.at(2).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
expect(devicelist.devices.at(3).get('id')).toBe('ae890ac52d0df67ed7cfdf51b644e901');
return test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid).el.querySelector('.chat-toolbar'));
}).then(function () {
const view = _converse.chatboxviews.get(contact_jid);
const toolbar = view.el.querySelector('.chat-toolbar');
expect(_.isNull(toolbar.querySelector('.toggle-omemo'))).toBe(false);
spyOn(view, 'toggleOMEMO').and.callThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
toolbar.querySelector('.toggle-omemo').click();
expect(view.toggleOMEMO).toHaveBeenCalled();
done();
});
}));
});
describe("A chatbox with an active OMEMO session", function() {
it("will not show the spoiler toolbar button",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
// TODO
done()
}));
});
}));

View File

@ -131,6 +131,8 @@
_converse.OPENED = 'opened';
_converse.PREBIND = "prebind";
_converse.IQ_TIMEOUT = 20000;
_converse.CONNECTION_STATUS = {
0: 'ERROR',
1: 'CONNECTING',

View File

@ -7,12 +7,11 @@
(function (root, factory) {
define([
"converse-core",
"tpl!toolbar_omemo",
"libsignal"
"tpl!toolbar_omemo"
], factory);
}(this, function (converse, tpl_toolbar_omemo, libsignal) {
}(this, function (converse, tpl_toolbar_omemo) {
const { Backbone, Promise, Strophe, sizzle, $build, _, b64_sha1 } = converse.env;
const { Backbone, Promise, Strophe, sizzle, $iq, _, b64_sha1 } = converse.env;
Strophe.addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
Strophe.addNamespace('OMEMO_DEVICELIST', Strophe.NS.OMEMO+".devicelist");
@ -23,11 +22,16 @@
const TRUSTED = 1;
const UNTRUSTED = -1;
function getDevicesForContact (_converse, jid) {
return new Promise((resolve, reject) => {
_converse.api.waitUntil('OMEMOInitialized').then(() => {
const devicelist = _converse.devicelists.get(jid);
resolve(devicelist ? devicelist.devices : []);
let devicelist = _converse.devicelists.get(jid);
if (_.isNil(devicelist)) {
devicelist = _converse.devicelists.create({'jid': jid});
}
devicelist.fetchDevices().then(() => resolve(devicelist.devices));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
}
@ -55,6 +59,14 @@
overrides: {
ChatBoxView: {
events: {
'click .toggle-omemo': 'toggleOMEMO',
},
toggleOMEMO (ev) {
// TODO:
ev.preventDefault();
},
addOMEMOToolbarButton (options) {
const { _converse } = this.__super__,
@ -65,13 +77,12 @@
]).then((support) => {
const client_supports = support[0],
server_supports = support[1];
if (client_supports && server_supports) {
this.el.querySelector('.chat-toolbar').insertAdjacentHTML(
'beforeend',
tpl_toolbar_omemo({'__': __}));
}
}, _.partial(_converse.log, _, Strophe.LogLevel.ERROR));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
},
renderToolbar (toolbar, options) {
@ -92,7 +103,6 @@
_converse.OMEMOSession = Backbone.Model.extend({
initialize () {
this.keyhelper = window.libsignal.KeyHelper;
},
@ -159,10 +169,28 @@
},
fetchDevicesFromServer () {
// TODO: send IQ stanza to get device list.
return Promise.resolve([]);
return new Promise((resolve, reject) => {
const stanza = $iq({
'type': 'get',
'from': _converse.bare_jid,
'to': this.get('jid')
}).c('query', {
'xmlns': Strophe.NS.DISCO_ITEMS,
'node': Strophe.NS.OMEMO_DEVICELIST
});
_converse.connection.sendIQ(
stanza,
(iq) => {
_.forEach(
iq.querySelectorAll('device'),
(dev) => this.devices.create({'id': dev.getAttribute('id')})
);
resolve();
},
reject,
_converse.IQ_TIMEOUT);
});
}
});
_converse.DeviceLists = Backbone.Collection.extend({

View File

@ -21,6 +21,7 @@ if (typeof define !== 'undefined') {
"converse-muc-views",
"converse-muc-views", // Views related to MUC
"converse-notification", // HTML5 Notifications
"converse-omemo",
"converse-ping", // XEP-0199 XMPP Ping
"converse-roster",
"converse-register", // XEP-0077 In-band registration

View File

@ -1 +1 @@
<li class="toggle-omemo fa fa-unlock" title="{{{__('Messages are being sent in plaintext')}}}"></li>
<li class="toggle-omemo fa fa-unlock" title="{{{o.__('Messages are being sent in plaintext')}}}"></li>