bookmarks.js 34 KB

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