diff --git a/spec/chatbox.js b/spec/chatbox.js index 67a69b80a..d564510de 100644 --- a/spec/chatbox.js +++ b/spec/chatbox.js @@ -10,6 +10,7 @@ } (this, function ($, jasmine, utils, converse, mock, test_utils) { "use strict"; var _ = converse.env._; + var $iq = converse.env.$iq; var $msg = converse.env.$msg; var Strophe = converse.env.Strophe; var moment = converse.env.moment; @@ -22,30 +23,37 @@ null, ['rosterGroupsFetched'], {}, function (done, _converse) { - test_utils.createContacts(_converse, 'current'); - test_utils.openControlBox(); - test_utils.openContactsPanel(_converse); - expect(_converse.chatboxes.length).toEqual(1); - var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; - var message = '/me is tired'; - 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(); + 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'); + test_utils.openControlBox(); + test_utils.openContactsPanel(_converse); + expect(_converse.chatboxes.length).toEqual(1); + var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; + var message = '/me is tired'; + 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); - var view = _converse.chatboxviews.get(sender_jid); - expect(_.includes(view.$el.find('.chat-msg-author').text(), '**Max Frankfurter')).toBeTruthy(); - expect(view.$el.find('.chat-msg-content').text()).toBe(' is tired'); + _converse.chatboxes.onMessage(msg); + var view = _converse.chatboxviews.get(sender_jid); + expect(_.includes(view.$el.find('.chat-msg-author').text(), '**Max Frankfurter')).toBeTruthy(); + expect(view.$el.find('.chat-msg-content').text()).toBe(' is tired'); - message = '/me is as well'; - test_utils.sendMessage(view, message); - 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'); - done(); + message = '/me is as well'; + test_utils.sendMessage(view, message); + 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'); + done(); + }); })); it("is created when you click on a roster item", @@ -527,7 +535,8 @@ describe("A Chat Message", 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( null, ['rosterGroupsFetched'], {}, function (done, _converse) { @@ -953,47 +962,55 @@ null, ['rosterGroupsFetched'], {}, function (done, _converse) { - test_utils.createContacts(_converse, 'current'); - test_utils.openControlBox(); - test_utils.openContactsPanel(_converse); + 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'); + test_utils.openControlBox(); + test_utils.openContactsPanel(_converse); - // 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); + // 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(_converse.xmppstatus.get('fullname')); - 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); - done(); + // 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(_converse.xmppstatus.get('fullname')); + 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); + done(); + }); })); it("will be discarded if it's a malicious message meant to look like a carbon copy", @@ -1561,41 +1578,48 @@ null, ['rosterGroupsFetched'], {}, 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 - 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); - - // 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(); + // 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'], {}, 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 - 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); - - // 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(); + // 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(); + }); })); }); diff --git a/spec/chatroom.js b/spec/chatroom.js index 1e9b7833d..260d271cd 100644 --- a/spec/chatroom.js +++ b/spec/chatroom.js @@ -546,8 +546,16 @@ null, ['rosterGroupsFetched'], {}, 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'); if (!view.$el.find('.chat-area').length) { view.renderChatArea(); } var message = '/me is tired'; diff --git a/spec/protocol.js b/spec/protocol.js index a68a70ba7..fd3214e6e 100644 --- a/spec/protocol.js +++ b/spec/protocol.js @@ -54,178 +54,185 @@ { roster_groups: false }, function (done, _converse) { - /* The process by which a user subscribes to a contact, including - * the interaction between roster items and subscription states. - */ - var contact, stanza, sent_stanza, IQ_id; - test_utils.openControlBox(_converse); - var panel = _converse.chatboxviews.get('controlbox').contactspanel; - spyOn(panel, "addContactFromForm").and.callThrough(); - spyOn(_converse.roster, "addAndSubscribe").and.callThrough(); - spyOn(_converse.roster, "addContact").and.callThrough(); - spyOn(_converse.roster, "sendContactAddIQ").and.callThrough(); - spyOn(_converse.api.vcard, "get").and.callThrough(); - var sendIQ = _converse.connection.sendIQ; - spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { - sent_stanza = iq; - IQ_id = sendIQ.bind(this)(iq, callback, errback); - }); - panel.delegateEvents(); // Rebind all events so that our spy gets called + 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 () { + /* The process by which a user subscribes to a contact, including + * the interaction between roster items and subscription states. + */ + test_utils.openControlBox(_converse); + var panel = _converse.chatboxviews.get('controlbox').contactspanel; + spyOn(panel, "addContactFromForm").and.callThrough(); + spyOn(_converse.roster, "addAndSubscribe").and.callThrough(); + spyOn(_converse.roster, "addContact").and.callThrough(); + spyOn(_converse.roster, "sendContactAddIQ").and.callThrough(); + spyOn(_converse.api.vcard, "get").and.callThrough(); - /* Add a new contact through the UI */ - var form = panel.el.querySelector('form.add-xmpp-contact'); - expect(_.isNull(form)).toBeTruthy(); + var sendIQ = _converse.connection.sendIQ; + spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) { + 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. - panel.$('.toggle-xmpp-contact-form').click(); + /* Add a new contact through the UI */ + var form = panel.el.querySelector('form.add-xmpp-contact'); + expect(_.isNull(form)).toBeTruthy(); - // Check that the form appears - form = panel.el.querySelector('form.add-xmpp-contact'); - expect(form.parentElement.offsetHeight).not.toBe(0); - expect(_.includes(form.parentElement.classList, 'collapsed')).toBeFalsy(); + // Click the "Add a contact" link. + panel.$('.toggle-xmpp-contact-form').click(); - // Fill in the form and submit - $(form).find('input').val('contact@example.org'); - $(form).submit(); + // Check that the form appears + form = panel.el.querySelector('form.add-xmpp-contact'); + 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 - * user's client interface and for the server to keep track of the - * subscription, the user's client SHOULD perform a "roster set" - * for the new roster item. - */ - expect(panel.addContactFromForm).toHaveBeenCalled(); - expect(_converse.roster.addAndSubscribe).toHaveBeenCalled(); - expect(_converse.roster.addContact).toHaveBeenCalled(); + // Fill in the form and submit + $(form).find('input').val('contact@example.org'); + $(form).submit(); - // The form should not be visible anymore (by virtue of its - // parent being collapsed) - expect(form.parentElement.offsetHeight).toBe(0); - expect(_.includes(form.parentElement.classList, 'collapsed')).toBeTrue; + /* In preparation for being able to render the contact in the + * user's client interface and for the server to keep track of the + * subscription, the user's client SHOULD perform a "roster set" + * 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 - * stanza of type='set' containing a element qualified by - * the 'jabber:iq:roster' namespace, which in turn contains an - * element that defines the new roster item; the - * element MUST possess a 'jid' attribute, MAY possess a 'name' - * attribute, MUST NOT possess a 'subscription' attribute, and MAY - * contain one or more child elements: - * - * - * - * - * MyBuddies - * - * - * - */ - expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled(); - expect(sent_stanza.toLocaleString()).toBe( - ""+ - ""+ - ""+ - ""+ - "" - ); - /* 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: - * - * - * - * - * MyBuddies - * - * - * - */ - 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)); - /* - * - */ - stanza = $iq({'type': 'result', 'id':IQ_id}); - _converse.connection._dataRecv(test_utils.createRequest(stanza)); + // The form should not be visible anymore (by virtue of its + // parent being collapsed) + expect(form.parentElement.offsetHeight).toBe(0); + expect(_.includes(form.parentElement.classList, 'collapsed')).toBeTrue; - // A contact should now have been created - expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy(); - expect(contact.get('jid')).toBe('contact@example.org'); - expect(_converse.api.vcard.get).toHaveBeenCalled(); + /* _converse request consists of sending an IQ + * stanza of type='set' containing a element qualified by + * the 'jabber:iq:roster' namespace, which in turn contains an + * element that defines the new roster item; the + * element MUST possess a 'jid' attribute, MAY possess a 'name' + * attribute, MUST NOT possess a 'subscription' attribute, and MAY + * contain one or more child elements: + * + * + * + * + * MyBuddies + * + * + * + */ + expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled(); + expect(sent_stanza.toLocaleString()).toBe( + ""+ + ""+ + ""+ + ""+ + "" + ); + /* 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: + * + * + * + * + * MyBuddies + * + * + * + */ + 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)); + /* + * + */ + stanza = $iq({'type': 'result', 'id':IQ_id}); + _converse.connection._dataRecv(test_utils.createRequest(stanza)); - /* To subscribe to the contact's presence information, - * the user's client MUST send a presence stanza of - * type='subscribe' to the contact: - * - * - */ - - 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) - ""+ - "Max Mustermann"+ - "" - ); - /* 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: - * - * - * - * - * MyBuddies - * - * - * - */ - 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 () { + // A contact should now have been created + expect(_converse.roster.get('contact@example.org') instanceof _converse.RosterContact).toBeTruthy(); + expect(contact.get('jid')).toBe('contact@example.org'); + expect(_converse.api.vcard.get).toHaveBeenCalled(); + /* To subscribe to the contact's presence information, + * the user's client MUST send a presence stanza of + * type='subscribe' to the contact: + * + * + */ + 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) + ""+ + "Max Mustermann"+ + "" + ); + /* 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: + * + * + * + * + * MyBuddies + * + * + * + */ + 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")'); expect($header.length).toBe(1); expect($header.is(":visible")).toBeTruthy(); @@ -234,13 +241,13 @@ spyOn(contact, "ackSubscribe").and.callThrough(); /* Here we assume the "happy path" that the contact - * approves the subscription request - * - * - */ + * approves the subscription request + * + * + */ stanza = $pres({ 'to': _converse.bare_jid, 'from': 'contact@example.org', @@ -249,31 +256,31 @@ sent_stanza = ""; // Reset _converse.connection._dataRecv(test_utils.createRequest(stanza)); /* Upon receiving the presence stanza of type "subscribed", - * the user SHOULD acknowledge receipt of that - * subscription state notification by sending a presence - * stanza of type "subscribe". - */ + * the user SHOULD acknowledge receipt of that + * subscription state notification by sending a presence + * stanza of type "subscribe". + */ expect(contact.ackSubscribe).toHaveBeenCalled(); expect(sent_stanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec) "" ); /* The user's server MUST initiate a roster push to all of the user's - * available resources that have requested the roster, - * containing an updated roster item for the contact with - * the 'subscription' attribute set to a value of "to"; - * - * - * - * - * MyBuddies - * - * - * - */ + * available resources that have requested the roster, + * containing an updated roster item for the contact with + * the 'subscription' attribute set to a value of "to"; + * + * + * + * + * MyBuddies + * + * + * + */ IQ_id = _converse.connection.getUniqueId('roster'); stanza = $iq({'type': 'set', 'id': IQ_id}) .c('query', {'xmlns': 'jabber:iq:roster'}) @@ -303,22 +310,22 @@ expect(contact.get('chat_status')).toBe('offline'); /* - */ + * from='contact@example.org/resource' + * to='user@example.com/resource'/> + */ stanza = $pres({'to': _converse.bare_jid, 'from': 'contact@example.org/resource'}); _converse.connection._dataRecv(test_utils.createRequest(stanza)); // Now the contact should also be online. expect(contact.get('chat_status')).toBe('online'); /* Section 8.3. Creating a Mutual Subscription - * - * If the contact wants to create a mutual subscription, - * the contact MUST send a subscription request to the - * user. - * - * - */ + * + * If the contact wants to create a mutual subscription, + * the contact MUST send a subscription request to the + * user. + * + * + */ spyOn(contact, 'authorize').and.callThrough(); spyOn(_converse.roster, 'handleIncomingSubscription').and.callThrough(); stanza = $pres({ @@ -329,32 +336,32 @@ expect(_converse.roster.handleIncomingSubscription).toHaveBeenCalled(); /* The user's client MUST send a presence stanza of type - * "subscribed" to the contact in order to approve the - * subscription request. - * - * - */ + * "subscribed" to the contact in order to approve the + * subscription request. + * + * + */ expect(contact.authorize).toHaveBeenCalled(); expect(sent_stanza.toLocaleString()).toBe( "" ); /* As a result, the user's server MUST initiate a - * roster push containing a roster item for the - * contact with the 'subscription' attribute set to - * a value of "both". - * - * - * - * - * MyBuddies - * - * - * - */ + * roster push containing a roster item for the + * contact with the 'subscription' attribute set to + * a value of "both". + * + * + * + * + * MyBuddies + * + * + * + */ stanza = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'}) .c('item', { 'jid': 'contact@example.org', @@ -368,7 +375,6 @@ expect($contacts.hasClass('both')).toBeTruthy(); done(); }); - }); })); it("Alternate Flow: Contact Declines Subscription Request", @@ -377,8 +383,8 @@ function (done, _converse) { /* 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; test_utils.openControlBox(_converse); // Add a new roster contact via roster push @@ -401,32 +407,32 @@ sent_IQ = iq; }); /* We now assume the contact declines the subscription - * requests. - * - /* Upon receiving the presence stanza of type "unsubscribed" - * addressed to the user, the user's server (1) MUST deliver - * that presence stanza to the user and (2) MUST initiate a - * roster push to all of the user's available resources that - * have requested the roster, containing an updated roster - * item for the contact with the 'subscription' attribute - * set to a value of "none" and with no 'ask' attribute: - * - * - * - * - * - * - * MyBuddies - * - * - * - */ + * requests. + * + * Upon receiving the presence stanza of type "unsubscribed" + * addressed to the user, the user's server (1) MUST deliver + * that presence stanza to the user and (2) MUST initiate a + * roster push to all of the user's available resources that + * have requested the roster, containing an updated roster + * item for the contact with the 'subscription' attribute + * set to a value of "none" and with no 'ask' attribute: + * + * + * + * + * + * + * MyBuddies + * + * + * + */ // FIXME: also add the stanza = $pres({ 'to': _converse.bare_jid, @@ -436,18 +442,18 @@ _converse.connection._dataRecv(test_utils.createRequest(stanza)); /* Upon receiving the presence stanza of type "unsubscribed", - * the user SHOULD acknowledge receipt of that subscription - * state notification through either "affirming" it by - * sending a presence stanza of type "unsubscribe - */ + * the user SHOULD acknowledge receipt of that subscription + * state notification through either "affirming" it by + * sending a presence stanza of type "unsubscribe + */ expect(contact.ackUnsubscribe).toHaveBeenCalled(); expect(sent_stanza.toLocaleString()).toBe( "" ); /* _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( ""+ ""+ @@ -486,24 +492,24 @@ expect(window.confirm).toHaveBeenCalled(); /* Section 8.6 Removing a Roster Item and Cancelling All - * Subscriptions - * - * First the user is removed from the roster - * Because there may be many steps involved in completely - * removing a roster item and cancelling subscriptions in - * both directions, the roster management protocol includes - * a "shortcut" method for doing so. The process may be - * initiated no matter what the current subscription state - * is by sending a roster set containing an item for the - * contact with the 'subscription' attribute set to a value - * of "remove": - * - * - * - * - * - * - */ + * Subscriptions + * + * First the user is removed from the roster + * Because there may be many steps involved in completely + * removing a roster item and cancelling subscriptions in + * both directions, the roster management protocol includes + * a "shortcut" method for doing so. The process may be + * initiated no matter what the current subscription state + * is by sending a roster set containing an item for the + * contact with the 'subscription' attribute set to a value + * of "remove": + * + * + * + * + * + * + */ expect(sent_IQ.toLocaleString()).toBe( ""+ ""+ diff --git a/tests/utils.js b/tests/utils.js index 3624e2f22..46835dbd2 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -11,7 +11,27 @@ if (typeof window.Promise === 'undefined') { 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) { iq = typeof iq.tree == "function" ? iq.tree() : iq;