Kaynağa Gözat

test: Add tests for one-on-one message retractions in chatview

JC Brand 6 ay önce
ebeveyn
işleme
65af7a6146

+ 1 - 0
karma.conf.js

@@ -69,6 +69,7 @@ module.exports = function(config) {
       { pattern: "src/plugins/chatview/tests/styling.js", type: 'module' },
       { pattern: "src/plugins/chatview/tests/unreads.js", type: 'module' },
       { pattern: "src/plugins/chatview/tests/xss.js", type: 'module' },
+      { pattern: "src/plugins/chatview/tests/retractions.js", type: 'module' },
       { pattern: "src/plugins/controlbox/tests/controlbox.js", type: 'module' },
       { pattern: "src/plugins/controlbox/tests/login.js", type: 'module' },
       { pattern: "src/plugins/headlines-view/tests/headline.js", type: 'module' },

+ 31 - 31
src/headless/plugins/status/status.js

@@ -8,17 +8,16 @@ import { isIdle, getIdleSeconds } from './utils.js';
 const { Strophe, $pres } = converse.env;
 
 export default class XMPPStatus extends ColorAwareModel(Model) {
-
-  constructor(attributes, options) {
+    constructor(attributes, options) {
         super(attributes, options);
         this.vcard = null;
     }
 
-    defaults () {
-        return { "status":  api.settings.get("default_state") }
+    defaults() {
+        return { 'status': api.settings.get('default_state') };
     }
 
-    getStatus () {
+    getStatus() {
         return this.get('status');
     }
 
@@ -34,20 +33,20 @@ export default class XMPPStatus extends ColorAwareModel(Model) {
         return super.get(attr);
     }
 
-  /**
-   * @param {string|Object} key
-   * @param {string|Object} [val]
-   * @param {Object} [options]
-   */
+    /**
+     * @param {string|Object} key
+     * @param {string|Object} [val]
+     * @param {Object} [options]
+     */
     set(key, val, options) {
         if (key === 'jid' || key === 'nickname') {
-            throw new Error('Readonly property')
+            throw new Error('Readonly property');
         }
         return super.set(key, val, options);
     }
 
-    initialize () {
-        this.on('change', item => {
+    initialize() {
+        this.on('change', (item) => {
             if (!(item.changed instanceof Object)) {
                 return;
             }
@@ -57,15 +56,15 @@ export default class XMPPStatus extends ColorAwareModel(Model) {
         });
     }
 
-    getDisplayName () {
+    getDisplayName() {
         return this.getFullname() || this.getNickname() || this.get('jid');
     }
 
-    getNickname () {
+    getNickname() {
         return api.settings.get('nickname');
     }
 
-    getFullname () {
+    getFullname() {
         return ''; // Gets overridden in converse-vcard
     }
 
@@ -74,8 +73,8 @@ export default class XMPPStatus extends ColorAwareModel(Model) {
      * @param {string} [to] - The JID to which this presence should be sent
      * @param {string} [status_message]
      */
-    async constructPresence (type, to=null, status_message) {
-        type = typeof type === 'string' ? type : (this.get('status') || api.settings.get("default_state"));
+    async constructPresence(type, to = null, status_message) {
+        type = typeof type === 'string' ? type : this.get('status') || api.settings.get('default_state');
         status_message = typeof status_message === 'string' ? status_message : this.get('status_message');
 
         let presence;
@@ -84,30 +83,31 @@ export default class XMPPStatus extends ColorAwareModel(Model) {
             presence = $pres({ to, type });
             const { xmppstatus } = _converse.state;
             const nick = xmppstatus.getNickname();
-            if (nick) presence.c('nick', {'xmlns': Strophe.NS.NICK}).t(nick).up();
-
-        } else if ((type === 'unavailable') ||
-                (type === 'probe') ||
-                (type === 'error') ||
-                (type === 'unsubscribe') ||
-                (type === 'unsubscribed') ||
-                (type === 'subscribed')) {
+            if (nick) presence.c('nick', { 'xmlns': Strophe.NS.NICK }).t(nick).up();
+        } else if (
+            type === 'unavailable' ||
+            type === 'probe' ||
+            type === 'error' ||
+            type === 'unsubscribe' ||
+            type === 'unsubscribed' ||
+            type === 'subscribed'
+        ) {
             presence = $pres({ to, type });
-
         } else if (type === 'offline') {
             presence = $pres({ to, type: 'unavailable' });
-
         } else if (type === 'online') {
             presence = $pres({ to });
-
         } else {
             presence = $pres({ to }).c('show').t(type).up();
         }
 
         if (status_message) presence.c('status').t(status_message).up();
 
-        const priority = api.settings.get("priority");
-        presence.c('priority').t(Number.isNaN(Number(priority)) ? 0 : priority).up();
+        const priority = api.settings.get('priority');
+        presence
+            .c('priority')
+            .t(Number.isNaN(Number(priority)) ? 0 : priority)
+            .up();
 
         if (isIdle()) {
             const idle_since = new Date();

+ 94 - 0
src/plugins/chatview/tests/retractions.js

@@ -0,0 +1,94 @@
+/*global mock, converse */
+
+const { Strophe, u, stx } = converse.env;
+
+describe("A sent chat message", function () {
+
+    beforeAll(() => jasmine.addMatchers({ toEqualStanza: jasmine.toEqualStanza }));
+
+    it("can be retracted", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+        await mock.waitForRoster(_converse, 'current', 1);
+        const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+        const view = await mock.openChatBoxFor(_converse, contact_jid);
+
+        view.model.sendMessage({ body : 'hello world'});
+        await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
+
+        const message = view.model.messages.at(0);
+        expect(view.model.messages.length).toBe(1);
+        expect(message.get('retracted')).toBeFalsy();
+        expect(message.get('editable')).toBeTruthy();
+
+        const retract_button = await u.waitUntil(() => view.querySelector('.chat-msg__content .chat-msg__action-retract'));
+        retract_button.click();
+        await u.waitUntil(() => u.isVisible(document.querySelector('#converse-modals .modal')));
+        const submit_button = document.querySelector('#converse-modals .modal button[type="submit"]');
+        submit_button.click();
+
+        const sent_stanzas = _converse.api.connection.get().sent_stanzas;
+        await u.waitUntil(() => view.querySelectorAll('.chat-msg--retracted').length === 1);
+
+        const msg_obj = view.model.messages.at(0);
+        const retraction_stanza = await u.waitUntil(() => sent_stanzas.filter(s => s.querySelector('message apply-to[xmlns="urn:xmpp:fasten:0"]')).pop());
+        expect(retraction_stanza).toEqualStanza(
+            stx`<message id="${retraction_stanza.getAttribute('id')}" to="${contact_jid}" type="chat" xmlns="jabber:client">
+                <store xmlns="urn:xmpp:hints"/>
+                <apply-to id="${msg_obj.get('origin_id')}" xmlns="urn:xmpp:fasten:0">
+                    <retract xmlns="urn:xmpp:message-retract:0"/>
+                </apply-to>
+            </message>`);
+
+        expect(view.model.messages.length).toBe(1);
+        expect(message.get('retracted')).toBeTruthy();
+        expect(message.get('editable')).toBeFalsy();
+        expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
+        const el = view.querySelector('.chat-msg--retracted .chat-msg__message');
+        expect(el.textContent.trim()).toBe('You have removed this message');
+    }));
+});
+
+describe("A received chat message", function () {
+
+    it("can be followed up with a retraction", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+        await mock.waitForRoster(_converse, 'current', 1);
+        const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+        const view = await mock.openChatBoxFor(_converse, contact_jid);
+
+        const received_stanza = stx`
+            <message xmlns="jabber:client"
+                    to="${_converse.bare_jid}"
+                    type="chat"
+                    id="29132ea0-0121-2897-b121-36638c259554"
+                    from="${contact_jid}">
+                <body>😊</body>
+                <markable xmlns="urn:xmpp:chat-markers:0"/>
+                <origin-id xmlns="urn:xmpp:sid:0" id="29132ea0-0121-2897-b121-36638c259554"/>
+                <stanza-id xmlns="urn:xmpp:sid:0" id="kxViLhgbnNMcWv10" by="${_converse.bare_jid}"/>
+            </message>`;
+
+        _converse.api.connection.get()._dataRecv(mock.createRequest(received_stanza));
+        await u.waitUntil(() => view.model.messages.length === 1);
+        await u.waitUntil(() => view.querySelectorAll('.chat-msg').length === 1);
+
+        const retraction_stanza = stx`
+            <message id="${u.getUniqueId()}"
+                     to="${_converse.bare_jid}"
+                     from="${contact_jid}"
+                     type="chat"
+                     xmlns="jabber:client">
+                <apply-to id="29132ea0-0121-2897-b121-36638c259554" xmlns="urn:xmpp:fasten:0">
+                    <retract xmlns="urn:xmpp:message-retract:0"/>
+                </apply-to>
+            </message>`;
+        _converse.api.connection.get()._dataRecv(mock.createRequest(retraction_stanza));
+        await u.waitUntil(() => view.querySelectorAll('.chat-msg--retracted').length === 1);
+
+        expect(view.model.messages.length).toBe(1);
+
+        const message = view.model.messages.at(0);
+        expect(message.get('retracted')).toBeTruthy();
+        expect(view.querySelectorAll('.chat-msg--retracted').length).toBe(1);
+        const msg_el = view.querySelector('.chat-msg--retracted .chat-msg__message');
+        expect(msg_el.textContent.trim()).toBe('Mercutio has removed this message');
+    }));
+});

+ 3 - 1
src/shared/chat/message.js

@@ -173,7 +173,9 @@ export default class Message extends CustomElement {
             const modname = this.model.mod ? this.model.mod.getDisplayName() : __('A moderator');
             return __('%1$s has removed this message', modname);
         } else {
-            return __('%1$s has removed this message', this.model.getDisplayName());
+            return this.model.get('sender') === 'me' ?
+                __('You have removed this message') :
+                __('%1$s has removed this message', this.model.getDisplayName());
         }
     }