bookmarks.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  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.initConverse(
  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.initConverse(
  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. await test_utils.waitUntil(() => _converse.api.rooms.get().length);
  156. expect(_.isUndefined(_converse.chatboxviews.get(jid))).toBeFalsy();
  157. done();
  158. }));
  159. describe("when bookmarked", function () {
  160. it("displays that it's bookmarked through its bookmark icon", mock.initConverse(
  161. null, ['rosterGroupsFetched'], {},
  162. async function (done, _converse) {
  163. test_utils.waitUntilDiscoConfirmed(
  164. _converse, _converse.bare_jid,
  165. [{'category': 'pubsub', 'type': 'pep'}],
  166. ['http://jabber.org/protocol/pubsub#publish-options']
  167. );
  168. await test_utils.openChatRoom(_converse, 'lounge', 'localhost', 'dummy');
  169. const view = _converse.chatboxviews.get('lounge@localhost');
  170. await test_utils.waitUntil(() => !_.isNull(view.el.querySelector('.toggle-bookmark')));
  171. var bookmark_icon = view.el.querySelector('.toggle-bookmark');
  172. expect(_.includes(bookmark_icon.classList, 'button-on')).toBeFalsy();
  173. view.model.set('bookmarked', true);
  174. expect(_.includes(bookmark_icon.classList, 'button-on')).toBeTruthy();
  175. view.model.set('bookmarked', false);
  176. expect(_.includes(bookmark_icon.classList, 'button-on')).toBeFalsy();
  177. done();
  178. }));
  179. it("can be unbookmarked", mock.initConverse(
  180. null, ['rosterGroupsFetched'], {},
  181. async function (done, _converse) {
  182. let sent_stanza, IQ_id;
  183. await test_utils.waitUntilDiscoConfirmed(
  184. _converse, _converse.bare_jid,
  185. [{'category': 'pubsub', 'type': 'pep'}],
  186. ['http://jabber.org/protocol/pubsub#publish-options']
  187. );
  188. const sendIQ = _converse.connection.sendIQ;
  189. await test_utils.openChatRoom(_converse, 'theplay', 'conference.shakespeare.lit', 'JC');
  190. const jid = 'theplay@conference.shakespeare.lit';
  191. const view = _converse.chatboxviews.get(jid);
  192. await test_utils.waitUntil(() => !_.isNull(view.el.querySelector('.toggle-bookmark')));
  193. spyOn(view, 'toggleBookmark').and.callThrough();
  194. spyOn(_converse.bookmarks, 'sendBookmarkStanza').and.callThrough();
  195. view.delegateEvents();
  196. await new Promise((success, error) => {
  197. _converse.bookmarks.create({
  198. 'jid': view.model.get('jid'),
  199. 'autojoin': false,
  200. 'name': 'The Play',
  201. 'nick': ' Othello'
  202. }, {success, error});
  203. });
  204. expect(_converse.bookmarks.length).toBe(1);
  205. expect(view.model.get('bookmarked')).toBeTruthy();
  206. var bookmark_icon = view.el.querySelector('.toggle-bookmark');
  207. expect(u.hasClass('button-on', bookmark_icon)).toBeTruthy();
  208. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
  209. sent_stanza = iq;
  210. IQ_id = sendIQ.bind(this)(iq, callback, errback);
  211. });
  212. spyOn(_converse.connection, 'getUniqueId').and.callThrough();
  213. bookmark_icon.click();
  214. expect(view.toggleBookmark).toHaveBeenCalled();
  215. expect(u.hasClass('button-on', bookmark_icon)).toBeFalsy();
  216. expect(_converse.bookmarks.length).toBe(0);
  217. // Check that an IQ stanza is sent out, containing no
  218. // conferences to bookmark (since we removed the one and
  219. // only bookmark).
  220. expect(sent_stanza.toLocaleString()).toBe(
  221. `<iq from="dummy@localhost/resource" id="${IQ_id}" type="set" xmlns="jabber:client">`+
  222. `<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
  223. `<publish node="storage:bookmarks">`+
  224. `<item id="current">`+
  225. `<storage xmlns="storage:bookmarks"/>`+
  226. `</item>`+
  227. `</publish>`+
  228. `<publish-options>`+
  229. `<x type="submit" xmlns="jabber:x:data">`+
  230. `<field type="hidden" var="FORM_TYPE">`+
  231. `<value>http://jabber.org/protocol/pubsub#publish-options</value>`+
  232. `</field>`+
  233. `<field var="pubsub#persist_items">`+
  234. `<value>true</value>`+
  235. `</field>`+
  236. `<field var="pubsub#access_model">`+
  237. `<value>whitelist</value>`+
  238. `</field>`+
  239. `</x>`+
  240. `</publish-options>`+
  241. `</pubsub>`+
  242. `</iq>`
  243. );
  244. done();
  245. }));
  246. });
  247. describe("and when autojoin is set", function () {
  248. it("will be be opened and joined automatically upon login", mock.initConverse(
  249. null, ['rosterGroupsFetched'], {},
  250. async function (done, _converse) {
  251. await test_utils.waitUntilDiscoConfirmed(
  252. _converse, _converse.bare_jid,
  253. [{'category': 'pubsub', 'type': 'pep'}],
  254. ['http://jabber.org/protocol/pubsub#publish-options']
  255. );
  256. spyOn(_converse.api.rooms, 'create').and.callThrough();
  257. const jid = 'theplay@conference.shakespeare.lit';
  258. const model = _converse.bookmarks.create({
  259. 'jid': jid,
  260. 'autojoin': false,
  261. 'name': 'The Play',
  262. 'nick': ''
  263. });
  264. expect(_converse.api.rooms.create).not.toHaveBeenCalled();
  265. _converse.bookmarks.remove(model);
  266. _converse.bookmarks.create({
  267. 'jid': jid,
  268. 'autojoin': true,
  269. 'name': 'Hamlet',
  270. 'nick': ''
  271. });
  272. expect(_converse.api.rooms.create).toHaveBeenCalled();
  273. done();
  274. }));
  275. });
  276. });
  277. describe("Bookmarks", function () {
  278. it("can be pushed from the XMPP server", mock.initConverse(
  279. ['send'], ['rosterGroupsFetched', 'connected'], {},
  280. async function (done, _converse) {
  281. await test_utils.waitUntilDiscoConfirmed(
  282. _converse, _converse.bare_jid,
  283. [{'category': 'pubsub', 'type': 'pep'}],
  284. ['http://jabber.org/protocol/pubsub#publish-options']
  285. );
  286. await test_utils.waitUntil(() => _converse.bookmarks);
  287. // Emit here instead of mocking fetching of bookmarks.
  288. _converse.emit('bookmarksInitialized');
  289. /* The stored data is automatically pushed to all of the user's
  290. * connected resources.
  291. *
  292. * Publisher receives event notification
  293. * -------------------------------------
  294. * <message from='juliet@capulet.lit'
  295. * to='juliet@capulet.lit/balcony'
  296. * type='headline'
  297. * id='rnfoo1'>
  298. * <event xmlns='http://jabber.org/protocol/pubsub#event'>
  299. * <items node='storage:bookmarks'>
  300. * <item id='current'>
  301. * <storage xmlns='storage:bookmarks'>
  302. * <conference name='The Play&apos;s the Thing'
  303. * autojoin='true'
  304. * jid='theplay@conference.shakespeare.lit'>
  305. * <nick>JC</nick>
  306. * </conference>
  307. * </storage>
  308. * </item>
  309. * </items>
  310. * </event>
  311. * </message>
  312. */
  313. var stanza = $msg({
  314. 'from': 'dummy@localhost',
  315. 'to': 'dummy@localhost/resource',
  316. 'type': 'headline',
  317. 'id': 'rnfoo1'
  318. }).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
  319. .c('items', {'node': 'storage:bookmarks'})
  320. .c('item', {'id': 'current'})
  321. .c('storage', {'xmlns': 'storage:bookmarks'})
  322. .c('conference', {'name': 'The Play&apos;s the Thing',
  323. 'autojoin': 'true',
  324. 'jid':'theplay@conference.shakespeare.lit'})
  325. .c('nick').t('JC');
  326. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  327. await test_utils.waitUntil(() => _converse.bookmarks.length);
  328. expect(_converse.bookmarks.length).toBe(1);
  329. expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
  330. done();
  331. }));
  332. it("can be retrieved from the XMPP server", mock.initConverse(
  333. {'connection': ['send']}, ['chatBoxesFetched', 'roomsPanelRendered', 'rosterGroupsFetched'], {},
  334. async function (done, _converse) {
  335. await test_utils.waitUntilDiscoConfirmed(
  336. _converse, _converse.bare_jid,
  337. [{'category': 'pubsub', 'type': 'pep'}],
  338. ['http://jabber.org/protocol/pubsub#publish-options']
  339. );
  340. /* Client requests all items
  341. * -------------------------
  342. *
  343. * <iq from='juliet@capulet.lit/randomID' type='get' id='retrieve1'>
  344. * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  345. * <items node='storage:bookmarks'/>
  346. * </pubsub>
  347. * </iq>
  348. */
  349. let IQ_id;
  350. const call = await test_utils.waitUntil(() =>
  351. _.filter(
  352. _converse.connection.send.calls.all(),
  353. call => {
  354. const stanza = call.args[0];
  355. if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
  356. return;
  357. }
  358. if (sizzle('items[node="storage:bookmarks"]', stanza).length) {
  359. IQ_id = stanza.getAttribute('id');
  360. return true;
  361. }
  362. }
  363. ).pop()
  364. );
  365. expect(Strophe.serialize(call.args[0])).toBe(
  366. `<iq from="dummy@localhost/resource" id="${IQ_id}" type="get" xmlns="jabber:client">`+
  367. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  368. '<items node="storage:bookmarks"/>'+
  369. '</pubsub>'+
  370. '</iq>');
  371. /*
  372. * Server returns all items
  373. * ------------------------
  374. * <iq type='result'
  375. * to='juliet@capulet.lit/randomID'
  376. * id='retrieve1'>
  377. * <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  378. * <items node='storage:bookmarks'>
  379. * <item id='current'>
  380. * <storage xmlns='storage:bookmarks'>
  381. * <conference name='The Play&apos;s the Thing'
  382. * autojoin='true'
  383. * jid='theplay@conference.shakespeare.lit'>
  384. * <nick>JC</nick>
  385. * </conference>
  386. * </storage>
  387. * </item>
  388. * </items>
  389. * </pubsub>
  390. * </iq>
  391. */
  392. expect(_converse.bookmarks.models.length).toBe(0);
  393. spyOn(_converse.bookmarks, 'onBookmarksReceived').and.callThrough();
  394. var stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
  395. .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
  396. .c('items', {'node': 'storage:bookmarks'})
  397. .c('item', {'id': 'current'})
  398. .c('storage', {'xmlns': 'storage:bookmarks'})
  399. .c('conference', {
  400. 'name': 'The Play&apos;s the Thing',
  401. 'autojoin': 'true',
  402. 'jid': 'theplay@conference.shakespeare.lit'
  403. }).c('nick').t('JC').up().up()
  404. .c('conference', {
  405. 'name': 'Another room',
  406. 'autojoin': 'false',
  407. 'jid': 'another@conference.shakespeare.lit'
  408. }); // Purposefully exclude the <nick> element to test #1043
  409. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  410. await test_utils.waitUntil(() => _converse.bookmarks.onBookmarksReceived.calls.count());
  411. expect(_converse.bookmarks.models.length).toBe(2);
  412. expect(_converse.bookmarks.findWhere({'jid': 'theplay@conference.shakespeare.lit'}).get('autojoin')).toBe(true);
  413. expect(_converse.bookmarks.findWhere({'jid': 'another@conference.shakespeare.lit'}).get('autojoin')).toBe(false);
  414. done();
  415. }));
  416. describe("The rooms panel", function () {
  417. it("shows a list of bookmarks", mock.initConverse(
  418. {'connection': ['send']}, ['rosterGroupsFetched'], {},
  419. async function (done, _converse) {
  420. await test_utils.waitUntilDiscoConfirmed(
  421. _converse, _converse.bare_jid,
  422. [{'category': 'pubsub', 'type': 'pep'}],
  423. ['http://jabber.org/protocol/pubsub#publish-options']
  424. );
  425. test_utils.openControlBox();
  426. let IQ_id;
  427. const call = await test_utils.waitUntil(() =>
  428. _.filter(
  429. _converse.connection.send.calls.all(),
  430. call => {
  431. const stanza = call.args[0];
  432. if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
  433. return;
  434. }
  435. if (sizzle('items[node="storage:bookmarks"]', stanza).length) {
  436. IQ_id = stanza.getAttribute('id');
  437. return true;
  438. }
  439. }
  440. ).pop()
  441. );
  442. expect(Strophe.serialize(call.args[0])).toBe(
  443. `<iq from="dummy@localhost/resource" id="${IQ_id}" type="get" xmlns="jabber:client">`+
  444. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  445. '<items node="storage:bookmarks"/>'+
  446. '</pubsub>'+
  447. '</iq>'
  448. );
  449. const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
  450. .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
  451. .c('items', {'node': 'storage:bookmarks'})
  452. .c('item', {'id': 'current'})
  453. .c('storage', {'xmlns': 'storage:bookmarks'})
  454. .c('conference', {
  455. 'name': 'The Play&apos;s the Thing',
  456. 'autojoin': 'false',
  457. 'jid': 'theplay@conference.shakespeare.lit'
  458. }).c('nick').t('JC').up().up()
  459. .c('conference', {
  460. 'name': '1st Bookmark',
  461. 'autojoin': 'false',
  462. 'jid': 'first@conference.shakespeare.lit'
  463. }).c('nick').t('JC').up().up()
  464. .c('conference', {
  465. 'autojoin': 'false',
  466. 'jid': 'noname@conference.shakespeare.lit'
  467. }).c('nick').t('JC').up().up()
  468. .c('conference', {
  469. 'name': 'Bookmark with a very very long name that will be shortened',
  470. 'autojoin': 'false',
  471. 'jid': 'longname@conference.shakespeare.lit'
  472. }).c('nick').t('JC').up().up()
  473. .c('conference', {
  474. 'name': 'Another room',
  475. 'autojoin': 'false',
  476. 'jid': 'another@conference.shakespeare.lit'
  477. }).c('nick').t('JC').up().up();
  478. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  479. await test_utils.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length);
  480. expect(document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(5);
  481. let els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
  482. expect(els[0].textContent).toBe("1st Bookmark");
  483. expect(els[1].textContent).toBe("Another room");
  484. expect(els[2].textContent).toBe("Bookmark with a very very long name that will be shortened");
  485. expect(els[3].textContent).toBe("noname@conference.shakespeare.lit");
  486. expect(els[4].textContent).toBe("The Play's the Thing");
  487. spyOn(window, 'confirm').and.returnValue(true);
  488. document.querySelector('#chatrooms .bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)').click();
  489. expect(window.confirm).toHaveBeenCalled();
  490. await test_utils.waitUntil(() => document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length === 4)
  491. els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
  492. expect(els[0].textContent).toBe("1st Bookmark");
  493. expect(els[1].textContent).toBe("Bookmark with a very very long name that will be shortened");
  494. expect(els[2].textContent).toBe("noname@conference.shakespeare.lit");
  495. expect(els[3].textContent).toBe("The Play's the Thing");
  496. done();
  497. }));
  498. it("remembers the toggle state of the bookmarks list", mock.initConverse(
  499. {'connection': ['send']}, ['rosterGroupsFetched'], {},
  500. async function (done, _converse) {
  501. test_utils.openControlBox();
  502. await test_utils.waitUntilDiscoConfirmed(
  503. _converse, _converse.bare_jid,
  504. [{'category': 'pubsub', 'type': 'pep'}],
  505. ['http://jabber.org/protocol/pubsub#publish-options']
  506. );
  507. let IQ_id;
  508. const call = await test_utils.waitUntil(() =>
  509. _.filter(
  510. _converse.connection.send.calls.all(),
  511. call => {
  512. const stanza = call.args[0];
  513. if (!(stanza instanceof Element) || stanza.nodeName !== 'iq') {
  514. return;
  515. }
  516. if (sizzle('items[node="storage:bookmarks"]', stanza).length) {
  517. IQ_id = stanza.getAttribute('id');
  518. return true;
  519. }
  520. }
  521. ).pop()
  522. );
  523. expect(Strophe.serialize(call.args[0])).toBe(
  524. `<iq from="dummy@localhost/resource" id="${IQ_id}" type="get" xmlns="jabber:client">`+
  525. '<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
  526. '<items node="storage:bookmarks"/>'+
  527. '</pubsub>'+
  528. '</iq>'
  529. );
  530. const stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
  531. .c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
  532. .c('items', {'node': 'storage:bookmarks'})
  533. .c('item', {'id': 'current'})
  534. .c('storage', {'xmlns': 'storage:bookmarks'});
  535. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  536. _converse.bookmarks.create({
  537. 'jid': 'theplay@conference.shakespeare.lit',
  538. 'autojoin': false,
  539. 'name': 'The Play',
  540. 'nick': ''
  541. });
  542. await test_utils.waitUntil(() => $('#chatrooms .bookmarks.rooms-list .room-item:visible').length);
  543. expect($('#chatrooms .bookmarks.rooms-list').hasClass('collapsed')).toBeFalsy();
  544. expect($('#chatrooms .bookmarks.rooms-list .room-item:visible').length).toBe(1);
  545. expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
  546. $('#chatrooms .bookmarks-toggle')[0].click();
  547. expect($('#chatrooms .bookmarks.rooms-list').hasClass('collapsed')).toBeTruthy();
  548. expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.CLOSED);
  549. $('#chatrooms .bookmarks-toggle')[0].click();
  550. expect($('#chatrooms .bookmarks.rooms-list').hasClass('collapsed')).toBeFalsy();
  551. expect($('#chatrooms .bookmarks.rooms-list .room-item:visible').length).toBe(1);
  552. expect(_converse.bookmarksview.list_model.get('toggle-state')).toBe(_converse.OPENED);
  553. done();
  554. }));
  555. });
  556. });
  557. describe("When hide_open_bookmarks is true and a bookmarked room is opened", function () {
  558. it("can be closed", mock.initConverse(
  559. null, ['rosterGroupsFetched'],
  560. { hide_open_bookmarks: true },
  561. async function (done, _converse) {
  562. const jid = 'room@conference.example.org';
  563. await test_utils.waitUntilDiscoConfirmed(
  564. _converse, _converse.bare_jid,
  565. [{'category': 'pubsub', 'type': 'pep'}],
  566. ['http://jabber.org/protocol/pubsub#publish-options']
  567. );
  568. // XXX Create bookmarks view here, otherwise we need to mock stanza
  569. // traffic for it to get created.
  570. _converse.bookmarksview = new _converse.BookmarksView(
  571. {'model': _converse.bookmarks}
  572. );
  573. _converse.emit('bookmarksInitialized');
  574. // Check that it's there
  575. _converse.bookmarks.create({
  576. 'jid': jid,
  577. 'autojoin': false,
  578. 'name': 'The Play',
  579. 'nick': ' Othello'
  580. });
  581. expect(_converse.bookmarks.length).toBe(1);
  582. const room_els = _converse.bookmarksview.el.querySelectorAll(".open-room");
  583. expect(room_els.length).toBe(1);
  584. // Check that it disappears once the room is opened
  585. const bookmark = _converse.bookmarksview.el.querySelector(".open-room");
  586. bookmark.click();
  587. await test_utils.waitUntil(() => _converse.chatboxviews.get(jid));
  588. expect(u.hasClass('hidden', _converse.bookmarksview.el.querySelector(".available-chatroom"))).toBeTruthy();
  589. // Check that it reappears once the room is closed
  590. const view = _converse.chatboxviews.get(jid);
  591. view.close();
  592. expect(u.hasClass('hidden', _converse.bookmarksview.el.querySelector(".available-chatroom"))).toBeFalsy();
  593. done();
  594. }));
  595. });
  596. }));