bookmarks.js 34 KB

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