Bugfix for headline messages.

Couldn't handle messages with no "from" attribute.
Some refactoring to add code that checks if a messages is a headline to the
utils module.
Updated tests. Add sinon so that we can test returned value of spy.
This commit is contained in:
JC Brand 2016-03-28 10:49:52 +00:00
parent b3e9a177e2
commit f353fe8611
9 changed files with 96 additions and 36 deletions

View File

@ -30,7 +30,8 @@
"skeleton-sass": "~2.0.3", "skeleton-sass": "~2.0.3",
"strophejs": "1.2.4", "strophejs": "1.2.4",
"strophejs-plugins": "https://github.com/strophe/strophejs-plugins.git#amd", "strophejs-plugins": "https://github.com/strophe/strophejs-plugins.git#amd",
"bourbon": "~4.2.3" "bourbon": "~4.2.3",
"sinon": "^1.17.3"
}, },
"resolutions": { "resolutions": {
"backbone": "1.1.2" "backbone": "1.1.2"

View File

@ -20,6 +20,7 @@
"global", "global",
"it", "it",
"jasmine", "jasmine",
"sinon",
"module", "module",
"require", "require",
"runs", "runs",

View File

@ -3,13 +3,11 @@
define([ define([
"jquery", "jquery",
"underscore", "underscore",
"utils",
"mock", "mock",
"test_utils" "test_utils"
], function ($, _, mock, test_utils) { ], factory);
return factory($, _, mock, test_utils); } (this, function ($, _, utils, mock, test_utils) {
}
);
} (this, function ($, _, mock, test_utils) {
var $msg = converse_api.env.$msg; var $msg = converse_api.env.$msg;
var Strophe = converse_api.env.Strophe; var Strophe = converse_api.env.Strophe;
var moment = converse_api.env.moment; var moment = converse_api.env.moment;
@ -461,6 +459,7 @@
it("is ignored if it's intended for a different resource", function () { it("is ignored if it's intended for a different resource", function () {
// Send a message from a different resource // Send a message from a different resource
spyOn(converse, 'log'); spyOn(converse, 'log');
spyOn(converse.chatboxes, 'getChatBox');
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
var msg = $msg({ var msg = $msg({
from: sender_jid, from: sender_jid,
@ -471,7 +470,35 @@
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
converse.chatboxes.onMessage(msg); converse.chatboxes.onMessage(msg);
expect(converse.log).toHaveBeenCalledWith( expect(converse.log).toHaveBeenCalledWith(
"Ignore incoming message intended for a different resource: dummy@localhost/some-other-resource", "info"); "onMessage: Ignoring incoming message intended for a different resource: dummy@localhost/some-other-resource", "info");
expect(converse.chatboxes.getChatBox).not.toHaveBeenCalled();
});
it("is ignored if it's a malformed headline message", function () {
/* 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",
"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();
}); });
it("can be a carbon message, as defined in XEP-0280", function () { it("can be a carbon message, as defined in XEP-0280", function () {

View File

@ -2,13 +2,11 @@
(function (root, factory) { (function (root, factory) {
define([ define([
"jquery", "jquery",
"utils",
"mock", "mock",
"test_utils" "test_utils"
], function ($, mock, test_utils) { ], factory);
return factory($, mock, test_utils); } (this, function ($, utils, mock, test_utils) {
}
);
} (this, function ($, mock, test_utils) {
"use strict"; "use strict";
var $msg = converse_api.env.$msg, var $msg = converse_api.env.$msg,
_ = converse_api.env._; _ = converse_api.env._;
@ -30,6 +28,7 @@
* </x> * </x>
* </message> * </message>
*/ */
sinon.spy(utils, 'isHeadlineMessage');
runs(function () { runs(function () {
var stanza = $msg({ var stanza = $msg({
'type': 'headline', 'type': 'headline',
@ -50,6 +49,9 @@
converse.chatboxviews.keys(), converse.chatboxviews.keys(),
'notify.example.com') 'notify.example.com')
).toBeTruthy(); ).toBeTruthy();
expect(utils.isHeadlineMessage.called).toBeTruthy();
expect(utils.isHeadlineMessage.returned(true)).toBeTruthy();
utils.isHeadlineMessage.restore(); // unwraps
}); });
}); });
}); });

View File

@ -1308,22 +1308,39 @@
}, },
onMessage: function (message) { onMessage: function (message) {
/* Handler method for all incoming single-user chat "message" stanzas. /* Handler method for all incoming single-user chat "message"
* stanzas.
*/ */
var $message = $(message), var $message = $(message),
contact_jid, $forwarded, $delay, from_bare_jid, from_resource, is_me, msgid, contact_jid, $forwarded, $delay, from_bare_jid,
from_resource, is_me, msgid,
chatbox, resource, chatbox, resource,
from_jid = $message.attr('from'), from_jid = $message.attr('from'),
to_jid = $message.attr('to'), to_jid = $message.attr('to'),
to_resource = Strophe.getResourceFromJid(to_jid); to_resource = Strophe.getResourceFromJid(to_jid);
if (to_resource && to_resource !== converse.resource) { if (to_resource && to_resource !== converse.resource) {
converse.log('Ignore incoming message intended for a different resource: '+to_jid, 'info'); converse.log(
'onMessage: Ignoring incoming message intended for a different resource: '+to_jid,
'info'
);
return true; return true;
} } else if (from_jid === converse.connection.jid) {
if (from_jid === converse.connection.jid) { // FIXME: Forwarded messages should be sent to specific
// FIXME: Forwarded messages should be sent to specific resources, not broadcasted // resources, not broadcasted
converse.log("Ignore incoming message sent from this client's JID: "+from_jid, 'info'); converse.log(
"onMessage: Ignoring incoming message sent from this client's JID: "+from_jid,
'info'
);
return true;
} else if (utils.isHeadlineMessage(message)) {
// XXX: Ideally we wouldn't have to check for headline
// messages, but Prosody sends headline messages with the
// wrong type ('chat'), so we need to filter them out here.
converse.log(
"onMessage: Ignoring incoming headline message sent with type 'chat' from JID: "+from_jid,
'info'
);
return true; return true;
} }
$forwarded = $message.find('forwarded'); $forwarded = $message.find('forwarded');

View File

@ -22,12 +22,9 @@
var onHeadlineMessage = function (message) { var onHeadlineMessage = function (message) {
/* Handler method for all incoming messages of type "headline". /* Handler method for all incoming messages of type "headline".
*/ */
var $message = $(message), from_jid = $message.attr('from'); var $message = $(message),
if ($message.attr('type') === 'headline' || from_jid.indexOf('@') === -1) { from_jid = $message.attr('from');
// Some servers (I'm looking at you Prosody) don't set the message if (utils.isHeadlineMessage(message)) {
// type to "headline" when sending server messages. For now we
// check if an @ signal is included, and if not, we assume it's
// a headline message.
converse.chatboxes.create({ converse.chatboxes.create({
'id': from_jid, 'id': from_jid,
'jid': from_jid, 'jid': from_jid,

View File

@ -68,19 +68,16 @@
return true; return true;
}; };
converse.shouldNotifyOfMessage = function ($message) { converse.shouldNotifyOfMessage = function (message) {
/* Is this a message worthy of notification? /* Is this a message worthy of notification?
*/ */
var $forwarded = $message.find('forwarded'); var $message = $(message),
$forwarded = $message.find('forwarded');
if ($forwarded.length) { if ($forwarded.length) {
return false; return false;
} } else if ($message.attr('type') === 'groupchat') {
if ($message.attr('type') === 'groupchat') {
return converse.shouldNotifyOfGroupMessage($message); return converse.shouldNotifyOfGroupMessage($message);
} } else if (utils.isHeadlineMessage(message)) {
if ($message.attr('type') === 'headline' || $message.attr('from').indexOf('@') === -1) {
// XXX: 2nd check is workaround for Prosody which doesn't give type "headline"
// We want to show notifications for headline messages. // We want to show notifications for headline messages.
return true; return true;
} }
@ -198,7 +195,7 @@
* to play sounds and show HTML5 notifications. * to play sounds and show HTML5 notifications.
*/ */
var $message = $(message); var $message = $(message);
if (!converse.shouldNotifyOfMessage($message)) { if (!converse.shouldNotifyOfMessage(message)) {
return false; return false;
} }
converse.playSoundNotification($message); converse.playSoundNotification($message);

View File

@ -125,6 +125,21 @@
return str; return str;
}, },
isHeadlineMessage: function (message) {
var $message = $(message),
from_jid = $message.attr('from');
if ($message.attr('type') === 'headline' ||
// Some servers (I'm looking at you Prosody) don't set the message
// type to "headline" when sending server messages. For now we
// check if an @ signal is included, and if not, we assume it's
// a headline message.
(typeof from_jid !== 'undefined' && from_jid.indexOf('@') === -1)
) {
return true;
}
return false;
},
refreshWebkit: function () { refreshWebkit: function () {
/* This works around a webkit bug. Refreshes the browser's viewport, /* This works around a webkit bug. Refreshes the browser's viewport,
* otherwise chatboxes are not moved along when one is closed. * otherwise chatboxes are not moved along when one is closed.

View File

@ -1,6 +1,7 @@
// Extra test dependencies // Extra test dependencies
config.paths.mock = "tests/mock"; config.paths.mock = "tests/mock";
config.paths.test_utils = "tests/utils"; config.paths.test_utils = "tests/utils";
config.paths.sinon = "components/sinon/lib/sinon";
config.paths.jasmine = "components/jasmine/lib/jasmine-core/jasmine"; config.paths.jasmine = "components/jasmine/lib/jasmine-core/jasmine";
config.paths["jasmine-html"] = "components/jasmine/lib/jasmine-core/jasmine-html"; config.paths["jasmine-html"] = "components/jasmine/lib/jasmine-core/jasmine-html";
config.paths["console-runner"] = "node_modules/phantom-jasmine/lib/console-runner"; config.paths["console-runner"] = "node_modules/phantom-jasmine/lib/console-runner";
@ -34,9 +35,11 @@ require([
"jquery", "jquery",
"converse", "converse",
"mock", "mock",
"jasmine-html" "jasmine-html",
], function($, converse, mock, jasmine) { "sinon"
], function($, converse, mock, jasmine, sinon) {
// Set up converse.js // Set up converse.js
window.sinon = sinon;
window.converse_api = converse; window.converse_api = converse;
window.localStorage.clear(); window.localStorage.clear();
window.sessionStorage.clear(); window.sessionStorage.clear();