muc-api.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*global mock, converse */
  2. const Model = converse.env.Model;
  3. const { $pres, $iq, Strophe, sizzle, u } = converse.env;
  4. describe("Groupchats", function () {
  5. describe("The \"rooms\" API", function () {
  6. it("has a method 'close' which closes rooms by JID or all rooms when called with no arguments",
  7. mock.initConverse([], {}, async function (done, _converse) {
  8. await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
  9. _converse.connection.IQ_stanzas = [];
  10. await mock.openAndEnterChatRoom(_converse, 'leisure@montague.lit', 'romeo');
  11. _converse.connection.IQ_stanzas = [];
  12. await mock.openAndEnterChatRoom(_converse, 'news@montague.lit', 'romeo');
  13. expect(u.isVisible(_converse.chatboxviews.get('lounge@montague.lit'))).toBeTruthy();
  14. expect(u.isVisible(_converse.chatboxviews.get('leisure@montague.lit'))).toBeTruthy();
  15. expect(u.isVisible(_converse.chatboxviews.get('news@montague.lit'))).toBeTruthy();
  16. await _converse.api.roomviews.close('lounge@montague.lit');
  17. expect(_converse.chatboxviews.get('lounge@montague.lit')).toBeUndefined();
  18. expect(u.isVisible(_converse.chatboxviews.get('leisure@montague.lit'))).toBeTruthy();
  19. expect(u.isVisible(_converse.chatboxviews.get('news@montague.lit'))).toBeTruthy();
  20. await _converse.api.roomviews.close(['leisure@montague.lit', 'news@montague.lit']);
  21. expect(_converse.chatboxviews.get('lounge@montague.lit')).toBeUndefined();
  22. expect(_converse.chatboxviews.get('leisure@montague.lit')).toBeUndefined();
  23. expect(_converse.chatboxviews.get('news@montague.lit')).toBeUndefined();
  24. await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'romeo');
  25. await mock.openAndEnterChatRoom(_converse, 'leisure@montague.lit', 'romeo');
  26. expect(u.isVisible(_converse.chatboxviews.get('lounge@montague.lit'))).toBeTruthy();
  27. expect(u.isVisible(_converse.chatboxviews.get('leisure@montague.lit'))).toBeTruthy();
  28. await _converse.api.roomviews.close();
  29. expect(_converse.chatboxviews.get('lounge@montague.lit')).toBeUndefined();
  30. expect(_converse.chatboxviews.get('leisure@montague.lit')).toBeUndefined();
  31. done();
  32. }));
  33. it("has a method 'get' which returns a wrapped groupchat (if it exists)",
  34. mock.initConverse([], {}, async function (done, _converse) {
  35. await mock.waitForRoster(_converse, 'current');
  36. const rosterview = document.querySelector('converse-roster');
  37. await u.waitUntil(() => rosterview.querySelectorAll('.roster-group .group-toggle').length, 300);
  38. let muc_jid = 'chillout@montague.lit';
  39. await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  40. let room = await _converse.api.rooms.get(muc_jid);
  41. expect(room instanceof Object).toBeTruthy();
  42. let chatroomview = _converse.chatboxviews.get(muc_jid);
  43. expect(chatroomview.is_chatroom).toBeTruthy();
  44. expect(u.isVisible(chatroomview)).toBeTruthy();
  45. await chatroomview.close();
  46. // Test with mixed case
  47. muc_jid = 'Leisure@montague.lit';
  48. await mock.openAndEnterChatRoom(_converse, muc_jid, 'romeo');
  49. room = await _converse.api.rooms.get(muc_jid);
  50. expect(room instanceof Object).toBeTruthy();
  51. chatroomview = _converse.chatboxviews.get(muc_jid.toLowerCase());
  52. expect(u.isVisible(chatroomview)).toBeTruthy();
  53. muc_jid = 'leisure@montague.lit';
  54. room = await _converse.api.rooms.get(muc_jid);
  55. expect(room instanceof Object).toBeTruthy();
  56. chatroomview = _converse.chatboxviews.get(muc_jid.toLowerCase());
  57. expect(u.isVisible(chatroomview)).toBeTruthy();
  58. muc_jid = 'leiSure@montague.lit';
  59. room = await _converse.api.rooms.get(muc_jid);
  60. expect(room instanceof Object).toBeTruthy();
  61. chatroomview = _converse.chatboxviews.get(muc_jid.toLowerCase());
  62. expect(u.isVisible(chatroomview)).toBeTruthy();
  63. chatroomview.close();
  64. // Non-existing room
  65. muc_jid = 'chillout2@montague.lit';
  66. room = await _converse.api.rooms.get(muc_jid);
  67. expect(room).toBe(null);
  68. done();
  69. }));
  70. it("has a method 'open' which opens (optionally configures) and returns a wrapped chat box",
  71. mock.initConverse(['chatBoxesFetched'], {}, async function (done, _converse) {
  72. // Mock 'getDiscoInfo', otherwise the room won't be
  73. // displayed as it waits first for the features to be returned
  74. // (when it's a new room being created).
  75. spyOn(_converse.ChatRoom.prototype, 'getDiscoInfo').and.callFake(() => Promise.resolve());
  76. let jid = 'lounge@montague.lit';
  77. let chatroomview, IQ_id;
  78. await mock.openControlBox(_converse);
  79. await mock.waitForRoster(_converse, 'current');
  80. const rosterview = document.querySelector('converse-roster');
  81. await u.waitUntil(() => rosterview.querySelectorAll('.roster-group .group-toggle').length);
  82. let room = await _converse.api.rooms.open(jid);
  83. // Test on groupchat that's not yet open
  84. expect(room instanceof Model).toBeTruthy();
  85. chatroomview = _converse.chatboxviews.get(jid);
  86. expect(chatroomview.is_chatroom).toBeTruthy();
  87. await u.waitUntil(() => u.isVisible(chatroomview));
  88. // Test again, now that the room exists.
  89. room = await _converse.api.rooms.open(jid);
  90. expect(room instanceof Model).toBeTruthy();
  91. chatroomview = _converse.chatboxviews.get(jid);
  92. expect(chatroomview.is_chatroom).toBeTruthy();
  93. expect(u.isVisible(chatroomview)).toBeTruthy();
  94. await chatroomview.close();
  95. // Test with mixed case in JID
  96. jid = 'Leisure@montague.lit';
  97. room = await _converse.api.rooms.open(jid);
  98. expect(room instanceof Model).toBeTruthy();
  99. chatroomview = _converse.chatboxviews.get(jid.toLowerCase());
  100. await u.waitUntil(() => u.isVisible(chatroomview));
  101. jid = 'leisure@montague.lit';
  102. room = await _converse.api.rooms.open(jid);
  103. expect(room instanceof Model).toBeTruthy();
  104. chatroomview = _converse.chatboxviews.get(jid.toLowerCase());
  105. await u.waitUntil(() => u.isVisible(chatroomview));
  106. jid = 'leiSure@montague.lit';
  107. room = await _converse.api.rooms.open(jid);
  108. expect(room instanceof Model).toBeTruthy();
  109. chatroomview = _converse.chatboxviews.get(jid.toLowerCase());
  110. await u.waitUntil(() => u.isVisible(chatroomview));
  111. chatroomview.close();
  112. _converse.muc_instant_rooms = false;
  113. const sendIQ = _converse.connection.sendIQ;
  114. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
  115. IQ_id = sendIQ.bind(this)(iq, callback, errback);
  116. });
  117. // Test with configuration
  118. room = await _converse.api.rooms.open('room@conference.example.org', {
  119. 'nick': 'some1',
  120. 'auto_configure': true,
  121. 'roomconfig': {
  122. 'getmemberlist': ['moderator', 'participant'],
  123. 'changesubject': false,
  124. 'membersonly': true,
  125. 'persistentroom': true,
  126. 'publicroom': true,
  127. 'roomdesc': 'Welcome to this groupchat',
  128. 'whois': 'anyone'
  129. }
  130. });
  131. expect(room instanceof Model).toBeTruthy();
  132. chatroomview = _converse.chatboxviews.get('room@conference.example.org');
  133. // We pretend this is a new room, so no disco info is returned.
  134. const features_stanza = $iq({
  135. from: 'room@conference.example.org',
  136. 'id': IQ_id,
  137. 'to': 'romeo@montague.lit/desktop',
  138. 'type': 'error'
  139. }).c('error', {'type': 'cancel'})
  140. .c('item-not-found', {'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas"});
  141. _converse.connection._dataRecv(mock.createRequest(features_stanza));
  142. /* <presence xmlns="jabber:client" to="romeo@montague.lit/pda" from="room@conference.example.org/yo">
  143. * <x xmlns="http://jabber.org/protocol/muc#user">
  144. * <item affiliation="owner" jid="romeo@montague.lit/pda" role="moderator"/>
  145. * <status code="110"/>
  146. * <status code="201"/>
  147. * </x>
  148. * </presence>
  149. */
  150. const presence = $pres({
  151. from:'room@conference.example.org/some1',
  152. to:'romeo@montague.lit/pda'
  153. })
  154. .c('x', {xmlns:'http://jabber.org/protocol/muc#user'})
  155. .c('item', {
  156. affiliation: 'owner',
  157. jid: 'romeo@montague.lit/pda',
  158. role: 'moderator'
  159. }).up()
  160. .c('status', {code:'110'}).up()
  161. .c('status', {code:'201'});
  162. _converse.connection._dataRecv(mock.createRequest(presence));
  163. expect(_converse.connection.sendIQ).toHaveBeenCalled();
  164. const IQ_stanzas = _converse.connection.IQ_stanzas;
  165. const iq = await u.waitUntil(() => IQ_stanzas.filter(s => s.querySelector(`query[xmlns="${Strophe.NS.MUC_OWNER}"]`)).pop());
  166. expect(Strophe.serialize(iq)).toBe(
  167. `<iq id="${iq.getAttribute('id')}" to="room@conference.example.org" type="get" xmlns="jabber:client">`+
  168. `<query xmlns="http://jabber.org/protocol/muc#owner"/></iq>`);
  169. const node = u.toStanza(`
  170. <iq xmlns="jabber:client"
  171. type="result"
  172. to="romeo@montague.lit/pda"
  173. from="room@conference.example.org" id="${iq.getAttribute('id')}">
  174. <query xmlns="http://jabber.org/protocol/muc#owner">
  175. <x xmlns="jabber:x:data" type="form">
  176. <title>Configuration for room@conference.example.org</title>
  177. <instructions>Complete and submit this form to configure the room.</instructions>
  178. <field var="FORM_TYPE" type="hidden">
  179. <value>http://jabber.org/protocol/muc#roomconfig</value>
  180. </field>
  181. <field type="text-single" var="muc#roomconfig_roomname" label="Name">
  182. <value>Room</value>
  183. </field>
  184. <field type="text-single" var="muc#roomconfig_roomdesc" label="Description"><value/></field>
  185. <field type="boolean" var="muc#roomconfig_persistentroom" label="Make Room Persistent?"/>
  186. <field type="boolean" var="muc#roomconfig_publicroom" label="Make Room Publicly Searchable?"><value>1</value></field>
  187. <field type="boolean" var="muc#roomconfig_changesubject" label="Allow Occupants to Change Subject?"/>
  188. <field type="list-single" var="muc#roomconfig_whois" label="Who May Discover Real JIDs?"><option label="Moderators Only">
  189. <value>moderators</value></option><option label="Anyone"><value>anyone</value></option>
  190. </field>
  191. <field label="Roles and Affiliations that May Retrieve Member List"
  192. type="list-multi"
  193. var="muc#roomconfig_getmemberlist">
  194. <value>moderator</value>
  195. <value>participant</value>
  196. <value>visitor</value>
  197. </field>
  198. <field type="text-private" var="muc#roomconfig_roomsecret" label="Password"><value/></field>
  199. <field type="boolean" var="muc#roomconfig_moderatedroom" label="Make Room Moderated?"/>
  200. <field type="boolean" var="muc#roomconfig_membersonly" label="Make Room Members-Only?"/>
  201. <field type="text-single" var="muc#roomconfig_historylength" label="Maximum Number of History Messages Returned by Room">
  202. <value>20</value></field>
  203. </x>
  204. </query>
  205. </iq>`);
  206. spyOn(chatroomview.model, 'sendConfiguration').and.callThrough();
  207. _converse.connection._dataRecv(mock.createRequest(node));
  208. await u.waitUntil(() => chatroomview.model.sendConfiguration.calls.count() === 1);
  209. const sent_stanza = IQ_stanzas.filter(s => s.getAttribute('type') === 'set').pop();
  210. expect(sizzle('field[var="muc#roomconfig_roomname"] value', sent_stanza).pop().textContent.trim()).toBe('Room');
  211. expect(sizzle('field[var="muc#roomconfig_roomdesc"] value', sent_stanza).pop().textContent.trim()).toBe('Welcome to this groupchat');
  212. expect(sizzle('field[var="muc#roomconfig_persistentroom"] value', sent_stanza).pop().textContent.trim()).toBe('1');
  213. expect(sizzle('field[var="muc#roomconfig_getmemberlist"] value', sent_stanza).map(e => e.textContent.trim()).join(' ')).toBe('moderator participant');
  214. expect(sizzle('field[var="muc#roomconfig_publicroom"] value ', sent_stanza).pop().textContent.trim()).toBe('1');
  215. expect(sizzle('field[var="muc#roomconfig_changesubject"] value', sent_stanza).pop().textContent.trim()).toBe('0');
  216. expect(sizzle('field[var="muc#roomconfig_whois"] value ', sent_stanza).pop().textContent.trim()).toBe('anyone');
  217. expect(sizzle('field[var="muc#roomconfig_membersonly"] value', sent_stanza).pop().textContent.trim()).toBe('1');
  218. expect(sizzle('field[var="muc#roomconfig_historylength"] value', sent_stanza).pop().textContent.trim()).toBe('20');
  219. done();
  220. }));
  221. });
  222. });