bookmarks.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /* global mock, converse */
  2. const Strophe = converse.env.Strophe;
  3. describe("A chat room", function () {
  4. it("can be bookmarked", mock.initConverse(['rosterGroupsFetched'], {}, async function (done, _converse) {
  5. await mock.waitUntilDiscoConfirmed(
  6. _converse, _converse.bare_jid,
  7. [{'category': 'pubsub', 'type': 'pep'}],
  8. ['http://jabber.org/protocol/pubsub#publish-options']
  9. );
  10. const { u, $iq } = converse.env;
  11. let sent_stanza, IQ_id;
  12. const sendIQ = _converse.connection.sendIQ;
  13. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
  14. sent_stanza = iq;
  15. IQ_id = sendIQ.bind(this)(iq, callback, errback);
  16. });
  17. spyOn(_converse.connection, 'getUniqueId').and.callThrough();
  18. await mock.openChatRoom(_converse, 'theplay', 'conference.shakespeare.lit', 'JC');
  19. const jid = 'theplay@conference.shakespeare.lit';
  20. const view = _converse.chatboxviews.get(jid);
  21. spyOn(view, 'renderBookmarkForm').and.callThrough();
  22. spyOn(view, 'closeForm').and.callThrough();
  23. await u.waitUntil(() => view.el.querySelector('.toggle-bookmark') !== null);
  24. const toggle = view.el.querySelector('.toggle-bookmark');
  25. expect(toggle.title).toBe('Bookmark this groupchat');
  26. toggle.click();
  27. expect(view.renderBookmarkForm).toHaveBeenCalled();
  28. view.el.querySelector('.button-cancel').click();
  29. expect(view.closeForm).toHaveBeenCalled();
  30. expect(u.hasClass('on-button', toggle), false);
  31. expect(toggle.title).toBe('Bookmark this groupchat');
  32. toggle.click();
  33. expect(view.renderBookmarkForm).toHaveBeenCalled();
  34. /* Client uploads data:
  35. * --------------------
  36. * <iq from='juliet@capulet.lit/balcony' type='set' id='pip1'>
  37. * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  38. * <publish node='storage:bookmarks'>
  39. * <item id='current'>
  40. * <storage xmlns='storage:bookmarks'>
  41. * <conference name='The Play&apos;s the Thing'
  42. * autojoin='true'
  43. * jid='theplay@conference.shakespeare.lit'>
  44. * <nick>JC</nick>
  45. * </conference>
  46. * </storage>
  47. * </item>
  48. * </publish>
  49. * <publish-options>
  50. * <x xmlns='jabber:x:data' type='submit'>
  51. * <field var='FORM_TYPE' type='hidden'>
  52. * <value>http://jabber.org/protocol/pubsub#publish-options</value>
  53. * </field>
  54. * <field var='pubsub#persist_items'>
  55. * <value>true</value>
  56. * </field>
  57. * <field var='pubsub#access_model'>
  58. * <value>whitelist</value>
  59. * </field>
  60. * </x>
  61. * </publish-options>
  62. * </pubsub>
  63. * </iq>
  64. */
  65. expect(view.model.get('bookmarked')).toBeFalsy();
  66. const form = view.el.querySelector('.chatroom-form');
  67. form.querySelector('input[name="name"]').value = 'Play&apos;s the Thing';
  68. form.querySelector('input[name="autojoin"]').checked = 'checked';
  69. form.querySelector('input[name="nick"]').value = 'JC';
  70. _converse.connection.IQ_stanzas = [];
  71. view.el.querySelector('.btn-primary').click();
  72. await u.waitUntil(() => sent_stanza);
  73. expect(Strophe.serialize(sent_stanza)).toBe(
  74. `<iq from="romeo@montague.lit/orchard" id="${IQ_id}" type="set" xmlns="jabber:client">`+
  75. `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
  76. `<publish node="storage:bookmarks">`+
  77. `<item id="current">`+
  78. `<storage xmlns="storage:bookmarks">`+
  79. `<conference autojoin="true" jid="theplay@conference.shakespeare.lit" name="Play&amp;apos;s the Thing">`+
  80. `<nick>JC</nick>`+
  81. `</conference>`+
  82. `</storage>`+
  83. `</item>`+
  84. `</publish>`+
  85. `<publish-options>`+
  86. `<x type="submit" xmlns="jabber:x:data">`+
  87. `<field type="hidden" var="FORM_TYPE">`+
  88. `<value>http://jabber.org/protocol/pubsub#publish-options</value>`+
  89. `</field>`+
  90. `<field var="pubsub#persist_items">`+
  91. `<value>true</value>`+
  92. `</field>`+
  93. `<field var="pubsub#access_model">`+
  94. `<value>whitelist</value>`+
  95. `</field>`+
  96. `</x>`+
  97. `</publish-options>`+
  98. `</pubsub>`+
  99. `</iq>`
  100. );
  101. /* Server acknowledges successful storage
  102. *
  103. * <iq to='juliet@capulet.lit/balcony' type='result' id='pip1'/>
  104. */
  105. const stanza = $iq({
  106. 'to':_converse.connection.jid,
  107. 'type':'result',
  108. 'id':IQ_id
  109. });
  110. _converse.connection._dataRecv(mock.createRequest(stanza));
  111. await u.waitUntil(() => view.model.get('bookmarked'));
  112. expect(view.model.get('bookmarked')).toBeTruthy();
  113. await u.waitUntil(() => view.el.querySelector('.toggle-bookmark')?.title === 'Unbookmark this groupchat');
  114. expect(u.hasClass('on-button', view.el.querySelector('.toggle-bookmark')), true);
  115. // We ignore this IQ stanza... (unless it's an error stanza), so
  116. // nothing to test for here.
  117. done();
  118. }));
  119. it("will be automatically opened if 'autojoin' is set on the bookmark", mock.initConverse(
  120. ['rosterGroupsFetched'], {},
  121. async function (done, _converse) {
  122. const { u, _ } = converse.env;
  123. await mock.waitUntilDiscoConfirmed(
  124. _converse, _converse.bare_jid,
  125. [{'category': 'pubsub', 'type': 'pep'}],
  126. ['http://jabber.org/protocol/pubsub#publish-options']
  127. );
  128. await u.waitUntil(() => _converse.bookmarks);
  129. let jid = 'lounge@montague.lit';
  130. _converse.bookmarks.create({
  131. 'jid': jid,
  132. 'autojoin': false,
  133. 'name': 'The Lounge',
  134. 'nick': ' Othello'
  135. });
  136. expect(_converse.chatboxviews.get(jid) === undefined).toBeTruthy();
  137. jid = 'theplay@conference.shakespeare.lit';
  138. _converse.bookmarks.create({
  139. 'jid': jid,
  140. 'autojoin': true,
  141. 'name': 'The Play',
  142. 'nick': ' Othello'
  143. });
  144. await new Promise(resolve => _converse.api.listen.once('chatRoomViewInitialized', resolve));
  145. expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
  146. // Check that we don't auto-join if muc_respect_autojoin is false
  147. _converse.muc_respect_autojoin = false;
  148. jid = 'balcony@conference.shakespeare.lit';
  149. _converse.bookmarks.create({
  150. 'jid': jid,
  151. 'autojoin': true,
  152. 'name': 'Balcony',
  153. 'nick': ' Othello'
  154. });
  155. expect(_converse.chatboxviews.get(jid) === undefined).toBe(true);
  156. done();
  157. }));
  158. describe("when bookmarked", function () {
  159. it("will use the nickname from the bookmark", mock.initConverse(
  160. ['rosterGroupsFetched'], {}, async function (done, _converse) {
  161. const { u } = converse.env;
  162. await mock.waitUntilBookmarksReturned(_converse);
  163. const muc_jid = 'coven@chat.shakespeare.lit';
  164. _converse.bookmarks.create({
  165. 'jid': muc_jid,
  166. 'autojoin': false,
  167. 'name': 'The Play',
  168. 'nick': 'Othello'
  169. });
  170. spyOn(_converse.ChatRoom.prototype, 'getAndPersistNickname').and.callThrough();
  171. const room_creation_promise = _converse.api.rooms.open(muc_jid);
  172. await mock.getRoomFeatures(_converse, muc_jid);
  173. const room = await room_creation_promise;
  174. await u.waitUntil(() => room.getAndPersistNickname.calls.count());
  175. expect(room.get('nick')).toBe('Othello');
  176. done();
  177. }));
  178. it("displays that it's bookmarked through its bookmark icon", mock.initConverse(
  179. ['rosterGroupsFetched'], {},
  180. async function (done, _converse) {
  181. const { u } = converse.env;
  182. mock.waitUntilDiscoConfirmed(
  183. _converse, _converse.bare_jid,
  184. [{'category': 'pubsub', 'type': 'pep'}],
  185. ['http://jabber.org/protocol/pubsub#publish-options']
  186. );
  187. await _converse.api.rooms.open(`lounge@montague.lit`);
  188. const view = _converse.chatboxviews.get('lounge@montague.lit');
  189. expect(view.el.querySelector('.chatbox-title__text .fa-bookmark')).toBe(null);
  190. _converse.bookmarks.create({
  191. 'jid': view.model.get('jid'),
  192. 'autojoin': false,
  193. 'name': 'The lounge',
  194. 'nick': ' some1'
  195. });
  196. view.model.set('bookmarked', true);
  197. await u.waitUntil(() => view.el.querySelector('.chatbox-title__text .fa-bookmark') !== null);
  198. view.model.set('bookmarked', false);
  199. await u.waitUntil(() => view.el.querySelector('.chatbox-title__text .fa-bookmark') === null);
  200. done();
  201. }));
  202. it("can be unbookmarked", mock.initConverse(
  203. ['rosterGroupsFetched'], {}, async function (done, _converse) {
  204. const { u, Strophe } = converse.env;
  205. await mock.waitUntilBookmarksReturned(_converse);
  206. const muc_jid = 'theplay@conference.shakespeare.lit';
  207. await _converse.api.rooms.open(muc_jid);
  208. const view = _converse.chatboxviews.get(muc_jid);
  209. await u.waitUntil(() => view.el.querySelector('.toggle-bookmark'));
  210. spyOn(view, 'toggleBookmark').and.callThrough();
  211. spyOn(_converse.bookmarks, 'sendBookmarkStanza').and.callThrough();
  212. view.delegateEvents();
  213. _converse.bookmarks.create({
  214. 'jid': view.model.get('jid'),
  215. 'autojoin': false,
  216. 'name': 'The Play',
  217. 'nick': ' Othello'
  218. });
  219. expect(_converse.bookmarks.length).toBe(1);
  220. await u.waitUntil(() => _converse.chatboxes.length >= 1);
  221. expect(view.model.get('bookmarked')).toBeTruthy();
  222. await u.waitUntil(() => view.el.querySelector('.chatbox-title__text .fa-bookmark') !== null);
  223. spyOn(_converse.connection, 'getUniqueId').and.callThrough();
  224. const bookmark_icon = view.el.querySelector('.toggle-bookmark');
  225. bookmark_icon.click();
  226. expect(view.toggleBookmark).toHaveBeenCalled();
  227. await u.waitUntil(() => view.el.querySelector('.chatbox-title__text .fa-bookmark') === null);
  228. expect(_converse.bookmarks.length).toBe(0);
  229. // Check that an IQ stanza is sent out, containing no
  230. // conferences to bookmark (since we removed the one and
  231. // only bookmark).
  232. const sent_stanza = _converse.connection.IQ_stanzas.pop();
  233. expect(Strophe.serialize(sent_stanza)).toBe(
  234. `<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="set" xmlns="jabber:client">`+
  235. `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
  236. `<publish node="storage:bookmarks">`+
  237. `<item id="current">`+
  238. `<storage xmlns="storage:bookmarks"/>`+
  239. `</item>`+
  240. `</publish>`+
  241. `<publish-options>`+
  242. `<x type="submit" xmlns="jabber:x:data">`+
  243. `<field type="hidden" var="FORM_TYPE">`+
  244. `<value>http://jabber.org/protocol/pubsub#publish-options</value>`+
  245. `</field>`+
  246. `<field var="pubsub#persist_items">`+
  247. `<value>true</value>`+
  248. `</field>`+
  249. `<field var="pubsub#access_model">`+
  250. `<value>whitelist</value>`+
  251. `</field>`+
  252. `</x>`+
  253. `</publish-options>`+
  254. `</pubsub>`+
  255. `</iq>`
  256. );
  257. done();
  258. }));
  259. });
  260. describe("and when autojoin is set", function () {
  261. it("will be be opened and joined automatically upon login", mock.initConverse(
  262. ['rosterGroupsFetched'], {},
  263. async function (done, _converse) {
  264. await mock.waitUntilBookmarksReturned(_converse);
  265. spyOn(_converse.api.rooms, 'create').and.callThrough();
  266. const jid = 'theplay@conference.shakespeare.lit';
  267. const model = _converse.bookmarks.create({
  268. 'jid': jid,
  269. 'autojoin': false,
  270. 'name': 'The Play',
  271. 'nick': ''
  272. });
  273. expect(_converse.api.rooms.create).not.toHaveBeenCalled();
  274. _converse.bookmarks.remove(model);
  275. _converse.bookmarks.create({
  276. 'jid': jid,
  277. 'autojoin': true,
  278. 'name': 'Hamlet',
  279. 'nick': ''
  280. });
  281. expect(_converse.api.rooms.create).toHaveBeenCalled();
  282. done();
  283. }));
  284. });
  285. });
  286. describe("Bookmarks", function () {
  287. it("can be pushed from the XMPP server", mock.initConverse(
  288. ['rosterGroupsFetched', 'connected'], {}, async function (done, _converse) {
  289. const { $msg, u } = converse.env;
  290. await mock.waitUntilBookmarksReturned(_converse);
  291. /* The stored data is automatically pushed to all of the user's
  292. * connected resources.
  293. *
  294. * Publisher receives event notification
  295. * -------------------------------------
  296. * <message from='juliet@capulet.lit'
  297. * to='juliet@capulet.lit/balcony'
  298. * type='headline'
  299. * id='rnfoo1'>
  300. * <event xmlns='http://jabber.org/protocol/pubsub#event'>
  301. * <items node='storage:bookmarks'>
  302. * <item id='current'>
  303. * <storage xmlns='storage:bookmarks'>
  304. * <conference name='The Play&apos;s the Thing'
  305. * autojoin='true'
  306. * jid='theplay@conference.shakespeare.lit'>
  307. * <nick>JC</nick>
  308. * </conference>
  309. * </storage>
  310. * </item>
  311. * </items>
  312. * </event>
  313. * </message>
  314. */
  315. const stanza = $msg({
  316. 'from': 'romeo@montague.lit',
  317. 'to': 'romeo@montague.lit/orchard',
  318. 'type': 'headline',
  319. 'id': 'rnfoo1'
  320. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  321. .c('items', {'node': 'storage:bookmarks'})
  322. .c('item', {'id': 'current'})
  323. .c('storage', {'xmlns': 'storage:bookmarks'})
  324. .c('conference', {'name': 'The Play&apos;s the Thing',
  325. 'autojoin': 'true',
  326. 'jid':'theplay@conference.shakespeare.lit'})
  327. .c('nick').t('JC');
  328. _converse.connection._dataRecv(mock.createRequest(stanza));
  329. await u.waitUntil(() => _converse.bookmarks.length);
  330. expect(_converse.bookmarks.length).toBe(1);
  331. expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
  332. done();
  333. }));
  334. it("can be retrieved from the XMPP server", mock.initConverse(
  335. ['chatBoxesFetched', 'roomsPanelRendered', 'rosterGroupsFetched'], {},
  336. async function (done, _converse) {
  337. const { Strophe, sizzle, u, $iq } = converse.env;
  338. await mock.waitUntilDiscoConfirmed(
  339. _converse, _converse.bare_jid,
  340. [{'category': 'pubsub', 'type': 'pep'}],
  341. ['http://jabber.org/protocol/pubsub#publish-options']
  342. );
  343. /* Client requests all items
  344. * -------------------------
  345. *
  346. * <iq from='juliet@capulet.lit/randomID' type='get' id='retrieve1'>
  347. * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  348. * <items node='storage:bookmarks'/>
  349. * </pubsub>
  350. * </iq>
  351. */
  352. const IQ_stanzas = _converse.connection.IQ_stanzas;
  353. const sent_stanza = await u.waitUntil(
  354. () => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
  355. expect(Strophe.serialize(sent_stanza)).toBe(
  356. `<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
  357. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  358. '<items node="storage:bookmarks"/>'+
  359. '</pubsub>'+
  360. '</iq>');
  361. /*
  362. * Server returns all items
  363. * ------------------------
  364. * <iq type='result'
  365. * to='juliet@capulet.lit/randomID'
  366. * id='retrieve1'>
  367. * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  368. * <items node='storage:bookmarks'>
  369. * <item id='current'>
  370. * <storage xmlns='storage:bookmarks'>
  371. * <conference name='The Play&apos;s the Thing'
  372. * autojoin='true'
  373. * jid='theplay@conference.shakespeare.lit'>
  374. * <nick>JC</nick>
  375. * </conference>
  376. * </storage>
  377. * </item>
  378. * </items>
  379. * </pubsub>
  380. * </iq>
  381. */
  382. expect(_converse.bookmarks.models.length).toBe(0);
  383. spyOn(_converse.bookmarks, 'onBookmarksReceived').and.callThrough();
  384. var stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':sent_stanza.getAttribute('id')})
  385. .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
  386. .c('items', {'node': 'storage:bookmarks'})
  387. .c('item', {'id': 'current'})
  388. .c('storage', {'xmlns': 'storage:bookmarks'})
  389. .c('conference', {
  390. 'name': 'The Play&apos;s the Thing',
  391. 'autojoin': 'true',
  392. 'jid': 'theplay@conference.shakespeare.lit'
  393. }).c('nick').t('JC').up().up()
  394. .c('conference', {
  395. 'name': 'Another room',
  396. 'autojoin': 'false',
  397. 'jid': 'another@conference.shakespeare.lit'
  398. }); // Purposefully exclude the <nick> element to test #1043
  399. _converse.connection._dataRecv(mock.createRequest(stanza));
  400. await u.waitUntil(() => _converse.bookmarks.onBookmarksReceived.calls.count());
  401. await _converse.api.waitUntil('bookmarksInitialized');
  402. expect(_converse.bookmarks.models.length).toBe(2);
  403. expect(_converse.bookmarks.findWhere({'jid': 'theplay@conference.shakespeare.lit'}).get('autojoin')).toBe(true);
  404. expect(_converse.bookmarks.findWhere({'jid': 'another@conference.shakespeare.lit'}).get('autojoin')).toBe(false);
  405. done();
  406. }));
  407. describe("The rooms panel", function () {
  408. it("shows a list of bookmarks", mock.initConverse(
  409. ['rosterGroupsFetched'], {},
  410. async function (done, _converse) {
  411. await mock.waitUntilDiscoConfirmed(
  412. _converse, _converse.bare_jid,
  413. [{'category': 'pubsub', 'type': 'pep'}],
  414. ['http://jabber.org/protocol/pubsub#publish-options']
  415. );
  416. mock.openControlBox(_converse);
  417. const { Strophe, u, sizzle, $iq } = converse.env;
  418. const IQ_stanzas = _converse.connection.IQ_stanzas;
  419. const sent_stanza = await u.waitUntil(
  420. () => IQ_stanzas.filter(s => sizzle('items[node="storage:bookmarks"]', s).length).pop());
  421. expect(Strophe.serialize(sent_stanza)).toBe(
  422. `<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
  423. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  424. '<items node="storage:bookmarks"/>'+
  425. '</pubsub>'+
  426. '</iq>'
  427. );
  428. const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':sent_stanza.getAttribute('id')})
  429. .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
  430. .c('items', {'node': 'storage:bookmarks'})
  431. .c('item', {'id': 'current'})
  432. .c('storage', {'xmlns': 'storage:bookmarks'})
  433. .c('conference', {
  434. 'name': 'The Play&apos;s the Thing',
  435. 'autojoin': 'false',
  436. 'jid': 'theplay@conference.shakespeare.lit'
  437. }).c('nick').t('JC').up().up()
  438. .c('conference', {
  439. 'name': '1st Bookmark',
  440. 'autojoin': 'false',
  441. 'jid': 'first@conference.shakespeare.lit'
  442. }).c('nick').t('JC').up().up()
  443. .c('conference', {
  444. 'autojoin': 'false',
  445. 'jid': 'noname@conference.shakespeare.lit'
  446. }).c('nick').t('JC').up().up()
  447. .c('conference', {
  448. 'name': 'Bookmark with a very very long name that will be shortened',
  449. 'autojoin': 'false',
  450. 'jid': 'longname@conference.shakespeare.lit'
  451. }).c('nick').t('JC').up().up()
  452. .c('conference', {
  453. 'name': 'Another room',
  454. 'autojoin': 'false',
  455. 'jid': 'another@conference.shakespeare.lit'
  456. }).c('nick').t('JC').up().up();
  457. _converse.connection._dataRecv(mock.createRequest(stanza));
  458. await u.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length);
  459. expect(document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(5);
  460. let els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
  461. expect(els[0].textContent).toBe("1st Bookmark");
  462. expect(els[1].textContent).toBe("Another room");
  463. expect(els[2].textContent).toBe("Bookmark with a very very long name that will be shortened");
  464. expect(els[3].textContent).toBe("noname@conference.shakespeare.lit");
  465. expect(els[4].textContent).toBe("The Play's the Thing");
  466. spyOn(window, 'confirm').and.returnValue(true);
  467. document.querySelector('#chatrooms .bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)').click();
  468. expect(window.confirm).toHaveBeenCalled();
  469. await u.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length === 4)
  470. els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
  471. expect(els[0].textContent).toBe("1st Bookmark");
  472. expect(els[1].textContent).toBe("Bookmark with a very very long name that will be shortened");
  473. expect(els[2].textContent).toBe("noname@conference.shakespeare.lit");
  474. expect(els[3].textContent).toBe("The Play's the Thing");
  475. done();
  476. }));
  477. it("remembers the toggle state of the bookmarks list", mock.initConverse(
  478. ['rosterGroupsFetched'], {}, async function (done, _converse) {
  479. await mock.openControlBox(_converse);
  480. await mock.waitUntilDiscoConfirmed(
  481. _converse, _converse.bare_jid,
  482. [{'category': 'pubsub', 'type': 'pep'}],
  483. ['http://jabber.org/protocol/pubsub#publish-options']
  484. );
  485. const { Strophe, u, sizzle, $iq } = converse.env;
  486. const IQ_stanzas = _converse.connection.IQ_stanzas;
  487. const sent_stanza = await u.waitUntil(
  488. () => IQ_stanzas.filter(s => sizzle('iq items[node="storage:bookmarks"]', s).length).pop());
  489. expect(Strophe.serialize(sent_stanza)).toBe(
  490. `<iq from="romeo@montague.lit/orchard" id="${sent_stanza.getAttribute('id')}" type="get" xmlns="jabber:client">`+
  491. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  492. '<items node="storage:bookmarks"/>'+
  493. '</pubsub>'+
  494. '</iq>'
  495. );
  496. const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id': sent_stanza.getAttribute('id')})
  497. .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
  498. .c('items', {'node': 'storage:bookmarks'})
  499. .c('item', {'id': 'current'})
  500. .c('storage', {'xmlns': 'storage:bookmarks'});
  501. _converse.connection._dataRecv(mock.createRequest(stanza));
  502. await _converse.api.waitUntil('bookmarksInitialized');
  503. _converse.bookmarks.create({
  504. 'jid': 'theplay@conference.shakespeare.lit',
  505. 'autojoin': false,
  506. 'name': 'The Play',
  507. 'nick': ''
  508. });
  509. const el = _converse.chatboxviews.el
  510. const selector = '#chatrooms .bookmarks.rooms-list .room-item';
  511. await u.waitUntil(() => sizzle(selector, el).filter(u.isVisible).length);
  512. expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', el).pop())).toBeFalsy();
  513. expect(sizzle(selector, el).filter(u.isVisible).length).toBe(1);
  514. expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
  515. sizzle('#chatrooms .bookmarks-toggle', el).pop().click();
  516. expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', el).pop())).toBeTruthy();
  517. expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.CLOSED);
  518. sizzle('#chatrooms .bookmarks-toggle', el).pop().click();
  519. expect(u.hasClass('collapsed', sizzle('#chatrooms .bookmarks.rooms-list', el).pop())).toBeFalsy();
  520. expect(sizzle(selector, el).filter(u.isVisible).length).toBe(1);
  521. expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
  522. done();
  523. }));
  524. });
  525. });
  526. describe("When hide_open_bookmarks is true and a bookmarked room is opened", function () {
  527. it("can be closed", mock.initConverse(
  528. ['rosterGroupsFetched'],
  529. { hide_open_bookmarks: true },
  530. async function (done, _converse) {
  531. await mock.openControlBox(_converse);
  532. await mock.waitUntilBookmarksReturned(_converse);
  533. // Check that it's there
  534. const jid = 'room@conference.example.org';
  535. _converse.bookmarks.create({
  536. 'jid': jid,
  537. 'autojoin': false,
  538. 'name': 'The Play',
  539. 'nick': ' Othello'
  540. });
  541. expect(_converse.bookmarks.length).toBe(1);
  542. const u = converse.env.utils;
  543. const bmarks_view = _converse.bookmarksview;
  544. await u.waitUntil(() => bmarks_view.el.querySelectorAll(".open-room").length, 500);
  545. const room_els = bmarks_view.el.querySelectorAll(".open-room");
  546. expect(room_els.length).toBe(1);
  547. const bookmark = _converse.bookmarksview.el.querySelector(".open-room");
  548. bookmark.click();
  549. await u.waitUntil(() => _converse.chatboxviews.get(jid));
  550. expect(u.hasClass('hidden', _converse.bookmarksview.el.querySelector(".available-chatroom"))).toBeTruthy();
  551. // Check that it reappears once the room is closed
  552. const view = _converse.chatboxviews.get(jid);
  553. view.close();
  554. await u.waitUntil(() => !u.hasClass('hidden', _converse.bookmarksview.el.querySelector(".available-chatroom")));
  555. done();
  556. }));
  557. });