chatbox.js 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683
  1. (function (root, factory) {
  2. define([
  3. "jquery",
  4. "jasmine",
  5. "utils",
  6. "converse-core",
  7. "mock",
  8. "test-utils"
  9. ], factory);
  10. } (this, function ($, jasmine, utils, converse, mock, test_utils) {
  11. "use strict";
  12. var _ = converse.env._;
  13. var $iq = converse.env.$iq;
  14. var $msg = converse.env.$msg;
  15. var Strophe = converse.env.Strophe;
  16. var u = converse.env.utils;
  17. return describe("Chatboxes", function () {
  18. describe("A Chatbox", function () {
  19. it("has a /help command to show the available commands",
  20. mock.initConverseWithPromises(
  21. null, ['rosterGroupsFetched'], {},
  22. function (done, _converse) {
  23. test_utils.createContacts(_converse, 'current');
  24. test_utils.openControlBox();
  25. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  26. test_utils.openChatBoxFor(_converse, contact_jid);
  27. var view = _converse.chatboxviews.get(contact_jid);
  28. test_utils.sendMessage(view, '/help');
  29. const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info:not(.chat-date)'), 0);
  30. expect(info_messages.length).toBe(3);
  31. expect(info_messages.pop().textContent).toBe('/help: Show this menu');
  32. expect(info_messages.pop().textContent).toBe('/me: Write in the third person');
  33. expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
  34. var msg = $msg({
  35. from: contact_jid,
  36. to: _converse.connection.jid,
  37. type: 'chat',
  38. id: (new Date()).getTime()
  39. }).c('body').t('hello world').tree();
  40. _converse.chatboxes.onMessage(msg);
  41. expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
  42. done();
  43. }));
  44. it("supports the /me command",
  45. mock.initConverseWithPromises(
  46. null, ['rosterGroupsFetched'], {},
  47. function (done, _converse) {
  48. var view;
  49. test_utils.createContacts(_converse, 'current');
  50. test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
  51. .then(() => test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname')), 300)
  52. .then(function () {
  53. test_utils.openControlBox();
  54. expect(_converse.chatboxes.length).toEqual(1);
  55. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  56. var message = '/me is tired';
  57. var msg = $msg({
  58. from: sender_jid,
  59. to: _converse.connection.jid,
  60. type: 'chat',
  61. id: (new Date()).getTime()
  62. }).c('body').t(message).up()
  63. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  64. _converse.chatboxes.onMessage(msg);
  65. view = _converse.chatboxviews.get(sender_jid);
  66. test_utils.waitUntil(function () {
  67. return u.isVisible(view.el);
  68. }).then(function () {
  69. expect(view.el.querySelectorAll('.chat-action').length).toBe(1);
  70. expect(_.includes(view.el.querySelector('.chat-msg-author').textContent, '**Max Frankfurter')).toBeTruthy();
  71. expect($(view.el).find('.chat-msg-text').text()).toBe(' is tired');
  72. message = '/me is as well';
  73. test_utils.sendMessage(view, message);
  74. expect(view.el.querySelectorAll('.chat-action').length).toBe(2);
  75. return test_utils.waitUntil(() => $(view.el).find('.chat-msg-author:last').text() === '**Max Mustermann');
  76. }).then(function () {
  77. expect($(view.el).find('.chat-msg-text:last').text()).toBe(' is as well');
  78. expect($(view.el).find('.chat-msg:last').hasClass('chat-msg-followup')).toBe(false);
  79. // Check that /me messages after a normal message don't
  80. // get the 'chat-msg-followup' class.
  81. message = 'This a normal message';
  82. test_utils.sendMessage(view, message);
  83. let message_el = view.el.querySelector('.message:last-child');
  84. expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy();
  85. message = '/me wrote a 3rd person message';
  86. test_utils.sendMessage(view, message);
  87. message_el = view.el.querySelector('.message:last-child');
  88. expect(view.el.querySelectorAll('.chat-action').length).toBe(3);
  89. expect($(view.el).find('.chat-msg-text:last').text()).toBe(' wrote a 3rd person message');
  90. expect($(view.el).find('.chat-msg-author:last').is(':visible')).toBeTruthy();
  91. expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy();
  92. done();
  93. });
  94. });
  95. }));
  96. it("is created when you click on a roster item",
  97. mock.initConverseWithPromises(
  98. null, ['rosterGroupsFetched'], {},
  99. function (done, _converse) {
  100. test_utils.createContacts(_converse, 'current');
  101. test_utils.openControlBox();
  102. var i, $el, jid, chatboxview;
  103. // openControlBox was called earlier, so the controlbox is
  104. // visible, but no other chat boxes have been created.
  105. expect(_converse.chatboxes.length).toEqual(1);
  106. spyOn(_converse.chatboxviews, 'trimChats');
  107. expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
  108. test_utils.waitUntil(function () {
  109. return $(_converse.rosterview.el).find('.roster-group li').length;
  110. }, 700).then(function () {
  111. var online_contacts = $(_converse.rosterview.el).find('.roster-group .current-xmpp-contact a.open-chat');
  112. expect(online_contacts.length).toBe(15);
  113. for (i=0; i<online_contacts.length; i++) {
  114. $el = $(online_contacts[i]);
  115. jid = $el.text().trim().replace(/ /g,'.').toLowerCase() + '@localhost';
  116. $el[0].click();
  117. chatboxview = _converse.chatboxviews.get(jid);
  118. expect(_converse.chatboxes.length).toEqual(i+2);
  119. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  120. // Check that new chat boxes are created to the left of the
  121. // controlbox (but to the right of all existing chat boxes)
  122. expect($("#conversejs .chatbox").length).toBe(i+2);
  123. expect($("#conversejs .chatbox")[1].id).toBe(chatboxview.model.get('box_id'));
  124. }
  125. done();
  126. });
  127. }));
  128. it("can be trimmed to conserve space",
  129. mock.initConverseWithPromises(
  130. null, ['rosterGroupsFetched'], {},
  131. function (done, _converse) {
  132. test_utils.createContacts(_converse, 'current');
  133. test_utils.openControlBox();
  134. var i, $el, jid, chatbox, chatboxview, trimmedview;
  135. // openControlBox was called earlier, so the controlbox is
  136. // visible, but no other chat boxes have been created.
  137. var trimmed_chatboxes = _converse.minimized_chats;
  138. expect(_converse.chatboxes.length).toEqual(1);
  139. spyOn(_converse.chatboxviews, 'trimChats');
  140. spyOn(trimmed_chatboxes, 'addChat').and.callThrough();
  141. spyOn(trimmed_chatboxes, 'removeChat').and.callThrough();
  142. expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
  143. _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attached.
  144. test_utils.waitUntil(function () {
  145. return $(_converse.rosterview.el).find('.roster-group li').length;
  146. }, 700).then(function () {
  147. // Test that they can be maximized again
  148. var online_contacts = $(_converse.rosterview.el).find('.roster-group .current-xmpp-contact a.open-chat');
  149. expect(online_contacts.length).toBe(15);
  150. for (i=0; i<online_contacts.length; i++) {
  151. $el = $(online_contacts[i]);
  152. jid = _.trim($el.text().trim()).replace(/ /g,'.').toLowerCase() + '@localhost';
  153. $el[0].click();
  154. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  155. chatboxview = _converse.chatboxviews.get(jid);
  156. spyOn(chatboxview, 'minimize').and.callThrough();
  157. chatboxview.model.set({'minimized': true});
  158. expect(trimmed_chatboxes.addChat).toHaveBeenCalled();
  159. expect(chatboxview.minimize).toHaveBeenCalled();
  160. }
  161. return test_utils.waitUntil(function () {
  162. return _converse.chatboxviews.keys().length > 1;
  163. }, 500);
  164. }).then(function () {
  165. var key = _converse.chatboxviews.keys()[1];
  166. trimmedview = trimmed_chatboxes.get(key);
  167. chatbox = trimmedview.model;
  168. spyOn(chatbox, 'maximize').and.callThrough();
  169. spyOn(trimmedview, 'restore').and.callThrough();
  170. trimmedview.delegateEvents();
  171. trimmedview.el.querySelector("a.restore-chat").click();
  172. expect(trimmedview.restore).toHaveBeenCalled();
  173. expect(chatbox.maximize).toHaveBeenCalled();
  174. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  175. done();
  176. });
  177. done();
  178. }));
  179. it("can be opened in minimized mode initially",
  180. mock.initConverseWithPromises(
  181. null, ['rosterGroupsFetched'], {},
  182. function (done, _converse) {
  183. test_utils.createContacts(_converse, 'current');
  184. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  185. var chat = _converse.api.chats.create(sender_jid, {
  186. minimized: true
  187. });
  188. var chatBoxView = _converse.chatboxviews.get(sender_jid);
  189. expect(u.isVisible(chatBoxView.el)).toBeFalsy();
  190. var minimized_chat = _converse.minimized_chats.get(sender_jid);
  191. expect(minimized_chat).toBeTruthy();
  192. expect($(minimized_chat.el).is(':visible')).toBeTruthy();
  193. done();
  194. }));
  195. it("is focused if its already open and you click on its corresponding roster item",
  196. mock.initConverseWithPromises(
  197. null, ['rosterGroupsFetched'], {},
  198. function (done, _converse) {
  199. test_utils.createContacts(_converse, 'current');
  200. _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
  201. test_utils.openControlBox();
  202. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  203. var $el, jid, chatbox;
  204. // openControlBox was called earlier, so the controlbox is
  205. // visible, but no other chat boxes have been created.
  206. expect(_converse.chatboxes.length).toEqual(1);
  207. chatbox = test_utils.openChatBoxFor(_converse, contact_jid);
  208. $el = $(_converse.rosterview.el).find('a.open-chat:contains("'+chatbox.getDisplayName()+'")');
  209. jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost';
  210. spyOn(_converse, 'emit');
  211. $el[0].click();
  212. test_utils.waitUntil(function () {
  213. return _converse.emit.calls.count();
  214. }, 300).then(function () {
  215. expect(_converse.chatboxes.length).toEqual(2);
  216. expect(_converse.emit).toHaveBeenCalledWith('chatBoxFocused', jasmine.any(Object));
  217. done();
  218. });
  219. }));
  220. it("can be saved to, and retrieved from, browserStorage",
  221. mock.initConverseWithPromises(
  222. null, ['rosterGroupsFetched'], {},
  223. function (done, _converse) {
  224. test_utils.createContacts(_converse, 'current');
  225. test_utils.openControlBox();
  226. spyOn(_converse, 'emit');
  227. spyOn(_converse.chatboxviews, 'trimChats');
  228. test_utils.openControlBox();
  229. test_utils.openChatBoxes(_converse, 6);
  230. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  231. // We instantiate a new ChatBoxes collection, which by default
  232. // will be empty.
  233. var newchatboxes = new _converse.ChatBoxes();
  234. expect(newchatboxes.length).toEqual(0);
  235. // The chatboxes will then be fetched from browserStorage inside the
  236. // onConnected method
  237. newchatboxes.onConnected();
  238. expect(newchatboxes.length).toEqual(7);
  239. // Check that the chatboxes items retrieved from browserStorage
  240. // have the same attributes values as the original ones.
  241. var attrs = ['id', 'box_id', 'visible'];
  242. var new_attrs, old_attrs;
  243. for (var i=0; i<attrs.length; i++) {
  244. new_attrs = _.map(_.map(newchatboxes.models, 'attributes'), attrs[i]);
  245. old_attrs = _.map(_.map(_converse.chatboxes.models, 'attributes'), attrs[i]);
  246. expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
  247. }
  248. _converse.rosterview.render();
  249. done();
  250. }));
  251. it("can be closed by clicking a DOM element with class 'close-chatbox-button'",
  252. mock.initConverseWithPromises(
  253. null, ['rosterGroupsFetched'], {},
  254. function (done, _converse) {
  255. test_utils.createContacts(_converse, 'current');
  256. test_utils.openControlBox();
  257. test_utils.waitUntil(function () {
  258. return $(_converse.rosterview.el).find('.roster-group').length;
  259. }, 300).then(function () {
  260. var chatbox = test_utils.openChatBoxes(_converse, 1)[0],
  261. controlview = _converse.chatboxviews.get('controlbox'), // The controlbox is currently open
  262. chatview = _converse.chatboxviews.get(chatbox.get('jid'));
  263. spyOn(chatview, 'close').and.callThrough();
  264. spyOn(controlview, 'close').and.callThrough();
  265. spyOn(_converse, 'emit');
  266. // We need to rebind all events otherwise our spy won't be called
  267. controlview.delegateEvents();
  268. chatview.delegateEvents();
  269. controlview.el.querySelector('.close-chatbox-button').click();
  270. expect(controlview.close).toHaveBeenCalled();
  271. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  272. expect(_converse.emit.calls.count(), 1);
  273. chatview.el.querySelector('.close-chatbox-button').click();
  274. expect(chatview.close).toHaveBeenCalled();
  275. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  276. expect(_converse.emit.calls.count(), 2);
  277. done();
  278. });
  279. }));
  280. it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'",
  281. mock.initConverseWithPromises(
  282. null, ['rosterGroupsFetched'], {},
  283. function (done, _converse) {
  284. var chatview;
  285. test_utils.createContacts(_converse, 'current');
  286. test_utils.openControlBox();
  287. test_utils.waitUntil(function () {
  288. return $(_converse.rosterview.el).find('.roster-group').length;
  289. }, 300)
  290. .then(function () {
  291. var chatbox = test_utils.openChatBoxes(_converse, 1)[0],
  292. trimmed_chatboxes = _converse.minimized_chats,
  293. trimmedview;
  294. chatview = _converse.chatboxviews.get(chatbox.get('jid'));
  295. spyOn(chatview, 'minimize').and.callThrough();
  296. spyOn(_converse, 'emit');
  297. // We need to rebind all events otherwise our spy won't be called
  298. chatview.delegateEvents();
  299. chatview.el.querySelector('.toggle-chatbox-button').click();
  300. expect(chatview.minimize).toHaveBeenCalled();
  301. expect(_converse.emit).toHaveBeenCalledWith('chatBoxMinimized', jasmine.any(Object));
  302. expect(_converse.emit.calls.count(), 2);
  303. expect(u.isVisible(chatview.el)).toBeFalsy();
  304. expect(chatview.model.get('minimized')).toBeTruthy();
  305. chatview.el.querySelector('.toggle-chatbox-button').click();
  306. trimmedview = trimmed_chatboxes.get(chatview.model.get('id'));
  307. spyOn(trimmedview, 'restore').and.callThrough();
  308. trimmedview.delegateEvents();
  309. trimmedview.el.querySelector("a.restore-chat").click();
  310. expect(trimmedview.restore).toHaveBeenCalled();
  311. expect(_converse.emit).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
  312. return test_utils.waitUntil(function () {
  313. return $(chatview.el).find('.chat-body').is(':visible');
  314. }, 500);
  315. }).then(function () {
  316. expect($(chatview.el).find('.toggle-chatbox-button').hasClass('fa-minus')).toBeTruthy();
  317. expect($(chatview.el).find('.toggle-chatbox-button').hasClass('fa-plus')).toBeFalsy();
  318. expect(chatview.model.get('minimized')).toBeFalsy();
  319. done();
  320. });
  321. }));
  322. it("will be removed from browserStorage when closed",
  323. mock.initConverseWithPromises(
  324. null, ['rosterGroupsFetched'], {},
  325. function (done, _converse) {
  326. test_utils.createContacts(_converse, 'current');
  327. test_utils.openControlBox();
  328. test_utils.waitUntil(function () {
  329. return $(_converse.rosterview.el).find('.roster-group').length;
  330. }, 300).then(function () {
  331. spyOn(_converse, 'emit');
  332. spyOn(_converse.chatboxviews, 'trimChats');
  333. _converse.chatboxes.browserStorage._clear();
  334. test_utils.closeControlBox();
  335. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  336. expect(_converse.chatboxes.length).toEqual(1);
  337. expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
  338. test_utils.openChatBoxes(_converse, 6);
  339. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  340. expect(_converse.chatboxes.length).toEqual(7);
  341. expect(_converse.emit).toHaveBeenCalledWith('chatBoxOpened', jasmine.any(Object));
  342. test_utils.closeAllChatBoxes(_converse);
  343. expect(_converse.chatboxes.length).toEqual(1);
  344. expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
  345. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  346. var newchatboxes = new _converse.ChatBoxes();
  347. expect(newchatboxes.length).toEqual(0);
  348. expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
  349. // onConnected will fetch chatboxes in browserStorage, but
  350. // because there aren't any open chatboxes, there won't be any
  351. // in browserStorage either. XXX except for the controlbox
  352. newchatboxes.onConnected();
  353. expect(newchatboxes.length).toEqual(1);
  354. expect(newchatboxes.models[0].id).toBe("controlbox");
  355. done();
  356. });
  357. }));
  358. describe("A chat toolbar", function () {
  359. it("can be found on each chat box",
  360. mock.initConverseWithPromises(
  361. null, ['rosterGroupsFetched'], {},
  362. function (done, _converse) {
  363. test_utils.createContacts(_converse, 'current');
  364. test_utils.openControlBox();
  365. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  366. test_utils.openChatBoxFor(_converse, contact_jid);
  367. var chatbox = _converse.chatboxes.get(contact_jid);
  368. var view = _converse.chatboxviews.get(contact_jid);
  369. expect(chatbox).toBeDefined();
  370. expect(view).toBeDefined();
  371. var $toolbar = $(view.el).find('ul.chat-toolbar');
  372. expect($toolbar.length).toBe(1);
  373. expect($toolbar.children('li').length).toBe(2);
  374. done();
  375. }));
  376. it("contains a button for inserting emojis",
  377. mock.initConverseWithPromises(
  378. null, ['rosterGroupsFetched'], {},
  379. function (done, _converse) {
  380. test_utils.createContacts(_converse, 'current');
  381. test_utils.openControlBox();
  382. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  383. test_utils.openChatBoxFor(_converse, contact_jid);
  384. var view = _converse.chatboxviews.get(contact_jid);
  385. var toolbar = view.el.querySelector('ul.chat-toolbar');
  386. expect(toolbar.querySelectorAll('li.toggle-smiley').length).toBe(1);
  387. // Register spies
  388. spyOn(view, 'toggleEmojiMenu').and.callThrough();
  389. spyOn(view, 'insertEmoji').and.callThrough();
  390. view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
  391. toolbar.querySelector('li.toggle-smiley').click();
  392. var timeout = false;
  393. test_utils.waitUntil(function () {
  394. return utils.isVisible(view.el.querySelector('.toggle-smiley .emoji-picker-container'));
  395. }, 500).then(function () {
  396. var picker = view.el.querySelector('.toggle-smiley .emoji-picker-container');
  397. var items = picker.querySelectorAll('.emoji-picker li');
  398. items[0].click()
  399. expect(view.insertEmoji).toHaveBeenCalled();
  400. setTimeout(function () { timeout = true; }, 100);
  401. return test_utils.waitUntil(function () {
  402. return timeout;
  403. }, 500);
  404. }).then(function () {
  405. timeout = false;
  406. toolbar.querySelector('li.toggle-smiley').click(); // Close the panel again
  407. return test_utils.waitUntil(function () {
  408. return !view.el.querySelector('.toggle-smiley .toolbar-menu').offsetHeight;
  409. }, 500);
  410. }).then(function () {
  411. setTimeout(function () { timeout = true; }, 100);
  412. return test_utils.waitUntil(function () {
  413. return timeout;
  414. }, 500);
  415. }).then(function () {
  416. toolbar.querySelector('li.toggle-smiley').click();
  417. expect(view.toggleEmojiMenu).toHaveBeenCalled();
  418. return test_utils.waitUntil(function () {
  419. var $picker = $(view.el).find('.toggle-smiley .emoji-picker-container');
  420. return u.isVisible($picker[0]);
  421. }, 500);
  422. }).then(function () {
  423. var nodes = view.el.querySelectorAll('.toggle-smiley ul li');
  424. nodes[nodes.length-1].click();
  425. expect(view.el.querySelector('textarea.chat-textarea').value).toBe(':grinning: ');
  426. expect(view.insertEmoji).toHaveBeenCalled();
  427. done();
  428. }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
  429. }));
  430. it("contains a button for starting an encrypted chat session",
  431. mock.initConverseWithPromises(
  432. null, ['rosterGroupsFetched'], {},
  433. function (done, _converse) {
  434. var timeout = true, $toolbar, view;
  435. test_utils.createContacts(_converse, 'current');
  436. test_utils.openControlBox();
  437. test_utils.waitUntil(function () {
  438. return $(_converse.rosterview.el).find('.roster-group').length;
  439. }, 300).then(function () {
  440. // TODO: More tests can be added here...
  441. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  442. test_utils.openChatBoxFor(_converse, contact_jid);
  443. view = _converse.chatboxviews.get(contact_jid);
  444. $toolbar = $(view.el).find('ul.chat-toolbar');
  445. expect($toolbar.find('.toggle-otr').length).toBe(1);
  446. // Register spies
  447. spyOn(view, 'toggleOTRMenu').and.callThrough();
  448. view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
  449. timeout = false;
  450. $toolbar[0].querySelector('.toggle-otr').click();
  451. return test_utils.waitUntil(function () {
  452. return view.el.querySelector('.otr-menu').offsetHeight;
  453. }, 300)
  454. }).then(function () {
  455. expect(view.toggleOTRMenu).toHaveBeenCalled();
  456. done();
  457. });
  458. }));
  459. it("can contain a button for starting a call",
  460. mock.initConverseWithPromises(
  461. null, ['rosterGroupsFetched'], {},
  462. function (done, _converse) {
  463. test_utils.createContacts(_converse, 'current');
  464. test_utils.openControlBox();
  465. var view;
  466. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  467. spyOn(_converse, 'emit');
  468. // First check that the button doesn't show if it's not enabled
  469. // via "visible_toolbar_buttons"
  470. _converse.visible_toolbar_buttons.call = false;
  471. test_utils.openChatBoxFor(_converse, contact_jid);
  472. view = _converse.chatboxviews.get(contact_jid);
  473. var toolbar = view.el.querySelector('ul.chat-toolbar');
  474. var call_button = toolbar.querySelector('.toggle-call');
  475. expect(_.isNull(call_button)).toBeTruthy();
  476. view.close();
  477. // Now check that it's shown if enabled and that it emits
  478. // callButtonClicked
  479. _converse.visible_toolbar_buttons.call = true; // enable the button
  480. test_utils.openChatBoxFor(_converse, contact_jid);
  481. view = _converse.chatboxviews.get(contact_jid);
  482. toolbar = view.el.querySelector('ul.chat-toolbar');
  483. call_button = toolbar.querySelector('.toggle-call');
  484. call_button.click();
  485. expect(_converse.emit).toHaveBeenCalledWith('callButtonClicked', jasmine.any(Object));
  486. done();
  487. }));
  488. });
  489. describe("A Chat Status Notification", function () {
  490. it("does not open a new chatbox",
  491. mock.initConverseWithPromises(
  492. null, ['rosterGroupsFetched'], {},
  493. function (done, _converse) {
  494. test_utils.createContacts(_converse, 'current');
  495. test_utils.openControlBox();
  496. spyOn(_converse, 'emit');
  497. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  498. // <composing> state
  499. var msg = $msg({
  500. 'from': sender_jid,
  501. 'to': _converse.connection.jid,
  502. 'type': 'chat',
  503. 'id': (new Date()).getTime()
  504. }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  505. _converse.chatboxes.onMessage(msg);
  506. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  507. done();
  508. }));
  509. describe("An active notification", function () {
  510. it("is sent when the user opens a chat box",
  511. mock.initConverseWithPromises(
  512. null, ['rosterGroupsFetched'], {},
  513. function (done, _converse) {
  514. test_utils.createContacts(_converse, 'current');
  515. test_utils.openControlBox();
  516. test_utils.waitUntil(function () {
  517. return $(_converse.rosterview.el).find('.roster-group').length;
  518. }, 300).then(function () {
  519. spyOn(_converse.connection, 'send');
  520. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  521. test_utils.openChatBoxFor(_converse, contact_jid);
  522. var view = _converse.chatboxviews.get(contact_jid);
  523. expect(view.model.get('chat_state')).toBe('active');
  524. expect(_converse.connection.send).toHaveBeenCalled();
  525. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  526. expect($stanza.attr('to')).toBe(contact_jid);
  527. expect($stanza.children().length).toBe(3);
  528. expect($stanza.children().get(0).tagName).toBe('active');
  529. expect($stanza.children().get(1).tagName).toBe('no-store');
  530. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  531. done();
  532. });
  533. }));
  534. it("is sent when the user maximizes a minimized a chat box", mock.initConverseWithPromises(
  535. null, ['rosterGroupsFetched'], {},
  536. function (done, _converse) {
  537. test_utils.createContacts(_converse, 'current');
  538. test_utils.openControlBox();
  539. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  540. test_utils.waitUntil(function () {
  541. return $(_converse.rosterview.el).find('.roster-group').length;
  542. }, 500).then(function () {
  543. test_utils.openChatBoxFor(_converse, contact_jid);
  544. var view = _converse.chatboxviews.get(contact_jid);
  545. view.model.minimize();
  546. expect(view.model.get('chat_state')).toBe('inactive');
  547. spyOn(_converse.connection, 'send');
  548. view.model.maximize();
  549. return test_utils.waitUntil(function () {
  550. return view.model.get('chat_state') === 'active';
  551. }, 700);
  552. }).then(function () {
  553. expect(_converse.connection.send).toHaveBeenCalled();
  554. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  555. return call.args[0] instanceof Strophe.Builder;
  556. });
  557. expect(calls.length).toBe(1);
  558. var $stanza = $(calls[0].args[0].tree());
  559. expect($stanza.attr('to')).toBe(contact_jid);
  560. expect($stanza.children().length).toBe(3);
  561. expect($stanza.children().get(0).tagName).toBe('active');
  562. expect($stanza.children().get(1).tagName).toBe('no-store');
  563. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  564. done();
  565. });
  566. }));
  567. });
  568. describe("A composing notification", function () {
  569. it("is sent as soon as the user starts typing a message which is not a command",
  570. mock.initConverseWithPromises(
  571. null, ['rosterGroupsFetched'], {},
  572. function (done, _converse) {
  573. test_utils.createContacts(_converse, 'current');
  574. test_utils.openControlBox();
  575. test_utils.waitUntil(function () {
  576. return $(_converse.rosterview.el).find('.roster-group').length;
  577. }, 300).then(function () {
  578. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  579. test_utils.openChatBoxFor(_converse, contact_jid);
  580. var view = _converse.chatboxviews.get(contact_jid);
  581. expect(view.model.get('chat_state')).toBe('active');
  582. spyOn(_converse.connection, 'send');
  583. spyOn(_converse, 'emit');
  584. view.keyPressed({
  585. target: $(view.el).find('textarea.chat-textarea'),
  586. keyCode: 1
  587. });
  588. expect(view.model.get('chat_state')).toBe('composing');
  589. expect(_converse.connection.send).toHaveBeenCalled();
  590. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  591. expect($stanza.attr('to')).toBe(contact_jid);
  592. expect($stanza.children().get(0).tagName).toBe('composing');
  593. expect($stanza.children().get(1).tagName).toBe('no-store');
  594. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  595. // The notification is not sent again
  596. view.keyPressed({
  597. target: $(view.el).find('textarea.chat-textarea'),
  598. keyCode: 1
  599. });
  600. expect(view.model.get('chat_state')).toBe('composing');
  601. expect(_converse.emit.calls.count(), 1);
  602. done();
  603. });
  604. }));
  605. it("will be shown if received",
  606. mock.initConverseWithPromises(
  607. null, ['rosterGroupsFetched'], {},
  608. function (done, _converse) {
  609. test_utils.createContacts(_converse, 'current');
  610. test_utils.openControlBox();
  611. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  612. spyOn(_converse, 'emit');
  613. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  614. test_utils.openChatBoxFor(_converse, sender_jid);
  615. // <composing> state
  616. var msg = $msg({
  617. from: sender_jid,
  618. to: _converse.connection.jid,
  619. type: 'chat',
  620. id: (new Date()).getTime()
  621. }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  622. _converse.chatboxes.onMessage(msg);
  623. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  624. var view = _converse.chatboxviews.get(sender_jid);
  625. expect(view).toBeDefined();
  626. test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
  627. .then(function () {
  628. var view = _converse.chatboxviews.get(sender_jid);
  629. // Check that the notification appears inside the chatbox in the DOM
  630. var events = view.el.querySelectorAll('.chat-state-notification');
  631. expect(events.length).toBe(1);
  632. expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
  633. // Check that it doesn't appear twice
  634. msg = $msg({
  635. from: sender_jid,
  636. to: _converse.connection.jid,
  637. type: 'chat',
  638. id: (new Date()).getTime()
  639. }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  640. _converse.chatboxes.onMessage(msg);
  641. events = view.el.querySelectorAll('.chat-state-notification');
  642. expect(events.length).toBe(1);
  643. expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
  644. done();
  645. })
  646. }));
  647. it("can be a composing carbon message that this user sent from a different client",
  648. mock.initConverseWithPromises(
  649. null, ['rosterGroupsFetched'], {},
  650. function (done, _converse) {
  651. var contact, sent_stanza, IQ_id, stanza;
  652. test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
  653. .then(function () {
  654. return test_utils.waitUntil(function () {
  655. return _converse.xmppstatus.vcard.get('fullname');
  656. }, 300);
  657. }).then(function () {
  658. test_utils.createContacts(_converse, 'current');
  659. // Send a message from a different resource
  660. spyOn(_converse, 'log');
  661. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  662. test_utils.openChatBoxFor(_converse, recipient_jid);
  663. var msg = $msg({
  664. 'from': _converse.bare_jid,
  665. 'id': (new Date()).getTime(),
  666. 'to': _converse.connection.jid,
  667. 'type': 'chat',
  668. 'xmlns': 'jabber:client'
  669. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  670. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  671. .c('message', {
  672. 'xmlns': 'jabber:client',
  673. 'from': _converse.bare_jid+'/another-resource',
  674. 'to': recipient_jid,
  675. 'type': 'chat'
  676. }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  677. _converse.chatboxes.onMessage(msg);
  678. // Check that the chatbox and its view now exist
  679. var chatbox = _converse.chatboxes.get(recipient_jid);
  680. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  681. // Check that the message was received and check the message parameters
  682. expect(chatbox.messages.length).toEqual(1);
  683. var msg_obj = chatbox.messages.models[0];
  684. expect(msg_obj.get('sender')).toEqual('me');
  685. expect(msg_obj.get('delayed')).toEqual(false);
  686. var $chat_content = $(chatboxview.el).find('.chat-content');
  687. var status_text = $chat_content.find('.chat-info.chat-state-notification').text();
  688. expect(status_text).toBe('Typing from another device');
  689. done();
  690. });
  691. }));
  692. });
  693. describe("A paused notification", function () {
  694. it("is sent if the user has stopped typing since 30 seconds",
  695. mock.initConverseWithPromises(
  696. null, ['rosterGroupsFetched'], {},
  697. function (done, _converse) {
  698. var view, contact_jid;
  699. test_utils.createContacts(_converse, 'current');
  700. test_utils.openControlBox();
  701. test_utils.waitUntil(function () {
  702. return $(_converse.rosterview.el).find('.roster-group li').length;
  703. }, 700).then(function () {
  704. _converse.TIMEOUTS.PAUSED = 200; // Make the timeout shorter so that we can test
  705. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  706. test_utils.openChatBoxFor(_converse, contact_jid);
  707. view = _converse.chatboxviews.get(contact_jid);
  708. spyOn(_converse.connection, 'send');
  709. spyOn(view, 'setChatState').and.callThrough();
  710. expect(view.model.get('chat_state')).toBe('active');
  711. view.keyPressed({
  712. target: $(view.el).find('textarea.chat-textarea'),
  713. keyCode: 1
  714. });
  715. expect(view.model.get('chat_state')).toBe('composing');
  716. expect(_converse.connection.send).toHaveBeenCalled();
  717. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  718. expect($stanza.children().get(0).tagName).toBe('composing');
  719. return test_utils.waitUntil(function () {
  720. return view.model.get('chat_state') === 'paused';
  721. }, 500);
  722. }).then(function () {
  723. expect(_converse.connection.send).toHaveBeenCalled();
  724. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  725. return call.args[0] instanceof Strophe.Builder;
  726. });
  727. expect(calls.length).toBe(2);
  728. var $stanza = $(calls[1].args[0].tree());
  729. expect($stanza.attr('to')).toBe(contact_jid);
  730. expect($stanza.children().length).toBe(3);
  731. expect($stanza.children().get(0).tagName).toBe('paused');
  732. expect($stanza.children().get(1).tagName).toBe('no-store');
  733. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  734. // Test #359. A paused notification should not be sent
  735. // out if the user simply types longer than the
  736. // timeout.
  737. view.keyPressed({
  738. target: $(view.el).find('textarea.chat-textarea'),
  739. keyCode: 1
  740. });
  741. expect(view.setChatState).toHaveBeenCalled();
  742. expect(view.model.get('chat_state')).toBe('composing');
  743. view.keyPressed({
  744. target: $(view.el).find('textarea.chat-textarea'),
  745. keyCode: 1
  746. });
  747. expect(view.model.get('chat_state')).toBe('composing');
  748. done();
  749. });
  750. }));
  751. it("will be shown if received",
  752. mock.initConverseWithPromises(
  753. null, ['rosterGroupsFetched'], {},
  754. function (done, _converse) {
  755. test_utils.createContacts(_converse, 'current');
  756. test_utils.openControlBox();
  757. test_utils.waitUntil(function () {
  758. return $(_converse.rosterview.el).find('.roster-group').length;
  759. }, 300)
  760. .then(function () {
  761. // TODO: only show paused state if the previous state was composing
  762. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  763. spyOn(_converse, 'emit');
  764. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  765. // <paused> state
  766. var msg = $msg({
  767. from: sender_jid,
  768. to: _converse.connection.jid,
  769. type: 'chat',
  770. id: (new Date()).getTime()
  771. }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  772. _converse.chatboxes.onMessage(msg);
  773. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  774. var view = _converse.chatboxviews.get(sender_jid);
  775. test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
  776. .then(function () {
  777. var view = _converse.chatboxviews.get(sender_jid);
  778. var event = view.el.querySelector('.chat-info.chat-state-notification');
  779. expect(event.textContent).toEqual(mock.cur_names[1] + ' has stopped typing');
  780. done();
  781. });
  782. });
  783. }));
  784. it("can be a paused carbon message that this user sent from a different client",
  785. mock.initConverseWithPromises(
  786. null, ['rosterGroupsFetched'], {},
  787. function (done, _converse) {
  788. var contact, sent_stanza, IQ_id, stanza;
  789. test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
  790. .then(function () {
  791. return test_utils.waitUntil(function () {
  792. return _converse.xmppstatus.vcard.get('fullname');
  793. }, 300);
  794. }).then(function () {
  795. test_utils.createContacts(_converse, 'current');
  796. // Send a message from a different resource
  797. spyOn(_converse, 'log');
  798. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  799. test_utils.openChatBoxFor(_converse, recipient_jid);
  800. var msg = $msg({
  801. 'from': _converse.bare_jid,
  802. 'id': (new Date()).getTime(),
  803. 'to': _converse.connection.jid,
  804. 'type': 'chat',
  805. 'xmlns': 'jabber:client'
  806. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  807. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  808. .c('message', {
  809. 'xmlns': 'jabber:client',
  810. 'from': _converse.bare_jid+'/another-resource',
  811. 'to': recipient_jid,
  812. 'type': 'chat'
  813. }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  814. _converse.chatboxes.onMessage(msg);
  815. // Check that the chatbox and its view now exist
  816. var chatbox = _converse.chatboxes.get(recipient_jid);
  817. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  818. // Check that the message was received and check the message parameters
  819. expect(chatbox.messages.length).toEqual(1);
  820. var msg_obj = chatbox.messages.models[0];
  821. expect(msg_obj.get('sender')).toEqual('me');
  822. expect(msg_obj.get('delayed')).toEqual(false);
  823. var $chat_content = $(chatboxview.el).find('.chat-content');
  824. var status_text = $chat_content.find('.chat-info.chat-state-notification').text();
  825. expect(status_text).toBe('Stopped typing on the other device');
  826. done();
  827. });
  828. }));
  829. });
  830. describe("An inactive notifciation", function () {
  831. it("is sent if the user has stopped typing since 2 minutes",
  832. mock.initConverseWithPromises(
  833. null, ['rosterGroupsFetched'], {},
  834. function (done, _converse) {
  835. var view, contact_jid;
  836. test_utils.createContacts(_converse, 'current');
  837. test_utils.openControlBox();
  838. test_utils.waitUntil(function () {
  839. return $(_converse.rosterview.el).find('.roster-group').length;
  840. }, 500).then(function () {
  841. // Make the timeouts shorter so that we can test
  842. _converse.TIMEOUTS.PAUSED = 200;
  843. _converse.TIMEOUTS.INACTIVE = 200;
  844. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  845. test_utils.openChatBoxFor(_converse, contact_jid);
  846. view = _converse.chatboxviews.get(contact_jid);
  847. return test_utils.waitUntil(function () {
  848. return view.model.get('chat_state') === 'active';
  849. }, 500);
  850. }).then(function () {
  851. console.log('chat_state set to active');
  852. view = _converse.chatboxviews.get(contact_jid);
  853. expect(view.model.get('chat_state')).toBe('active');
  854. view.keyPressed({
  855. target: $(view.el).find('textarea.chat-textarea'),
  856. keyCode: 1
  857. });
  858. return test_utils.waitUntil(function () {
  859. return view.model.get('chat_state') === 'composing';
  860. }, 500);
  861. }).then(function () {
  862. console.log('chat_state set to composing');
  863. view = _converse.chatboxviews.get(contact_jid);
  864. expect(view.model.get('chat_state')).toBe('composing');
  865. spyOn(_converse.connection, 'send');
  866. return test_utils.waitUntil(function () {
  867. return view.model.get('chat_state') === 'paused';
  868. }, 500);
  869. }).then(function () {
  870. console.log('chat_state set to paused');
  871. return test_utils.waitUntil(function () {
  872. return view.model.get('chat_state') === 'inactive';
  873. }, 500);
  874. }).then(function () {
  875. console.log('chat_state set to inactive');
  876. expect(_converse.connection.send).toHaveBeenCalled();
  877. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  878. return call.args[0] instanceof Strophe.Builder;
  879. });
  880. expect(calls.length).toBe(2);
  881. var $stanza = $(calls[0].args[0].tree());
  882. expect($stanza.attr('to')).toBe(contact_jid);
  883. expect($stanza.children().length).toBe(3);
  884. expect($stanza.children().get(0).tagName).toBe('paused');
  885. expect($stanza.children().get(1).tagName).toBe('no-store');
  886. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  887. $stanza = $(_converse.connection.send.calls.mostRecent().args[0].tree());
  888. expect($stanza.attr('to')).toBe(contact_jid);
  889. expect($stanza.children().length).toBe(3);
  890. expect($stanza.children().get(0).tagName).toBe('inactive');
  891. expect($stanza.children().get(1).tagName).toBe('no-store');
  892. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  893. }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
  894. .then(done);
  895. }));
  896. it("is sent when the user a minimizes a chat box",
  897. mock.initConverseWithPromises(
  898. null, ['rosterGroupsFetched'], {},
  899. function (done, _converse) {
  900. test_utils.createContacts(_converse, 'current');
  901. test_utils.openControlBox();
  902. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  903. test_utils.openChatBoxFor(_converse, contact_jid);
  904. var view = _converse.chatboxviews.get(contact_jid);
  905. spyOn(_converse.connection, 'send');
  906. view.minimize();
  907. expect(view.model.get('chat_state')).toBe('inactive');
  908. expect(_converse.connection.send).toHaveBeenCalled();
  909. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  910. expect($stanza.attr('to')).toBe(contact_jid);
  911. expect($stanza.children().get(0).tagName).toBe('inactive');
  912. done();
  913. }));
  914. it("is sent if the user closes a chat box",
  915. mock.initConverseWithPromises(
  916. null, ['rosterGroupsFetched'], {},
  917. function (done, _converse) {
  918. test_utils.createContacts(_converse, 'current');
  919. test_utils.openControlBox();
  920. test_utils.waitUntil(function () {
  921. return $(_converse.rosterview.el).find('.roster-group').length;
  922. }, 300).then(function () {
  923. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  924. test_utils.openChatBoxFor(_converse, contact_jid);
  925. var view = _converse.chatboxviews.get(contact_jid);
  926. expect(view.model.get('chat_state')).toBe('active');
  927. spyOn(_converse.connection, 'send');
  928. view.close();
  929. expect(view.model.get('chat_state')).toBe('inactive');
  930. expect(_converse.connection.send).toHaveBeenCalled();
  931. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  932. expect($stanza.attr('to')).toBe(contact_jid);
  933. expect($stanza.children().length).toBe(3);
  934. expect($stanza.children().get(0).tagName).toBe('inactive');
  935. expect($stanza.children().get(1).tagName).toBe('no-store');
  936. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  937. done();
  938. });
  939. }));
  940. it("will clear any other chat status notifications",
  941. mock.initConverseWithPromises(
  942. null, ['rosterGroupsFetched'], {},
  943. function (done, _converse) {
  944. test_utils.createContacts(_converse, 'current');
  945. test_utils.openControlBox();
  946. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  947. spyOn(_converse, 'emit');
  948. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  949. test_utils.openChatBoxFor(_converse, sender_jid);
  950. var view = _converse.chatboxviews.get(sender_jid);
  951. expect(view.el.querySelectorAll('.chat-event').length).toBe(0);
  952. // Insert <composing> message, to also check that
  953. // text messages are inserted correctly with
  954. // temporary chat events in the chat contents.
  955. var msg = $msg({
  956. 'to': _converse.bare_jid,
  957. 'xmlns': 'jabber:client',
  958. 'from': sender_jid,
  959. 'type': 'chat'})
  960. .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
  961. .tree();
  962. _converse.chatboxes.onMessage(msg);
  963. expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
  964. msg = $msg({
  965. from: sender_jid,
  966. to: _converse.connection.jid,
  967. type: 'chat',
  968. id: (new Date()).getTime()
  969. }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  970. _converse.chatboxes.onMessage(msg);
  971. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  972. expect($(view.el).find('.chat-state-notification').length).toBe(0);
  973. done();
  974. }));
  975. });
  976. describe("A gone notifciation", function () {
  977. it("will be shown if received",
  978. mock.initConverseWithPromises(
  979. null, ['rosterGroupsFetched'], {},
  980. function (done, _converse) {
  981. test_utils.createContacts(_converse, 'current');
  982. test_utils.openControlBox();
  983. spyOn(_converse, 'emit');
  984. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  985. // <paused> state
  986. var msg = $msg({
  987. from: sender_jid,
  988. to: _converse.connection.jid,
  989. type: 'chat',
  990. id: (new Date()).getTime()
  991. }).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  992. _converse.chatboxes.onMessage(msg);
  993. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  994. var view = _converse.chatboxviews.get(sender_jid);
  995. test_utils.waitUntil(() => view.model.vcard.get('fullname') === mock.cur_names[1])
  996. .then(function () {
  997. var view = _converse.chatboxviews.get(sender_jid);
  998. var event = view.el.querySelector('.chat-state-notification');
  999. expect(event.textContent).toEqual(mock.cur_names[1] + ' has gone away');
  1000. done();
  1001. });
  1002. }));
  1003. });
  1004. });
  1005. });
  1006. describe("Special Messages", function () {
  1007. it("'/clear' can be used to clear messages in a conversation",
  1008. mock.initConverseWithPromises(
  1009. null, ['rosterGroupsFetched'], {},
  1010. function (done, _converse) {
  1011. test_utils.createContacts(_converse, 'current');
  1012. test_utils.openControlBox();
  1013. spyOn(_converse, 'emit');
  1014. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1015. test_utils.openChatBoxFor(_converse, contact_jid);
  1016. var view = _converse.chatboxviews.get(contact_jid);
  1017. var message = 'This message is another sent from this chatbox';
  1018. // Lets make sure there is at least one message already
  1019. // (e.g for when this test is run on its own).
  1020. test_utils.sendMessage(view, message);
  1021. expect(view.model.messages.length > 0).toBeTruthy();
  1022. expect(view.model.messages.browserStorage.records.length > 0).toBeTruthy();
  1023. expect(_converse.emit).toHaveBeenCalledWith('messageSend', message);
  1024. message = '/clear';
  1025. spyOn(view, 'onMessageSubmitted').and.callThrough();
  1026. spyOn(view, 'clearMessages').and.callThrough();
  1027. spyOn(window, 'confirm').and.callFake(function () {
  1028. return true;
  1029. });
  1030. test_utils.sendMessage(view, message);
  1031. expect(view.onMessageSubmitted).toHaveBeenCalled();
  1032. expect(view.clearMessages).toHaveBeenCalled();
  1033. expect(window.confirm).toHaveBeenCalled();
  1034. expect(view.model.messages.length, 0); // The messages must be removed from the chatbox
  1035. expect(view.model.messages.browserStorage.records.length, 0); // And also from browserStorage
  1036. expect(_converse.emit.calls.count(), 1);
  1037. expect(_converse.emit.calls.mostRecent().args, ['messageSend', message]);
  1038. done();
  1039. }));
  1040. });
  1041. describe("A Message Counter", function () {
  1042. it("is incremented when the message is received and the window is not focused",
  1043. mock.initConverseWithPromises(
  1044. null, ['rosterGroupsFetched'], {},
  1045. function (done, _converse) {
  1046. test_utils.createContacts(_converse, 'current');
  1047. test_utils.openControlBox();
  1048. spyOn(_converse, 'emit');
  1049. expect(_converse.msg_counter).toBe(0);
  1050. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1051. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1052. var previous_state = _converse.windowState;
  1053. var message = 'This message will increment the message counter';
  1054. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1055. msg = $msg({
  1056. from: sender_jid,
  1057. to: _converse.connection.jid,
  1058. type: 'chat',
  1059. id: (new Date()).getTime()
  1060. }).c('body').t(message).up()
  1061. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1062. _converse.windowState = 'hidden';
  1063. _converse.chatboxes.onMessage(msg);
  1064. expect(_converse.incrementMsgCounter).toHaveBeenCalled();
  1065. expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
  1066. expect(_converse.msg_counter).toBe(1);
  1067. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1068. _converse.windowSate = previous_state;
  1069. done();
  1070. }));
  1071. it("is cleared when the window is focused",
  1072. mock.initConverseWithPromises(
  1073. null, ['rosterGroupsFetched'], {},
  1074. function (done, _converse) {
  1075. test_utils.createContacts(_converse, 'current');
  1076. test_utils.openControlBox();
  1077. _converse.windowState = 'hidden';
  1078. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1079. _converse.saveWindowState(null, 'focus');
  1080. _converse.saveWindowState(null, 'blur');
  1081. expect(_converse.clearMsgCounter).toHaveBeenCalled();
  1082. done();
  1083. }));
  1084. it("is not incremented when the message is received and the window is focused",
  1085. mock.initConverseWithPromises(
  1086. null, ['rosterGroupsFetched'], {},
  1087. function (done, _converse) {
  1088. test_utils.createContacts(_converse, 'current');
  1089. test_utils.openControlBox();
  1090. expect(_converse.msg_counter).toBe(0);
  1091. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1092. _converse.saveWindowState(null, 'focus');
  1093. var message = 'This message will not increment the message counter';
  1094. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1095. msg = $msg({
  1096. from: sender_jid,
  1097. to: _converse.connection.jid,
  1098. type: 'chat',
  1099. id: (new Date()).getTime()
  1100. }).c('body').t(message).up()
  1101. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1102. _converse.chatboxes.onMessage(msg);
  1103. expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
  1104. expect(_converse.msg_counter).toBe(0);
  1105. done();
  1106. }));
  1107. it("is incremented from zero when chatbox was closed after viewing previously received messages and the window is not focused now",
  1108. mock.initConverseWithPromises(
  1109. null, ['rosterGroupsFetched'], {},
  1110. function (done, _converse) {
  1111. test_utils.createContacts(_converse, 'current');
  1112. // initial state
  1113. expect(_converse.msg_counter).toBe(0);
  1114. var message = 'This message will always increment the message counter from zero',
  1115. sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1116. msgFactory = function () {
  1117. return $msg({
  1118. from: sender_jid,
  1119. to: _converse.connection.jid,
  1120. type: 'chat',
  1121. id: (new Date()).getTime()
  1122. })
  1123. .c('body').t(message).up()
  1124. .c('active', {'xmlns': Strophe.NS.CHATSTATES})
  1125. .tree();
  1126. };
  1127. // leave converse-chat page
  1128. _converse.windowState = 'hidden';
  1129. _converse.chatboxes.onMessage(msgFactory());
  1130. expect(_converse.msg_counter).toBe(1);
  1131. // come back to converse-chat page
  1132. _converse.saveWindowState(null, 'focus');
  1133. var view = _converse.chatboxviews.get(sender_jid);
  1134. expect(u.isVisible(view.el)).toBeTruthy();
  1135. expect(_converse.msg_counter).toBe(0);
  1136. // close chatbox and leave converse-chat page again
  1137. view.close();
  1138. _converse.windowState = 'hidden';
  1139. // check that msg_counter is incremented from zero again
  1140. _converse.chatboxes.onMessage(msgFactory());
  1141. view = _converse.chatboxviews.get(sender_jid);
  1142. expect(u.isVisible(view.el)).toBeTruthy();
  1143. expect(_converse.msg_counter).toBe(1);
  1144. done();
  1145. }));
  1146. });
  1147. describe("A ChatBox's Unread Message Count", function () {
  1148. it("is incremented when the message is received and ChatBoxView is scrolled up",
  1149. mock.initConverseWithPromises(
  1150. null, ['rosterGroupsFetched'], {},
  1151. function (done, _converse) {
  1152. test_utils.createContacts(_converse, 'current');
  1153. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1154. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1155. test_utils.openChatBoxFor(_converse, sender_jid);
  1156. var chatbox = _converse.chatboxes.get(sender_jid);
  1157. chatbox.save('scrolled', true);
  1158. _converse.chatboxes.onMessage(msg);
  1159. expect(chatbox.get('num_unread')).toBe(1);
  1160. done();
  1161. }));
  1162. it("is not incremented when the message is received and ChatBoxView is scrolled down",
  1163. mock.initConverseWithPromises(
  1164. null, ['rosterGroupsFetched'], {},
  1165. function (done, _converse) {
  1166. test_utils.createContacts(_converse, 'current');
  1167. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1168. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be read');
  1169. test_utils.openChatBoxFor(_converse, sender_jid);
  1170. var chatbox = _converse.chatboxes.get(sender_jid);
  1171. _converse.chatboxes.onMessage(msg);
  1172. expect(chatbox.get('num_unread')).toBe(0);
  1173. done();
  1174. }));
  1175. it("is incremeted when message is received, chatbox is scrolled down and the window is not focused",
  1176. mock.initConverseWithPromises(
  1177. null, ['rosterGroupsFetched'], {},
  1178. function (done, _converse) {
  1179. test_utils.createContacts(_converse, 'current');
  1180. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1181. var msgFactory = function () {
  1182. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1183. };
  1184. test_utils.openChatBoxFor(_converse, sender_jid);
  1185. var chatbox = _converse.chatboxes.get(sender_jid);
  1186. _converse.windowState = 'hidden';
  1187. _converse.chatboxes.onMessage(msgFactory());
  1188. expect(chatbox.get('num_unread')).toBe(1);
  1189. done();
  1190. }));
  1191. it("is incremeted when message is received, chatbox is scrolled up and the window is not focused",
  1192. mock.initConverseWithPromises(
  1193. null, ['rosterGroupsFetched'], {},
  1194. function (done, _converse) {
  1195. test_utils.createContacts(_converse, 'current');
  1196. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1197. var msgFactory = function () {
  1198. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1199. };
  1200. test_utils.openChatBoxFor(_converse, sender_jid);
  1201. var chatbox = _converse.chatboxes.get(sender_jid);
  1202. chatbox.save('scrolled', true);
  1203. _converse.windowState = 'hidden';
  1204. _converse.chatboxes.onMessage(msgFactory());
  1205. expect(chatbox.get('num_unread')).toBe(1);
  1206. done();
  1207. }));
  1208. it("is cleared when ChatBoxView was scrolled down and the window become focused",
  1209. mock.initConverseWithPromises(
  1210. null, ['rosterGroupsFetched'], {},
  1211. function (done, _converse) {
  1212. test_utils.createContacts(_converse, 'current');
  1213. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1214. var msgFactory = function () {
  1215. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1216. };
  1217. test_utils.openChatBoxFor(_converse, sender_jid);
  1218. var chatbox = _converse.chatboxes.get(sender_jid);
  1219. _converse.windowState = 'hidden';
  1220. _converse.chatboxes.onMessage(msgFactory());
  1221. expect(chatbox.get('num_unread')).toBe(1);
  1222. _converse.saveWindowState(null, 'focus');
  1223. expect(chatbox.get('num_unread')).toBe(0);
  1224. done();
  1225. }));
  1226. it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
  1227. mock.initConverseWithPromises(
  1228. null, ['rosterGroupsFetched'], {},
  1229. function (done, _converse) {
  1230. test_utils.createContacts(_converse, 'current');
  1231. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1232. var msgFactory = function () {
  1233. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1234. };
  1235. test_utils.openChatBoxFor(_converse, sender_jid);
  1236. var chatbox = _converse.chatboxes.get(sender_jid);
  1237. chatbox.save('scrolled', true);
  1238. _converse.windowState = 'hidden';
  1239. _converse.chatboxes.onMessage(msgFactory());
  1240. expect(chatbox.get('num_unread')).toBe(1);
  1241. _converse.saveWindowState(null, 'focus');
  1242. expect(chatbox.get('num_unread')).toBe(1);
  1243. done();
  1244. }));
  1245. });
  1246. describe("A RosterView's Unread Message Count", function () {
  1247. it("is updated when message is received and chatbox is scrolled up",
  1248. mock.initConverseWithPromises(
  1249. null, ['rosterGroupsFetched'], {},
  1250. function (done, _converse) {
  1251. test_utils.createContacts(_converse, 'current');
  1252. test_utils.waitUntil(function () {
  1253. return $(_converse.rosterview.el).find('.roster-group').length;
  1254. }, 500)
  1255. .then(function () {
  1256. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1257. test_utils.openChatBoxFor(_converse, sender_jid);
  1258. var chatbox = _converse.chatboxes.get(sender_jid);
  1259. chatbox.save('scrolled', true);
  1260. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1261. _converse.chatboxes.onMessage(msg);
  1262. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1263. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1264. expect($msgIndicator.text()).toBe('1');
  1265. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  1266. _converse.chatboxes.onMessage(msg);
  1267. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1268. expect($msgIndicator.text()).toBe('2');
  1269. done();
  1270. });
  1271. }));
  1272. it("is updated when message is received and chatbox is minimized",
  1273. mock.initConverseWithPromises(
  1274. null, ['rosterGroupsFetched'], {},
  1275. function (done, _converse) {
  1276. test_utils.createContacts(_converse, 'current');
  1277. test_utils.waitUntil(function () {
  1278. return $(_converse.rosterview.el).find('.roster-group').length;
  1279. }, 500)
  1280. .then(function () {
  1281. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1282. test_utils.openChatBoxFor(_converse, sender_jid);
  1283. var chatbox = _converse.chatboxes.get(sender_jid);
  1284. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1285. chatboxview.minimize();
  1286. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1287. _converse.chatboxes.onMessage(msg);
  1288. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1289. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1290. expect($msgIndicator.text()).toBe('1');
  1291. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  1292. _converse.chatboxes.onMessage(msg);
  1293. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1294. expect($msgIndicator.text()).toBe('2');
  1295. done();
  1296. });
  1297. }));
  1298. it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
  1299. mock.initConverseWithPromises(
  1300. null, ['rosterGroupsFetched'], {},
  1301. function (done, _converse) {
  1302. test_utils.createContacts(_converse, 'current');
  1303. test_utils.waitUntil(function () {
  1304. return $(_converse.rosterview.el).find('.roster-group').length;
  1305. }, 500)
  1306. .then(function () {
  1307. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1308. test_utils.openChatBoxFor(_converse, sender_jid);
  1309. var chatbox = _converse.chatboxes.get(sender_jid);
  1310. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1311. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
  1312. var selectMsgsIndicator = function () { return $($(_converse.rosterview.el).find(msgsIndicatorSelector)); };
  1313. var msgFactory = function () {
  1314. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1315. };
  1316. chatboxview.minimize();
  1317. _converse.chatboxes.onMessage(msgFactory());
  1318. expect(selectMsgsIndicator().text()).toBe('1');
  1319. _converse.chatboxes.onMessage(msgFactory());
  1320. expect(selectMsgsIndicator().text()).toBe('2');
  1321. chatboxview.maximize();
  1322. expect(selectMsgsIndicator().length).toBe(0);
  1323. done();
  1324. });
  1325. }));
  1326. it("is cleared when unread messages are viewed which were received in scrolled-up chatbox",
  1327. mock.initConverseWithPromises(
  1328. null, ['rosterGroupsFetched'], {},
  1329. function (done, _converse) {
  1330. test_utils.createContacts(_converse, 'current');
  1331. test_utils.waitUntil(function () {
  1332. return $(_converse.rosterview.el).find('.roster-group').length;
  1333. }, 500)
  1334. .then(function () {
  1335. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1336. test_utils.openChatBoxFor(_converse, sender_jid);
  1337. var chatbox = _converse.chatboxes.get(sender_jid);
  1338. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1339. var msgFactory = function () {
  1340. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1341. };
  1342. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1343. selectMsgsIndicator = function () { return $($(_converse.rosterview.el).find(msgsIndicatorSelector)); };
  1344. chatbox.save('scrolled', true);
  1345. _converse.chatboxes.onMessage(msgFactory());
  1346. expect(selectMsgsIndicator().text()).toBe('1');
  1347. chatboxview.viewUnreadMessages();
  1348. _converse.rosterview.render();
  1349. expect(selectMsgsIndicator().length).toBe(0);
  1350. done();
  1351. });
  1352. }));
  1353. it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
  1354. mock.initConverseWithPromises(
  1355. null, ['rosterGroupsFetched'], {},
  1356. function (done, _converse) {
  1357. test_utils.createContacts(_converse, 'current');
  1358. test_utils.waitUntil(function () {
  1359. return $(_converse.rosterview.el).find('.roster-group').length;
  1360. }, 500)
  1361. .then(function () {
  1362. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1363. test_utils.openChatBoxFor(_converse, sender_jid);
  1364. var chatbox = _converse.chatboxes.get(sender_jid);
  1365. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1366. var msgFactory = function () {
  1367. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1368. };
  1369. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1370. selectMsgsIndicator = function () { return $($(_converse.rosterview.el).find(msgsIndicatorSelector)); };
  1371. chatbox.save('scrolled', true);
  1372. _converse.chatboxes.onMessage(msgFactory());
  1373. expect(selectMsgsIndicator().text()).toBe('1');
  1374. test_utils.openChatBoxFor(_converse, sender_jid);
  1375. expect(selectMsgsIndicator().text()).toBe('1');
  1376. done();
  1377. });
  1378. }));
  1379. });
  1380. describe("A Minimized ChatBoxView's Unread Message Count", function () {
  1381. it("is displayed when scrolled up chatbox is minimized after receiving unread messages",
  1382. mock.initConverseWithPromises(
  1383. null, ['rosterGroupsFetched'], {},
  1384. function (done, _converse) {
  1385. test_utils.createContacts(_converse, 'current');
  1386. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1387. test_utils.openChatBoxFor(_converse, sender_jid);
  1388. var msgFactory = function () {
  1389. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1390. };
  1391. var selectUnreadMsgCount = function () {
  1392. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  1393. return $(minimizedChatBoxView.el).find('.message-count');
  1394. };
  1395. var chatbox = _converse.chatboxes.get(sender_jid);
  1396. chatbox.save('scrolled', true);
  1397. _converse.chatboxes.onMessage(msgFactory());
  1398. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1399. chatboxview.minimize();
  1400. var $unreadMsgCount = selectUnreadMsgCount();
  1401. expect(u.isVisible($unreadMsgCount[0])).toBeTruthy();
  1402. expect($unreadMsgCount.html()).toBe('1');
  1403. done();
  1404. }));
  1405. it("is incremented when message is received and windows is not focused",
  1406. mock.initConverseWithPromises(
  1407. null, ['rosterGroupsFetched'], {},
  1408. function (done, _converse) {
  1409. test_utils.createContacts(_converse, 'current');
  1410. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1411. test_utils.openChatBoxFor(_converse, sender_jid);
  1412. var msgFactory = function () {
  1413. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1414. };
  1415. var selectUnreadMsgCount = function () {
  1416. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  1417. return $(minimizedChatBoxView.el).find('.message-count');
  1418. };
  1419. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1420. chatboxview.minimize();
  1421. _converse.chatboxes.onMessage(msgFactory());
  1422. var $unreadMsgCount = selectUnreadMsgCount();
  1423. expect(u.isVisible($unreadMsgCount[0])).toBeTruthy();
  1424. expect($unreadMsgCount.html()).toBe('1');
  1425. done();
  1426. }));
  1427. it("will render Openstreetmap-URL from geo-URI",
  1428. mock.initConverseWithPromises(
  1429. null, ['rosterGroupsFetched'], {},
  1430. function (done, _converse) {
  1431. test_utils.createContacts(_converse, 'current');
  1432. var base_url = document.URL.split(window.location.pathname)[0];
  1433. var message = "geo:37.786971,-122.399677";
  1434. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1435. test_utils.openChatBoxFor(_converse, contact_jid);
  1436. var view = _converse.chatboxviews.get(contact_jid);
  1437. spyOn(view.model, 'sendMessage').and.callThrough();
  1438. test_utils.sendMessage(view, message);
  1439. test_utils.waitUntil(function () {
  1440. return $(view.el).find('.chat-content').find('.chat-msg').length;
  1441. }, 1000).then(function () {
  1442. expect(view.model.sendMessage).toHaveBeenCalled();
  1443. var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg-text');
  1444. expect(msg.html()).toEqual(
  1445. '<a target="_blank" rel="noopener" href="https://www.openstreetmap.org/?mlat=37.786971&amp;'+
  1446. 'mlon=-122.399677#map=18/37.786971/-122.399677">https://www.openstreetmap.org/?mlat=37.7869'+
  1447. '71&amp;mlon=-122.399677#map=18/37.786971/-122.399677</a>');
  1448. done();
  1449. });
  1450. }));
  1451. });
  1452. });
  1453. }));