xmpp.chapril.org-conversejs/spec/chatbox.js
JC Brand da0c858fe0 Used debounce ineffectually. Fixed now.
The ChatBox.show() method was being called for every invocation, 100ms after
the fact, instead of it being called only once.

Fixed that and also changed to call it at the start.
2016-02-16 07:47:16 +00:00

1180 lines
69 KiB
JavaScript

/*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<online_contacts.length; i++) {
$el = $(online_contacts[i]);
jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost';
$el.click();
chatboxview = this.chatboxviews.get(jid);
expect(this.chatboxes.length).toEqual(i+2);
expect(this.chatboxviews.trimChats).toHaveBeenCalled();
// Check that new chat boxes are created to the left of the
// controlbox (but to the right of all existing chat boxes)
expect($("#conversejs .chatbox").length).toBe(i+2);
expect($("#conversejs .chatbox")[1].id).toBe(chatboxview.model.get('box_id'));
}
}.bind(converse));
it("can be trimmed to conserve space", function () {
var i, $el, jid, chatbox, chatboxview, trimmedview;
// openControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created.
var trimmed_chatboxes = converse.minimized_chats;
expect(this.chatboxes.length).toEqual(1);
spyOn(this.chatboxviews, 'trimChats');
spyOn(trimmed_chatboxes, 'addChat').andCallThrough();
spyOn(trimmed_chatboxes, 'removeChat').andCallThrough();
expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
// Test that they can be trimmed
runs(function () {
converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
}.bind(this));
waits(50);
runs(function () {
// Test that they can be maximized again
var online_contacts = this.rosterview.$el.find('dt.roster-group').siblings('dd.current-xmpp-contact').find('a.open-chat');
for (i=0; i<online_contacts.length; i++) {
$el = $(online_contacts[i]);
jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost';
$el.click();
expect(this.chatboxviews.trimChats).toHaveBeenCalled();
chatboxview = this.chatboxviews.get(jid);
spyOn(chatboxview, 'hide').andCallThrough();
chatboxview.model.set({'minimized': true});
expect(trimmed_chatboxes.addChat).toHaveBeenCalled();
expect(chatboxview.hide).toHaveBeenCalled();
trimmedview = trimmed_chatboxes.get(jid);
}
var key = this.chatboxviews.keys()[1];
trimmedview = trimmed_chatboxes.get(key);
chatbox = trimmedview.model;
spyOn(chatbox, 'maximize').andCallThrough();
spyOn(trimmedview, 'restore').andCallThrough();
trimmedview.delegateEvents();
trimmedview.$("a.restore-chat").click();
}.bind(this));
waits(250);
runs(function () {
expect(trimmedview.restore).toHaveBeenCalled();
expect(chatbox.maximize).toHaveBeenCalled();
expect(this.chatboxviews.trimChats).toHaveBeenCalled();
}.bind(this));
}.bind(converse));
it("is focused if its already open and you click on its corresponding roster item", function () {
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
var $el, jid, chatboxview, chatbox;
// openControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created.
expect(converse.chatboxes.length).toEqual(1);
chatbox = test_utils.openChatBoxFor(contact_jid);
chatboxview = converse.chatboxviews.get(contact_jid);
spyOn(chatboxview, 'focus');
// Test that they can be trimmed
runs(function () {
converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
});
waits(300); // ChatBox.show() is debounced for 250ms
runs(function () {
$el = converse.rosterview.$el.find('a.open-chat:contains("'+chatbox.get('fullname')+'")');
jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost';
$el.click();
expect(converse.chatboxes.length).toEqual(2);
expect(chatboxview.focus).toHaveBeenCalled();
});
});
it("can be saved to, and retrieved from, browserStorage", function () {
spyOn(converse, 'emit');
spyOn(this.chatboxviews, 'trimChats');
runs(function () {
test_utils.openControlBox();
});
waits(250);
runs(function () {
test_utils.openChatBoxes(6);
expect(this.chatboxviews.trimChats).toHaveBeenCalled();
// We instantiate a new ChatBoxes collection, which by default
// will be empty.
var newchatboxes = new this.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<attrs.length; i++) {
new_attrs = _.pluck(_.pluck(newchatboxes.models, 'attributes'), attrs[i]);
old_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]);
expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
}
this.rosterview.render();
}.bind(converse));
}.bind(converse));
it("can be closed by clicking a DOM element with class 'close-chatbox-button'", function () {
var chatbox = test_utils.openChatBoxes(1)[0],
controlview = this.chatboxviews.get('controlbox'), // The controlbox is currently open
chatview = this.chatboxviews.get(chatbox.get('jid'));
spyOn(chatview, 'close').andCallThrough();
spyOn(controlview, 'close').andCallThrough();
spyOn(converse, 'emit');
// We need to rebind all events otherwise our spy won't be called
controlview.delegateEvents();
chatview.delegateEvents();
runs(function () {
controlview.$el.find('.close-chatbox-button').click();
});
waits(250);
runs(function () {
expect(controlview.close).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
expect(converse.emit.callCount, 1);
chatview.$el.find('.close-chatbox-button').click();
});
waits(250);
runs(function () {
expect(chatview.close).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
expect(converse.emit.callCount, 2);
});
}.bind(converse));
it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'", function () {
var chatbox = test_utils.openChatBoxes(1)[0],
chatview = this.chatboxviews.get(chatbox.get('jid')),
trimmed_chatboxes = this.minimized_chats,
trimmedview;
spyOn(chatview, 'minimize').andCallThrough();
spyOn(converse, 'emit');
// We need to rebind all events otherwise our spy won't be called
chatview.delegateEvents();
runs(function () {
chatview.$el.find('.toggle-chatbox-button').click();
});
waits(250);
runs(function () {
expect(chatview.minimize).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('chatBoxMinimized', jasmine.any(Object));
expect(converse.emit.callCount, 2);
expect(chatview.$el.is(':visible')).toBeFalsy();
expect(chatview.model.get('minimized')).toBeTruthy();
chatview.$el.find('.toggle-chatbox-button').click();
trimmedview = trimmed_chatboxes.get(chatview.model.get('id'));
spyOn(trimmedview, 'restore').andCallThrough();
trimmedview.delegateEvents();
trimmedview.$("a.restore-chat").click();
});
waits(250);
runs(function () {
expect(trimmedview.restore).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
expect(chatview.$el.find('.chat-body').is(':visible')).toBeTruthy();
expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeTruthy();
expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeFalsy();
expect(chatview.model.get('minimized')).toBeFalsy();
});
}.bind(converse));
it("will be removed from browserStorage when closed", function () {
spyOn(converse, 'emit');
spyOn(converse.chatboxviews, 'trimChats');
this.chatboxes.browserStorage._clear();
runs(function () {
test_utils.closeControlBox();
});
waits(50);
runs(function () {
expect(converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
expect(converse.chatboxes.length).toEqual(1);
expect(converse.chatboxes.pluck('id')).toEqual(['controlbox']);
test_utils.openChatBoxes(6);
expect(converse.chatboxviews.trimChats).toHaveBeenCalled();
expect(converse.chatboxes.length).toEqual(7);
expect(converse.emit).toHaveBeenCalledWith('chatBoxOpened', jasmine.any(Object));
test_utils.closeAllChatBoxes();
});
waits(50);
runs(function () {
expect(converse.chatboxes.length).toEqual(1);
expect(converse.chatboxes.pluck('id')).toEqual(['controlbox']);
expect(converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
var newchatboxes = new this.ChatBoxes();
expect(newchatboxes.length).toEqual(0);
expect(converse.chatboxes.pluck('id')).toEqual(['controlbox']);
// onConnected will fetch chatboxes in browserStorage, but
// because there aren't any open chatboxes, there won't be any
// in browserStorage either. XXX except for the controlbox
newchatboxes.onConnected();
expect(newchatboxes.length).toEqual(1);
expect(newchatboxes.models[0].id).toBe("controlbox");
}.bind(converse));
}.bind(converse));
describe("A chat toolbar", function () {
it("can be found on each chat box", function () {
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var chatbox = this.chatboxes.get(contact_jid);
var view = this.chatboxviews.get(contact_jid);
expect(chatbox).toBeDefined();
expect(view).toBeDefined();
var $toolbar = view.$el.find('ul.chat-toolbar');
expect($toolbar.length).toBe(1);
expect($toolbar.children('li').length).toBe(3);
}.bind(converse));
it("contains a button for inserting emoticons", function () {
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost',
view, $toolbar, $textarea;
waits(300); // ChatBox.show() is debounced for 250ms
runs(function () {
test_utils.openChatBoxFor(contact_jid);
view = converse.chatboxviews.get(contact_jid);
$toolbar = view.$el.find('ul.chat-toolbar');
$textarea = view.$el.find('textarea.chat-textarea');
expect($toolbar.children('li.toggle-smiley').length).toBe(1);
// Register spies
spyOn(view, 'toggleEmoticonMenu').andCallThrough();
spyOn(view, 'insertEmoticon').andCallThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
$toolbar.children('li.toggle-smiley').click();
});
waits(250);
runs(function () {
expect(view.toggleEmoticonMenu).toHaveBeenCalled();
var $menu = view.$el.find('.toggle-smiley ul');
var $items = $menu.children('li');
expect($menu.is(':visible')).toBeTruthy();
expect($items.length).toBe(13);
expect($($items[0]).children('a').data('emoticon')).toBe(':)');
expect($($items[1]).children('a').data('emoticon')).toBe(';)');
expect($($items[2]).children('a').data('emoticon')).toBe(':D');
expect($($items[3]).children('a').data('emoticon')).toBe(':P');
expect($($items[4]).children('a').data('emoticon')).toBe('8)');
expect($($items[5]).children('a').data('emoticon')).toBe('>:)');
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);
// XXX: This is stupid, fullname is actually only the
// users first name
expect(msg_obj.get('fullname')).toEqual(mock.cur_names[0].split(' ')[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].split(' ')[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(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].split(' ')[0]);
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.split(' ')[0]);
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 <time> element, with the required
// props.
var $time = $chat_content.find('time');
var message_date = new Date();
expect($time.length).toEqual(1);
expect($time.attr('class')).toEqual('chat-info chat-date');
expect($time.data('isodate')).toEqual(moment(message_date).format());
expect($time.text()).toEqual(moment(message_date).format("dddd MMM Do YYYY"));
// Normal checks for the 2nd message
expect(chatbox.messages.length).toEqual(2);
msg_obj = chatbox.messages.models[1];
expect(msg_obj.get('message')).toEqual(message);
expect(msg_obj.get('fullname')).toEqual(contact_name.split(' ')[0]);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(false);
msg_txt = $chat_content.find('.chat-message').last().find('.chat-msg-content').text();
expect(msg_txt).toEqual(message);
sender_txt = $chat_content.find('span.chat-msg-them').last().text();
expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
}.bind(converse));
it("can be sent from a chatbox, and will appear inside it", function () {
spyOn(converse, 'emit');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
expect(converse.emit).toHaveBeenCalledWith('chatBoxFocused', jasmine.any(Object));
var view = this.chatboxviews.get(contact_jid);
var message = 'This message is sent from this chatbox';
spyOn(view, 'sendMessage').andCallThrough();
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
expect(view.model.messages.length, 2);
expect(converse.emit.mostRecentCall.args, ['messageSend', message]);
expect(view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content').text()).toEqual(message);
}.bind(converse));
it("is sanitized to prevent Javascript injection attacks", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = '<p>This message contains <em>some</em> <b>markup</b></p>';
spyOn(view, 'sendMessage').andCallThrough();
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('&lt;p&gt;This message contains &lt;em&gt;some&lt;/em&gt; &lt;b&gt;markup&lt;/b&gt;&lt;/p&gt;');
}.bind(converse));
it("can contain hyperlinks, which will be clickable", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = 'This message contains a hyperlink: www.opkode.com';
spyOn(view, 'sendMessage').andCallThrough();
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('This message contains a hyperlink: <a target="_blank" href="http://www.opkode.com">www.opkode.com</a>');
}.bind(converse));
it("should display emoticons correctly", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var messages = [':)', ';)', ':D', ':P', '8)', '>:)', ':S', ':\\', '>:(', ':(', ':O', '(^.^)b', '<3'];
var emoticons = [
'<span class="emoticon icon-smiley"></span>', '<span class="emoticon icon-wink"></span>',
'<span class="emoticon icon-grin"></span>', '<span class="emoticon icon-tongue"></span>',
'<span class="emoticon icon-cool"></span>', '<span class="emoticon icon-evil"></span>',
'<span class="emoticon icon-confused"></span>', '<span class="emoticon icon-wondering"></span>',
'<span class="emoticon icon-angry"></span>', '<span class="emoticon icon-sad"></span>',
'<span class="emoticon icon-shocked"></span>', '<span class="emoticon icon-thumbs-up"></span>',
'<span class="emoticon icon-heart"></span>'
];
spyOn(view, 'sendMessage').andCallThrough();
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.html()).toEqual(emoticons[i]);
}
}.bind(converse));
it("will have properly escaped URLs", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
spyOn(view, 'sendMessage').andCallThrough();
var message = "http://www.opkode.com/'onmouseover='alert(1)'whatever";
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="http://www.opkode.com/%27onmouseover=%27alert%281%29%27whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>');
message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever';
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>');
message = "https://en.wikipedia.org/wiki/Ender's_Game";
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender\'s_Game</a>');
message = "https://en.wikipedia.org/wiki/Ender%27s_Game";
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender%27s_Game</a>');
}.bind(converse));
}.bind(converse));
describe("An OTR Chat Message", function () {
it("will not be carbon copied when it's sent out", function () {
var msgtext = "?OTR,1,3,?OTR:AAIDAAAAAAEAAAABAAAAwCQ8HKsag0y0DGKsneo0kzKu1ua5L93M4UKTkCf1I2kbm2RgS5kIxDTxrTj3wVRB+H5Si86E1fKtuBgsDf/bKkGTM0h/49vh5lOD9HkE8cnSrFEn5GN,";
var sender_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
converse_api.chats.open(sender_jid);
var chatbox = converse.chatboxes.get(sender_jid);
spyOn(converse.connection, 'send');
chatbox.set('otr_status', 1); // Set OTR status to UNVERIFIED, to mock an encrypted session
chatbox.trigger('sendMessage', new converse.Message({ message: msgtext }));
var $sent = $(converse.connection.send.argsForCall[0][0].tree());
expect($sent.find('body').siblings('private').length).toBe(1);
expect($sent.find('private').length).toBe(1);
expect($sent.find('private').attr('xmlns')).toBe('urn:xmpp:carbons:2');
chatbox.set('otr_status', 0); // Reset again to UNENCRYPTED
});
});
describe("A Chat Status Notification", function () {
it("does not open automatically if a chat state notification is received", function () {
spyOn(converse, 'emit');
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// <composing> state
var msg = $msg({
from: sender_jid,
to: this.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
this.chatboxes.onMessage(msg);
expect(converse.emit).toHaveBeenCalledWith('message', msg);
var chatboxview = this.chatboxviews.get(sender_jid);
expect(chatboxview).toBeDefined();
expect(chatboxview.$el.is(':visible')).toBeFalsy(); // The chat box is not visible
}.bind(converse));
describe("An active notification", function () {
it("is sent when the user opens a chat box", function () {
waits(300); // ChatBox.show() is debounced for 250ms
runs(function () {
spyOn(converse.connection, 'send');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('active');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('active');
});
});
it("is sent when the user maximizes a minimized a chat box", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
view.minimize();
expect(view.model.get('chat_state')).toBe('inactive');
spyOn(converse.connection, 'send');
view.maximize();
expect(view.model.get('chat_state')).toBe('active');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('active');
}.bind(converse));
}.bind(converse));
describe("A composing notification", function () {
it("is sent as soon as the user starts typing a message which is not a command", function () {
waits(300); // ChatBox.show() is debounced for 250ms
runs(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('active');
spyOn(converse.connection, 'send');
view.keyPressed({
target: view.$el.find('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('composing');
// The notification is not sent again
view.keyPressed({
target: view.$el.find('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
expect(converse.emit.callCount, 1);
});
});
it("will be shown if received", function () {
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
spyOn(converse, 'emit');
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// <composing> state
var msg = $msg({
from: sender_jid,
to: this.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
this.chatboxes.onMessage(msg);
expect(converse.emit).toHaveBeenCalledWith('message', msg);
var chatboxview = this.chatboxviews.get(sender_jid);
expect(chatboxview).toBeDefined();
// Check that the notification appears inside the chatbox in the DOM
var $events = chatboxview.$el.find('.chat-event');
expect($events.length).toBe(1);
expect($events.text()).toEqual(mock.cur_names[1].split(' ')[0] + ' is typing');
}.bind(converse));
}.bind(converse));
describe("A paused notification", function () {
it("is sent if the user has stopped typing since 30 seconds", function () {
var view, contact_jid;
converse.TIMEOUTS.PAUSED = 200; // Make the timeout shorter so that we can test
waits(300); // ChatBox.show() is debounced for 250ms
runs(function () {
contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
view = converse.chatboxviews.get(contact_jid);
spyOn(converse.connection, 'send');
spyOn(view, 'setChatState').andCallThrough();
expect(view.model.get('chat_state')).toBe('active');
view.keyPressed({
target: view.$el.find('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.children().prop('tagName')).toBe('composing');
});
waits(250);
runs(function () {
expect(view.model.get('chat_state')).toBe('paused');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[1][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('paused');
// Test #359. A paused notification should not be sent
// out if the user simply types longer than the
// timeout.
view.keyPressed({
target: view.$el.find('textarea.chat-textarea'),
keyCode: 1
});
expect(view.setChatState).toHaveBeenCalled();
expect(view.model.get('chat_state')).toBe('composing');
});
waits(100);
runs(function () {
view.keyPressed({
target: view.$el.find('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
});
waits(150);
runs(function () {
expect(view.model.get('chat_state')).toBe('composing');
});
});
it("will be shown if received", function () {
// TODO: only show paused state if the previous state was composing
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
spyOn(converse, 'emit');
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// <paused> state
var msg = $msg({
from: sender_jid,
to: this.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
this.chatboxes.onMessage(msg);
expect(converse.emit).toHaveBeenCalledWith('message', msg);
var chatboxview = this.chatboxviews.get(sender_jid);
var $events = chatboxview.$el.find('.chat-event');
expect($events.length).toBe(1);
expect($events.text()).toEqual(mock.cur_names[1].split(' ')[0] + ' has stopped typing');
}.bind(converse));
}.bind(converse));
describe("An inactive notifciation", function () {
it("is sent if the user has stopped typing since 2 minutes", function () {
// Make the timeouts shorter so that we can test
this.TIMEOUTS.PAUSED = 200;
this.TIMEOUTS.INACTIVE = 200;
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
runs(function () {
expect(view.model.get('chat_state')).toBe('active');
view.keyPressed({
target: view.$el.find('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
});
waits(250);
runs(function () {
expect(view.model.get('chat_state')).toBe('paused');
spyOn(converse.connection, 'send');
});
waits(250);
runs(function () {
expect(view.model.get('chat_state')).toBe('inactive');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('inactive');
});
}.bind(converse));
it("is sent when the user a minimizes a chat box", function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
spyOn(converse.connection, 'send');
view.minimize();
expect(view.model.get('chat_state')).toBe('inactive');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('inactive');
}.bind(converse));
it("is sent if the user closes a chat box", function () {
waits(300); // ChatBox.show() is debounced for 250ms
runs(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('active');
spyOn(converse.connection, 'send');
view.close();
expect(view.model.get('chat_state')).toBe('inactive');
expect(converse.connection.send).toHaveBeenCalled();
var $stanza = $(converse.connection.send.argsForCall[0][0].tree());
expect($stanza.attr('to')).toBe(contact_jid);
expect($stanza.children().length).toBe(1);
expect($stanza.children().prop('tagName')).toBe('inactive');
});
});
it("will clear any other chat status notifications if its received", function () {
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
spyOn(converse, 'emit');
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(sender_jid);
var view = this.chatboxviews.get(sender_jid);
expect(view.$el.find('.chat-event').length).toBe(0);
view.showStatusNotification(sender_jid+' '+'is typing');
expect(view.$el.find('.chat-event').length).toBe(1);
var msg = $msg({
from: sender_jid,
to: this.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
this.chatboxes.onMessage(msg);
expect(converse.emit).toHaveBeenCalledWith('message', msg);
expect(view.$el.find('.chat-event').length).toBe(0);
}.bind(converse));
}.bind(converse));
describe("A gone notifciation", function () {
it("will be shown if received", function () {
spyOn(converse, 'emit');
var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// <paused> state
var msg = $msg({
from: sender_jid,
to: this.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
this.chatboxes.onMessage(msg);
expect(converse.emit).toHaveBeenCalledWith('message', msg);
var chatboxview = this.chatboxviews.get(sender_jid);
var $events = chatboxview.$el.find('.chat-event');
expect($events.length).toBe(1);
expect($events.text()).toEqual(mock.cur_names[1].split(' ')[0] + ' has gone away');
}.bind(converse));
}.bind(converse));
}.bind(converse));
}.bind(converse));
describe("Special Messages", function () {
beforeEach(function () {
test_utils.closeAllChatBoxes();
test_utils.removeControlBox();
converse.roster.browserStorage._clear();
test_utils.initConverse();
test_utils.createContacts('current');
test_utils.openControlBox();
test_utils.openContactsPanel();
});
it("'/clear' can be used to clear messages in a conversation", function () {
spyOn(converse, 'emit');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = 'This message is another sent from this chatbox';
// Lets make sure there is at least one message already
// (e.g for when this test is run on its own).
test_utils.sendMessage(view, message);
expect(view.model.messages.length > 0).toBeTruthy();
expect(view.model.messages.browserStorage.records.length > 0).toBeTruthy();
expect(converse.emit).toHaveBeenCalledWith('messageSend', message);
message = '/clear';
spyOn(view, 'onMessageSubmitted').andCallThrough();
spyOn(view, 'clearMessages').andCallThrough();
spyOn(window, 'confirm').andCallFake(function () {
return true;
});
test_utils.sendMessage(view, message);
expect(view.onMessageSubmitted).toHaveBeenCalled();
expect(view.clearMessages).toHaveBeenCalled();
expect(window.confirm).toHaveBeenCalled();
expect(view.model.messages.length, 0); // The messages must be removed from the chatbox
expect(view.model.messages.browserStorage.records.length, 0); // And also from browserStorage
expect(converse.emit.callCount, 1);
expect(converse.emit.mostRecentCall.args, ['messageSend', message]);
}.bind(converse));
}.bind(converse));
describe("A Message Counter", function () {
beforeEach(function () {
converse.clearMsgCounter();
}.bind(converse));
it("is incremented when the message is received and the window is not focused", function () {
spyOn(converse, 'emit');
expect(this.msg_counter).toBe(0);
spyOn(converse, 'incrementMsgCounter').andCallThrough();
$(window).trigger('blur');
var message = 'This message will increment the message counter';
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
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(converse.incrementMsgCounter).toHaveBeenCalled();
expect(this.msg_counter).toBe(1);
expect(converse.emit).toHaveBeenCalledWith('message', msg);
}.bind(converse));
it("is cleared when the window is focused", function () {
spyOn(converse, 'clearMsgCounter').andCallThrough();
runs(function () {
$(window).triggerHandler('blur');
$(window).triggerHandler('focus');
});
waits(50);
runs(function () {
expect(converse.clearMsgCounter).toHaveBeenCalled();
});
}.bind(converse));
it("is not incremented when the message is received and the window is focused", function () {
expect(this.msg_counter).toBe(0);
spyOn(converse, 'incrementMsgCounter').andCallThrough();
$(window).trigger('focus');
var message = 'This message will not increment the message counter';
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
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(converse.incrementMsgCounter).not.toHaveBeenCalled();
expect(this.msg_counter).toBe(0);
}.bind(converse));
}.bind(converse));
}, converse, mock, test_utils));
}));