2
0

bookmarks.js 31 KB

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