rai.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*global mock, converse */
  2. const { Strophe } = converse.env;
  3. const u = converse.env.utils;
  4. // See: https://xmpp.org/rfcs/rfc3921.html
  5. describe("XEP-0437 Room Activity Indicators", function () {
  6. it("will be activated for a MUC that becomes hidden",
  7. mock.initConverse(
  8. ['rosterGroupsFetched'], {
  9. 'allow_bookmarks': false, // Hack to get the rooms list to render
  10. 'muc_subscribe_to_rai': true,
  11. 'view_mode': 'fullscreen'},
  12. async function (done, _converse) {
  13. expect(_converse.session.get('rai_enabled_domains')).toBe(undefined);
  14. const muc_jid = 'lounge@montague.lit';
  15. await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  16. const view = _converse.api.chatviews.get(muc_jid);
  17. expect(view.model.get('hidden')).toBe(false);
  18. const sent_IQs = _converse.connection.IQ_stanzas;
  19. const iq_get = await u.waitUntil(() => sent_IQs.filter(iq => iq.querySelector(`iq query[xmlns="${Strophe.NS.MAM}"]`)).pop());
  20. const first_msg_id = _converse.connection.getUniqueId();
  21. const last_msg_id = _converse.connection.getUniqueId();
  22. let message = u.toStanza(
  23. `<message xmlns="jabber:client"
  24. to="romeo@montague.lit/orchard"
  25. from="${muc_jid}">
  26. <result xmlns="urn:xmpp:mam:2" queryid="${iq_get.querySelector('query').getAttribute('queryid')}" id="${first_msg_id}">
  27. <forwarded xmlns="urn:xmpp:forward:0">
  28. <delay xmlns="urn:xmpp:delay" stamp="2018-01-09T06:15:23Z"/>
  29. <message from="${muc_jid}/some1" type="groupchat">
  30. <body>1st MAM Message</body>
  31. </message>
  32. </forwarded>
  33. </result>
  34. </message>`);
  35. _converse.connection._dataRecv(mock.createRequest(message));
  36. message = u.toStanza(
  37. `<message xmlns="jabber:client"
  38. to="romeo@montague.lit/orchard"
  39. from="${muc_jid}">
  40. <result xmlns="urn:xmpp:mam:2" queryid="${iq_get.querySelector('query').getAttribute('queryid')}" id="${last_msg_id}">
  41. <forwarded xmlns="urn:xmpp:forward:0">
  42. <delay xmlns="urn:xmpp:delay" stamp="2018-01-09T06:16:23Z"/>
  43. <message from="${muc_jid}/some1" type="groupchat">
  44. <body>2nd MAM Message</body>
  45. </message>
  46. </forwarded>
  47. </result>
  48. </message>`);
  49. _converse.connection._dataRecv(mock.createRequest(message));
  50. const result = u.toStanza(
  51. `<iq type='result' id='${iq_get.getAttribute('id')}'>
  52. <fin xmlns='urn:xmpp:mam:2'>
  53. <set xmlns='http://jabber.org/protocol/rsm'>
  54. <first index='0'>${first_msg_id}</first>
  55. <last>${last_msg_id}</last>
  56. <count>2</count>
  57. </set>
  58. </fin>
  59. </iq>`);
  60. _converse.connection._dataRecv(mock.createRequest(result));
  61. await u.waitUntil(() => view.model.messages.length === 2);
  62. const sent_stanzas = [];
  63. spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
  64. view.model.save({'hidden': true});
  65. await u.waitUntil(() => sent_stanzas.length === 3);
  66. expect(Strophe.serialize(sent_stanzas[0])).toBe(
  67. `<message from="${_converse.jid}" id="${sent_stanzas[0].getAttribute('id')}" to="lounge@montague.lit" type="groupchat" xmlns="jabber:client">`+
  68. `<received id="${last_msg_id}" xmlns="urn:xmpp:chat-markers:0"/>`+
  69. `</message>`
  70. );
  71. expect(Strophe.serialize(sent_stanzas[1])).toBe(
  72. `<presence to="${muc_jid}/romeo" type="unavailable" xmlns="jabber:client">`+
  73. `<priority>0</priority>`+
  74. `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
  75. `</presence>`
  76. );
  77. expect(Strophe.serialize(sent_stanzas[2])).toBe(
  78. `<presence to="montague.lit" xmlns="jabber:client">`+
  79. `<priority>0</priority>`+
  80. `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
  81. `<rai xmlns="urn:xmpp:rai:0"/>`+
  82. `</presence>`
  83. );
  84. await u.waitUntil(() => view.model.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED);
  85. expect(view.model.get('has_activity')).toBe(false);
  86. const lview = _converse.rooms_list_view
  87. const room_el = await u.waitUntil(() => lview.el.querySelector(".available-chatroom"));
  88. expect(Array.from(room_el.classList).includes('unread-msgs')).toBeFalsy();
  89. const activity_stanza = u.toStanza(`
  90. <message from="${Strophe.getDomainFromJid(muc_jid)}">
  91. <rai xmlns="urn:xmpp:rai:0">
  92. <activity>${muc_jid}</activity>
  93. </rai>
  94. </message>
  95. `);
  96. _converse.connection._dataRecv(mock.createRequest(activity_stanza));
  97. await u.waitUntil(() => view.model.get('has_activity'));
  98. expect(Array.from(room_el.classList).includes('unread-msgs')).toBeTruthy();
  99. done();
  100. }));
  101. it("will be activated for a MUC that starts out hidden",
  102. mock.initConverse(
  103. ['rosterGroupsFetched'], {
  104. 'allow_bookmarks': false, // Hack to get the rooms list to render
  105. 'muc_subscribe_to_rai': true,
  106. 'view_mode': 'fullscreen'},
  107. async function (done, _converse) {
  108. const { api } = _converse;
  109. expect(_converse.session.get('rai_enabled_domains')).toBe(undefined);
  110. const muc_jid = 'lounge@montague.lit';
  111. const nick = 'romeo';
  112. const sent_stanzas = _converse.connection.sent_stanzas;
  113. const muc_creation_promise = await api.rooms.open(muc_jid, {nick, 'hidden': true}, false);
  114. await mock.getRoomFeatures(_converse, muc_jid, []);
  115. await mock.receiveOwnMUCPresence(_converse, muc_jid, nick);
  116. await muc_creation_promise;
  117. const view = api.chatviews.get(muc_jid);
  118. await u.waitUntil(() => (view.model.session.get('connection_status') === converse.ROOMSTATUS.ENTERED));
  119. expect(view.model.get('hidden')).toBe(true);
  120. const getSentPresences = () => sent_stanzas.filter(s => s.nodeName === 'presence');
  121. await u.waitUntil(() => getSentPresences().length === 3, 500);
  122. const sent_presences = getSentPresences();
  123. expect(Strophe.serialize(sent_presences[1])).toBe(
  124. `<presence to="${muc_jid}/romeo" type="unavailable" xmlns="jabber:client">`+
  125. `<priority>0</priority>`+
  126. `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
  127. `</presence>`
  128. );
  129. expect(Strophe.serialize(sent_presences[2])).toBe(
  130. `<presence to="montague.lit" xmlns="jabber:client">`+
  131. `<priority>0</priority>`+
  132. `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
  133. `<rai xmlns="urn:xmpp:rai:0"/>`+
  134. `</presence>`
  135. );
  136. await u.waitUntil(() => view.model.session.get('connection_status') === converse.ROOMSTATUS.DISCONNECTED);
  137. expect(view.model.get('has_activity')).toBe(false);
  138. const lview = _converse.rooms_list_view
  139. const room_el = await u.waitUntil(() => lview.el.querySelector(".available-chatroom"));
  140. expect(Array.from(room_el.classList).includes('unread-msgs')).toBeFalsy();
  141. const activity_stanza = u.toStanza(`
  142. <message from="${Strophe.getDomainFromJid(muc_jid)}">
  143. <rai xmlns="urn:xmpp:rai:0">
  144. <activity>${muc_jid}</activity>
  145. </rai>
  146. </message>
  147. `);
  148. _converse.connection._dataRecv(mock.createRequest(activity_stanza));
  149. await u.waitUntil(() => view.model.get('has_activity'));
  150. expect(Array.from(room_el.classList).includes('unread-msgs')).toBeTruthy();
  151. done();
  152. }));
  153. it("may not be activated due to server resource constraints",
  154. mock.initConverse(
  155. ['rosterGroupsFetched'], {
  156. 'allow_bookmarks': false, // Hack to get the rooms list to render
  157. 'muc_subscribe_to_rai': true,
  158. 'view_mode': 'fullscreen'},
  159. async function (done, _converse) {
  160. expect(_converse.session.get('rai_enabled_domains')).toBe(undefined);
  161. const muc_jid = 'lounge@montague.lit';
  162. const muc_domain = Strophe.getDomainFromJid(muc_jid);
  163. await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  164. const view = _converse.api.chatviews.get(muc_jid);
  165. expect(view.model.get('hidden')).toBe(false);
  166. const sent_stanzas = [];
  167. spyOn(_converse.connection, 'send').and.callFake(s => sent_stanzas.push(s?.nodeTree ?? s));
  168. view.model.save({'hidden': true});
  169. await u.waitUntil(() => sent_stanzas.filter(s => s.nodeName === 'presence').length === 2);
  170. expect(Strophe.serialize(sent_stanzas[0])).toBe(
  171. `<presence to="${muc_jid}/romeo" type="unavailable" xmlns="jabber:client">`+
  172. `<priority>0</priority>`+
  173. `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
  174. `</presence>`
  175. );
  176. expect(Strophe.serialize(sent_stanzas[1])).toBe(
  177. `<presence to="montague.lit" xmlns="jabber:client">`+
  178. `<priority>0</priority>`+
  179. `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
  180. `<rai xmlns="urn:xmpp:rai:0"/>`+
  181. `</presence>`
  182. );
  183. expect(view.model.session.get('connection_status')).toBe(converse.ROOMSTATUS.DISCONNECTED);
  184. expect(_converse.session.get('rai_enabled_domains')).toBe(` ${muc_domain}`);
  185. // If an error presence with "resource-constraint" is returned, we rejoin
  186. const activity_stanza = u.toStanza(`
  187. <presence type="error" from="${Strophe.getDomainFromJid(muc_jid)}">
  188. <error type="wait"><resource-constraint xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error>
  189. </presence>
  190. `);
  191. _converse.connection._dataRecv(mock.createRequest(activity_stanza));
  192. await u.waitUntil(() => view.model.session.get('connection_status') === converse.ROOMSTATUS.CONNECTING);
  193. expect(_converse.session.get('rai_enabled_domains')).toBe(' ');
  194. done();
  195. }));
  196. });