chatbox.js 87 KB

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