(function (root, factory) { define([ "jquery", "jasmine", "utils", "converse-core", "mock", "test-utils" ], factory); } (this, function ($, jasmine, utils, converse, mock, test_utils) { "use strict"; var _ = converse.env._; var $iq = converse.env.$iq; var $msg = converse.env.$msg; var Strophe = converse.env.Strophe; var Promise = converse.env.Promise; var moment = converse.env.moment; var u = converse.env.utils; return describe("Chatboxes", function() { describe("A Chatbox", function () { it("supports the /me command", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp') .then(function () { return test_utils.waitUntil(function () { return _converse.xmppstatus.get('fullname'); }, 300); }).then(function () { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); expect(_converse.chatboxes.length).toEqual(1); var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var message = '/me is tired'; var msg = $msg({ from: sender_jid, to: _converse.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); _converse.chatboxes.onMessage(msg); var view = _converse.chatboxviews.get(sender_jid); expect(_.includes($(view.el).find('.chat-msg-author').text(), '**Max Frankfurter')).toBeTruthy(); expect($(view.el).find('.chat-msg-content').text()).toBe(' is tired'); message = '/me is as well'; test_utils.sendMessage(view, message); expect(_.includes($(view.el).find('.chat-msg-author:last').text(), '**Max Mustermann')).toBeTruthy(); expect($(view.el).find('.chat-msg-content:last').text()).toBe(' is as well'); done(); }); })); it("is created when you click on a roster item", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); var i, $el, jid, chatboxview; // openControlBox was called earlier, so the controlbox is // visible, but no other chat boxes have been created. expect(_converse.chatboxes.length).toEqual(1); spyOn(_converse.chatboxviews, 'trimChats'); expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open test_utils.waitUntil(function () { return $(_converse.rosterview.el).find('.roster-group li').length; }, 700).then(function () { var online_contacts = $(_converse.rosterview.el).find('.roster-group .current-xmpp-contact a.open-chat'); expect(online_contacts.length).toBe(15); for (i=0; i 1; }, 500); }).then(function () { var key = _converse.chatboxviews.keys()[1]; trimmedview = trimmed_chatboxes.get(key); chatbox = trimmedview.model; spyOn(chatbox, 'maximize').and.callThrough(); spyOn(trimmedview, 'restore').and.callThrough(); trimmedview.delegateEvents(); trimmedview.el.querySelector("a.restore-chat").click(); expect(trimmedview.restore).toHaveBeenCalled(); expect(chatbox.maximize).toHaveBeenCalled(); expect(_converse.chatboxviews.trimChats).toHaveBeenCalled(); done(); }); done(); })); it("can be opened in minimized mode initially", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var chat = _converse.api.chats.open(sender_jid, { minimized: true }); var chatBoxView = _converse.chatboxviews.get(sender_jid); expect(u.isVisible(chatBoxView.el)).toBeFalsy(); var minimized_chat = _converse.minimized_chats.get(sender_jid); expect(minimized_chat).toBeTruthy(); expect($(minimized_chat.el).is(':visible')).toBeTruthy(); done(); })); it("is focused if its already open and you click on its corresponding roster item", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced. test_utils.openControlBox(); test_utils.openContactsPanel(_converse); var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; var $el, jid, chatbox; // openControlBox was called earlier, so the controlbox is // visible, but no other chat boxes have been created. expect(_converse.chatboxes.length).toEqual(1); spyOn(_converse.ChatBoxView.prototype, 'focus'); chatbox = test_utils.openChatBoxFor(_converse, contact_jid); $el = $(_converse.rosterview.el).find('a.open-chat:contains("'+chatbox.get('fullname')+'")'); jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost'; $el[0].click(); expect(_converse.chatboxes.length).toEqual(2); var chatboxview = _converse.chatboxviews.get(contact_jid); expect(chatboxview.focus).toHaveBeenCalled(); done(); })); it("can be saved to, and retrieved from, browserStorage", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); spyOn(_converse, 'emit'); spyOn(_converse.chatboxviews, 'trimChats'); test_utils.openControlBox(); test_utils.openChatBoxes(_converse, 6); expect(_converse.chatboxviews.trimChats).toHaveBeenCalled(); // We instantiate a new ChatBoxes collection, which by default // will be empty. var newchatboxes = new _converse.ChatBoxes(); expect(newchatboxes.length).toEqual(0); // The chatboxes will then be fetched from browserStorage inside the // onConnected method newchatboxes.onConnected(); expect(newchatboxes.length).toEqual(7); // Check that the chatboxes items retrieved from browserStorage // have the same attributes values as the original ones. var attrs = ['id', 'box_id', 'visible']; var new_attrs, old_attrs; for (var i=0; i element has type // "cancel", then we know the messages wasn't sent, // and can give the user a nicer indication of // that. /* * yo * * */ var sender_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var fullname = _converse.xmppstatus.get('fullname'); fullname = _.isEmpty(fullname)? _converse.bare_jid: fullname; _converse.api.chats.open(sender_jid); var msg_text = 'This message will not be sent, due to an error'; var view = _converse.chatboxviews.get(sender_jid); var message = view.model.messages.create({ 'msgid': '82bc02ce-9651-4336-baf0-fa04762ed8d2', 'fullname': fullname, 'sender': 'me', 'time': moment().format(), 'message': msg_text }); view.sendMessage(message); var $chat_content = $(view.el).find('.chat-content'); var msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text(); expect(msg_txt).toEqual(msg_text); // We send another message, for which an error will // not be received, to test that errors appear // after the relevant message. msg_text = 'This message will be sent, and not receive an error'; message = view.model.messages.create({ 'msgid': '6fcdeee3-000f-4ce8-a17e-9ce28f0ae104', 'fullname': fullname, 'sender': 'me', 'time': moment().format(), 'message': msg_text }); view.sendMessage(message); msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text(); expect(msg_txt).toEqual(msg_text); /* * * * Server-to-server connection failed: Connecting failed: connection timeout * * */ var error_txt = 'Server-to-server connection failed: Connecting failed: connection timeout'; var stanza = $msg({ 'to': _converse.connection.jid, 'type':'error', 'id':'82bc02ce-9651-4336-baf0-fa04762ed8d2', 'from': sender_jid }) .c('error', {'type': 'cancel'}) .c('remote-server-not-found', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" }).up() .c('text', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" }) .t('Server-to-server connection failed: Connecting failed: connection timeout'); _converse.connection._dataRecv(test_utils.createRequest(stanza)); expect($chat_content.find('.chat-error').text()).toEqual(error_txt); /* Incoming error messages that are not tied to a * certain show message (via the msgid attribute), * are not shown at all. The reason for this is * that we may get error messages for chat state * notifications as well. */ stanza = $msg({ 'to': _converse.connection.jid, 'type':'error', 'id':'some-other-unused-id', 'from': sender_jid }) .c('error', {'type': 'cancel'}) .c('remote-server-not-found', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" }).up() .c('text', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" }) .t('Server-to-server connection failed: Connecting failed: connection timeout'); _converse.connection._dataRecv(test_utils.createRequest(stanza)); expect($chat_content.find('.chat-error').length).toEqual(1); done(); })); }); it("will cause the chat area to be scrolled down only if it was at the bottom originally", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; test_utils.openChatBoxFor(_converse, sender_jid); var chatboxview = _converse.chatboxviews.get(sender_jid); spyOn(chatboxview, 'onScrolledDown').and.callThrough(); // Create enough messages so that there's a scrollbar. var message = 'This message is received while the chat area is scrolled up'; for (var i=0; i<20; i++) { _converse.chatboxes.onMessage($msg({ from: sender_jid, to: _converse.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t('Message: '+i).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()); } return test_utils.waitUntil(function () { return chatboxview.content.scrollTop; }, 1000).then(function () { return test_utils.waitUntil(function () { return !chatboxview.model.get('auto_scrolled'); }, 500); }).then(function () { chatboxview.content.scrollTop = 0; return test_utils.waitUntil(function () { return chatboxview.model.get('scrolled'); }, 900); }).then(function () { _converse.chatboxes.onMessage($msg({ from: sender_jid, to: _converse.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()); // Now check that the message appears inside the chatbox in the DOM var $chat_content = $(chatboxview.el).find('.chat-content'); var msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text(); expect(msg_txt).toEqual(message); return test_utils.waitUntil(function () { return u.isVisible(chatboxview.el.querySelector('.new-msgs-indicator')); }, 500); }).then(function () { expect(chatboxview.model.get('scrolled')).toBe(true); expect(chatboxview.content.scrollTop).toBe(0); expect(u.isVisible(chatboxview.el.querySelector('.new-msgs-indicator'))).toBeTruthy(); // Scroll down again chatboxview.content.scrollTop = chatboxview.content.scrollHeight; return test_utils.waitUntil(function () { return !u.isVisible(chatboxview.el.querySelector('.new-msgs-indicator')); }, 700); }).then(done); })); it("is ignored if it's intended for a different resource and filter_by_resource is set to true", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); test_utils.waitUntil(function () { return $(_converse.rosterview.el).find('.roster-group').length; }, 300) .then(function () { // Send a message from a different resource var message, sender_jid, msg; spyOn(_converse, 'log'); spyOn(_converse.chatboxes, 'getChatBox').and.callThrough(); _converse.filter_by_resource = true; sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; msg = $msg({ from: sender_jid, to: _converse.bare_jid+"/some-other-resource", type: 'chat', id: (new Date()).getTime() }).c('body').t("This message will not be shown").up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); _converse.chatboxes.onMessage(msg); expect(_converse.log).toHaveBeenCalledWith( "onMessage: Ignoring incoming message intended for a different resource: dummy@localhost/some-other-resource", Strophe.LogLevel.INFO); expect(_converse.chatboxes.getChatBox).not.toHaveBeenCalled(); _converse.filter_by_resource = false; message = "This message sent to a different resource will be shown"; msg = $msg({ from: sender_jid, to: _converse.bare_jid+"/some-other-resource", type: 'chat', id: '134234623462346' }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); _converse.chatboxes.onMessage(msg); expect(_converse.chatboxes.getChatBox).toHaveBeenCalled(); var chatboxview = _converse.chatboxviews.get(sender_jid); var $chat_content = $(chatboxview.el).find('.chat-content:last'); var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text(); expect(msg_txt).toEqual(message); done(); }); })); }); it("can be received out of order, and will still be displayed in the right order", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); test_utils.waitUntil(function () { return $(_converse.rosterview.el).find('.roster-group').length; }, 300) .then(function () { var message, msg; spyOn(_converse, 'log'); spyOn(_converse.chatboxes, 'getChatBox').and.callThrough(); _converse.filter_by_resource = true; var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; /* * * * * Call me but love, and I'll be new baptized; Henceforth I never will be Romeo. * * * */ msg = $msg({'id': 'aeb213', 'to': _converse.bare_jid}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-02T13:08:25Z'}).up() .c('message', { 'xmlns': 'jabber:client', 'to': _converse.bare_jid, 'from': sender_jid, 'type': 'chat'}) .c('body').t("message from today") .tree(); _converse.chatboxes.onMessage(msg); msg = $msg({'id': 'aeb214', 'to': _converse.bare_jid}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2017-12-31T23:08:25Z'}).up() .c('message', { 'xmlns': 'jabber:client', 'to': _converse.bare_jid, 'from': sender_jid, 'type': 'chat'}) .c('body').t("Older message") .tree(); _converse.chatboxes.onMessage(msg); msg = $msg({'id': 'aeb215', 'to': _converse.bare_jid}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-01T13:18:23Z'}).up() .c('message', { 'xmlns': 'jabber:client', 'to': _converse.bare_jid, 'from': sender_jid, 'type': 'chat'}) .c('body').t("Inbetween message") .tree(); _converse.chatboxes.onMessage(msg); msg = $msg({'id': 'aeb216', 'to': _converse.bare_jid}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-01T13:18:23Z'}).up() .c('message', { 'xmlns': 'jabber:client', 'to': _converse.bare_jid, 'from': sender_jid, 'type': 'chat'}) .c('body').t("another inbetween message") .tree(); _converse.chatboxes.onMessage(msg); msg = $msg({'id': 'aeb217', 'to': _converse.bare_jid}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-02T12:18:23Z'}).up() .c('message', { 'xmlns': 'jabber:client', 'to': _converse.bare_jid, 'from': sender_jid, 'type': 'chat'}) .c('body').t("An earlier message today") .tree(); _converse.chatboxes.onMessage(msg); msg = $msg({'id': 'aeb218', 'to': _converse.bare_jid}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('delay', {'xmlns': 'urn:xmpp:delay', 'stamp':'2018-01-02T23:28:23Z'}).up() .c('message', { 'xmlns': 'jabber:client', 'to': _converse.bare_jid, 'from': sender_jid, 'type': 'chat'}) .c('body').t("newer message from today") .tree(); _converse.chatboxes.onMessage(msg); var chatboxview = _converse.chatboxviews.get(sender_jid); var $chat_content = $(chatboxview.el).find('.chat-content'); chatboxview.clearSpinner(); //cleanup var $time = $chat_content.find('time'); expect($time.length).toEqual(3); $time = $chat_content.find('time:first'); expect($time.data('isodate')).toEqual('2017-12-31T00:00:00+00:00'); expect($time[0].nextElementSibling.querySelector('.chat-msg-content').textContent).toBe('Older message'); var $el = $chat_content.find('.chat-message:first').find('.chat-msg-content') expect($el.text()).toEqual('Older message'); $time = $chat_content.find('time:eq(1)'); expect($time.data('isodate')).toEqual('2018-01-01T00:00:00+00:00'); expect($time[0].nextElementSibling.querySelector('.chat-msg-content').textContent).toBe('Inbetween message'); $el = $chat_content.find('.chat-message:eq(1)'); expect($el.find('.chat-msg-content').text()).toEqual('Inbetween message'); expect($el[0].nextElementSibling.querySelector('.chat-msg-content').textContent).toEqual('another inbetween message'); $el = $chat_content.find('.chat-message:eq(2)'); expect($el.find('.chat-msg-content').text()).toEqual('another inbetween message'); $time = $chat_content.find('time:last'); expect($time.data('isodate')).toEqual('2018-01-02T00:00:00+00:00'); expect($time[0].nextElementSibling.querySelector('.chat-msg-content').textContent).toBe('An earlier message today'); $el = $chat_content.find('.chat-message:eq(3)'); expect($el.find('.chat-msg-content').text()).toEqual('An earlier message today'); $el = $chat_content.find('.chat-message:eq(4)'); expect($el.find('.chat-msg-content').text()).toEqual('message from today'); expect($el[0].nextElementSibling.querySelector('.chat-msg-content').textContent).toEqual('newer message from today'); done(); }); })); it("is ignored if it's a malformed headline message", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); /* Ideally we wouldn't have to filter out headline * messages, but Prosody gives them the wrong 'type' :( */ sinon.spy(_converse, 'log'); sinon.spy(_converse.chatboxes, 'getChatBox'); sinon.spy(utils, 'isHeadlineMessage'); var msg = $msg({ from: 'localhost', to: _converse.bare_jid, type: 'chat', id: (new Date()).getTime() }).c('body').t("This headline message will not be shown").tree(); _converse.chatboxes.onMessage(msg); expect(_converse.log.calledWith( "onMessage: Ignoring incoming headline message sent with type 'chat' from JID: localhost", Strophe.LogLevel.INFO )).toBeTruthy(); expect(utils.isHeadlineMessage.called).toBeTruthy(); expect(utils.isHeadlineMessage.returned(true)).toBeTruthy(); expect(_converse.chatboxes.getChatBox.called).toBeFalsy(); // Remove sinon spies _converse.log.restore(); _converse.chatboxes.getChatBox.restore(); utils.isHeadlineMessage.restore(); done(); })); it("can be a carbon message, as defined in XEP-0280", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); // Send a message from a different resource spyOn(_converse, 'log'); var msgtext = 'This is a carbon message'; var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost'; var msg = $msg({ 'from': sender_jid, 'id': (new Date()).getTime(), 'to': _converse.connection.jid, 'type': 'chat', 'xmlns': 'jabber:client' }).c('received', {'xmlns': 'urn:xmpp:carbons:2'}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('message', { 'xmlns': 'jabber:client', 'from': sender_jid, 'to': _converse.bare_jid+'/another-resource', 'type': 'chat' }).c('body').t(msgtext).tree(); _converse.chatboxes.onMessage(msg); // Check that the chatbox and its view now exist var chatbox = _converse.chatboxes.get(sender_jid); var chatboxview = _converse.chatboxviews.get(sender_jid); expect(chatbox).toBeDefined(); expect(chatboxview).toBeDefined(); // Check that the message was received and check the message parameters expect(chatbox.messages.length).toEqual(1); var msg_obj = chatbox.messages.models[0]; expect(msg_obj.get('message')).toEqual(msgtext); expect(msg_obj.get('fullname')).toEqual(mock.cur_names[1]); expect(msg_obj.get('sender')).toEqual('them'); expect(msg_obj.get('delayed')).toEqual(false); // Now check that the message appears inside the chatbox in the DOM var $chat_content = $(chatboxview.el).find('.chat-content'); var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text(); expect(msg_txt).toEqual(msgtext); var sender_txt = $chat_content.find('span.chat-msg-them').text(); expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy(); done(); })); it("can be a carbon message that this user sent from a different client, as defined in XEP-0280", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { var contact, sent_stanza, IQ_id, stanza; test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp') .then(function () { return test_utils.waitUntil(function () { return _converse.xmppstatus.get('fullname'); }, 300); }).then(function () { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); // Send a message from a different resource spyOn(_converse, 'log'); var msgtext = 'This is a sent carbon message'; var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var msg = $msg({ 'from': _converse.bare_jid, 'id': (new Date()).getTime(), 'to': _converse.connection.jid, 'type': 'chat', 'xmlns': 'jabber:client' }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('message', { 'xmlns': 'jabber:client', 'from': _converse.bare_jid+'/another-resource', 'to': recipient_jid, 'type': 'chat' }).c('body').t(msgtext).tree(); _converse.chatboxes.onMessage(msg); // Check that the chatbox and its view now exist var chatbox = _converse.chatboxes.get(recipient_jid); var chatboxview = _converse.chatboxviews.get(recipient_jid); expect(chatbox).toBeDefined(); expect(chatboxview).toBeDefined(); // Check that the message was received and check the message parameters expect(chatbox.messages.length).toEqual(1); var msg_obj = chatbox.messages.models[0]; expect(msg_obj.get('message')).toEqual(msgtext); expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname')); expect(msg_obj.get('sender')).toEqual('me'); expect(msg_obj.get('delayed')).toEqual(false); // Now check that the message appears inside the chatbox in the DOM var $chat_content = $(chatboxview.el).find('.chat-content'); var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text(); expect(msg_txt).toEqual(msgtext); done(); }); })); it("will be discarded if it's a malicious message meant to look like a carbon copy", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); /* * * * * Please come to Creepy Valley tonight, alone! * * * * */ spyOn(_converse, 'log'); var msgtext = 'Please come to Creepy Valley tonight, alone!'; var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost'; var impersonated_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; var msg = $msg({ 'from': sender_jid, 'id': (new Date()).getTime(), 'to': _converse.connection.jid, 'type': 'chat', 'xmlns': 'jabber:client' }).c('received', {'xmlns': 'urn:xmpp:carbons:2'}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('message', { 'xmlns': 'jabber:client', 'from': impersonated_jid, 'to': _converse.connection.jid, 'type': 'chat' }).c('body').t(msgtext).tree(); _converse.chatboxes.onMessage(msg); // Check that chatbox for impersonated user is not created. var chatbox = _converse.chatboxes.get(impersonated_jid); expect(chatbox).not.toBeDefined(); // Check that the chatbox for the malicous user is not created chatbox = _converse.chatboxes.get(sender_jid); expect(chatbox).not.toBeDefined(); done(); })); it("received for a minimized chat box will increment a counter on its header", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); test_utils.waitUntil(function () { return $(_converse.rosterview.el).find('.roster-group').length; }, 300) .then(function () { var contact_name = mock.cur_names[0]; var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost'; spyOn(_converse, 'emit').and.callThrough(); test_utils.openChatBoxFor(_converse, contact_jid); var chatview = _converse.chatboxviews.get(contact_jid); expect(u.isVisible(chatview.el)).toBeTruthy(); expect(chatview.model.get('minimized')).toBeFalsy(); chatview.el.querySelector('.toggle-chatbox-button').click(); expect(chatview.model.get('minimized')).toBeTruthy(); var message = 'This message is sent to a minimized chatbox'; var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var msg = $msg({ from: sender_jid, to: _converse.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); _converse.chatboxes.onMessage(msg); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); var trimmed_chatboxes = _converse.minimized_chats; var trimmedview = trimmed_chatboxes.get(contact_jid); var $count = $(trimmedview.el).find('.chat-head-message-count'); expect(u.isVisible(chatview.el)).toBeFalsy(); expect(trimmedview.model.get('minimized')).toBeTruthy(); expect(u.isVisible($count[0])).toBeTruthy(); expect($count.html()).toBe('1'); _converse.chatboxes.onMessage( $msg({ from: mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost', to: _converse.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t('This message is also sent to a minimized chatbox').up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree() ); expect(u.isVisible(chatview.el)).toBeFalsy(); expect(trimmedview.model.get('minimized')).toBeTruthy(); $count = $(trimmedview.el).find('.chat-head-message-count'); expect(u.isVisible($count[0])).toBeTruthy(); expect($count.html()).toBe('2'); trimmedview.el.querySelector('.restore-chat').click(); expect(trimmed_chatboxes.keys().length).toBe(0); done(); }); })); it("will indicate when it has a time difference of more than a day between it and its predecessor", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, function (done, _converse) { test_utils.createContacts(_converse, 'current'); test_utils.openControlBox(); test_utils.openContactsPanel(_converse); test_utils.waitUntil(function () { return $(_converse.rosterview.el).find('.roster-group').length; }, 300) .then(function () { spyOn(_converse, 'emit'); var contact_name = mock.cur_names[1]; var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost'; test_utils.openChatBoxFor(_converse, contact_jid); test_utils.clearChatBoxMessages(_converse, contact_jid); var one_day_ago = moment(); one_day_ago.subtract('days', 1); var message = 'This is a day old message'; var chatbox = _converse.chatboxes.get(contact_jid); var chatboxview = _converse.chatboxviews.get(contact_jid); var $chat_content = $(chatboxview.el).find('.chat-content'); var msg_obj; var msg_txt; var sender_txt; var msg = $msg({ from: contact_jid, to: _converse.connection.jid, type: 'chat', id: one_day_ago.unix() }).c('body').t(message).up() .c('delay', { xmlns:'urn:xmpp:delay', from: 'localhost', stamp: one_day_ago.format() }) .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); _converse.chatboxes.onMessage(msg); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); expect(chatbox.messages.length).toEqual(1); msg_obj = chatbox.messages.models[0]; expect(msg_obj.get('message')).toEqual(message); expect(msg_obj.get('fullname')).toEqual(contact_name); expect(msg_obj.get('sender')).toEqual('them'); expect(msg_obj.get('delayed')).toEqual(true); msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text(); expect(msg_txt).toEqual(message); sender_txt = $chat_content.find('span.chat-msg-them').text(); expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy(); var $time = $chat_content.find('time'); expect($time.length).toEqual(1); expect($time.attr('class')).toEqual('chat-info chat-date'); expect($time.data('isodate')).toEqual(moment(one_day_ago.startOf('day')).format()); expect($time.text()).toEqual(moment(one_day_ago.startOf('day')).format("dddd MMM Do YYYY")); message = 'This is a current message'; msg = $msg({ from: contact_jid, to: _converse.connection.jid, type: 'chat', id: new Date().getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); _converse.chatboxes.onMessage(msg); expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object)); // Check that there is a