diff --git a/karma.conf.js b/karma.conf.js index 19d0f9c59..5b698bb48 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -12,6 +12,7 @@ module.exports = function(config) { { pattern: 'dist/*.css.map', included: false }, { pattern: "dist/icons.js", served: true }, { pattern: "dist/emojis.js", served: true }, + "src/shared/tests/tests.css", "node_modules/lodash/lodash.min.js", "dist/converse.js", "dist/converse.css", diff --git a/src/headless/plugins/muc/utils.js b/src/headless/plugins/muc/utils.js index 6c313ae61..478dcee95 100644 --- a/src/headless/plugins/muc/utils.js +++ b/src/headless/plugins/muc/utils.js @@ -102,9 +102,9 @@ export async function onDirectMUCInvitation (message) { let contact = _converse.roster.get(from); contact = contact ? contact.getDisplayName() : from; if (!reason) { - result = confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid)); + result = await api.confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid)); } else { - result = confirm( + result = await api.confirm( __( '%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"', contact, @@ -114,7 +114,7 @@ export async function onDirectMUCInvitation (message) { ); } } - if (result === true) { + if (result) { const chatroom = await openChatRoom(room_jid, { 'password': x_el.getAttribute('password') }); if (chatroom.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED) { _converse.chatboxes.get(room_jid).rejoin(); diff --git a/src/modals/tests/user-details-modal.js b/src/modals/tests/user-details-modal.js index 41215841f..057480709 100644 --- a/src/modals/tests/user-details-modal.js +++ b/src/modals/tests/user-details-modal.js @@ -19,7 +19,8 @@ describe("The User Details Modal", function () { show_modal_button.click(); const modal = _converse.api.modal.get('user-details-modal'); await u.waitUntil(() => u.isVisible(modal.el), 1000); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true)); + spyOn(view.model.contact, 'removeFromRoster').and.callFake(callback => callback()); let remove_contact_button = modal.el.querySelector('button.remove-contact'); expect(u.isVisible(remove_contact_button)).toBeTruthy(); @@ -45,7 +46,7 @@ describe("The User Details Modal", function () { show_modal_button.click(); let modal = _converse.api.modal.get('user-details-modal'); await u.waitUntil(() => u.isVisible(modal.el), 2000); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true)); spyOn(view.model.contact, 'removeFromRoster').and.callFake((callback, errback) => errback()); let remove_contact_button = modal.el.querySelector('button.remove-contact'); diff --git a/src/modals/user-details.js b/src/modals/user-details.js index 9a4ce9b38..4da3fba11 100644 --- a/src/modals/user-details.js +++ b/src/modals/user-details.js @@ -83,11 +83,11 @@ const UserDetailsModal = BootstrapModal.extend({ u.removeClass('fa-spin', refresh_icon); }, - removeContact (ev) { + async removeContact (ev) { ev?.preventDefault?.(); if (!api.settings.get('allow_contact_removal')) { return; } - const result = confirm(__("Are you sure you want to remove this contact?")); - if (result === true) { + const result = await api.confirm(__("Are you sure you want to remove this contact?")); + if (result) { // XXX: The `dismissHandler` in bootstrap.native tries to // reference the remove button after it's been cleared from // the DOM, so we delay removing the contact to give it time. diff --git a/src/plugins/bookmark-views/tests/bookmarks.js b/src/plugins/bookmark-views/tests/bookmarks.js index 1588288aa..8e3c79b7b 100644 --- a/src/plugins/bookmark-views/tests/bookmarks.js +++ b/src/plugins/bookmark-views/tests/bookmarks.js @@ -557,9 +557,9 @@ describe("Bookmarks", function () { expect(els[3].textContent).toBe("noname@conference.shakespeare.lit"); expect(els[4].textContent).toBe("The Play's the Thing"); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); document.querySelector('#chatrooms .bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)').click(); - expect(window.confirm).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); await u.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length === 4) els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link'); expect(els[0].textContent).toBe("1st Bookmark"); diff --git a/src/plugins/bookmark-views/utils.js b/src/plugins/bookmark-views/utils.js index 7fcbe587a..db1a21f1d 100644 --- a/src/plugins/bookmark-views/utils.js +++ b/src/plugins/bookmark-views/utils.js @@ -25,11 +25,12 @@ export function getHeadingButtons (view, buttons) { return buttons; } -export function removeBookmarkViaEvent (ev) { +export async function removeBookmarkViaEvent (ev) { ev.preventDefault(); const name = ev.target.getAttribute('data-bookmark-name'); const jid = ev.target.getAttribute('data-room-jid'); - if (confirm(__('Are you sure you want to remove the bookmark "%1$s"?', name))) { + const result = await api.confirm(__('Are you sure you want to remove the bookmark "%1$s"?', name)); + if (result) { invokeMap(_converse.bookmarks.where({ jid }), Model.prototype.destroy); } } diff --git a/src/plugins/chatview/tests/chatbox.js b/src/plugins/chatview/tests/chatbox.js index 6f7aee8ea..c1ce92c3b 100644 --- a/src/plugins/chatview/tests/chatbox.js +++ b/src/plugins/chatview/tests/chatbox.js @@ -49,7 +49,7 @@ describe("Chatboxes", function () { const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit'; await mock.openChatBoxFor(_converse, contact_jid); const view = _converse.chatboxviews.get(contact_jid); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); for (const i of Array(10).keys()) { mock.sendMessage(view, `Message ${i}`); @@ -64,7 +64,7 @@ describe("Chatboxes", function () { preventDefault: function preventDefault () {}, keyCode: 13 // Enter }); - await u.waitUntil(() => window.confirm.calls.count() === 1); + await u.waitUntil(() => _converse.api.confirm.calls.count() === 1); await u.waitUntil(() => sizzle('converse-chat-message', view).length === 0); expect(true).toBe(true); })); @@ -916,15 +916,15 @@ describe("Chatboxes", function () { message = '/clear'; const message_form = view.querySelector('converse-message-form'); - spyOn(window, 'confirm').and.callFake(() => true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); view.querySelector('.chat-textarea').value = message; message_form.onKeyDown({ target: view.querySelector('textarea.chat-textarea'), preventDefault: function preventDefault () {}, keyCode: 13 }); - await u.waitUntil(() => window.confirm.calls.count() === 1); - expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?'); + await u.waitUntil(() => _converse.api.confirm.calls.count() === 1); + expect(_converse.api.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?'); await u.waitUntil(() => view.model.messages.length === 0); await u.waitUntil(() => !view.querySelectorAll('.chat-msg__body').length); })); diff --git a/src/plugins/chatview/tests/corrections.js b/src/plugins/chatview/tests/corrections.js index d1b7eb37e..c1fe33e73 100644 --- a/src/plugins/chatview/tests/corrections.js +++ b/src/plugins/chatview/tests/corrections.js @@ -268,23 +268,26 @@ describe("A Chat Message", function () { expect(view.querySelectorAll('.chat-msg .chat-msg__action').length).toBe(2); // Test confirmation dialog - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); textarea.value = 'But soft, what light through yonder airlock breaks?'; action = view.querySelector('.chat-msg .chat-msg__action'); action.style.opacity = 1; action.click(); - expect(window.confirm).toHaveBeenCalledWith( + + await u.waitUntil(() => _converse.api.confirm.calls.count()); + expect(_converse.api.confirm).toHaveBeenCalledWith( 'You have an unsent message which will be lost if you continue. Are you sure?'); expect(view.model.messages.at(0).get('correcting')).toBe(true); expect(textarea.value).toBe('But soft, what light through yonder window breaks?'); textarea.value = 'But soft, what light through yonder airlock breaks?' action.click(); + + await u.waitUntil(() => _converse.api.confirm.calls.count() === 2); expect(view.model.messages.at(0).get('correcting')).toBe(false); - expect(window.confirm.calls.count()).toBe(2); - expect(window.confirm.calls.argsFor(0)).toEqual( + expect(_converse.api.confirm.calls.argsFor(0)).toEqual( ['You have an unsent message which will be lost if you continue. Are you sure?']); - expect(window.confirm.calls.argsFor(1)).toEqual( + expect(_converse.api.confirm.calls.argsFor(1)).toEqual( ['You have an unsent message which will be lost if you continue. Are you sure?']); })); diff --git a/src/plugins/chatview/utils.js b/src/plugins/chatview/utils.js index 0f9bd1b67..e88634557 100644 --- a/src/plugins/chatview/utils.js +++ b/src/plugins/chatview/utils.js @@ -31,8 +31,8 @@ export async function getHeadingStandaloneButton (promise_or_data) { } export async function clearMessages (chat) { - const result = confirm(__('Are you sure you want to clear the messages from this conversation?')); - if (result === true) { + const result = await api.confirm(__('Are you sure you want to clear the messages from this conversation?')); + if (result) { await chat.clearMessages(); } } diff --git a/src/plugins/muc-views/tests/muc.js b/src/plugins/muc-views/tests/muc.js index 549f548fc..0ae1dc620 100644 --- a/src/plugins/muc-views/tests/muc.js +++ b/src/plugins/muc-views/tests/muc.js @@ -1385,7 +1385,7 @@ describe("Groupchats", function () { const from_jid = name.replace(/ /g,'.').toLowerCase() + '@montague.lit'; await u.waitUntil(() => _converse.roster.get(from_jid).vcard.get('fullname')); - spyOn(window, 'confirm').and.callFake(() => true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo'); const view = _converse.chatboxviews.get('lounge@montague.lit'); await view.close(); // Hack, otherwise we have to mock stanzas. @@ -1402,7 +1402,7 @@ describe("Groupchats", function () { `); await _converse.onDirectMUCInvitation(stanza); - expect(window.confirm).toHaveBeenCalledWith( + expect(_converse.api.confirm).toHaveBeenCalledWith( name + ' has invited you to join a groupchat: '+ muc_jid + ', and left the following reason: "'+reason+'"'); expect(_converse.chatboxes.models.length).toBe(2); @@ -2461,15 +2461,15 @@ describe("Groupchats", function () { const view = _converse.chatboxviews.get('lounge@montague.lit'); const textarea = await u.waitUntil(() => view.querySelector('.chat-textarea')); textarea.value = '/clear'; - spyOn(window, 'confirm').and.callFake(() => false); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(false)); const message_form = view.querySelector('converse-muc-message-form'); message_form.onKeyDown({ target: textarea, preventDefault: function preventDefault () {}, keyCode: 13 }); - await u.waitUntil(() => window.confirm.calls.count() === 1); - expect(window.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?'); + await u.waitUntil(() => _converse.api.confirm.calls.count() === 1); + expect(_converse.api.confirm).toHaveBeenCalledWith('Are you sure you want to clear the messages from this conversation?'); })); it("takes /owner to make a user an owner", mock.initConverse([], {}, async function (_converse) { diff --git a/src/plugins/muc-views/tests/muclist.js b/src/plugins/muc-views/tests/muclist.js index 10a92556a..cbfc54378 100644 --- a/src/plugins/muc-views/tests/muclist.js +++ b/src/plugins/muc-views/tests/muclist.js @@ -313,7 +313,7 @@ describe("A groupchat shown in the groupchats list", function () { async function (_converse) { const u = converse.env.utils; - spyOn(window, 'confirm').and.callFake(() => true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); expect(_converse.chatboxes.length).toBe(1); await mock.waitForRoster(_converse, 'current', 0); await mock.openChatRoom(_converse, 'lounge', 'conference.shakespeare.lit', 'JC'); @@ -328,7 +328,7 @@ describe("A groupchat shown in the groupchats list", function () { const rooms_list = document.querySelector('converse-rooms-list'); const close_el = rooms_list.querySelector(".close-room"); close_el.click(); - expect(window.confirm).toHaveBeenCalledWith( + expect(_converse.api.confirm).toHaveBeenCalledWith( 'Are you sure you want to leave the groupchat lounge@conference.shakespeare.lit?'); await u.waitUntil(() => rooms_list.querySelectorAll(".open-room").length === 0); diff --git a/src/plugins/omemo/profile.js b/src/plugins/omemo/profile.js index 93b826946..5ea302651 100644 --- a/src/plugins/omemo/profile.js +++ b/src/plugins/omemo/profile.js @@ -60,10 +60,13 @@ export class Profile extends CustomElement { async generateOMEMODeviceBundle (ev) { ev.preventDefault(); - if (confirm(__( + + const result = await api.confirm(__( 'Are you sure you want to generate new OMEMO keys? ' + - 'This will remove your old keys and all previously encrypted messages will no longer be decryptable on this device.' - ))) { + 'This will remove your old keys and all previously ' + + 'encrypted messages will no longer be decryptable on this device.')); + + if (result) { await api.omemo.bundle.generate(); await this.setAttributes(); this.requestUpdate(); diff --git a/src/plugins/roomslist/view.js b/src/plugins/roomslist/view.js index b3c83fb79..b8e68765a 100644 --- a/src/plugins/roomslist/view.js +++ b/src/plugins/roomslist/view.js @@ -74,7 +74,8 @@ export class RoomsList extends CustomElement { async closeRoom (ev) { // eslint-disable-line class-methods-use-this ev.preventDefault(); const name = ev.target.getAttribute('data-room-name'); - if (confirm(__("Are you sure you want to leave the groupchat %1$s?", name))) { + const result = await api.confirm(__("Are you sure you want to leave the groupchat %1$s?", name)); + if (result) { const jid = ev.target.getAttribute('data-room-jid'); const room = await api.rooms.get(jid); room.close(); diff --git a/src/plugins/rosterview/contactview.js b/src/plugins/rosterview/contactview.js index 9bf68c34d..d4e59c972 100644 --- a/src/plugins/rosterview/contactview.js +++ b/src/plugins/rosterview/contactview.js @@ -77,10 +77,12 @@ export default class RosterContact extends CustomElement { this.model.openChat(); } - removeContact (ev) { + async removeContact (ev) { ev?.preventDefault?.(); if (!api.settings.get('allow_contact_removal')) { return; } - if (!confirm(__("Are you sure you want to remove this contact?"))) { return; } + + const result = await api.confirm(__("Are you sure you want to remove this contact?")); + if (!result) return; try { this.model.removeFromRoster(); @@ -108,10 +110,10 @@ export default class RosterContact extends CustomElement { this.model.authorize().subscribe(); } - declineRequest (ev) { + async declineRequest (ev) { if (ev && ev.preventDefault) { ev.preventDefault(); } - const result = confirm(__("Are you sure you want to decline this contact request?")); - if (result === true) { + const result = await api.confirm(__("Are you sure you want to decline this contact request?")); + if (result) { this.model.unauthorize().destroy(); } return this; diff --git a/src/plugins/rosterview/tests/protocol.js b/src/plugins/rosterview/tests/protocol.js index 296330c24..f446c89e2 100644 --- a/src/plugins/rosterview/tests/protocol.js +++ b/src/plugins/rosterview/tests/protocol.js @@ -447,7 +447,7 @@ describe("The Protocol", function () { const jid = 'abram@montague.lit'; await mock.openControlBox(_converse); await mock.waitForRoster(_converse, 'current'); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); // We now have a contact we want to remove expect(_converse.roster.get(jid) instanceof _converse.RosterContact).toBeTruthy(); @@ -457,7 +457,7 @@ describe("The Protocol", function () { // remove the first user header.parentElement.querySelector('li .remove-xmpp-contact').click(); - expect(window.confirm).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); /* Section 8.6 Removing a Roster Item and Cancelling All * Subscriptions @@ -478,14 +478,14 @@ describe("The Protocol", function () { * * */ - const sent_iq = _converse.connection.IQ_stanzas.pop(); - - expect(Strophe.serialize(sent_iq)).toBe( - ``+ + const iq_stanzas = _converse.connection.IQ_stanzas; + await u.waitUntil(() => Strophe.serialize(iq_stanzas.at(-1)) === + ``+ ``+ ``+ ``+ ``); + const sent_iq = iq_stanzas.at(-1); // Receive confirmation from the contact's server // diff --git a/src/plugins/rosterview/tests/roster.js b/src/plugins/rosterview/tests/roster.js index 3f0c7a8ab..b07810309 100644 --- a/src/plugins/rosterview/tests/roster.js +++ b/src/plugins/rosterview/tests/roster.js @@ -642,7 +642,7 @@ describe("The Contacts Roster", function () { const jid = name.replace(/ /g,'.').toLowerCase() + '@montague.lit'; const contact = _converse.roster.get(jid); var sent_IQ; - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); spyOn(contact, 'unauthorize').and.callFake(function () { return contact; }); spyOn(contact, 'removeFromRoster').and.callThrough(); const rosterview = document.querySelector('converse-roster'); @@ -653,7 +653,7 @@ describe("The Contacts Roster", function () { }); sizzle(`.remove-xmpp-contact[title="Click to remove ${name} as a contact"]`, rosterview).pop().click(); await u.waitUntil(() => (sizzle(".pending-contact-name:contains('"+name+"')", rosterview).length === 0), 1000); - expect(window.confirm).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); expect(contact.removeFromRoster).toHaveBeenCalled(); expect(Strophe.serialize(sent_IQ)).toBe( ``+ @@ -684,18 +684,19 @@ describe("The Contacts Roster", function () { }, 700) const remove_el = await u.waitUntil(() => sizzle(`.remove-xmpp-contact[title="Click to remove ${name} as a contact"]`, rosterview).pop()); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.callFake(() => Promise.resolve(true)); remove_el.click(); - expect(window.confirm).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); - const iq = _converse.connection.IQ_stanzas.pop(); - expect(Strophe.serialize(iq)).toBe( - ``+ + const iq_stanzas = _converse.connection.IQ_stanzas; + await u.waitUntil(() => Strophe.serialize(iq_stanzas.at(-1)) === + ``+ ``+ ``+ ``+ ``); + const iq = iq_stanzas.at(-1); const stanza = u.toStanza(``); _converse.connection._dataRecv(mock.createRequest(stanza)); await u.waitUntil(() => rosterview.querySelector(`ul[data-group="Pending contacts"]`) === null); @@ -709,7 +710,7 @@ describe("The Contacts Roster", function () { await Promise.all(_converse.roster.map(contact => u.waitUntil(() => contact.vcard.get('fullname')))); await u.waitUntil(() => _converse.roster.at(0).vcard.get('fullname')) const rosterview = document.querySelector('converse-roster'); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true)); for (let i=0; i { sent_IQ = iq; callback(); }); sizzle(`.remove-xmpp-contact[title="Click to remove ${name} as a contact"]`, rosterview).pop().click(); - expect(window.confirm).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); + await u.waitUntil(() => sent_IQ); + expect(Strophe.serialize(sent_IQ)).toBe( ``+ ``+ @@ -858,15 +861,13 @@ describe("The Contacts Roster", function () { }); const rosterview = document.querySelector('converse-roster'); await u.waitUntil(() => sizzle('.roster-group', rosterview).filter(u.isVisible).map(e => e.querySelector('li')).length, 1000); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true)); spyOn(contact, 'removeFromRoster').and.callThrough(); - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) { - if (typeof callback === "function") { return callback(); } - }); + spyOn(_converse.connection, 'sendIQ').and.callFake((iq, callback) => callback?.()); expect(u.isVisible(rosterview.querySelector('.roster-group'))).toBe(true); sizzle(`.remove-xmpp-contact[title="Click to remove ${name} as a contact"]`, rosterview).pop().click(); - expect(window.confirm).toHaveBeenCalled(); - expect(_converse.connection.sendIQ).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); + await u.waitUntil(() => _converse.connection.sendIQ.calls.count()); expect(contact.removeFromRoster).toHaveBeenCalled(); await u.waitUntil(() => rosterview.querySelectorAll('.roster-group').length === 0); })); @@ -1126,7 +1127,7 @@ describe("The Contacts Roster", function () { await mock.openControlBox(_converse); await mock.waitForRoster(_converse, "current", 0); const name = mock.req_names[0]; - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true)); _converse.roster.create({ 'jid': name.replace(/ /g,'.').toLowerCase() + '@montague.lit', 'subscription': 'none', @@ -1139,7 +1140,7 @@ describe("The Contacts Roster", function () { expect(u.isVisible(rosterview.querySelector(`ul[data-group="Contact requests"]`))).toEqual(true); expect(sizzle('.roster-group', rosterview).filter(u.isVisible).map(e => e.querySelector('li')).length).toBe(1); sizzle('.roster-group', rosterview).filter(u.isVisible).map(e => e.querySelector('li .decline-xmpp-request'))[0].click(); - expect(window.confirm).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); await u.waitUntil(() => rosterview.querySelector(`ul[data-group="Contact requests"]`) === null); })); @@ -1191,12 +1192,12 @@ describe("The Contacts Roster", function () { const name = mock.req_names.sort()[1]; const jid = name.replace(/ /g,'.').toLowerCase() + '@montague.lit'; const contact = _converse.roster.get(jid); - spyOn(window, 'confirm').and.returnValue(true); + spyOn(_converse.api, 'confirm').and.returnValue(Promise.resolve(true)); spyOn(contact, 'unauthorize').and.callFake(function () { return contact; }); const req_contact = await u.waitUntil(() => sizzle(".req-contact-name:contains('"+name+"')", rosterview).pop()); req_contact.parentElement.parentElement.querySelector('.decline-xmpp-request').click(); - expect(window.confirm).toHaveBeenCalled(); - expect(contact.unauthorize).toHaveBeenCalled(); + expect(_converse.api.confirm).toHaveBeenCalled(); + await u.waitUntil(() => contact.unauthorize.calls.count()); // There should now be one less contact expect(_converse.roster.length).toEqual(mock.req_names.length-1); })); diff --git a/src/shared/chat/message-actions.js b/src/shared/chat/message-actions.js index 1393644fd..6a60ba406 100644 --- a/src/shared/chat/message-actions.js +++ b/src/shared/chat/message-actions.js @@ -65,16 +65,15 @@ class MessageActions extends CustomElement { `; } - onMessageEditButtonClicked (ev) { + async onMessageEditButtonClicked (ev) { ev.preventDefault(); const currently_correcting = this.model.collection.findWhere('correcting'); // TODO: Use state intead of DOM querying // Then this code can also be put on the model const unsent_text = u.ancestor(this, '.chatbox')?.querySelector('.chat-textarea')?.value; if (unsent_text && (!currently_correcting || currently_correcting.getMessageText() !== unsent_text)) { - if (!confirm(__('You have an unsent message which will be lost if you continue. Are you sure?'))) { - return; - } + const result = await api.confirm(__('You have an unsent message which will be lost if you continue. Are you sure?')); + if (!result) return; } if (currently_correcting !== this.model) { currently_correcting?.save('correcting', false); diff --git a/src/shared/tests/tests.css b/src/shared/tests/tests.css new file mode 100644 index 000000000..00ed756b4 --- /dev/null +++ b/src/shared/tests/tests.css @@ -0,0 +1,3 @@ +body { + overflow: auto !important; +}