From c7bf1713d8ae7eb0cf39bfde4faa902318eae596 Mon Sep 17 00:00:00 2001 From: JC Brand Date: Wed, 8 Apr 2015 10:35:14 +0200 Subject: [PATCH] Add new spec protocol for testing the XMPP protocol. Already testing the first 3 sections of RFC-3921 section 8.2 "User subscribes to contact". --- converse.js | 2 +- spec/protocol.js | 189 +++++++++++++++++++++++++++++++++++++++++++++++ tests/main.js | 1 + 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 spec/protocol.js diff --git a/converse.js b/converse.js index 70b0d2d78..47c4123dd 100644 --- a/converse.js +++ b/converse.js @@ -3696,7 +3696,7 @@ this.model.get('jid'), this.model.get('fullname'), [], - function (iq) { this.model.authorize().subscribe(); }.bind(this) + function () { this.model.authorize().subscribe(); }.bind(this) ); }, diff --git a/spec/protocol.js b/spec/protocol.js new file mode 100644 index 000000000..d88ceb949 --- /dev/null +++ b/spec/protocol.js @@ -0,0 +1,189 @@ +(function (root, factory) { + define([ + "jquery", + "mock", + "test_utils" + ], function ($, mock, test_utils) { + return factory($, mock, test_utils); + } + ); +} (this, function ($, mock, test_utils) { + var Strophe = converse_api.env.Strophe; + var $iq = converse_api.env.$iq; + + describe("The Protocol", $.proxy(function (mock, test_utils) { + describe("Integration of Roster Items and Presence Subscriptions", $.proxy(function (mock, test_utils) { + /* Some level of integration between roster items and presence + * subscriptions is normally expected by an instant messaging user + * regarding the user's subscriptions to and from other contacts. This + * section describes the level of integration that MUST be supported + * within an XMPP instant messaging applications. + * + * There are four primary subscription states: + * + * None -- the user does not have a subscription to the contact's + * presence information, and the contact does not have a subscription + * to the user's presence information + * To -- the user has a subscription to the contact's presence + * information, but the contact does not have a subscription to the + * user's presence information + * From -- the contact has a subscription to the user's presence + * information, but the user does not have a subscription to the + * contact's presence information + * Both -- both the user and the contact have subscriptions to each + * other's presence information (i.e., the union of 'from' and 'to') + * + * Each of these states is reflected in the roster of both the user and + * the contact, thus resulting in durable subscription states. + * + * The 'from' and 'to' addresses are OPTIONAL in roster pushes; if + * included, their values SHOULD be the full JID of the resource for + * that session. A client MUST acknowledge each roster push with an IQ + * stanza of type "result". + */ + beforeEach(function () { + test_utils.closeAllChatBoxes(); + test_utils.removeControlBox(); + converse.roster.browserStorage._clear(); + test_utils.initConverse(); + test_utils.openControlBox(); + test_utils.openContactsPanel(); + }); + + it("User Subscribes to Contact", $.proxy(function () { + /* The process by which a user subscribes to a contact, including + * the interaction between roster items and subscription states. + */ + var stanzaID; + var sentStanza; + var panel = this.chatboxviews.get('controlbox').contactspanel; + spyOn(panel, "addContactFromForm").andCallThrough(); + spyOn(converse.roster, "addAndSubscribe").andCallThrough(); + spyOn(converse.roster, "addContact").andCallThrough(); + spyOn(converse.roster, "sendContactAddIQ").andCallThrough(); + var sendIQ = this.connection.sendIQ; + spyOn(this.connection, 'sendIQ').andCallFake(function (iq, callback, errback) { + sentStanza = iq; + stanzaID = sendIQ.bind(this)(iq, callback, errback); + }); + panel.delegateEvents(); // Rebind all events so that our spy gets called + + /* Add a new contact through the UI */ + var $form = panel.$('form.add-xmpp-contact'); + expect($form.is(":visible")).toBeFalsy(); + // Click the "Add a contact" link. + panel.$('.toggle-xmpp-contact-form').click(); + // Check that the $form appears + expect($form.is(":visible")).toBeTruthy(); + // Fill in the form and submit + $form.find('input').val('contact@example.org'); + $form.submit(); + + /* 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(); + // The form should not be visible anymore. + expect($form.is(":visible")).toBeFalsy(); + + /* This 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(sentStanza.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 this 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 contact; + var send = converse.connection.send; + var create = converse.roster.create; + spyOn(converse.roster, 'create').andCallFake(function () { + contact = create.apply(converse.roster, arguments); + spyOn(contact, 'subscribe').andCallThrough(); + spyOn(converse.connection, 'send').andCallFake(function (stanza) { + sentStanza = stanza; + }); + return contact; + }); + var iq = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'}) + .c('item', { + 'jid': 'contact@example.org', + 'subscription': 'none', + 'name': 'contact@example.org'}); + this.connection._dataRecv(test_utils.createRequest(iq)); + /* + * + */ + iq = $iq({'type': 'result', 'id':stanzaID}); + this.connection._dataRecv(test_utils.createRequest(iq)); + + // A contact should now have been created + expect(this.roster.get('contact@example.org') instanceof converse.RosterContact).toBeTruthy(); + expect(contact.get('jid')).toBe('contact@example.org'); + + /* To subscribe to the contact's presence information, + * the user's client MUST send a presence stanza of + * type='subscribe' to the contact: + * + * + */ + expect(contact.subscribe).toHaveBeenCalled(); + expect(sentStanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec) + "" + ); + }, converse)); + + it("Alternate Flow: Contact Declines Subscription Request", $.proxy(function () { + // TODO + }, converse)); + + it("Creating a Mutual Subscription", $.proxy(function () { + // TODO + }, converse)); + + it("Alternate Flow: User Declines Subscription Request", $.proxy(function () { + // TODO + }, converse)); + }, converse, mock, test_utils)); + }, converse, mock, test_utils)); +})); diff --git a/tests/main.js b/tests/main.js index daca682cc..827f989e7 100644 --- a/tests/main.js +++ b/tests/main.js @@ -60,6 +60,7 @@ require([ require([ "console-runner", "spec/converse", + "spec/protocol", "spec/otr", "spec/eventemitter", "spec/controlbox",