bookmarks.js 29 KB

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