/*global converse */ (function (root, factory) { define([ "jquery", "underscore", "mock", "test_utils" ], function ($, _, mock, test_utils) { return factory($, _, mock, test_utils); } ); } (this, function ($, _, mock, test_utils) { var $msg = converse_api.env.$msg; var Strophe = converse_api.env.Strophe; var moment = converse_api.env.moment; return describe("Chatboxes", $.proxy(function(mock, test_utils) { describe("A Chatbox", function () { beforeEach(function () { runs(function () { test_utils.closeAllChatBoxes(); test_utils.removeControlBox(); test_utils.clearBrowserStorage(); test_utils.initConverse(); test_utils.createContacts('current'); test_utils.openControlBox(); test_utils.openContactsPanel(); }); }); it("is created when you click on a roster item", function () { var i, $el, jid, chatboxview; // openControlBox was called earlier, so the controlbox is // visible, but no other chat boxes have been created. expect(this.chatboxes.length).toEqual(1); spyOn(this.chatboxviews, 'trimChats'); expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open var online_contacts = this.rosterview.$el.find('dt.roster-group').siblings('dd.current-xmpp-contact').find('a.open-chat'); for (i=0; i:)'); expect($($items[6]).children('a').data('emoticon')).toBe(':S'); expect($($items[7]).children('a').data('emoticon')).toBe(':\\'); expect($($items[8]).children('a').data('emoticon')).toBe('>:('); expect($($items[9]).children('a').data('emoticon')).toBe(':('); expect($($items[10]).children('a').data('emoticon')).toBe(':O'); expect($($items[11]).children('a').data('emoticon')).toBe('(^.^)b'); expect($($items[12]).children('a').data('emoticon')).toBe('<3'); $items.first().click(); }); waits(250); runs(function () { expect(view.insertEmoticon).toHaveBeenCalled(); expect($textarea.val()).toBe(':) '); expect(view.$el.find('.toggle-smiley ul').is(':visible')).toBeFalsy(); $toolbar.children('li.toggle-smiley').click(); }); waits(250); runs(function () { expect(view.toggleEmoticonMenu).toHaveBeenCalled(); expect(view.$el.find('.toggle-smiley ul').is(':visible')).toBeTruthy(); view.$el.find('.toggle-smiley ul').children('li').last().click(); }); waits(250); runs(function () { expect(view.insertEmoticon).toHaveBeenCalled(); expect(view.$el.find('.toggle-smiley ul').is(':visible')).toBeFalsy(); expect($textarea.val()).toBe(':) <3 '); }); }); it("contains a button for starting an encrypted chat session", function () { // TODO: More tests can be added here... var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; test_utils.openChatBoxFor(contact_jid); var view = this.chatboxviews.get(contact_jid); var $toolbar = view.$el.find('ul.chat-toolbar'); expect($toolbar.children('li.toggle-otr').length).toBe(1); // Register spies spyOn(view, 'toggleOTRMenu').andCallThrough(); view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called runs(function () { $toolbar.children('li.toggle-otr').click(); }); waits(250); runs(function () { expect(view.toggleOTRMenu).toHaveBeenCalled(); var $menu = view.$el.find('.toggle-otr ul'); expect($menu.is(':visible')).toBeTruthy(); expect($menu.children('li').length).toBe(2); }); }.bind(converse)); it("can contain a button for starting a call", function () { var view, callButton, $toolbar; var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; spyOn(converse, 'emit'); // First check that the button doesn't show if it's not enabled // via "visible_toolbar_buttons" converse.visible_toolbar_buttons.call = false; test_utils.openChatBoxFor(contact_jid); view = this.chatboxviews.get(contact_jid); $toolbar = view.$el.find('ul.chat-toolbar'); callButton = $toolbar.find('.toggle-call'); expect(callButton.length).toBe(0); view.close(); // Now check that it's shown if enabled and that it emits // callButtonClicked converse.visible_toolbar_buttons.call = true; // enable the button test_utils.openChatBoxFor(contact_jid); view = this.chatboxviews.get(contact_jid); $toolbar = view.$el.find('ul.chat-toolbar'); callButton = $toolbar.find('.toggle-call'); expect(callButton.length).toBe(1); callButton.click(); expect(converse.emit).toHaveBeenCalledWith('callButtonClicked', jasmine.any(Object)); }.bind(converse)); it("can contain a button for clearing messages", function () { var view, clearButton, $toolbar; var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost'; // First check that the button doesn't show if it's not enabled // via "visible_toolbar_buttons" converse.visible_toolbar_buttons.clear = false; test_utils.openChatBoxFor(contact_jid); view = this.chatboxviews.get(contact_jid); view = this.chatboxviews.get(contact_jid); $toolbar = view.$el.find('ul.chat-toolbar'); clearButton = $toolbar.find('.toggle-clear'); expect(clearButton.length).toBe(0); view.close(); // Now check that it's shown if enabled and that it calls // clearMessages converse.visible_toolbar_buttons.clear = true; // enable the button test_utils.openChatBoxFor(contact_jid); view = this.chatboxviews.get(contact_jid); $toolbar = view.$el.find('ul.chat-toolbar'); clearButton = $toolbar.find('.toggle-clear'); expect(clearButton.length).toBe(1); spyOn(view, 'clearMessages'); view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called clearButton.click(); expect(view.clearMessages).toHaveBeenCalled(); }.bind(converse)); }.bind(converse)); describe("A Chat Message", function () { beforeEach(function () { runs(function () { test_utils.closeAllChatBoxes(); }); waits(250); runs(function () {}); }); it("can be received which will open a chatbox and be displayed inside it", function () { spyOn(converse, 'emit'); var message = 'This is a received message'; var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var msg = $msg({ from: sender_jid, to: this.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); // We don't already have an open chatbox for this user expect(this.chatboxes.get(sender_jid)).not.toBeDefined(); runs(function () { // onMessage is a handler for received XMPP messages this.chatboxes.onMessage(msg); expect(converse.emit).toHaveBeenCalledWith('message', msg); }.bind(converse)); waits(50); runs(function () { // Check that the chatbox and its view now exist var chatbox = this.chatboxes.get(sender_jid); var chatboxview = this.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(message); expect(msg_obj.get('fullname')).toEqual(mock.cur_names[0]); 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(message); var sender_txt = $chat_content.find('span.chat-msg-them').text(); expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy(); }.bind(converse)); }.bind(converse)); it("is ignored if it's intended for a different resource", function () { // Send a message from a different resource spyOn(converse, 'log'); var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var 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( "Ignore incoming message intended for a different resource: dummy@localhost/some-other-resource", "info"); }); it("can be a carbon message, as defined in XEP-0280", function () { // 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': converse.bare_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(); }); it("can be a carbon message that this user sent from a different client, as defined in XEP-0280", function () { // 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(mock.cur_names[5]); 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); }); it("received for a minimized chat box will increment a counter on its header", function () { var contact_name = mock.cur_names[0]; var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost'; spyOn(this, 'emit'); test_utils.openChatBoxFor(contact_jid); var chatview = this.chatboxviews.get(contact_jid); expect(chatview.$el.is(':visible')).toBeTruthy(); expect(chatview.model.get('minimized')).toBeFalsy(); chatview.$el.find('.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: this.connection.jid, type: 'chat', id: (new Date()).getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); this.chatboxes.onMessage(msg); expect(this.emit).toHaveBeenCalledWith('message', msg); var trimmed_chatboxes = this.minimized_chats; var trimmedview = trimmed_chatboxes.get(contact_jid); var $count = trimmedview.$el.find('.chat-head-message-count'); expect(chatview.$el.is(':visible')).toBeFalsy(); expect(trimmedview.model.get('minimized')).toBeTruthy(); expect($count.is(':visible')).toBeTruthy(); expect($count.html()).toBe('1'); this.chatboxes.onMessage( $msg({ from: mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost', to: this.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(chatview.$el.is(':visible')).toBeFalsy(); expect(trimmedview.model.get('minimized')).toBeTruthy(); $count = trimmedview.$el.find('.chat-head-message-count'); expect($count.is(':visible')).toBeTruthy(); expect($count.html()).toBe('2'); trimmedview.$el.find('.restore-chat').click(); expect(trimmed_chatboxes.keys().length).toBe(0); }.bind(converse)); it("will indicate when it has a time difference of more than a day between it and its predecessor", function () { spyOn(converse, 'emit'); var contact_name = mock.cur_names[1]; var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost'; test_utils.openChatBoxFor(contact_jid); test_utils.clearChatBoxMessages(contact_jid); var one_day_ago = moment(); one_day_ago.subtract('days', 1); var message = 'This is a day old message'; var chatbox = this.chatboxes.get(contact_jid); var chatboxview = this.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: this.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(); this.chatboxes.onMessage(msg); expect(converse.emit).toHaveBeenCalledWith('message', msg); 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(); message = 'This is a current message'; msg = $msg({ from: contact_jid, to: this.connection.jid, type: 'chat', id: new Date().getTime() }).c('body').t(message).up() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); this.chatboxes.onMessage(msg); expect(converse.emit).toHaveBeenCalledWith('message', msg); // Check that there is a