瀏覽代碼

Use async/await instead of promise callbacks

JC Brand 6 年之前
父節點
當前提交
439d13d311
共有 1 個文件被更改,包括 746 次插入818 次删除
  1. 746 818
      spec/omemo.js

+ 746 - 818
spec/omemo.js

@@ -37,81 +37,71 @@
         ).pop();
     }
 
-    function initializedOMEMO (_converse) {
-        return test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
-        .then(iq_stanza => {
-            const stanza = $iq({
-                'from': _converse.bare_jid,
-                'id': iq_stanza.nodeTree.getAttribute('id'),
-                'to': _converse.bare_jid,
-                'type': 'result',
-            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                            .c('device', {'id': '482886413b977930064a5888b92134fe'});
-            _converse.connection._dataRecv(test_utils.createRequest(stanza));
-            return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse))
-        }).then(iq_stanza => {
-            const stanza = $iq({
-                'from': _converse.bare_jid,
-                'id': iq_stanza.nodeTree.getAttribute('id'),
-                'to': _converse.bare_jid,
-                'type': 'result'});
-            _converse.connection._dataRecv(test_utils.createRequest(stanza));
-            return test_utils.waitUntil(() => bundleHasBeenPublished(_converse))
-        }).then(iq_stanza => {
-            const stanza = $iq({
-                'from': _converse.bare_jid,
-                'id': iq_stanza.nodeTree.getAttribute('id'),
-                'to': _converse.bare_jid,
-                'type': 'result'});
-            _converse.connection._dataRecv(test_utils.createRequest(stanza));
-            return _converse.api.waitUntil('OMEMOInitialized');
-        });
+    async function initializedOMEMO (_converse) {
+        let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
+        let stanza = $iq({
+            'from': _converse.bare_jid,
+            'id': iq_stanza.nodeTree.getAttribute('id'),
+            'to': _converse.bare_jid,
+            'type': 'result',
+        }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+            .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                    .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                        .c('device', {'id': '482886413b977930064a5888b92134fe'});
+        _converse.connection._dataRecv(test_utils.createRequest(stanza));
+        iq_stanza = await test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse))
+
+        stanza = $iq({
+            'from': _converse.bare_jid,
+            'id': iq_stanza.nodeTree.getAttribute('id'),
+            'to': _converse.bare_jid,
+            'type': 'result'});
+        _converse.connection._dataRecv(test_utils.createRequest(stanza));
+        iq_stanza = await test_utils.waitUntil(() => bundleHasBeenPublished(_converse))
+
+        stanza = $iq({
+            'from': _converse.bare_jid,
+            'id': iq_stanza.nodeTree.getAttribute('id'),
+            'to': _converse.bare_jid,
+            'type': 'result'});
+        _converse.connection._dataRecv(test_utils.createRequest(stanza));
+        await _converse.api.waitUntil('OMEMOInitialized');
     }
 
 
     describe("The OMEMO module", function() {
 
         it("adds methods for encrypting and decrypting messages via AES GCM",
-                mock.initConverseWithPromises(
-                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                    function (done, _converse) {
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                async function (done, _converse) {
 
             const message = 'This message will be encrypted'
-            let view;
             test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-            test_utils.openChatBoxFor(_converse, contact_jid)
-            .then((v) => {
-                view = v;
-                return view.model.encryptMessage(message);
-            }).then(payload => {
-                return view.model.decryptMessage(payload);
-            }).then(result => {
-                expect(result).toBe(message);
-                done();
-            });
+            const view = await test_utils.openChatBoxFor(_converse, contact_jid);
+            const payload = await view.model.encryptMessage(message);
+            const result = await view.model.decryptMessage(payload);
+            expect(result).toBe(message);
+            done();
         }));
 
 
         it("enables encrypted messages to be sent and received",
-                mock.initConverseWithPromises(
-                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                    function (done, _converse) {
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                async function (done, _converse) {
 
-            let view, sent_stanza;
+            let sent_stanza;
             test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-
-            return test_utils.waitUntil(() => initializedOMEMO(_converse))
-            .then(() => test_utils.openChatBoxFor(_converse, contact_jid))
-            .then(() => test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid)))
-            .then(iq_stanza => {
-                const stanza = $iq({
+            await test_utils.waitUntil(() => initializedOMEMO(_converse));
+            await test_utils.openChatBoxFor(_converse, contact_jid);
+            let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid));
+            let stanza = $iq({
                     'from': contact_jid,
                     'id': iq_stanza.nodeTree.getAttribute('id'),
                     'to': _converse.connection.jid,
@@ -121,120 +111,108 @@
                         .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
                             .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
                                 .c('device', {'id': '555'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => _converse.omemo_store);
-            }).then(() => {
-                const devicelist = _converse.devicelists.get({'jid': contact_jid});
-                expect(devicelist.devices.length).toBe(1);
-
-                view = _converse.chatboxviews.get(contact_jid);
-                view.model.set('omemo_active', true);
-
-                const textarea = view.el.querySelector('.chat-textarea');
-                textarea.value = 'This message will be encrypted';
-                view.keyPressed({
-                    target: textarea,
-                    preventDefault: _.noop,
-                    keyCode: 13 // Enter
-                });
-                return test_utils.waitUntil(() => bundleFetched(_converse, contact_jid, '555'));
-            }).then(iq_stanza => {
-                const stanza = $iq({
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => _converse.omemo_store);
+            const devicelist = _converse.devicelists.get({'jid': contact_jid});
+            expect(devicelist.devices.length).toBe(1);
+
+            const view = _converse.chatboxviews.get(contact_jid);
+            view.model.set('omemo_active', true);
+
+            const textarea = view.el.querySelector('.chat-textarea');
+            textarea.value = 'This message will be encrypted';
+            view.keyPressed({
+                target: textarea,
+                preventDefault: _.noop,
+                keyCode: 13 // Enter
+            });
+            iq_stanza = await test_utils.waitUntil(() => bundleFetched(_converse, contact_jid, '555'));
+            stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {
+                'xmlns': 'http://jabber.org/protocol/pubsub'
+                }).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
+                    .c('item')
+                        .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
+                            .c('signedPreKeySignature').t(btoa('2222')).up()
+                            .c('identityKey').t(btoa('3333')).up()
+                            .c('prekeys')
+                                .c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
+                                .c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
+                                .c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            iq_stanza = await test_utils.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {
+                'xmlns': 'http://jabber.org/protocol/pubsub'
+                }).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:482886413b977930064a5888b92134fe"})
+                    .c('item')
+                        .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('100000')).up()
+                            .c('signedPreKeySignature').t(btoa('200000')).up()
+                            .c('identityKey').t(btoa('300000')).up()
+                            .c('prekeys')
+                                .c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
+                                .c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
+                                .c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
+
+            spyOn(_converse.connection, 'send').and.callFake(stanza => { sent_stanza = stanza });
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => sent_stanza);
+            expect(sent_stanza.toLocaleString()).toBe(
+                `<message from="dummy@localhost/resource" id="${sent_stanza.nodeTree.getAttribute("id")}" `+
+                            `to="max.frankfurter@localhost" `+
+                            `type="chat" xmlns="jabber:client">`+
+                    `<body>This is an OMEMO encrypted message which your client doesn’t seem to support. Find more information on https://conversations.im/omemo</body>`+
+                    `<encrypted xmlns="eu.siacs.conversations.axolotl">`+
+                        `<header sid="123456789">`+
+                            `<key rid="482886413b977930064a5888b92134fe">YzFwaDNSNzNYNw==</key>`+
+                            `<key rid="555">YzFwaDNSNzNYNw==</key>`+
+                            `<iv>${sent_stanza.nodeTree.querySelector("iv").textContent}</iv>`+
+                        `</header>`+
+                        `<payload>${sent_stanza.nodeTree.querySelector("payload").textContent}</payload>`+
+                    `</encrypted>`+
+                    `<store xmlns="urn:xmpp:hints"/>`+
+                `</message>`);
+            // Test reception of an encrypted message
+            const obj = await view.model.encryptMessage('This is an encrypted message from the contact')
+            // XXX: Normally the key will be encrypted via libsignal.
+            // However, we're mocking libsignal in the tests, so we include
+            // it as plaintext in the message.
+            stanza = $msg({
                     'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {
-                    'xmlns': 'http://jabber.org/protocol/pubsub'
-                    }).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
-                        .c('item')
-                            .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
-                                .c('signedPreKeySignature').t(btoa('2222')).up()
-                                .c('identityKey').t(btoa('3333')).up()
-                                .c('prekeys')
-                                    .c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
-                                    .c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
-                                    .c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                return test_utils.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
-            }).then(iq_stanza => {
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {
-                    'xmlns': 'http://jabber.org/protocol/pubsub'
-                    }).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:482886413b977930064a5888b92134fe"})
-                        .c('item')
-                            .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('100000')).up()
-                                .c('signedPreKeySignature').t(btoa('200000')).up()
-                                .c('identityKey').t(btoa('300000')).up()
-                                .c('prekeys')
-                                    .c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
-                                    .c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
-                                    .c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
-
-                spyOn(_converse.connection, 'send').and.callFake(stanza => { sent_stanza = stanza });
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => sent_stanza);
-            }).then(() => {
-                expect(sent_stanza.toLocaleString()).toBe(
-                    `<message from="dummy@localhost/resource" id="${sent_stanza.nodeTree.getAttribute("id")}" `+
-                             `to="max.frankfurter@localhost" `+
-                             `type="chat" xmlns="jabber:client">`+
-                        `<body>This is an OMEMO encrypted message which your client doesn’t seem to support. Find more information on https://conversations.im/omemo</body>`+
-                        `<encrypted xmlns="eu.siacs.conversations.axolotl">`+
-                            `<header sid="123456789">`+
-                                `<key rid="482886413b977930064a5888b92134fe">YzFwaDNSNzNYNw==</key>`+
-                                `<key rid="555">YzFwaDNSNzNYNw==</key>`+
-                                `<iv>${sent_stanza.nodeTree.querySelector("iv").textContent}</iv>`+
-                            `</header>`+
-                            `<payload>${sent_stanza.nodeTree.querySelector("payload").textContent}</payload>`+
-                        `</encrypted>`+
-                        `<store xmlns="urn:xmpp:hints"/>`+
-                    `</message>`);
-
-                // Test reception of an encrypted message
-                return view.model.encryptMessage('This is an encrypted message from the contact')
-            }).then(obj => {
-                // XXX: Normally the key will be encrypted via libsignal.
-                // However, we're mocking libsignal in the tests, so we include
-                // it as plaintext in the message.
-
-                // u.stringToArrayBuffer(atob(u.arrayBufferToBase64(obj.key_and_tag)));
-                // u.stringToArrayBuffer(u.arrayBufferToString(obj.key_and_tag));
-                const stanza = $msg({
-                        'from': contact_jid,
-                        'to': _converse.connection.jid,
-                        'type': 'chat',
-                        'id': 'qwerty'
-                    }).c('body').t('This is a fallback message').up()
-                        .c('encrypted', {'xmlns': Strophe.NS.OMEMO})
-                            .c('header', {'sid':  '555'})
-                                .c('key', {'rid':  _converse.omemo_store.get('device_id')}).t(u.arrayBufferToBase64(obj.key_and_tag)).up()
-                                .c('iv').t(obj.iv)
-                                .up().up()
-                            .c('payload').t(obj.payload);
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => view.model.messages.length > 1);
-            }).then(() => {
-                expect(view.model.messages.length).toBe(2);
-                const last_msg = view.model.messages.at(1);
-                expect(view.el.querySelectorAll('.chat-msg__body')[1].textContent.trim())
-                    .toBe('This is an encrypted message from the contact');
-                done();
-            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
+                    'to': _converse.connection.jid,
+                    'type': 'chat',
+                    'id': 'qwerty'
+                }).c('body').t('This is a fallback message').up()
+                    .c('encrypted', {'xmlns': Strophe.NS.OMEMO})
+                        .c('header', {'sid':  '555'})
+                            .c('key', {'rid':  _converse.omemo_store.get('device_id')}).t(u.arrayBufferToBase64(obj.key_and_tag)).up()
+                            .c('iv').t(obj.iv)
+                            .up().up()
+                        .c('payload').t(obj.payload);
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => view.model.messages.length > 1);
+            expect(view.model.messages.length).toBe(2);
+            const last_msg = view.model.messages.at(1);
+            expect(view.el.querySelectorAll('.chat-msg__body')[1].textContent.trim())
+                .toBe('This is an encrypted message from the contact');
+            done();
         }));
 
 
         it("can receive a PreKeySignalMessage",
-                mock.initConverseWithPromises(
-                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                    function (done, _converse) {
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                async function (done, _converse) {
 
             _converse.NUM_PREKEYS = 5; // Restrict to 5, otherwise the resulting stanza is too large to easily test
             let view, sent_stanza;
@@ -242,717 +220,667 @@
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
-            return test_utils.waitUntil(() => initializedOMEMO(_converse))
-            .then(() => _converse.ChatBox.prototype.encryptMessage('This is an encrypted message from the contact'))
-            .then(obj => {
-                // XXX: Normally the key will be encrypted via libsignal.
-                // However, we're mocking libsignal in the tests, so we include
-                // it as plaintext in the message.
-                const stanza = $msg({
-                        'from': contact_jid,
-                        'to': _converse.connection.jid,
-                        'type': 'chat',
-                        'id': 'qwerty'
-                    }).c('body').t('This is a fallback message').up()
-                        .c('encrypted', {'xmlns': Strophe.NS.OMEMO})
-                            .c('header', {'sid':  '555'})
-                                .c('key', {
-                                    'prekey': 'true',
-                                    'rid':  _converse.omemo_store.get('device_id')
-                                }).t(u.arrayBufferToBase64(obj.key_and_tag)).up()
-                                .c('iv').t(obj.iv)
-                                .up().up()
-                            .c('payload').t(obj.payload);
-
-                const generateMissingPreKeys = _converse.omemo_store.generateMissingPreKeys;
-                spyOn(_converse.omemo_store, 'generateMissingPreKeys').and.callFake(() => {
-                    // Since it's difficult to override
-                    // decryptPreKeyWhisperMessage, where a prekey will be
-                    // removed from the store, we do it here, before the
-                    // missing prekeys are generated.
-                    _converse.omemo_store.removePreKey(1);
-                    return generateMissingPreKeys.apply(_converse.omemo_store, arguments);
-                });
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid))
-            }).then(iq_stanza => deviceListFetched(_converse, contact_jid))
-              .then(iq_stanza => {
-                const stanza = $iq({
+            await test_utils.waitUntil(() => initializedOMEMO(_converse));
+            const obj = await _converse.ChatBox.prototype.encryptMessage('This is an encrypted message from the contact');
+            // XXX: Normally the key will be encrypted via libsignal.
+            // However, we're mocking libsignal in the tests, so we include
+            // it as plaintext in the message.
+            let stanza = $msg({
                     'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
                     'to': _converse.connection.jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .c('device', {'id': '555'});
+                    'type': 'chat',
+                    'id': 'qwerty'
+                }).c('body').t('This is a fallback message').up()
+                    .c('encrypted', {'xmlns': Strophe.NS.OMEMO})
+                        .c('header', {'sid':  '555'})
+                            .c('key', {
+                                'prekey': 'true',
+                                'rid':  _converse.omemo_store.get('device_id')
+                            }).t(u.arrayBufferToBase64(obj.key_and_tag)).up()
+                            .c('iv').t(obj.iv)
+                            .up().up()
+                        .c('payload').t(obj.payload);
+
+            const generateMissingPreKeys = _converse.omemo_store.generateMissingPreKeys;
+            spyOn(_converse.omemo_store, 'generateMissingPreKeys').and.callFake(() => {
+                // Since it's difficult to override
+                // decryptPreKeyWhisperMessage, where a prekey will be
+                // removed from the store, we do it here, before the
+                // missing prekeys are generated.
+                _converse.omemo_store.removePreKey(1);
+                return generateMissingPreKeys.apply(_converse.omemo_store, arguments);
+            });
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            let iq_stanza = await test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid));
+            iq_stanza = await deviceListFetched(_converse, contact_jid);
+            stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.connection.jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .c('device', {'id': '555'});
 
-                // XXX: the bundle gets published twice, we want to make sure
-                // that we wait for the 2nd, so we clear all the already sent
-                // stanzas.
-                _converse.connection.IQ_stanzas = [];
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => _converse.omemo_store);
-            }).then(() => test_utils.waitUntil(() => bundleHasBeenPublished(_converse)))
-            .then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" type="set" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
-                                `<item>`+
-                                    `<bundle xmlns="eu.siacs.conversations.axolotl">`+
-                                        `<signedPreKeyPublic signedPreKeyId="0">${btoa("1234")}</signedPreKeyPublic>`+
-                                            `<signedPreKeySignature>${btoa("11112222333344445555")}</signedPreKeySignature>`+
-                                            `<identityKey>${btoa("1234")}</identityKey>`+
-                                        `<prekeys>`+
-                                            `<preKeyPublic preKeyId="0">${btoa("1234")}</preKeyPublic>`+
-                                            `<preKeyPublic preKeyId="1">${btoa("1234")}</preKeyPublic>`+
-                                            `<preKeyPublic preKeyId="2">${btoa("1234")}</preKeyPublic>`+
-                                            `<preKeyPublic preKeyId="3">${btoa("1234")}</preKeyPublic>`+
-                                            `<preKeyPublic preKeyId="4">${btoa("1234")}</preKeyPublic>`+
-                                        `</prekeys>`+
-                                    `</bundle>`+
-                                `</item>`+
-                            `</publish>`+
-                        `</pubsub>`+
-                    `</iq>`)
-                const own_device = _converse.devicelists.get(_converse.bare_jid).devices.get(_converse.omemo_store.get('device_id'));
-                expect(own_device.get('bundle').prekeys.length).toBe(5);
-                expect(_converse.omemo_store.generateMissingPreKeys).toHaveBeenCalled();
-                done();
-            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
+            // XXX: the bundle gets published twice, we want to make sure
+            // that we wait for the 2nd, so we clear all the already sent
+            // stanzas.
+            _converse.connection.IQ_stanzas = [];
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => _converse.omemo_store);
+            iq_stanza = await test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" type="set" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
+                            `<item>`+
+                                `<bundle xmlns="eu.siacs.conversations.axolotl">`+
+                                    `<signedPreKeyPublic signedPreKeyId="0">${btoa("1234")}</signedPreKeyPublic>`+
+                                        `<signedPreKeySignature>${btoa("11112222333344445555")}</signedPreKeySignature>`+
+                                        `<identityKey>${btoa("1234")}</identityKey>`+
+                                    `<prekeys>`+
+                                        `<preKeyPublic preKeyId="0">${btoa("1234")}</preKeyPublic>`+
+                                        `<preKeyPublic preKeyId="1">${btoa("1234")}</preKeyPublic>`+
+                                        `<preKeyPublic preKeyId="2">${btoa("1234")}</preKeyPublic>`+
+                                        `<preKeyPublic preKeyId="3">${btoa("1234")}</preKeyPublic>`+
+                                        `<preKeyPublic preKeyId="4">${btoa("1234")}</preKeyPublic>`+
+                                    `</prekeys>`+
+                                `</bundle>`+
+                            `</item>`+
+                        `</publish>`+
+                    `</pubsub>`+
+                `</iq>`)
+            const own_device = _converse.devicelists.get(_converse.bare_jid).devices.get(_converse.omemo_store.get('device_id'));
+            expect(own_device.get('bundle').prekeys.length).toBe(5);
+            expect(_converse.omemo_store.generateMissingPreKeys).toHaveBeenCalled();
+            done();
         }));
 
 
         it("updates device lists based on PEP messages",
             mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched'], {'allow_non_roster_messaging': true},
-                function (done, _converse) {
+                async function (done, _converse) {
 
             test_utils.createContacts(_converse, 'current', 1);
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
             // Wait until own devices are fetched
-            test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
-            .then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="dummy@localhost" type="get" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
-                        `</pubsub>`+
-                    `</iq>`);
-
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .c('device', {'id': '555'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => _converse.omemo_store);
-            }).then(() => {
-                expect(_converse.chatboxes.length).toBe(1);
-                expect(_converse.devicelists.length).toBe(1);
-                const devicelist = _converse.devicelists.get(_converse.bare_jid);
-                expect(devicelist.devices.length).toBe(2);
-                expect(devicelist.devices.at(0).get('id')).toBe('555');
-                expect(devicelist.devices.at(1).get('id')).toBe('123456789');
-                return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return _converse.api.waitUntil('OMEMOInitialized');
-            }).then(() => {
-                let stanza = $msg({
-                    'from': contact_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_01',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
-                        .c('item')
-                            .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('device', {'id': '1234'})
-                                .c('device', {'id': '4223'})
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                expect(_converse.devicelists.length).toBe(2);
-                let devices = _converse.devicelists.get(contact_jid).devices;
-                expect(devices.length).toBe(2);
-                expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('1234,4223');
-
-                stanza = $msg({
-                    'from': contact_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_02',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
-                        .c('item')
-                            .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('device', {'id': '4223'})
-                                .c('device', {'id': '4224'})
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                expect(_converse.devicelists.length).toBe(2);
-                expect(devices.length).toBe(2);
-                expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('4223,4224');
-
-                // Check that own devicelist gets updated
-                stanza = $msg({
-                    'from': _converse.bare_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_03',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
-                        .c('item')
-                            .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('device', {'id': '123456789'})
-                                .c('device', {'id': '555'})
-                                .c('device', {'id': '777'})
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                expect(_converse.devicelists.length).toBe(2);
-                devices = _converse.devicelists.get(_converse.bare_jid).devices;
-                expect(devices.length).toBe(3);
-                expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,555,777');
-
-                _converse.connection.IQ_stanzas = [];
-
-                // Check that own device gets re-added
-                stanza = $msg({
-                    'from': _converse.bare_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_04',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
-                        .c('item')
-                            .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('device', {'id': '444'})
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                // Check that our own device is added again, but that removed
-                // devices are not added.
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute(`id`)}" type="set" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<publish node="eu.siacs.conversations.axolotl.devicelist">`+
-                                `<item>`+
-                                    `<list xmlns="eu.siacs.conversations.axolotl">`+
-                                        `<device id="123456789"/>`+
-                                        `<device id="444"/>`+
-                                    `</list>`+
-                                `</item>`+
-                            `</publish>`+
-                        `</pubsub>`+
-                    `</iq>`);
-                expect(_converse.devicelists.length).toBe(2);
-                const devices = _converse.devicelists.get(_converse.bare_jid).devices;
-                // The device id for this device (123456789) was also generated and added to the list,
-                // which is why we have 2 devices now.
-                expect(devices.length).toBe(2);
-                expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,444');
-                done();
-            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
+            let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="dummy@localhost" type="get" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
+                    `</pubsub>`+
+                `</iq>`);
+
+            let stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .c('device', {'id': '555'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => _converse.omemo_store);
+            expect(_converse.chatboxes.length).toBe(1);
+            expect(_converse.devicelists.length).toBe(1);
+            const devicelist = _converse.devicelists.get(_converse.bare_jid);
+            expect(devicelist.devices.length).toBe(2);
+            expect(devicelist.devices.at(0).get('id')).toBe('555');
+            expect(devicelist.devices.at(1).get('id')).toBe('123456789');
+            iq_stanza = await test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            iq_stanza = await test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
+
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await _converse.api.waitUntil('OMEMOInitialized');
+
+            stanza = $msg({
+                'from': contact_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_01',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
+                    .c('item')
+                        .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('device', {'id': '1234'})
+                            .c('device', {'id': '4223'})
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            expect(_converse.devicelists.length).toBe(2);
+            let devices = _converse.devicelists.get(contact_jid).devices;
+            expect(devices.length).toBe(2);
+            expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('1234,4223');
+
+            stanza = $msg({
+                'from': contact_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_02',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
+                    .c('item')
+                        .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('device', {'id': '4223'})
+                            .c('device', {'id': '4224'})
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            expect(_converse.devicelists.length).toBe(2);
+            expect(devices.length).toBe(2);
+            expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('4223,4224');
+
+            // Check that own devicelist gets updated
+            stanza = $msg({
+                'from': _converse.bare_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_03',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
+                    .c('item')
+                        .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('device', {'id': '123456789'})
+                            .c('device', {'id': '555'})
+                            .c('device', {'id': '777'})
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            expect(_converse.devicelists.length).toBe(2);
+            devices = _converse.devicelists.get(_converse.bare_jid).devices;
+            expect(devices.length).toBe(3);
+            expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,555,777');
+
+            _converse.connection.IQ_stanzas = [];
+
+            // Check that own device gets re-added
+            stanza = $msg({
+                'from': _converse.bare_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_04',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
+                    .c('item')
+                        .c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('device', {'id': '444'})
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            iq_stanza = await test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
+            // Check that our own device is added again, but that removed
+            // devices are not added.
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute(`id`)}" type="set" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<publish node="eu.siacs.conversations.axolotl.devicelist">`+
+                            `<item>`+
+                                `<list xmlns="eu.siacs.conversations.axolotl">`+
+                                    `<device id="123456789"/>`+
+                                    `<device id="444"/>`+
+                                `</list>`+
+                            `</item>`+
+                        `</publish>`+
+                    `</pubsub>`+
+                `</iq>`);
+            expect(_converse.devicelists.length).toBe(2);
+            devices = _converse.devicelists.get(_converse.bare_jid).devices;
+            // The device id for this device (123456789) was also generated and added to the list,
+            // which is why we have 2 devices now.
+            expect(devices.length).toBe(2);
+            expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,444');
+            done();
         }));
 
 
         it("updates device bundles based on PEP messages",
             mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched'], {},
-                function (done, _converse) {
+                async function (done, _converse) {
 
             test_utils.createContacts(_converse, 'current');
             const contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
+            let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="dummy@localhost" type="get" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
+                    `</pubsub>`+
+                `</iq>`);
+
+            let stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .c('device', {'id': '555'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await await test_utils.waitUntil(() => _converse.omemo_store);
+            expect(_converse.devicelists.length).toBe(1);
+            let devicelist = _converse.devicelists.get(_converse.bare_jid);
+            expect(devicelist.devices.length).toBe(2);
+            expect(devicelist.devices.at(0).get('id')).toBe('555');
+            expect(devicelist.devices.at(1).get('id')).toBe('123456789');
+            iq_stanza = await test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            iq_stanza = await test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await _converse.api.waitUntil('OMEMOInitialized');
+            stanza = $msg({
+                'from': contact_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_01',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
+                    .c('item')
+                        .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('1111').up()
+                            .c('signedPreKeySignature').t('2222').up()
+                            .c('identityKey').t('3333').up()
+                            .c('prekeys')
+                                .c('preKeyPublic', {'preKeyId': '1001'}).up()
+                                .c('preKeyPublic', {'preKeyId': '1002'}).up()
+                                .c('preKeyPublic', {'preKeyId': '1003'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
 
-            test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
-            .then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="dummy@localhost" type="get" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
-                        `</pubsub>`+
-                    `</iq>`);
+            expect(_converse.devicelists.length).toBe(2);
+            devicelist = _converse.devicelists.get(contact_jid);
+            expect(devicelist.devices.length).toBe(1);
+            let device = devicelist.devices.at(0);
+            expect(device.get('bundle').identity_key).toBe('3333');
+            expect(device.get('bundle').signed_prekey.public_key).toBe('1111');
+            expect(device.get('bundle').signed_prekey.id).toBe(4223);
+            expect(device.get('bundle').signed_prekey.signature).toBe('2222');
+            expect(device.get('bundle').prekeys.length).toBe(3);
+            expect(device.get('bundle').prekeys[0].id).toBe(1001);
+            expect(device.get('bundle').prekeys[1].id).toBe(1002);
+            expect(device.get('bundle').prekeys[2].id).toBe(1003);
+
+            stanza = $msg({
+                'from': contact_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_02',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
+                    .c('item')
+                        .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('5555').up()
+                            .c('signedPreKeySignature').t('6666').up()
+                            .c('identityKey').t('7777').up()
+                            .c('prekeys')
+                                .c('preKeyPublic', {'preKeyId': '2001'}).up()
+                                .c('preKeyPublic', {'preKeyId': '2002'}).up()
+                                .c('preKeyPublic', {'preKeyId': '2003'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
 
-                const stanza = $iq({
-                    'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .c('device', {'id': '555'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => _converse.omemo_store);
-            }).then(() => {
-                expect(_converse.devicelists.length).toBe(1);
-                const devicelist = _converse.devicelists.get(_converse.bare_jid);
-                expect(devicelist.devices.length).toBe(2);
-                expect(devicelist.devices.at(0).get('id')).toBe('555');
-                expect(devicelist.devices.at(1).get('id')).toBe('123456789');
-                return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return _converse.api.waitUntil('OMEMOInitialized');
-            }).then(() => {
-                let stanza = $msg({
-                    'from': contact_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_01',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
-                        .c('item')
-                            .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('1111').up()
-                                .c('signedPreKeySignature').t('2222').up()
-                                .c('identityKey').t('3333').up()
-                                .c('prekeys')
-                                    .c('preKeyPublic', {'preKeyId': '1001'}).up()
-                                    .c('preKeyPublic', {'preKeyId': '1002'}).up()
-                                    .c('preKeyPublic', {'preKeyId': '1003'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                expect(_converse.devicelists.length).toBe(2);
-                let devicelist = _converse.devicelists.get(contact_jid);
-                expect(devicelist.devices.length).toBe(1);
-                let device = devicelist.devices.at(0);
-                expect(device.get('bundle').identity_key).toBe('3333');
-                expect(device.get('bundle').signed_prekey.public_key).toBe('1111');
-                expect(device.get('bundle').signed_prekey.id).toBe(4223);
-                expect(device.get('bundle').signed_prekey.signature).toBe('2222');
-                expect(device.get('bundle').prekeys.length).toBe(3);
-                expect(device.get('bundle').prekeys[0].id).toBe(1001);
-                expect(device.get('bundle').prekeys[1].id).toBe(1002);
-                expect(device.get('bundle').prekeys[2].id).toBe(1003);
-
-                stanza = $msg({
-                    'from': contact_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_02',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
-                        .c('item')
-                            .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('5555').up()
-                                .c('signedPreKeySignature').t('6666').up()
-                                .c('identityKey').t('7777').up()
-                                .c('prekeys')
-                                    .c('preKeyPublic', {'preKeyId': '2001'}).up()
-                                    .c('preKeyPublic', {'preKeyId': '2002'}).up()
-                                    .c('preKeyPublic', {'preKeyId': '2003'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                expect(_converse.devicelists.length).toBe(2);
-                devicelist = _converse.devicelists.get(contact_jid);
-                expect(devicelist.devices.length).toBe(1);
-                device = devicelist.devices.at(0);
-                expect(device.get('bundle').identity_key).toBe('7777');
-                expect(device.get('bundle').signed_prekey.public_key).toBe('5555');
-                expect(device.get('bundle').signed_prekey.id).toBe(4223);
-                expect(device.get('bundle').signed_prekey.signature).toBe('6666');
-                expect(device.get('bundle').prekeys.length).toBe(3);
-                expect(device.get('bundle').prekeys[0].id).toBe(2001);
-                expect(device.get('bundle').prekeys[1].id).toBe(2002);
-                expect(device.get('bundle').prekeys[2].id).toBe(2003);
-
-                stanza = $msg({
-                    'from': _converse.bare_jid,
-                    'to': _converse.bare_jid,
-                    'type': 'headline',
-                    'id': 'update_03',
-                }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
-                    .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:123456789'})
-                        .c('item')
-                            .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('signedPreKeyPublic', {'signedPreKeyId': '9999'}).t('8888').up()
-                                .c('signedPreKeySignature').t('3333').up()
-                                .c('identityKey').t('1111').up()
-                                .c('prekeys')
-                                    .c('preKeyPublic', {'preKeyId': '3001'}).up()
-                                    .c('preKeyPublic', {'preKeyId': '3002'}).up()
-                                    .c('preKeyPublic', {'preKeyId': '3003'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                expect(_converse.devicelists.length).toBe(2);
-                devicelist = _converse.devicelists.get(_converse.bare_jid);
-                expect(devicelist.devices.length).toBe(2);
-                expect(devicelist.devices.at(0).get('id')).toBe('555');
-                expect(devicelist.devices.at(1).get('id')).toBe('123456789');
-                device = devicelist.devices.at(1);
-                expect(device.get('bundle').identity_key).toBe('1111');
-                expect(device.get('bundle').signed_prekey.public_key).toBe('8888');
-                expect(device.get('bundle').signed_prekey.id).toBe(9999);
-                expect(device.get('bundle').signed_prekey.signature).toBe('3333');
-                expect(device.get('bundle').prekeys.length).toBe(3);
-                expect(device.get('bundle').prekeys[0].id).toBe(3001);
-                expect(device.get('bundle').prekeys[1].id).toBe(3002);
-                expect(device.get('bundle').prekeys[2].id).toBe(3003);
-                done();
-            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
+            expect(_converse.devicelists.length).toBe(2);
+            devicelist = _converse.devicelists.get(contact_jid);
+            expect(devicelist.devices.length).toBe(1);
+            device = devicelist.devices.at(0);
+            expect(device.get('bundle').identity_key).toBe('7777');
+            expect(device.get('bundle').signed_prekey.public_key).toBe('5555');
+            expect(device.get('bundle').signed_prekey.id).toBe(4223);
+            expect(device.get('bundle').signed_prekey.signature).toBe('6666');
+            expect(device.get('bundle').prekeys.length).toBe(3);
+            expect(device.get('bundle').prekeys[0].id).toBe(2001);
+            expect(device.get('bundle').prekeys[1].id).toBe(2002);
+            expect(device.get('bundle').prekeys[2].id).toBe(2003);
+
+            stanza = $msg({
+                'from': _converse.bare_jid,
+                'to': _converse.bare_jid,
+                'type': 'headline',
+                'id': 'update_03',
+            }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
+                .c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:123456789'})
+                    .c('item')
+                        .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('signedPreKeyPublic', {'signedPreKeyId': '9999'}).t('8888').up()
+                            .c('signedPreKeySignature').t('3333').up()
+                            .c('identityKey').t('1111').up()
+                            .c('prekeys')
+                                .c('preKeyPublic', {'preKeyId': '3001'}).up()
+                                .c('preKeyPublic', {'preKeyId': '3002'}).up()
+                                .c('preKeyPublic', {'preKeyId': '3003'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            expect(_converse.devicelists.length).toBe(2);
+            devicelist = _converse.devicelists.get(_converse.bare_jid);
+            expect(devicelist.devices.length).toBe(2);
+            expect(devicelist.devices.at(0).get('id')).toBe('555');
+            expect(devicelist.devices.at(1).get('id')).toBe('123456789');
+            device = devicelist.devices.at(1);
+            expect(device.get('bundle').identity_key).toBe('1111');
+            expect(device.get('bundle').signed_prekey.public_key).toBe('8888');
+            expect(device.get('bundle').signed_prekey.id).toBe(9999);
+            expect(device.get('bundle').signed_prekey.signature).toBe('3333');
+            expect(device.get('bundle').prekeys.length).toBe(3);
+            expect(device.get('bundle').prekeys[0].id).toBe(3001);
+            expect(device.get('bundle').prekeys[1].id).toBe(3002);
+            expect(device.get('bundle').prekeys[2].id).toBe(3003);
+            done();
         }));
 
         it("publishes a bundle with which an encrypted session can be created",
-                mock.initConverseWithPromises(
-                    null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                    function (done, _converse) {
+            mock.initConverseWithPromises(
+                null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
+                async function (done, _converse) {
 
             _converse.NUM_PREKEYS = 2; // Restrict to 2, otherwise the resulting stanza is too large to easily test
 
             test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
+            let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
+            let stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .c('device', {'id': '482886413b977930064a5888b92134fe'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            expect(_converse.devicelists.length).toBe(1);
+            await test_utils.openChatBoxFor(_converse, contact_jid);
+            iq_stanza = await ownDeviceHasBeenPublished(_converse);
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
 
-            test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
-            .then(iq_stanza => {
-                const stanza = $iq({
-                    'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .c('device', {'id': '482886413b977930064a5888b92134fe'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                expect(_converse.devicelists.length).toBe(1);
-                return test_utils.openChatBoxFor(_converse, contact_jid);
-
-            }).then(() => ownDeviceHasBeenPublished(_converse))
-              .then(iq_stanza => {
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" type="set" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
-                                `<item>`+
-                                    `<bundle xmlns="eu.siacs.conversations.axolotl">`+
-                                        `<signedPreKeyPublic signedPreKeyId="0">${btoa("1234")}</signedPreKeyPublic>`+
-                                            `<signedPreKeySignature>${btoa("11112222333344445555")}</signedPreKeySignature>`+
-                                            `<identityKey>${btoa("1234")}</identityKey>`+
-                                        `<prekeys>`+
-                                            `<preKeyPublic preKeyId="0">${btoa("1234")}</preKeyPublic>`+
-                                            `<preKeyPublic preKeyId="1">${btoa("1234")}</preKeyPublic>`+
-                                        `</prekeys>`+
-                                    `</bundle>`+
-                                `</item>`+
-                            `</publish>`+
-                        `</pubsub>`+
-                    `</iq>`)
-
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return _converse.api.waitUntil('OMEMOInitialized');
-            }).then(done).catch(_.partial(console.error, _));
+            iq_stanza = await test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" type="set" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
+                            `<item>`+
+                                `<bundle xmlns="eu.siacs.conversations.axolotl">`+
+                                    `<signedPreKeyPublic signedPreKeyId="0">${btoa("1234")}</signedPreKeyPublic>`+
+                                        `<signedPreKeySignature>${btoa("11112222333344445555")}</signedPreKeySignature>`+
+                                        `<identityKey>${btoa("1234")}</identityKey>`+
+                                    `<prekeys>`+
+                                        `<preKeyPublic preKeyId="0">${btoa("1234")}</preKeyPublic>`+
+                                        `<preKeyPublic preKeyId="1">${btoa("1234")}</preKeyPublic>`+
+                                    `</prekeys>`+
+                                `</bundle>`+
+                            `</item>`+
+                        `</publish>`+
+                    `</pubsub>`+
+                `</iq>`)
+
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await _converse.api.waitUntil('OMEMOInitialized');
+            done();
         }));
 
 
         it("adds a toolbar button for starting an encrypted chat session",
             mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                function (done, _converse) {
+                async function (done, _converse) {
 
-            let modal;
             test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
 
-            test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
-            .then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="dummy@localhost" type="get" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
-                        `</pubsub>`+
-                    `</iq>`);
-
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .c('device', {'id': '482886413b977930064a5888b92134fe'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return test_utils.waitUntil(() => _converse.omemo_store);
-            }).then(() => {
-                expect(_converse.devicelists.length).toBe(1);
-                const devicelist = _converse.devicelists.get(_converse.bare_jid);
-                expect(devicelist.devices.length).toBe(2);
-                expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
-                expect(devicelist.devices.at(1).get('id')).toBe('123456789');
-                // Check that own device was published
-                return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
-            }).then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute(`id`)}" type="set" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<publish node="eu.siacs.conversations.axolotl.devicelist">`+
-                                `<item>`+
-                                    `<list xmlns="eu.siacs.conversations.axolotl">`+
-                                        `<device id="482886413b977930064a5888b92134fe"/>`+
-                                        `<device id="123456789"/>`+
-                                    `</list>`+
-                                `</item>`+
-                            `</publish>`+
+            let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="dummy@localhost" type="get" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
                     `</pubsub>`+
-                    `</iq>`);
+                `</iq>`);
 
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                return test_utils.waitUntil(() => _.get(bundleHasBeenPublished(_converse), 'nodeTree'));
-            }).then(iq_el => {
-                expect(iq_el.getAttributeNames().sort().join()).toBe(["from", "type", "xmlns", "id"].sort().join());
-                expect(iq_el.querySelector('prekeys').childNodes.length).toBe(100);
-
-                const signed_prekeys = iq_el.querySelectorAll('signedPreKeyPublic');
-                expect(signed_prekeys.length).toBe(1);
-                const signed_prekey = signed_prekeys[0];
-                expect(signed_prekey.getAttribute('signedPreKeyId')).toBe('0')
-                expect(iq_el.querySelectorAll('signedPreKeySignature').length).toBe(1);
-                expect(iq_el.querySelectorAll('identityKey').length).toBe(1);
-
-                const stanza = $iq({
-                    'from': _converse.bare_jid,
-                    'id': iq_el.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-                return _converse.api.waitUntil('OMEMOInitialized', 1000);
-            }).then(() => {
-                return test_utils.openChatBoxFor(_converse, contact_jid);
-            }).then(() => {
-                return test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid));
-            }).then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
-                        `</pubsub>`+
-                    `</iq>`);
-
-                const stanza = $iq({
-                    'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .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));
-                const devicelist = _converse.devicelists.get(contact_jid);
-                return test_utils.waitUntil(() => devicelist.devices.length);
-            }).then(() => {
-                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(() => {
-                const view = _converse.chatboxviews.get(contact_jid);
-                const toolbar = view.el.querySelector('.chat-toolbar');
-                expect(view.model.get('omemo_active')).toBe(undefined);
-                const toggle = toolbar.querySelector('.toggle-omemo');
-                expect(_.isNull(toggle)).toBe(false);
-                expect(u.hasClass('fa-unlock', toggle)).toBe(true);
-                expect(u.hasClass('fa-lock', toggle)).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();
-                expect(view.model.get('omemo_active')).toBe(true);
-
-                return test_utils.waitUntil(() => u.hasClass('fa-lock', toolbar.querySelector('.toggle-omemo')));
-            }).then(() => {
-                const view = _converse.chatboxviews.get(contact_jid);
-                const toolbar = view.el.querySelector('.chat-toolbar');
-                const toggle = toolbar.querySelector('.toggle-omemo');
-                expect(u.hasClass('fa-unlock', toggle)).toBe(false);
-                expect(u.hasClass('fa-lock', toggle)).toBe(true);
-
-                const textarea = view.el.querySelector('.chat-textarea');
-                textarea.value = 'This message will be sent encrypted';
-                view.keyPressed({
-                    target: textarea,
-                    preventDefault: _.noop,
-                    keyCode: 13
-                });
-                done();
-            }).catch(_.partial(console.error, _));
+            let stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .c('device', {'id': '482886413b977930064a5888b92134fe'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => _converse.omemo_store);
+            expect(_converse.devicelists.length).toBe(1);
+            let devicelist = _converse.devicelists.get(_converse.bare_jid);
+            expect(devicelist.devices.length).toBe(2);
+            expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
+            expect(devicelist.devices.at(1).get('id')).toBe('123456789');
+            // Check that own device was published
+            iq_stanza = await test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute(`id`)}" type="set" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<publish node="eu.siacs.conversations.axolotl.devicelist">`+
+                            `<item>`+
+                                `<list xmlns="eu.siacs.conversations.axolotl">`+
+                                    `<device id="482886413b977930064a5888b92134fe"/>`+
+                                    `<device id="123456789"/>`+
+                                `</list>`+
+                            `</item>`+
+                        `</publish>`+
+                `</pubsub>`+
+                `</iq>`);
+
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            const iq_el = await test_utils.waitUntil(() => _.get(bundleHasBeenPublished(_converse), 'nodeTree'));
+            expect(iq_el.getAttributeNames().sort().join()).toBe(["from", "type", "xmlns", "id"].sort().join());
+            expect(iq_el.querySelector('prekeys').childNodes.length).toBe(100);
+
+            const signed_prekeys = iq_el.querySelectorAll('signedPreKeyPublic');
+            expect(signed_prekeys.length).toBe(1);
+            const signed_prekey = signed_prekeys[0];
+            expect(signed_prekey.getAttribute('signedPreKeyId')).toBe('0')
+            expect(iq_el.querySelectorAll('signedPreKeySignature').length).toBe(1);
+            expect(iq_el.querySelectorAll('identityKey').length).toBe(1);
+
+            stanza = $iq({
+                'from': _converse.bare_jid,
+                'id': iq_el.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await _converse.api.waitUntil('OMEMOInitialized', 1000);
+            await test_utils.openChatBoxFor(_converse, contact_jid);
+            iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="${contact_jid}" type="get" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<items node="eu.siacs.conversations.axolotl.devicelist"/>`+
+                    `</pubsub>`+
+                `</iq>`);
+
+            stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .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));
+            devicelist = _converse.devicelists.get(contact_jid);
+            await test_utils.waitUntil(() => devicelist.devices.length);
+            expect(_converse.devicelists.length).toBe(2);
+            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');
+            await test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid).el.querySelector('.chat-toolbar'));
+            const view = _converse.chatboxviews.get(contact_jid);
+            const toolbar = view.el.querySelector('.chat-toolbar');
+            expect(view.model.get('omemo_active')).toBe(undefined);
+            let toggle = toolbar.querySelector('.toggle-omemo');
+            expect(_.isNull(toggle)).toBe(false);
+            expect(u.hasClass('fa-unlock', toggle)).toBe(true);
+            expect(u.hasClass('fa-lock', toggle)).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();
+            expect(view.model.get('omemo_active')).toBe(true);
+
+            await test_utils.waitUntil(() => u.hasClass('fa-lock', toolbar.querySelector('.toggle-omemo')));
+            toggle = toolbar.querySelector('.toggle-omemo');
+            expect(u.hasClass('fa-unlock', toggle)).toBe(false);
+            expect(u.hasClass('fa-lock', toggle)).toBe(true);
+
+            const textarea = view.el.querySelector('.chat-textarea');
+            textarea.value = 'This message will be sent encrypted';
+            view.keyPressed({
+                target: textarea,
+                preventDefault: _.noop,
+                keyCode: 13
+            });
+            done();
         }));
 
 
         it("shows OMEMO device fingerprints in the user details modal",
             mock.initConverseWithPromises(
                 null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
-                function (done, _converse) {
+                async function (done, _converse) {
 
-            let modal;
             test_utils.createContacts(_converse, 'current', 1);
             _converse.emit('rosterContactsFetched');
             const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
-            test_utils.openChatBoxFor(_converse, contact_jid)
-            .then(() => {
-                // We simply emit, to avoid doing all the setup work
-                _converse.emit('OMEMOInitialized');
-
-                const view = _converse.chatboxviews.get(contact_jid);
-                const show_modal_button = view.el.querySelector('.show-user-details-modal');
-                show_modal_button.click();
-                modal = view.user_details_modal;
-
-                return test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
-            }).then(() => test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid)))
-            .then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="max.frankfurter@localhost" type="get" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub>`+
-                    `</iq>`);
-
-                const stanza = $iq({
-                    'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
-                    .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
-                        .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
-                            .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
-                                .c('device', {'id': '555'});
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                return test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
-            }).then(() => test_utils.waitUntil(() => bundleFetched(_converse, contact_jid, '555')))
-              .then(iq_stanza => {
-                expect(iq_stanza.toLocaleString()).toBe(
-                    `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="max.frankfurter@localhost" type="get" xmlns="jabber:client">`+
-                        `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
-                            `<items node="eu.siacs.conversations.axolotl.bundles:555"/>`+
-                        `</pubsub>`+
-                    `</iq>`);
-
-                const stanza = $iq({
-                    'from': contact_jid,
-                    'id': iq_stanza.nodeTree.getAttribute('id'),
-                    'to': _converse.bare_jid,
-                    'type': 'result',
-                }).c('pubsub', {
-                    'xmlns': 'http://jabber.org/protocol/pubsub'
-                    }).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
-                        .c('item')
-                            .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
-                                .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
-                                .c('signedPreKeySignature').t(btoa('2222')).up()
-                                .c('identityKey').t('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI').up()
-                                .c('prekeys')
-                                    .c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
-                                    .c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
-                                    .c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
-                _converse.connection._dataRecv(test_utils.createRequest(stanza));
-
-                const view = _converse.chatboxviews.get(contact_jid);
-                const modal = view.user_details_modal;
-                return test_utils.waitUntil(() => modal.el.querySelectorAll('.fingerprints .fingerprint').length);
-            }).then(() => {
-                const view = _converse.chatboxviews.get(contact_jid);
-                const modal = view.user_details_modal;
-                expect(modal.el.querySelectorAll('.fingerprints .fingerprint').length).toBe(1);
-                const el = modal.el.querySelector('.fingerprints .fingerprint');
-                expect(el.textContent.trim()).toBe(
-                    u.formatFingerprint(u.arrayBufferToHex(u.base64ToArrayBuffer('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI')))
-                );
-
-                expect(modal.el.querySelectorAll('input[type="radio"]').length).toBe(2);
-
-                const devicelist = _converse.devicelists.get(contact_jid);
-                expect(devicelist.devices.get('555').get('trusted')).toBe(0);
-
-                let trusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="1"]');
-                expect(trusted_radio.checked).toBe(true);
-
-                let untrusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="-1"]');
-                expect(untrusted_radio.checked).toBe(false);
-
-                // Test that the device can be set to untrusted
-                untrusted_radio.click();
-                trusted_radio = document.querySelector('input[type="radio"][name="555"][value="1"]');
-                expect(trusted_radio.hasAttribute('checked')).toBe(false);
-                expect(devicelist.devices.get('555').get('trusted')).toBe(-1);
-
-                untrusted_radio = document.querySelector('input[type="radio"][name="555"][value="-1"]');
-                expect(untrusted_radio.hasAttribute('checked')).toBe(true);
-
-                trusted_radio.click();
-                expect(devicelist.devices.get('555').get('trusted')).toBe(1);
-                done();
-            }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
+            await test_utils.openChatBoxFor(_converse, contact_jid)
+            // We simply emit, to avoid doing all the setup work
+            _converse.emit('OMEMOInitialized');
+
+            const view = _converse.chatboxviews.get(contact_jid);
+            const show_modal_button = view.el.querySelector('.show-user-details-modal');
+            show_modal_button.click();
+            const modal = view.user_details_modal;
+            await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
+            let iq_stanza = await test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="max.frankfurter@localhost" type="get" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub>`+
+                `</iq>`);
+            let stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
+                .c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
+                    .c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
+                        .c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
+                            .c('device', {'id': '555'});
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+            await test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
+            iq_stanza = await test_utils.waitUntil(() => bundleFetched(_converse, contact_jid, '555'));
+            expect(iq_stanza.toLocaleString()).toBe(
+                `<iq from="dummy@localhost" id="${iq_stanza.nodeTree.getAttribute("id")}" to="max.frankfurter@localhost" type="get" xmlns="jabber:client">`+
+                    `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
+                        `<items node="eu.siacs.conversations.axolotl.bundles:555"/>`+
+                    `</pubsub>`+
+                `</iq>`);
+            stanza = $iq({
+                'from': contact_jid,
+                'id': iq_stanza.nodeTree.getAttribute('id'),
+                'to': _converse.bare_jid,
+                'type': 'result',
+            }).c('pubsub', {
+                'xmlns': 'http://jabber.org/protocol/pubsub'
+                }).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
+                    .c('item')
+                        .c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
+                            .c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
+                            .c('signedPreKeySignature').t(btoa('2222')).up()
+                            .c('identityKey').t('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI').up()
+                            .c('prekeys')
+                                .c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
+                                .c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
+                                .c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
+            _converse.connection._dataRecv(test_utils.createRequest(stanza));
+
+            await test_utils.waitUntil(() => modal.el.querySelectorAll('.fingerprints .fingerprint').length);
+            expect(modal.el.querySelectorAll('.fingerprints .fingerprint').length).toBe(1);
+            const el = modal.el.querySelector('.fingerprints .fingerprint');
+            expect(el.textContent.trim()).toBe(
+                u.formatFingerprint(u.arrayBufferToHex(u.base64ToArrayBuffer('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI')))
+            );
+            expect(modal.el.querySelectorAll('input[type="radio"]').length).toBe(2);
+
+            const devicelist = _converse.devicelists.get(contact_jid);
+            expect(devicelist.devices.get('555').get('trusted')).toBe(0);
+
+            let trusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="1"]');
+            expect(trusted_radio.checked).toBe(true);
+
+            let untrusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="-1"]');
+            expect(untrusted_radio.checked).toBe(false);
+
+            // Test that the device can be set to untrusted
+            untrusted_radio.click();
+            trusted_radio = document.querySelector('input[type="radio"][name="555"][value="1"]');
+            expect(trusted_radio.hasAttribute('checked')).toBe(false);
+            expect(devicelist.devices.get('555').get('trusted')).toBe(-1);
+
+            untrusted_radio = document.querySelector('input[type="radio"][name="555"][value="-1"]');
+            expect(untrusted_radio.hasAttribute('checked')).toBe(true);
+
+            trusted_radio.click();
+            expect(devicelist.devices.get('555').get('trusted')).toBe(1);
+            done();
         }));
     });