ソースを参照

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".
JC Brand 10 年 前
コミット
c7bf1713d8
3 ファイル変更191 行追加1 行削除
  1. 1 1
      converse.js
  2. 189 0
      spec/protocol.js
  3. 1 0
      tests/main.js

+ 1 - 1
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)
                 );
             },
 

+ 189 - 0
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 <query/> element qualified by
+                * the 'jabber:iq:roster' namespace, which in turn contains an
+                * <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(sentStanza.toLocaleString()).toBe(
+                    "<iq type='set' xmlns='jabber:client' id='"+stanzaID+"'>"+
+                        "<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 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:
+                *
+                * <iq type='set'>
+                *     <query xmlns='jabber:iq:roster'>
+                *         <item
+                *             jid='contact@example.org'
+                *             subscription='none'
+                *             name='MyContact'>
+                *         <group>MyBuddies</group>
+                *         </item>
+                *     </query>
+                * </iq>
+                */
+                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 type='result' id='set1'/>
+                 */
+                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:
+                 *
+                 *  <presence to='contact@example.org' type='subscribe'/>
+                 */
+                expect(contact.subscribe).toHaveBeenCalled();
+                expect(sentStanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
+                    "<presence to='contact@example.org' type='subscribe' xmlns='jabber:client'/>"
+                );
+            }, 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));
+}));

+ 1 - 0
tests/main.js

@@ -60,6 +60,7 @@ require([
             require([
                 "console-runner",
                 "spec/converse",
+                "spec/protocol",
                 "spec/otr",
                 "spec/eventemitter",
                 "spec/controlbox",