Fix failing tests

This commit is contained in:
JC Brand 2017-12-03 12:21:57 +01:00
parent 393bbe020e
commit fc94127014
4 changed files with 469 additions and 404 deletions

View File

@ -10,6 +10,7 @@
} (this, function ($, jasmine, utils, converse, mock, test_utils) { } (this, function ($, jasmine, utils, converse, mock, test_utils) {
"use strict"; "use strict";
var _ = converse.env._; var _ = converse.env._;
var $iq = converse.env.$iq;
var $msg = converse.env.$msg; var $msg = converse.env.$msg;
var Strophe = converse.env.Strophe; var Strophe = converse.env.Strophe;
var moment = converse.env.moment; var moment = converse.env.moment;
@ -22,30 +23,37 @@
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { function (done, _converse) {
test_utils.createContacts(_converse, 'current'); test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
test_utils.openControlBox(); .then(function () {
test_utils.openContactsPanel(_converse); return test_utils.waitUntil(function () {
expect(_converse.chatboxes.length).toEqual(1); return _converse.xmppstatus.get('fullname');
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; }, 300);
var message = '/me is tired'; }).then(function () {
var msg = $msg({ test_utils.createContacts(_converse, 'current');
from: sender_jid, test_utils.openControlBox();
to: _converse.connection.jid, test_utils.openContactsPanel(_converse);
type: 'chat', expect(_converse.chatboxes.length).toEqual(1);
id: (new Date()).getTime() var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
}).c('body').t(message).up() var message = '/me is tired';
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree(); 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); _converse.chatboxes.onMessage(msg);
var view = _converse.chatboxviews.get(sender_jid); var view = _converse.chatboxviews.get(sender_jid);
expect(_.includes(view.$el.find('.chat-msg-author').text(), '**Max Frankfurter')).toBeTruthy(); expect(_.includes(view.$el.find('.chat-msg-author').text(), '**Max Frankfurter')).toBeTruthy();
expect(view.$el.find('.chat-msg-content').text()).toBe(' is tired'); expect(view.$el.find('.chat-msg-content').text()).toBe(' is tired');
message = '/me is as well'; message = '/me is as well';
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
expect(_.includes(view.$el.find('.chat-msg-author:last').text(), '**Max Mustermann')).toBeTruthy(); 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'); expect(view.$el.find('.chat-msg-content:last').text()).toBe(' is as well');
done(); done();
});
})); }));
it("is created when you click on a roster item", it("is created when you click on a roster item",
@ -527,7 +535,8 @@
describe("A Chat Message", function () { describe("A Chat Message", function () {
describe("when received from someone else", function () { describe("when received from someone else", function () {
it("can be received which will open a chatbox and be displayed inside it",
it("will open a chatbox and be displayed inside it",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { function (done, _converse) {
@ -953,47 +962,55 @@
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { function (done, _converse) {
test_utils.createContacts(_converse, 'current'); var contact, sent_stanza, IQ_id, stanza;
test_utils.openControlBox(); test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
test_utils.openContactsPanel(_converse); .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 // Send a message from a different resource
spyOn(_converse, 'log'); spyOn(_converse, 'log');
var msgtext = 'This is a sent carbon message'; var msgtext = 'This is a sent carbon message';
var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
var msg = $msg({ var msg = $msg({
'from': _converse.bare_jid, 'from': _converse.bare_jid,
'id': (new Date()).getTime(), 'id': (new Date()).getTime(),
'to': _converse.connection.jid, 'to': _converse.connection.jid,
'type': 'chat', 'type': 'chat',
'xmlns': 'jabber:client' 'xmlns': 'jabber:client'
}).c('sent', {'xmlns': 'urn:xmpp:carbons:2'}) }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
.c('message', { .c('message', {
'xmlns': 'jabber:client', 'xmlns': 'jabber:client',
'from': _converse.bare_jid+'/another-resource', 'from': _converse.bare_jid+'/another-resource',
'to': recipient_jid, 'to': recipient_jid,
'type': 'chat' 'type': 'chat'
}).c('body').t(msgtext).tree(); }).c('body').t(msgtext).tree();
_converse.chatboxes.onMessage(msg); _converse.chatboxes.onMessage(msg);
// Check that the chatbox and its view now exist // Check that the chatbox and its view now exist
var chatbox = _converse.chatboxes.get(recipient_jid); var chatbox = _converse.chatboxes.get(recipient_jid);
var chatboxview = _converse.chatboxviews.get(recipient_jid); var chatboxview = _converse.chatboxviews.get(recipient_jid);
expect(chatbox).toBeDefined(); expect(chatbox).toBeDefined();
expect(chatboxview).toBeDefined(); expect(chatboxview).toBeDefined();
// Check that the message was received and check the message parameters // Check that the message was received and check the message parameters
expect(chatbox.messages.length).toEqual(1); expect(chatbox.messages.length).toEqual(1);
var msg_obj = chatbox.messages.models[0]; var msg_obj = chatbox.messages.models[0];
expect(msg_obj.get('message')).toEqual(msgtext); expect(msg_obj.get('message')).toEqual(msgtext);
expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname')); expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
expect(msg_obj.get('sender')).toEqual('me'); expect(msg_obj.get('sender')).toEqual('me');
expect(msg_obj.get('delayed')).toEqual(false); expect(msg_obj.get('delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM // Now check that the message appears inside the chatbox in the DOM
var $chat_content = chatboxview.$el.find('.chat-content'); var $chat_content = chatboxview.$el.find('.chat-content');
var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text(); var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
expect(msg_txt).toEqual(msgtext); expect(msg_txt).toEqual(msgtext);
done(); done();
});
})); }));
it("will be discarded if it's a malicious message meant to look like a carbon copy", it("will be discarded if it's a malicious message meant to look like a carbon copy",
@ -1561,41 +1578,48 @@
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { function (done, _converse) {
test_utils.createContacts(_converse, 'current'); 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');
// Send a message from a different resource
spyOn(_converse, 'log');
var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, recipient_jid);
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('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg);
// Send a message from a different resource // Check that the chatbox and its view now exist
spyOn(_converse, 'log'); var chatbox = _converse.chatboxes.get(recipient_jid);
var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var chatboxview = _converse.chatboxviews.get(recipient_jid);
test_utils.openChatBoxFor(_converse, recipient_jid); // Check that the message was received and check the message parameters
var msg = $msg({ expect(chatbox.messages.length).toEqual(1);
'from': _converse.bare_jid, var msg_obj = chatbox.messages.models[0];
'id': (new Date()).getTime(), expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
'to': _converse.connection.jid, expect(msg_obj.get('sender')).toEqual('me');
'type': 'chat', expect(msg_obj.get('delayed')).toEqual(false);
'xmlns': 'jabber:client' var $chat_content = chatboxview.$el.find('.chat-content');
}).c('sent', {'xmlns': 'urn:xmpp:carbons:2'}) var status_text = $chat_content.find('.chat-info.chat-event').text();
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) expect(status_text).toBe('Typing from another device');
.c('message', { done();
'xmlns': 'jabber:client', });
'from': _converse.bare_jid+'/another-resource',
'to': recipient_jid,
'type': 'chat'
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).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);
// 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('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
expect(msg_obj.get('sender')).toEqual('me');
expect(msg_obj.get('delayed')).toEqual(false);
var $chat_content = chatboxview.$el.find('.chat-content');
var status_text = $chat_content.find('.chat-info.chat-event').text();
expect(status_text).toBe('Typing from another device');
done();
})); }));
}); });
@ -1702,41 +1726,48 @@
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { function (done, _converse) {
test_utils.createContacts(_converse, 'current'); 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');
// Send a message from a different resource
spyOn(_converse, 'log');
var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, recipient_jid);
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('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
_converse.chatboxes.onMessage(msg);
// Send a message from a different resource // Check that the chatbox and its view now exist
spyOn(_converse, 'log'); var chatbox = _converse.chatboxes.get(recipient_jid);
var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost'; var chatboxview = _converse.chatboxviews.get(recipient_jid);
test_utils.openChatBoxFor(_converse, recipient_jid); // Check that the message was received and check the message parameters
var msg = $msg({ expect(chatbox.messages.length).toEqual(1);
'from': _converse.bare_jid, var msg_obj = chatbox.messages.models[0];
'id': (new Date()).getTime(), expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
'to': _converse.connection.jid, expect(msg_obj.get('sender')).toEqual('me');
'type': 'chat', expect(msg_obj.get('delayed')).toEqual(false);
'xmlns': 'jabber:client' var $chat_content = chatboxview.$el.find('.chat-content');
}).c('sent', {'xmlns': 'urn:xmpp:carbons:2'}) var status_text = $chat_content.find('.chat-info.chat-event').text();
.c('forwarded', {'xmlns': 'urn:xmpp:forward:0'}) expect(status_text).toBe('Stopped typing on the other device');
.c('message', { done();
'xmlns': 'jabber:client', });
'from': _converse.bare_jid+'/another-resource',
'to': recipient_jid,
'type': 'chat'
}).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).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);
// 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('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
expect(msg_obj.get('sender')).toEqual('me');
expect(msg_obj.get('delayed')).toEqual(false);
var $chat_content = chatboxview.$el.find('.chat-content');
var status_text = $chat_content.find('.chat-info.chat-event').text();
expect(status_text).toBe('Stopped typing on the other device');
done();
})); }));
}); });

