xmpp.chapril.org-conversejs/spec/notification.js
JC Brand b5eea12d49 Refactor so that message attributes are parsed early
It's better to parse an incoming message stanza early, than to have
all kinds of methods throughout the codebase that does querySelector
etc.

Firstly, it allows us to catch and report errors and malicious stanzas early on.
It also simplifies programming because you don't need to try and
remember how to properly parse a stanza, all the work is done upfront
for you.
2020-04-27 16:34:37 +02:00

205 lines
11 KiB
JavaScript

/*global mock */
const _ = converse.env._;
const $msg = converse.env.$msg;
const u = converse.env.utils;
describe("Notifications", function () {
// Implement the protocol defined in https://xmpp.org/extensions/xep-0313.html#config
describe("When show_desktop_notifications is set to true", function () {
describe("And the desktop is not focused", function () {
describe("an HTML5 Notification", function () {
it("is shown when a new private message is received",
mock.initConverse(['rosterGroupsFetched'], {}, async (done, _converse) => {
await mock.waitForRoster(_converse, 'current');
spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
spyOn(_converse, 'isMessageToHiddenChat').and.returnValue(true);
const message = 'This message will show a desktop notification';
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit',
msg = $msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
id: u.getUniqueId()
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
await _converse.handleMessageStanza(msg); // This will emit 'message'
await u.waitUntil(() => _converse.api.chatviews.get(sender_jid));
expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
expect(_converse.showMessageNotification).toHaveBeenCalled();
done();
}));
it("is shown when you are mentioned in a groupchat",
mock.initConverse(['rosterGroupsFetched'], {}, async (done, _converse) => {
await mock.waitForRoster(_converse, 'current');
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
const view = _converse.api.chatviews.get('lounge@montague.lit');
if (!view.el.querySelectorAll('.chat-area').length) {
view.renderChatArea();
}
let no_notification = false;
if (typeof window.Notification === 'undefined') {
no_notification = true;
window.Notification = function () {
return {
'close': function () {}
};
};
}
spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
const message = 'romeo: This message will show a desktop notification';
const nick = mock.chatroom_names[0],
msg = $msg({
from: 'lounge@montague.lit/'+nick,
id: u.getUniqueId(),
to: 'romeo@montague.lit',
type: 'groupchat'
}).c('body').t(message).tree();
_converse.connection._dataRecv(mock.createRequest(msg));
await new Promise(resolve => view.once('messageInserted', resolve));
await u.waitUntil(() => _converse.areDesktopNotificationsEnabled.calls.count() === 1);
expect(_converse.showMessageNotification).toHaveBeenCalled();
if (no_notification) {
delete window.Notification;
}
done();
}));
it("is shown for headline messages",
mock.initConverse(['rosterGroupsFetched'], {}, async (done, _converse) => {
spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'isMessageToHiddenChat').and.returnValue(true);
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
const stanza = $msg({
'type': 'headline',
'from': 'notify.example.com',
'to': 'romeo@montague.lit',
'xml:lang': 'en'
})
.c('subject').t('SIEVE').up()
.c('body').t('<juliet@example.com> You got mail.').up()
.c('x', {'xmlns': 'jabber:x:oob'})
.c('url').t('imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18');
_converse.connection._dataRecv(mock.createRequest(stanza));
await u.waitUntil(() => _converse.chatboxviews.keys().length);
const view = _converse.chatboxviews.get('notify.example.com');
await new Promise(resolve => view.once('messageInserted', resolve));
expect(_converse.chatboxviews.keys().includes('notify.example.com')).toBeTruthy();
expect(_converse.showMessageNotification).toHaveBeenCalled();
done();
}));
it("is not shown for full JID headline messages if allow_non_roster_messaging is false", mock.initConverse((done, _converse) => {
_converse.allow_non_roster_messaging = false;
spyOn(_converse, 'showMessageNotification').and.callThrough();
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
const stanza = $msg({
'type': 'headline',
'from': 'someone@notify.example.com',
'to': 'romeo@montague.lit',
'xml:lang': 'en'
})
.c('subject').t('SIEVE').up()
.c('body').t('<juliet@example.com> You got mail.').up()
.c('x', {'xmlns': 'jabber:x:oob'})
.c('url').t('imap://romeo@example.com/INBOX;UIDVALIDITY=385759043/;UID=18');
_converse.connection._dataRecv(mock.createRequest(stanza));
expect(
_.includes(_converse.chatboxviews.keys(),
'someone@notify.example.com')
).toBeFalsy();
expect(_converse.showMessageNotification).not.toHaveBeenCalled();
done();
}));
it("is shown when a user changes their chat state (if show_chat_state_notifications is true)",
mock.initConverse(['rosterGroupsFetched'], {show_chat_state_notifications: true},
async (done, _converse) => {
await mock.waitForRoster(_converse, 'current', 3);
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
spyOn(_converse, 'showChatStateNotification');
const jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@montague.lit';
_converse.roster.get(jid).presence.set('show', 'busy'); // This will emit 'contactStatusChanged'
await u.waitUntil(() => _converse.areDesktopNotificationsEnabled.calls.count() === 1);
expect(_converse.showChatStateNotification).toHaveBeenCalled();
done()
}));
});
});
describe("When a new contact request is received", function () {
it("an HTML5 Notification is received", mock.initConverse((done, _converse) => {
spyOn(_converse, 'areDesktopNotificationsEnabled').and.returnValue(true);
spyOn(_converse, 'showContactRequestNotification');
_converse.api.trigger('contactRequest', {'fullname': 'Peter Parker', 'jid': 'peter@parker.com'});
expect(_converse.areDesktopNotificationsEnabled).toHaveBeenCalled();
expect(_converse.showContactRequestNotification).toHaveBeenCalled();
done();
}));
});
});
describe("When play_sounds is set to true", function () {
describe("A notification sound", function () {
it("is played when the current user is mentioned in a groupchat",
mock.initConverse(['rosterGroupsFetched'], {}, async (done, _converse) => {
mock.createContacts(_converse, 'current');
await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
_converse.play_sounds = true;
spyOn(_converse, 'playSoundNotification');
const view = _converse.chatboxviews.get('lounge@montague.lit');
if (!view.el.querySelectorAll('.chat-area').length) {
view.renderChatArea();
}
let text = 'This message will play a sound because it mentions romeo';
let message = $msg({
from: 'lounge@montague.lit/otheruser',
id: '1',
to: 'romeo@montague.lit',
type: 'groupchat'
}).c('body').t(text);
await view.model.handleMessageStanza(message.nodeTree);
await u.waitUntil(() => _converse.playSoundNotification.calls.count());
expect(_converse.playSoundNotification).toHaveBeenCalled();
text = "This message won't play a sound";
message = $msg({
from: 'lounge@montague.lit/otheruser',
id: '2',
to: 'romeo@montague.lit',
type: 'groupchat'
}).c('body').t(text);
await view.model.handleMessageStanza(message.nodeTree);
expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false;
text = "This message won't play a sound because it is sent by romeo";
message = $msg({
from: 'lounge@montague.lit/romeo',
id: '3',
to: 'romeo@montague.lit',
type: 'groupchat'
}).c('body').t(text);
await view.model.handleMessageStanza(message.nodeTree);
expect(_converse.playSoundNotification, 1);
_converse.play_sounds = false;
done();
}));
});
});
});