protocol.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. (function (root, factory) {
  2. define([
  3. "jquery",
  4. "mock",
  5. "test_utils"
  6. ], function ($, mock, test_utils) {
  7. return factory($, mock, test_utils);
  8. }
  9. );
  10. } (this, function ($, mock, test_utils) {
  11. var Strophe = converse_api.env.Strophe;
  12. var $iq = converse_api.env.$iq;
  13. describe("The Protocol", $.proxy(function (mock, test_utils) {
  14. describe("Integration of Roster Items and Presence Subscriptions", $.proxy(function (mock, test_utils) {
  15. /* Some level of integration between roster items and presence
  16. * subscriptions is normally expected by an instant messaging user
  17. * regarding the user's subscriptions to and from other contacts. This
  18. * section describes the level of integration that MUST be supported
  19. * within an XMPP instant messaging applications.
  20. *
  21. * There are four primary subscription states:
  22. *
  23. * None -- the user does not have a subscription to the contact's
  24. * presence information, and the contact does not have a subscription
  25. * to the user's presence information
  26. * To -- the user has a subscription to the contact's presence
  27. * information, but the contact does not have a subscription to the
  28. * user's presence information
  29. * From -- the contact has a subscription to the user's presence
  30. * information, but the user does not have a subscription to the
  31. * contact's presence information
  32. * Both -- both the user and the contact have subscriptions to each
  33. * other's presence information (i.e., the union of 'from' and 'to')
  34. *
  35. * Each of these states is reflected in the roster of both the user and
  36. * the contact, thus resulting in durable subscription states.
  37. *
  38. * The 'from' and 'to' addresses are OPTIONAL in roster pushes; if
  39. * included, their values SHOULD be the full JID of the resource for
  40. * that session. A client MUST acknowledge each roster push with an IQ
  41. * stanza of type "result".
  42. */
  43. beforeEach(function () {
  44. test_utils.closeAllChatBoxes();
  45. test_utils.removeControlBox();
  46. converse.roster.browserStorage._clear();
  47. test_utils.initConverse();
  48. test_utils.openControlBox();
  49. test_utils.openContactsPanel();
  50. });
  51. it("User Subscribes to Contact", $.proxy(function () {
  52. /* The process by which a user subscribes to a contact, including
  53. * the interaction between roster items and subscription states.
  54. */
  55. var stanzaID;
  56. var sentStanza;
  57. var panel = this.chatboxviews.get('controlbox').contactspanel;
  58. spyOn(panel, "addContactFromForm").andCallThrough();
  59. spyOn(converse.roster, "addAndSubscribe").andCallThrough();
  60. spyOn(converse.roster, "addContact").andCallThrough();
  61. spyOn(converse.roster, "sendContactAddIQ").andCallThrough();
  62. var sendIQ = this.connection.sendIQ;
  63. spyOn(this.connection, 'sendIQ').andCallFake(function (iq, callback, errback) {
  64. sentStanza = iq;
  65. stanzaID = sendIQ.bind(this)(iq, callback, errback);
  66. });
  67. panel.delegateEvents(); // Rebind all events so that our spy gets called
  68. /* Add a new contact through the UI */
  69. var $form = panel.$('form.add-xmpp-contact');
  70. expect($form.is(":visible")).toBeFalsy();
  71. // Click the "Add a contact" link.
  72. panel.$('.toggle-xmpp-contact-form').click();
  73. // Check that the $form appears
  74. expect($form.is(":visible")).toBeTruthy();
  75. // Fill in the form and submit
  76. $form.find('input').val('contact@example.org');
  77. $form.submit();
  78. /* In preparation for being able to render the contact in the
  79. * user's client interface and for the server to keep track of the
  80. * subscription, the user's client SHOULD perform a "roster set"
  81. * for the new roster item.
  82. */
  83. expect(panel.addContactFromForm).toHaveBeenCalled();
  84. expect(converse.roster.addAndSubscribe).toHaveBeenCalled();
  85. expect(converse.roster.addContact).toHaveBeenCalled();
  86. // The form should not be visible anymore.
  87. expect($form.is(":visible")).toBeFalsy();
  88. /* This request consists of sending an IQ
  89. * stanza of type='set' containing a <query/> element qualified by
  90. * the 'jabber:iq:roster' namespace, which in turn contains an
  91. * <item/> element that defines the new roster item; the <item/>
  92. * element MUST possess a 'jid' attribute, MAY possess a 'name'
  93. * attribute, MUST NOT possess a 'subscription' attribute, and MAY
  94. * contain one or more <group/> child elements:
  95. *
  96. * <iq type='set' id='set1'>
  97. * <query xmlns='jabber:iq:roster'>
  98. * <item
  99. * jid='contact@example.org'
  100. * name='MyContact'>
  101. * <group>MyBuddies</group>
  102. * </item>
  103. * </query>
  104. * </iq>
  105. */
  106. expect(converse.roster.sendContactAddIQ).toHaveBeenCalled();
  107. expect(sentStanza.toLocaleString()).toBe(
  108. "<iq type='set' xmlns='jabber:client' id='"+stanzaID+"'>"+
  109. "<query xmlns='jabber:iq:roster'>"+
  110. "<item jid='contact@example.org' name='contact@example.org'/>"+
  111. "</query>"+
  112. "</iq>"
  113. );
  114. /* As a result, the user's server (1) MUST initiate a roster push
  115. * for the new roster item to all available resources associated
  116. * with this user that have requested the roster, setting the
  117. * 'subscription' attribute to a value of "none"; and (2) MUST
  118. * reply to the sending resource with an IQ result indicating the
  119. * success of the roster set:
  120. *
  121. * <iq type='set'>
  122. * <query xmlns='jabber:iq:roster'>
  123. * <item
  124. * jid='contact@example.org'
  125. * subscription='none'
  126. * name='MyContact'>
  127. * <group>MyBuddies</group>
  128. * </item>
  129. * </query>
  130. * </iq>
  131. */
  132. var contact;
  133. var send = converse.connection.send;
  134. var create = converse.roster.create;
  135. spyOn(converse.roster, 'create').andCallFake(function () {
  136. contact = create.apply(converse.roster, arguments);
  137. spyOn(contact, 'subscribe').andCallThrough();
  138. spyOn(converse.connection, 'send').andCallFake(function (stanza) {
  139. sentStanza = stanza;
  140. });
  141. return contact;
  142. });
  143. var iq = $iq({'type': 'set'}).c('query', {'xmlns': 'jabber:iq:roster'})
  144. .c('item', {
  145. 'jid': 'contact@example.org',
  146. 'subscription': 'none',
  147. 'name': 'contact@example.org'});
  148. this.connection._dataRecv(test_utils.createRequest(iq));
  149. /*
  150. * <iq type='result' id='set1'/>
  151. */
  152. iq = $iq({'type': 'result', 'id':stanzaID});
  153. this.connection._dataRecv(test_utils.createRequest(iq));
  154. // A contact should now have been created
  155. expect(this.roster.get('contact@example.org') instanceof converse.RosterContact).toBeTruthy();
  156. expect(contact.get('jid')).toBe('contact@example.org');
  157. /* To subscribe to the contact's presence information,
  158. * the user's client MUST send a presence stanza of
  159. * type='subscribe' to the contact:
  160. *
  161. * <presence to='contact@example.org' type='subscribe'/>
  162. */
  163. expect(contact.subscribe).toHaveBeenCalled();
  164. expect(sentStanza.toLocaleString()).toBe( // Strophe adds the xmlns attr (although not in spec)
  165. "<presence to='contact@example.org' type='subscribe' xmlns='jabber:client'/>"
  166. );
  167. }, converse));
  168. it("Alternate Flow: Contact Declines Subscription Request", $.proxy(function () {
  169. // TODO
  170. }, converse));
  171. it("Creating a Mutual Subscription", $.proxy(function () {
  172. // TODO
  173. }, converse));
  174. it("Alternate Flow: User Declines Subscription Request", $.proxy(function () {
  175. // TODO
  176. }, converse));
  177. }, converse, mock, test_utils));
  178. }, converse, mock, test_utils));
  179. }));