View File

@ -546,8 +546,16 @@
null, ['rosterGroupsFetched'], {}, null, ['rosterGroupsFetched'], {},
function (done, _converse) { function (done, _converse) {
test_utils.createContacts(_converse, 'current');
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () { 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');
return test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
}).then(function () {
var view = _converse.chatboxviews.get('lounge@localhost'); var view = _converse.chatboxviews.get('lounge@localhost');
if (!view.$el.find('.chat-area').length) { view.renderChatArea(); } if (!view.$el.find('.chat-area').length) { view.renderChatArea(); }
var message = '/me is tired'; var message = '/me is tired';

View File

@ -54,178 +54,185 @@
{ roster_groups: false }, { roster_groups: false },
function (done, _converse) { function (done, _converse) {
/* The process by which a user subscribes to a contact, including var contact, sent_stanza, IQ_id, stanza;
* the interaction between roster items and subscription states. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
*/ .then(function () {
var contact, stanza, sent_stanza, IQ_id; return test_utils.waitUntil(function () {
test_utils.openControlBox(_converse); return _converse.xmppstatus.get('fullname');
var panel = _converse.chatboxviews.get('controlbox').contactspanel; }, 300);
spyOn(panel, "addContactFromForm").and.callThrough(); }).then(function () {
spyOn(_converse.roster, "addAndSubscribe").and.callThrough(); /* The process by which a user subscribes to a contact, including
spyOn(_converse.roster, "addContact").and.callThrough(); * the interaction between roster items and subscription states.
spyOn(_converse.roster, "sendContactAddIQ").and.callThrough(); */
spyOn(_converse.api.vcard, "get").and.callThrough(); test_utils.openControlBox(_converse);
var sendIQ = _converse.connection.sendIQ; var panel = _converse.chatboxviews.get('controlbox').contactspanel;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { spyOn(panel, "addContactFromForm").and.callThrough();
sent_stanza = iq; spyOn(_converse.roster, "addAndSubscribe").and.callThrough();
IQ_id = sendIQ.bind(this)(iq, callback, errback); spyOn(_converse.roster, "addContact").and.callThrough();
}); spyOn(_converse.roster, "sendContactAddIQ").and.callThrough();
panel.delegateEvents(); // Rebind all events so that our spy gets called spyOn(_converse.api.vcard, "get").and.callThrough();
/* Add a new contact through the UI */ var sendIQ = _converse.connection.sendIQ;
var form = panel.el.querySelector('form.add-xmpp-contact'); spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
expect(_.isNull(form)).toBeTruthy(); sent_stanza = iq;
IQ_id = sendIQ.bind(this)(iq, callback, errback);
});
panel.delegateEvents(); // Rebind all events so that our spy gets called
// Click the "Add a contact" link. /* Add a new contact through the UI */
panel.$('.toggle-xmpp-contact-form').click(); var form = panel.el.querySelector('form.add-xmpp-contact');
expect(_.isNull(form)).toBeTruthy();
// Check that the form appears // Click the "Add a contact" link.
form = panel.el.querySelector('form.add-xmpp-contact'); panel.$('.toggle-xmpp-contact-form').click();
expect(form.parentElement.offsetHeight).not.toBe(0);
expect(_.includes(form.parentElement.classList, 'collapsed')).toBeFalsy();
// Fill in the form and submit // Check that the form appears
$(form).find('input').val('contact@example.org'); form = panel.el.querySelector('form.add-xmpp-contact');
$(form).submit(); expect(form.parentElement.offsetHeight).not.toBe(0);
expect(_.includes(form.parentElement.classList, 'collapsed')).toBeFalsy();
/* In preparation for being able to render the contact in the // Fill in the form and submit
* user's client interface and for the server to keep track of the $(form).find('input').val('contact@example.org');
* subscription, the user's client SHOULD perform a "roster set" $(form).submit();
* for the new roster item.
*/
expect(panel.addContactFromForm).toHaveBeenCalled();
expect(_converse.roster.addAndSubscribe).toHaveBeenCalled();
expect(_converse.roster.addContact).toHaveBeenCalled();
// The form should not be visible anymore (by virtue of its /* In preparation for being able to render the contact in the
// parent being collapsed) * user's client interface and for the server to keep track of the
expect(form.parentElement.offsetHeight).toBe(0); * subscription, the user's client SHOULD perform a "roster set"
expect(_.includes(form.parentElement.classList, 'collapsed')).toBeTrue; * for the new roster item.
*/
expect(panel.addContactFromForm).toHaveBeenCalled();
expect(_converse.roster.addAndSubscribe).toHaveBeenCalled();
expect(_converse.roster.addContact).toHaveBeenCalled();
/* _converse request consists of sending an IQ // The form should not be visible anymore (by virtue of its
* stanza of type='set' containing a <query/> element qualified by // parent being collapsed)
* the 'jabber:iq:roster' namespace, which in turn contains an expect(form.parentElement.offsetHeight).toBe(0);
* <item/> element that defines the new roster item; the <item/> expect(_.includes(form.parentElement.classList, 'collapsed')).toBeTrue;
* element MUST possess a 'jid' attribute, MAY possess a 'name'
* attribute, MUST NOT possess a 'subscription' attribute, and MAY
* contain one or more <group/> child elements:
*
* <iq type='set' id='set1'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
"<query xmlns='jabber:iq:roster'>"+
"<item jid='contact@example.org' name='contact@example.org'/>"+
"</query>"+
"</iq>"
);
/* As a result, the user's server (1) MUST initiate a roster push
* for the new roster item to all available resources associated
* with _converse user that have requested the roster, setting the
* 'subscription' attribute to a value of "none"; and (2) MUST
* reply to the sending resource with an IQ result indicating the
* success of the roster set:
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='none'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
var create = _converse.roster.create;
var sent_stanzas = [];
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
sent_stanza = stanza;
sent_stanzas.push(stanza.toLocaleString());
});
spyOn(_converse.roster, 'create').and.callFake(function () {
contact = create.apply(_converse.roster, arguments);
spyOn(contact, 'subscribe').and.callThrough();
return contact;
});
stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'none',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
/*
* <iq type='result' id='set1'/>
*/
stanza = $iq({'type': 'result', 'id':IQ_id});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
// A contact should now have been created /* _converse request consists of sending an IQ
expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy(); * stanza of type='set' containing a <query/> element qualified by
expect(contact.get('jid')).toBe('contact@example.org'); * the 'jabber:iq:roster' namespace, which in turn contains an
expect(_converse.api.vcard.get).toHaveBeenCalled(); * <item/> element that defines the new roster item; the <item/>
* element MUST possess a 'jid' attribute, MAY possess a 'name'
* attribute, MUST NOT possess a 'subscription' attribute, and MAY
* contain one or more <group/> child elements:
*
* <iq type='set' id='set1'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe(
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
"<query xmlns='jabber:iq:roster'>"+
"<item jid='contact@example.org' name='contact@example.org'/>"+
"</query>"+
"</iq>"
);
/* As a result, the user's server (1) MUST initiate a roster push
* for the new roster item to all available resources associated
* with _converse user that have requested the roster, setting the
* 'subscription' attribute to a value of "none"; and (2) MUST
* reply to the sending resource with an IQ result indicating the
* success of the roster set:
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='none'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
var create = _converse.roster.create;
var sent_stanzas = [];
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
sent_stanza = stanza;
sent_stanzas.push(stanza.toLocaleString());
});
spyOn(_converse.roster, 'create').and.callFake(function () {
contact = create.apply(_converse.roster, arguments);
spyOn(contact, 'subscribe').and.callThrough();
return contact;
});
stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'none',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
/*
* <iq type='result' id='set1'/>
*/
stanza = $iq({'type': 'result', 'id':IQ_id});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
/* To subscribe to the contact's presence information, // A contact should now have been created
* the user's client MUST send a presence stanza of expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy();
* type='subscribe' to the contact: expect(contact.get('jid')).toBe('contact@example.org');
* expect(_converse.api.vcard.get).toHaveBeenCalled();
* <presence to='contact@example.org' type='subscribe'/>
*/
test_utils.waitUntil(function () {
return sent_stanzas.length == 1;
}, 300).then(function () {
expect(contact.subscribe).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
"<presence to='contact@example.org' type='subscribe' xmlns='jabber:client'>"+
"<nick xmlns='http://jabber.org/protocol/nick'>Max Mustermann</nick>"+
"</presence>"
);
/* As a result, the user's server MUST initiate a second roster
* push to all of the user's available resources that have
* requested the roster, setting the contact to the pending
* sub-state of the 'none' subscription state; _converse pending
* sub-state is denoted by the inclusion of the ask='subscribe'
* attribute in the roster item:
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='none'
* ask='subscribe'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
spyOn(_converse.roster, "updateContact").and.callThrough();
stanza = $iq({'type': 'set', 'from': 'dummy@localhost'})
.c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'none',
'ask': 'subscribe',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.updateContact).toHaveBeenCalled();
// Check that the user is now properly shown as a pending
// contact in the roster.
test_utils.waitUntil(function () {
return $('a:contains("Pending contacts")').length;
}, 300).then(function () {
/* To subscribe to the contact's presence information,
* the user's client MUST send a presence stanza of
* type='subscribe' to the contact:
*
* <presence to='contact@example.org' type='subscribe'/>
*/
return test_utils.waitUntil(function () {
return sent_stanzas.length == 1;
}, 300);
}).then(function () {
expect(contact.subscribe).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
"<presence to='contact@example.org' type='subscribe' xmlns='jabber:client'>"+
"<nick xmlns='http://jabber.org/protocol/nick'>Max Mustermann</nick>"+
"</presence>"
);
/* As a result, the user's server MUST initiate a second roster
* push to all of the user's available resources that have
* requested the roster, setting the contact to the pending
* sub-state of the 'none' subscription state; _converse pending
* sub-state is denoted by the inclusion of the ask='subscribe'
* attribute in the roster item:
*
* <iq type='set'>
* <query xmlns='jabber:iq:roster'>
* <item
* jid='contact@example.org'
* subscription='none'
* ask='subscribe'
* name='MyContact'>
* <group>MyBuddies</group>
* </item>
* </query>
* </iq>
*/
spyOn(_converse.roster, "updateContact").and.callThrough();
stanza = $iq({'type': 'set', 'from': 'dummy@localhost'})
.c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',
'subscription': 'none',
'ask': 'subscribe',
'name': 'contact@example.org'});
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(_converse.roster.updateContact).toHaveBeenCalled();
// Check that the user is now properly shown as a pending
// contact in the roster.
return test_utils.waitUntil(function () {
return $('a:contains("Pending contacts")').length;
}, 300);
}).then(function () {
var $header = $('a:contains("Pending contacts")'); var $header = $('a:contains("Pending contacts")');
expect($header.length).toBe(1); expect($header.length).toBe(1);
expect($header.is(":visible")).toBeTruthy(); expect($header.is(":visible")).toBeTruthy();
@ -234,13 +241,13 @@
spyOn(contact, "ackSubscribe").and.callThrough(); spyOn(contact, "ackSubscribe").and.callThrough();
/* Here we assume the "happy path" that the contact /* Here we assume the "happy path" that the contact
* approves the subscription request * approves the subscription request
* *
* <presence * <presence
* to='user@example.com' * to='user@example.com'
* from='contact@example.org' * from='contact@example.org'
* type='subscribed'/> * type='subscribed'/>
*/ */
stanza = $pres({ stanza = $pres({
'to': _converse.bare_jid, 'to': _converse.bare_jid,
'from': 'contact@example.org', 'from': 'contact@example.org',
@ -249,31 +256,31 @@
sent_stanza = ""; // Reset sent_stanza = ""; // Reset
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
/* Upon receiving the presence stanza of type "subscribed", /* Upon receiving the presence stanza of type "subscribed",
* the user SHOULD acknowledge receipt of that * the user SHOULD acknowledge receipt of that
* subscription state notification by sending a presence * subscription state notification by sending a presence
* stanza of type "subscribe". * stanza of type "subscribe".
*/ */
expect(contact.ackSubscribe).toHaveBeenCalled(); expect(contact.ackSubscribe).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec) expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
"<presence type='subscribe' to='contact@example.org' xmlns='jabber:client'/>" "<presence type='subscribe' to='contact@example.org' xmlns='jabber:client'/>"
); );
/* The user's server MUST initiate a roster push to all of the user's /* The user's server MUST initiate a roster push to all of the user's
* available resources that have requested the roster, * available resources that have requested the roster,
* containing an updated roster item for the contact with * containing an updated roster item for the contact with
* the 'subscription' attribute set to a value of "to"; * the 'subscription' attribute set to a value of "to";
* *
* <iq type='set'> * <iq type='set'>
* <query xmlns='jabber:iq:roster'> * <query xmlns='jabber:iq:roster'>
* <item * <item
* jid='contact@example.org' * jid='contact@example.org'
* subscription='to' * subscription='to'
* name='MyContact'> * name='MyContact'>
* <group>MyBuddies</group> * <group>MyBuddies</group>
* </item> * </item>
* </query> * </query>
* </iq> * </iq>
*/ */
IQ_id = _converse.connection.getUniqueId('roster'); IQ_id = _converse.connection.getUniqueId('roster');
stanza = $iq({'type': 'set', 'id': IQ_id}) stanza = $iq({'type': 'set', 'id': IQ_id})
.c('query', {'xmlns': 'jabber:iq:roster'}) .c('query', {'xmlns': 'jabber:iq:roster'})
@ -303,22 +310,22 @@
expect(contact.get('chat_status')).toBe('offline'); expect(contact.get('chat_status')).toBe('offline');
/* <presence /* <presence
* from='contact@example.org/resource' * from='contact@example.org/resource'
* to='user@example.com/resource'/> * to='user@example.com/resource'/>
*/ */
stanza = $pres({'to': _converse.bare_jid, 'from': 'contact@example.org/resource'}); stanza = $pres({'to': _converse.bare_jid, 'from': 'contact@example.org/resource'});
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
// Now the contact should also be online. // Now the contact should also be online.
expect(contact.get('chat_status')).toBe('online'); expect(contact.get('chat_status')).toBe('online');
/* Section 8.3. Creating a Mutual Subscription /* Section 8.3. Creating a Mutual Subscription
* *
* If the contact wants to create a mutual subscription, * If the contact wants to create a mutual subscription,
* the contact MUST send a subscription request to the * the contact MUST send a subscription request to the
* user. * user.
* *
* <presence from='contact@example.org' to='user@example.com' type='subscribe'/> * <presence from='contact@example.org' to='user@example.com' type='subscribe'/>
*/ */
spyOn(contact, 'authorize').and.callThrough(); spyOn(contact, 'authorize').and.callThrough();
spyOn(_converse.roster, 'handleIncomingSubscription').and.callThrough(); spyOn(_converse.roster, 'handleIncomingSubscription').and.callThrough();
stanza = $pres({ stanza = $pres({
@ -329,32 +336,32 @@
expect(_converse.roster.handleIncomingSubscription).toHaveBeenCalled(); expect(_converse.roster.handleIncomingSubscription).toHaveBeenCalled();
/* The user's client MUST send a presence stanza of type /* The user's client MUST send a presence stanza of type
* "subscribed" to the contact in order to approve the * "subscribed" to the contact in order to approve the
* subscription request. * subscription request.
* *
* <presence to='contact@example.org' type='subscribed'/> * <presence to='contact@example.org' type='subscribed'/>
*/ */
expect(contact.authorize).toHaveBeenCalled(); expect(contact.authorize).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( expect(sent_stanza.toLocaleString()).toBe(
"<presence to='contact@example.org' type='subscribed' xmlns='jabber:client'/>" "<presence to='contact@example.org' type='subscribed' xmlns='jabber:client'/>"
); );
/* As a result, the user's server MUST initiate a /* As a result, the user's server MUST initiate a
* roster push containing a roster item for the * roster push containing a roster item for the
* contact with the 'subscription' attribute set to * contact with the 'subscription' attribute set to
* a value of "both". * a value of "both".
* *
* <iq type='set'> * <iq type='set'>
* <query xmlns='jabber:iq:roster'> * <query xmlns='jabber:iq:roster'>
* <item * <item
* jid='contact@example.org' * jid='contact@example.org'
* subscription='both' * subscription='both'
* name='MyContact'> * name='MyContact'>
* <group>MyBuddies</group> * <group>MyBuddies</group>
* </item> * </item>
* </query> * </query>
* </iq> * </iq>
*/ */
stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'}) stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', { .c('item', {
'jid': 'contact@example.org', 'jid': 'contact@example.org',
@ -368,7 +375,6 @@
expect($contacts.hasClass('both')).toBeTruthy(); expect($contacts.hasClass('both')).toBeTruthy();
done(); done();
}); });
});
})); }));
it("Alternate Flow: Contact Declines Subscription Request", it("Alternate Flow: Contact Declines Subscription Request",
@ -377,8 +383,8 @@
function (done, _converse) { function (done, _converse) {
/* The process by which a user subscribes to a contact, including /* The process by which a user subscribes to a contact, including
* the interaction between roster items and subscription states. * the interaction between roster items and subscription states.
*/ */
var contact, stanza, sent_stanza, sent_IQ; var contact, stanza, sent_stanza, sent_IQ;
test_utils.openControlBox(_converse); test_utils.openControlBox(_converse);
// Add a new roster contact via roster push // Add a new roster contact via roster push
@ -401,32 +407,32 @@
sent_IQ = iq; sent_IQ = iq;
}); });
/* We now assume the contact declines the subscription /* We now assume the contact declines the subscription
* requests. * requests.
* *
/* Upon receiving the presence stanza of type "unsubscribed" * Upon receiving the presence stanza of type "unsubscribed"
* addressed to the user, the user's server (1) MUST deliver * addressed to the user, the user's server (1) MUST deliver
* that presence stanza to the user and (2) MUST initiate a * that presence stanza to the user and (2) MUST initiate a
* roster push to all of the user's available resources that * roster push to all of the user's available resources that
* have requested the roster, containing an updated roster * have requested the roster, containing an updated roster
* item for the contact with the 'subscription' attribute * item for the contact with the 'subscription' attribute
* set to a value of "none" and with no 'ask' attribute: * set to a value of "none" and with no 'ask' attribute:
* *
* <presence * <presence
* from='contact@example.org' * from='contact@example.org'
* to='user@example.com' * to='user@example.com'
* type='unsubscribed'/> * type='unsubscribed'/>
* *
* <iq type='set'> * <iq type='set'>
* <query xmlns='jabber:iq:roster'> * <query xmlns='jabber:iq:roster'>
* <item * <item
* jid='contact@example.org' * jid='contact@example.org'
* subscription='none' * subscription='none'
* name='MyContact'> * name='MyContact'>
* <group>MyBuddies</group> * <group>MyBuddies</group>
* </item> * </item>
* </query> * </query>
* </iq> * </iq>
*/ */
// FIXME: also add the <iq> // FIXME: also add the <iq>
stanza = $pres({ stanza = $pres({
'to': _converse.bare_jid, 'to': _converse.bare_jid,
@ -436,18 +442,18 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
/* Upon receiving the presence stanza of type "unsubscribed", /* Upon receiving the presence stanza of type "unsubscribed",
* the user SHOULD acknowledge receipt of that subscription * the user SHOULD acknowledge receipt of that subscription
* state notification through either "affirming" it by * state notification through either "affirming" it by
* sending a presence stanza of type "unsubscribe * sending a presence stanza of type "unsubscribe
*/ */
expect(contact.ackUnsubscribe).toHaveBeenCalled(); expect(contact.ackUnsubscribe).toHaveBeenCalled();
expect(sent_stanza.toLocaleString()).toBe( expect(sent_stanza.toLocaleString()).toBe(
"<presence type='unsubscribe' to='contact@example.org' xmlns='jabber:client'/>" "<presence type='unsubscribe' to='contact@example.org' xmlns='jabber:client'/>"
); );
/* _converse.js will then also automatically remove the /* _converse.js will then also automatically remove the
* contact from the user's roster. * contact from the user's roster.
*/ */
expect(sent_IQ.toLocaleString()).toBe( expect(sent_IQ.toLocaleString()).toBe(
"<iq type='set' xmlns='jabber:client'>"+ "<iq type='set' xmlns='jabber:client'>"+
"<query xmlns='jabber:iq:roster'>"+ "<query xmlns='jabber:iq:roster'>"+
@ -486,24 +492,24 @@
expect(window.confirm).toHaveBeenCalled(); expect(window.confirm).toHaveBeenCalled();
/* Section 8.6 Removing a Roster Item and Cancelling All /* Section 8.6 Removing a Roster Item and Cancelling All
* Subscriptions * Subscriptions
* *
* First the user is removed from the roster * First the user is removed from the roster
* Because there may be many steps involved in completely * Because there may be many steps involved in completely
* removing a roster item and cancelling subscriptions in * removing a roster item and cancelling subscriptions in
* both directions, the roster management protocol includes * both directions, the roster management protocol includes
* a "shortcut" method for doing so. The process may be * a "shortcut" method for doing so. The process may be
* initiated no matter what the current subscription state * initiated no matter what the current subscription state
* is by sending a roster set containing an item for the * is by sending a roster set containing an item for the
* contact with the 'subscription' attribute set to a value * contact with the 'subscription' attribute set to a value
* of "remove": * of "remove":
* *
* <iq type='set' id='remove1'> * <iq type='set' id='remove1'>
* <query xmlns='jabber:iq:roster'> * <query xmlns='jabber:iq:roster'>
* <item jid='contact@example.org' subscription='remove'/> * <item jid='contact@example.org' subscription='remove'/>
* </query> * </query>
* </iq> * </iq>
*/ */
expect(sent_IQ.toLocaleString()).toBe( expect(sent_IQ.toLocaleString()).toBe(
"<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+ "<iq type='set' xmlns='jabber:client' id='"+IQ_id+"'>"+
"<query xmlns='jabber:iq:roster'>"+ "<query xmlns='jabber:iq:roster'>"+

View File

@ -11,7 +11,27 @@
if (typeof window.Promise === 'undefined') { if (typeof window.Promise === 'undefined') {
waitUntilPromise.setPromiseImplementation(Promise); waitUntilPromise.setPromiseImplementation(Promise);
} }
utils.waitUntil = waitUntilPromise['default']; utils.waitUntil = waitUntilPromise.default;
utils.waitUntilFeatureSupportConfirmed = function (_converse, feature_name) {
var IQ_disco, stanza;
return utils.waitUntil(function () {
IQ_disco = _.filter(_converse.connection.IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('query[xmlns="http://jabber.org/protocol/disco#info"]');
}).pop();
return !_.isUndefined(IQ_disco);
}, 300).then(function () {
var info_IQ_id = IQ_disco.nodeTree.getAttribute('id');
stanza = $iq({
'type': 'result',
'from': 'localhost',
'to': 'dummy@localhost/resource',
'id': info_IQ_id
}).c('query', {'xmlns': 'http://jabber.org/protocol/disco#info'})
.c('feature', {'var': feature_name});
_converse.connection._dataRecv(utils.createRequest(stanza));
});
}
utils.createRequest = function (iq) { utils.createRequest = function (iq) {
iq = typeof iq.tree == "function" ? iq.tree() : iq; iq = typeof iq.tree == "function" ? iq.tree() : iq;