bookmarks.js 32 KB

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