rai.js 11 KB

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