chatbox.js 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  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.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. expect(_.includes($(view.el).find('.chat-msg-author:last').text(), '**Max Mustermann')).toBeTruthy();
  76. expect($(view.el).find('.chat-msg-text:last').text()).toBe(' is as well');
  77. expect($(view.el).find('.chat-msg:last').hasClass('chat-msg-followup')).toBe(false);
  78. // Check that /me messages after a normal message don't
  79. // get the 'chat-msg-followup' class.
  80. message = 'This a normal message';
  81. test_utils.sendMessage(view, message);
  82. let message_el = view.el.querySelector('.message:last-child');
  83. expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy();
  84. message = '/me wrote a 3rd person message';
  85. test_utils.sendMessage(view, message);
  86. message_el = view.el.querySelector('.message:last-child');
  87. expect(view.el.querySelectorAll('.chat-action').length).toBe(3);
  88. expect($(view.el).find('.chat-msg-text:last').text()).toBe(' wrote a 3rd person message');
  89. expect($(view.el).find('.chat-msg-author:last').is(':visible')).toBeTruthy();
  90. expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy();
  91. message = 'This a normal message';
  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.get('fullname')+'")');
  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. }, 150).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. }, 300);
  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. }, 300);
  410. }).then(function () {
  411. setTimeout(function () { timeout = true; }, 100);
  412. return test_utils.waitUntil(function () {
  413. return timeout;
  414. }, 300);
  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. }, 300);
  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 chatboxview = _converse.chatboxviews.get(sender_jid);
  625. expect(chatboxview).toBeDefined();
  626. // Check that the notification appears inside the chatbox in the DOM
  627. var events = chatboxview.el.querySelectorAll('.chat-state-notification');
  628. expect(events.length).toBe(1);
  629. expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
  630. // Check that it doesn't appear twice
  631. msg = $msg({
  632. from: sender_jid,
  633. to: _converse.connection.jid,
  634. type: 'chat',
  635. id: (new Date()).getTime()
  636. }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  637. _converse.chatboxes.onMessage(msg);
  638. events = chatboxview.el.querySelectorAll('.chat-state-notification');
  639. expect(events.length).toBe(1);
  640. expect(events[0].textContent).toEqual(mock.cur_names[1] + ' is typing');
  641. done();
  642. }));
  643. it("can be a composing carbon message that this user sent from a different client",
  644. mock.initConverseWithPromises(
  645. null, ['rosterGroupsFetched'], {},
  646. function (done, _converse) {
  647. var contact, sent_stanza, IQ_id, stanza;
  648. test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
  649. .then(function () {
  650. return test_utils.waitUntil(function () {
  651. return _converse.xmppstatus.get('fullname');
  652. }, 300);
  653. }).then(function () {
  654. test_utils.createContacts(_converse, 'current');
  655. // Send a message from a different resource
  656. spyOn(_converse, 'log');
  657. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  658. test_utils.openChatBoxFor(_converse, recipient_jid);
  659. var msg = $msg({
  660. 'from': _converse.bare_jid,
  661. 'id': (new Date()).getTime(),
  662. 'to': _converse.connection.jid,
  663. 'type': 'chat',
  664. 'xmlns': 'jabber:client'
  665. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  666. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  667. .c('message', {
  668. 'xmlns': 'jabber:client',
  669. 'from': _converse.bare_jid+'/another-resource',
  670. 'to': recipient_jid,
  671. 'type': 'chat'
  672. }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  673. _converse.chatboxes.onMessage(msg);
  674. // Check that the chatbox and its view now exist
  675. var chatbox = _converse.chatboxes.get(recipient_jid);
  676. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  677. // Check that the message was received and check the message parameters
  678. expect(chatbox.messages.length).toEqual(1);
  679. var msg_obj = chatbox.messages.models[0];
  680. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  681. expect(msg_obj.get('sender')).toEqual('me');
  682. expect(msg_obj.get('delayed')).toEqual(false);
  683. var $chat_content = $(chatboxview.el).find('.chat-content');
  684. var status_text = $chat_content.find('.chat-info.chat-state-notification').text();
  685. expect(status_text).toBe('Typing from another device');
  686. done();
  687. });
  688. }));
  689. });
  690. describe("A paused notification", function () {
  691. it("is sent if the user has stopped typing since 30 seconds",
  692. mock.initConverseWithPromises(
  693. null, ['rosterGroupsFetched'], {},
  694. function (done, _converse) {
  695. var view, contact_jid;
  696. test_utils.createContacts(_converse, 'current');
  697. test_utils.openControlBox();
  698. test_utils.waitUntil(function () {
  699. return $(_converse.rosterview.el).find('.roster-group li').length;
  700. }, 700).then(function () {
  701. _converse.TIMEOUTS.PAUSED = 200; // Make the timeout shorter so that we can test
  702. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  703. test_utils.openChatBoxFor(_converse, contact_jid);
  704. view = _converse.chatboxviews.get(contact_jid);
  705. spyOn(_converse.connection, 'send');
  706. spyOn(view, 'setChatState').and.callThrough();
  707. expect(view.model.get('chat_state')).toBe('active');
  708. view.keyPressed({
  709. target: $(view.el).find('textarea.chat-textarea'),
  710. keyCode: 1
  711. });
  712. expect(view.model.get('chat_state')).toBe('composing');
  713. expect(_converse.connection.send).toHaveBeenCalled();
  714. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  715. expect($stanza.children().get(0).tagName).toBe('composing');
  716. return test_utils.waitUntil(function () {
  717. return view.model.get('chat_state') === 'paused';
  718. }, 500);
  719. }).then(function () {
  720. expect(_converse.connection.send).toHaveBeenCalled();
  721. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  722. return call.args[0] instanceof Strophe.Builder;
  723. });
  724. expect(calls.length).toBe(2);
  725. var $stanza = $(calls[1].args[0].tree());
  726. expect($stanza.attr('to')).toBe(contact_jid);
  727. expect($stanza.children().length).toBe(3);
  728. expect($stanza.children().get(0).tagName).toBe('paused');
  729. expect($stanza.children().get(1).tagName).toBe('no-store');
  730. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  731. // Test #359. A paused notification should not be sent
  732. // out if the user simply types longer than the
  733. // timeout.
  734. view.keyPressed({
  735. target: $(view.el).find('textarea.chat-textarea'),
  736. keyCode: 1
  737. });
  738. expect(view.setChatState).toHaveBeenCalled();
  739. expect(view.model.get('chat_state')).toBe('composing');
  740. view.keyPressed({
  741. target: $(view.el).find('textarea.chat-textarea'),
  742. keyCode: 1
  743. });
  744. expect(view.model.get('chat_state')).toBe('composing');
  745. done();
  746. });
  747. }));
  748. it("will be shown if received",
  749. mock.initConverseWithPromises(
  750. null, ['rosterGroupsFetched'], {},
  751. function (done, _converse) {
  752. test_utils.createContacts(_converse, 'current');
  753. test_utils.openControlBox();
  754. test_utils.waitUntil(function () {
  755. return $(_converse.rosterview.el).find('.roster-group').length;
  756. }, 300)
  757. .then(function () {
  758. // TODO: only show paused state if the previous state was composing
  759. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  760. spyOn(_converse, 'emit');
  761. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  762. // <paused> state
  763. var msg = $msg({
  764. from: sender_jid,
  765. to: _converse.connection.jid,
  766. type: 'chat',
  767. id: (new Date()).getTime()
  768. }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  769. _converse.chatboxes.onMessage(msg);
  770. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  771. var chatboxview = _converse.chatboxviews.get(sender_jid);
  772. var $events = $(chatboxview.el).find('.chat-info.chat-state-notification');
  773. expect($events.text()).toEqual(mock.cur_names[1] + ' has stopped typing');
  774. done();
  775. });
  776. }));
  777. it("can be a paused carbon message that this user sent from a different client",
  778. mock.initConverseWithPromises(
  779. null, ['rosterGroupsFetched'], {},
  780. function (done, _converse) {
  781. var contact, sent_stanza, IQ_id, stanza;
  782. test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
  783. .then(function () {
  784. return test_utils.waitUntil(function () {
  785. return _converse.xmppstatus.get('fullname');
  786. }, 300);
  787. }).then(function () {
  788. test_utils.createContacts(_converse, 'current');
  789. // Send a message from a different resource
  790. spyOn(_converse, 'log');
  791. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  792. test_utils.openChatBoxFor(_converse, recipient_jid);
  793. var msg = $msg({
  794. 'from': _converse.bare_jid,
  795. 'id': (new Date()).getTime(),
  796. 'to': _converse.connection.jid,
  797. 'type': 'chat',
  798. 'xmlns': 'jabber:client'
  799. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  800. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  801. .c('message', {
  802. 'xmlns': 'jabber:client',
  803. 'from': _converse.bare_jid+'/another-resource',
  804. 'to': recipient_jid,
  805. 'type': 'chat'
  806. }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  807. _converse.chatboxes.onMessage(msg);
  808. // Check that the chatbox and its view now exist
  809. var chatbox = _converse.chatboxes.get(recipient_jid);
  810. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  811. // Check that the message was received and check the message parameters
  812. expect(chatbox.messages.length).toEqual(1);
  813. var msg_obj = chatbox.messages.models[0];
  814. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  815. expect(msg_obj.get('sender')).toEqual('me');
  816. expect(msg_obj.get('delayed')).toEqual(false);
  817. var $chat_content = $(chatboxview.el).find('.chat-content');
  818. var status_text = $chat_content.find('.chat-info.chat-state-notification').text();
  819. expect(status_text).toBe('Stopped typing on the other device');
  820. done();
  821. });
  822. }));
  823. });
  824. describe("An inactive notifciation", function () {
  825. it("is sent if the user has stopped typing since 2 minutes",
  826. mock.initConverseWithPromises(
  827. null, ['rosterGroupsFetched'], {},
  828. function (done, _converse) {
  829. var view, contact_jid;
  830. test_utils.createContacts(_converse, 'current');
  831. test_utils.openControlBox();
  832. test_utils.waitUntil(function () {
  833. return $(_converse.rosterview.el).find('.roster-group').length;
  834. }, 500).then(function () {
  835. // Make the timeouts shorter so that we can test
  836. _converse.TIMEOUTS.PAUSED = 200;
  837. _converse.TIMEOUTS.INACTIVE = 200;
  838. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  839. test_utils.openChatBoxFor(_converse, contact_jid);
  840. view = _converse.chatboxviews.get(contact_jid);
  841. return test_utils.waitUntil(function () {
  842. return view.model.get('chat_state') === 'active';
  843. }, 500);
  844. }).then(function () {
  845. console.log('chat_state set to active');
  846. view = _converse.chatboxviews.get(contact_jid);
  847. expect(view.model.get('chat_state')).toBe('active');
  848. view.keyPressed({
  849. target: $(view.el).find('textarea.chat-textarea'),
  850. keyCode: 1
  851. });
  852. return test_utils.waitUntil(function () {
  853. return view.model.get('chat_state') === 'composing';
  854. }, 500);
  855. }).then(function () {
  856. console.log('chat_state set to composing');
  857. view = _converse.chatboxviews.get(contact_jid);
  858. expect(view.model.get('chat_state')).toBe('composing');
  859. spyOn(_converse.connection, 'send');
  860. return test_utils.waitUntil(function () {
  861. return view.model.get('chat_state') === 'paused';
  862. }, 500);
  863. }).then(function () {
  864. console.log('chat_state set to paused');
  865. return test_utils.waitUntil(function () {
  866. return view.model.get('chat_state') === 'inactive';
  867. }, 500);
  868. }).then(function () {
  869. console.log('chat_state set to inactive');
  870. expect(_converse.connection.send).toHaveBeenCalled();
  871. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  872. return call.args[0] instanceof Strophe.Builder;
  873. });
  874. expect(calls.length).toBe(2);
  875. var $stanza = $(calls[0].args[0].tree());
  876. expect($stanza.attr('to')).toBe(contact_jid);
  877. expect($stanza.children().length).toBe(3);
  878. expect($stanza.children().get(0).tagName).toBe('paused');
  879. expect($stanza.children().get(1).tagName).toBe('no-store');
  880. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  881. $stanza = $(_converse.connection.send.calls.mostRecent().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('inactive');
  885. expect($stanza.children().get(1).tagName).toBe('no-store');
  886. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  887. }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
  888. .then(done);
  889. }));
  890. it("is sent when the user a minimizes a chat box",
  891. mock.initConverseWithPromises(
  892. null, ['rosterGroupsFetched'], {},
  893. function (done, _converse) {
  894. test_utils.createContacts(_converse, 'current');
  895. test_utils.openControlBox();
  896. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  897. test_utils.openChatBoxFor(_converse, contact_jid);
  898. var view = _converse.chatboxviews.get(contact_jid);
  899. spyOn(_converse.connection, 'send');
  900. view.minimize();
  901. expect(view.model.get('chat_state')).toBe('inactive');
  902. expect(_converse.connection.send).toHaveBeenCalled();
  903. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  904. expect($stanza.attr('to')).toBe(contact_jid);
  905. expect($stanza.children().get(0).tagName).toBe('inactive');
  906. done();
  907. }));
  908. it("is sent if the user closes a chat box",
  909. mock.initConverseWithPromises(
  910. null, ['rosterGroupsFetched'], {},
  911. function (done, _converse) {
  912. test_utils.createContacts(_converse, 'current');
  913. test_utils.openControlBox();
  914. test_utils.waitUntil(function () {
  915. return $(_converse.rosterview.el).find('.roster-group').length;
  916. }, 300).then(function () {
  917. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  918. test_utils.openChatBoxFor(_converse, contact_jid);
  919. var view = _converse.chatboxviews.get(contact_jid);
  920. expect(view.model.get('chat_state')).toBe('active');
  921. spyOn(_converse.connection, 'send');
  922. view.close();
  923. expect(view.model.get('chat_state')).toBe('inactive');
  924. expect(_converse.connection.send).toHaveBeenCalled();
  925. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  926. expect($stanza.attr('to')).toBe(contact_jid);
  927. expect($stanza.children().length).toBe(3);
  928. expect($stanza.children().get(0).tagName).toBe('inactive');
  929. expect($stanza.children().get(1).tagName).toBe('no-store');
  930. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  931. done();
  932. });
  933. }));
  934. it("will clear any other chat status notifications",
  935. mock.initConverseWithPromises(
  936. null, ['rosterGroupsFetched'], {},
  937. function (done, _converse) {
  938. test_utils.createContacts(_converse, 'current');
  939. test_utils.openControlBox();
  940. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  941. spyOn(_converse, 'emit');
  942. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  943. test_utils.openChatBoxFor(_converse, sender_jid);
  944. var view = _converse.chatboxviews.get(sender_jid);
  945. expect(view.el.querySelectorAll('.chat-event').length).toBe(0);
  946. // Insert <composing> message, to also check that
  947. // text messages are inserted correctly with
  948. // temporary chat events in the chat contents.
  949. var msg = $msg({
  950. 'to': _converse.bare_jid,
  951. 'xmlns': 'jabber:client',
  952. 'from': sender_jid,
  953. 'type': 'chat'})
  954. .c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
  955. .tree();
  956. _converse.chatboxes.onMessage(msg);
  957. expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
  958. msg = $msg({
  959. from: sender_jid,
  960. to: _converse.connection.jid,
  961. type: 'chat',
  962. id: (new Date()).getTime()
  963. }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  964. _converse.chatboxes.onMessage(msg);
  965. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  966. expect($(view.el).find('.chat-state-notification').length).toBe(0);
  967. done();
  968. }));
  969. });
  970. describe("A gone notifciation", function () {
  971. it("will be shown if received",
  972. mock.initConverseWithPromises(
  973. null, ['rosterGroupsFetched'], {},
  974. function (done, _converse) {
  975. test_utils.createContacts(_converse, 'current');
  976. test_utils.openControlBox();
  977. spyOn(_converse, 'emit');
  978. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  979. // <paused> state
  980. var msg = $msg({
  981. from: sender_jid,
  982. to: _converse.connection.jid,
  983. type: 'chat',
  984. id: (new Date()).getTime()
  985. }).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  986. _converse.chatboxes.onMessage(msg);
  987. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  988. var chatboxview = _converse.chatboxviews.get(sender_jid);
  989. var $events = $(chatboxview.el).find('.chat-state-notification');
  990. expect($events.text()).toEqual(mock.cur_names[1] + ' has gone away');
  991. done();
  992. }));
  993. });
  994. });
  995. });
  996. describe("Special Messages", function () {
  997. it("'/clear' can be used to clear messages in a conversation",
  998. mock.initConverseWithPromises(
  999. null, ['rosterGroupsFetched'], {},
  1000. function (done, _converse) {
  1001. test_utils.createContacts(_converse, 'current');
  1002. test_utils.openControlBox();
  1003. spyOn(_converse, 'emit');
  1004. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1005. test_utils.openChatBoxFor(_converse, contact_jid);
  1006. var view = _converse.chatboxviews.get(contact_jid);
  1007. var message = 'This message is another sent from this chatbox';
  1008. // Lets make sure there is at least one message already
  1009. // (e.g for when this test is run on its own).
  1010. test_utils.sendMessage(view, message);
  1011. expect(view.model.messages.length > 0).toBeTruthy();
  1012. expect(view.model.messages.browserStorage.records.length > 0).toBeTruthy();
  1013. expect(_converse.emit).toHaveBeenCalledWith('messageSend', message);
  1014. message = '/clear';
  1015. spyOn(view, 'onMessageSubmitted').and.callThrough();
  1016. spyOn(view, 'clearMessages').and.callThrough();
  1017. spyOn(window, 'confirm').and.callFake(function () {
  1018. return true;
  1019. });
  1020. test_utils.sendMessage(view, message);
  1021. expect(view.onMessageSubmitted).toHaveBeenCalled();
  1022. expect(view.clearMessages).toHaveBeenCalled();
  1023. expect(window.confirm).toHaveBeenCalled();
  1024. expect(view.model.messages.length, 0); // The messages must be removed from the chatbox
  1025. expect(view.model.messages.browserStorage.records.length, 0); // And also from browserStorage
  1026. expect(_converse.emit.calls.count(), 1);
  1027. expect(_converse.emit.calls.mostRecent().args, ['messageSend', message]);
  1028. done();
  1029. }));
  1030. });
  1031. describe("A Message Counter", function () {
  1032. it("is incremented when the message is received and the window is not focused",
  1033. mock.initConverseWithPromises(
  1034. null, ['rosterGroupsFetched'], {},
  1035. function (done, _converse) {
  1036. test_utils.createContacts(_converse, 'current');
  1037. test_utils.openControlBox();
  1038. spyOn(_converse, 'emit');
  1039. expect(_converse.msg_counter).toBe(0);
  1040. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1041. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1042. var previous_state = _converse.windowState;
  1043. var message = 'This message will increment the message counter';
  1044. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1045. msg = $msg({
  1046. from: sender_jid,
  1047. to: _converse.connection.jid,
  1048. type: 'chat',
  1049. id: (new Date()).getTime()
  1050. }).c('body').t(message).up()
  1051. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1052. _converse.windowState = 'hidden';
  1053. _converse.chatboxes.onMessage(msg);
  1054. expect(_converse.incrementMsgCounter).toHaveBeenCalled();
  1055. expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
  1056. expect(_converse.msg_counter).toBe(1);
  1057. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1058. _converse.windowSate = previous_state;
  1059. done();
  1060. }));
  1061. it("is cleared when the window is focused",
  1062. mock.initConverseWithPromises(
  1063. null, ['rosterGroupsFetched'], {},
  1064. function (done, _converse) {
  1065. test_utils.createContacts(_converse, 'current');
  1066. test_utils.openControlBox();
  1067. _converse.windowState = 'hidden';
  1068. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1069. _converse.saveWindowState(null, 'focus');
  1070. _converse.saveWindowState(null, 'blur');
  1071. expect(_converse.clearMsgCounter).toHaveBeenCalled();
  1072. done();
  1073. }));
  1074. it("is not incremented when the message is received and the window is focused",
  1075. mock.initConverseWithPromises(
  1076. null, ['rosterGroupsFetched'], {},
  1077. function (done, _converse) {
  1078. test_utils.createContacts(_converse, 'current');
  1079. test_utils.openControlBox();
  1080. expect(_converse.msg_counter).toBe(0);
  1081. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1082. _converse.saveWindowState(null, 'focus');
  1083. var message = 'This message will not increment the message counter';
  1084. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1085. msg = $msg({
  1086. from: sender_jid,
  1087. to: _converse.connection.jid,
  1088. type: 'chat',
  1089. id: (new Date()).getTime()
  1090. }).c('body').t(message).up()
  1091. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1092. _converse.chatboxes.onMessage(msg);
  1093. expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
  1094. expect(_converse.msg_counter).toBe(0);
  1095. done();
  1096. }));
  1097. it("is incremented from zero when chatbox was closed after viewing previously received messages and the window is not focused now",
  1098. mock.initConverseWithPromises(
  1099. null, ['rosterGroupsFetched'], {},
  1100. function (done, _converse) {
  1101. test_utils.createContacts(_converse, 'current');
  1102. // initial state
  1103. expect(_converse.msg_counter).toBe(0);
  1104. var message = 'This message will always increment the message counter from zero',
  1105. sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1106. msgFactory = function () {
  1107. return $msg({
  1108. from: sender_jid,
  1109. to: _converse.connection.jid,
  1110. type: 'chat',
  1111. id: (new Date()).getTime()
  1112. })
  1113. .c('body').t(message).up()
  1114. .c('active', {'xmlns': Strophe.NS.CHATSTATES})
  1115. .tree();
  1116. };
  1117. // leave converse-chat page
  1118. _converse.windowState = 'hidden';
  1119. _converse.chatboxes.onMessage(msgFactory());
  1120. expect(_converse.msg_counter).toBe(1);
  1121. // come back to converse-chat page
  1122. _converse.saveWindowState(null, 'focus');
  1123. var view = _converse.chatboxviews.get(sender_jid);
  1124. expect(u.isVisible(view.el)).toBeTruthy();
  1125. expect(_converse.msg_counter).toBe(0);
  1126. // close chatbox and leave converse-chat page again
  1127. view.close();
  1128. _converse.windowState = 'hidden';
  1129. // check that msg_counter is incremented from zero again
  1130. _converse.chatboxes.onMessage(msgFactory());
  1131. view = _converse.chatboxviews.get(sender_jid);
  1132. expect(u.isVisible(view.el)).toBeTruthy();
  1133. expect(_converse.msg_counter).toBe(1);
  1134. done();
  1135. }));
  1136. });
  1137. describe("A ChatBox's Unread Message Count", function () {
  1138. it("is incremented when the message is received and ChatBoxView is scrolled up",
  1139. mock.initConverseWithPromises(
  1140. null, ['rosterGroupsFetched'], {},
  1141. function (done, _converse) {
  1142. test_utils.createContacts(_converse, 'current');
  1143. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1144. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1145. test_utils.openChatBoxFor(_converse, sender_jid);
  1146. var chatbox = _converse.chatboxes.get(sender_jid);
  1147. chatbox.save('scrolled', true);
  1148. _converse.chatboxes.onMessage(msg);
  1149. expect(chatbox.get('num_unread')).toBe(1);
  1150. done();
  1151. }));
  1152. it("is not incremented when the message is received and ChatBoxView is scrolled down",
  1153. mock.initConverseWithPromises(
  1154. null, ['rosterGroupsFetched'], {},
  1155. function (done, _converse) {
  1156. test_utils.createContacts(_converse, 'current');
  1157. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1158. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be read');
  1159. test_utils.openChatBoxFor(_converse, sender_jid);
  1160. var chatbox = _converse.chatboxes.get(sender_jid);
  1161. _converse.chatboxes.onMessage(msg);
  1162. expect(chatbox.get('num_unread')).toBe(0);
  1163. done();
  1164. }));
  1165. it("is incremeted when message is received, chatbox is scrolled down and the window is not focused",
  1166. mock.initConverseWithPromises(
  1167. null, ['rosterGroupsFetched'], {},
  1168. function (done, _converse) {
  1169. test_utils.createContacts(_converse, 'current');
  1170. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1171. var msgFactory = function () {
  1172. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1173. };
  1174. test_utils.openChatBoxFor(_converse, sender_jid);
  1175. var chatbox = _converse.chatboxes.get(sender_jid);
  1176. _converse.windowState = 'hidden';
  1177. _converse.chatboxes.onMessage(msgFactory());
  1178. expect(chatbox.get('num_unread')).toBe(1);
  1179. done();
  1180. }));
  1181. it("is incremeted when message is received, chatbox is scrolled up and the window is not focused",
  1182. mock.initConverseWithPromises(
  1183. null, ['rosterGroupsFetched'], {},
  1184. function (done, _converse) {
  1185. test_utils.createContacts(_converse, 'current');
  1186. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1187. var msgFactory = function () {
  1188. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1189. };
  1190. test_utils.openChatBoxFor(_converse, sender_jid);
  1191. var chatbox = _converse.chatboxes.get(sender_jid);
  1192. chatbox.save('scrolled', true);
  1193. _converse.windowState = 'hidden';
  1194. _converse.chatboxes.onMessage(msgFactory());
  1195. expect(chatbox.get('num_unread')).toBe(1);
  1196. done();
  1197. }));
  1198. it("is cleared when ChatBoxView was scrolled down and the window become focused",
  1199. mock.initConverseWithPromises(
  1200. null, ['rosterGroupsFetched'], {},
  1201. function (done, _converse) {
  1202. test_utils.createContacts(_converse, 'current');
  1203. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1204. var msgFactory = function () {
  1205. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1206. };
  1207. test_utils.openChatBoxFor(_converse, sender_jid);
  1208. var chatbox = _converse.chatboxes.get(sender_jid);
  1209. _converse.windowState = 'hidden';
  1210. _converse.chatboxes.onMessage(msgFactory());
  1211. expect(chatbox.get('num_unread')).toBe(1);
  1212. _converse.saveWindowState(null, 'focus');
  1213. expect(chatbox.get('num_unread')).toBe(0);
  1214. done();
  1215. }));
  1216. it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
  1217. mock.initConverseWithPromises(
  1218. null, ['rosterGroupsFetched'], {},
  1219. function (done, _converse) {
  1220. test_utils.createContacts(_converse, 'current');
  1221. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1222. var msgFactory = function () {
  1223. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1224. };
  1225. test_utils.openChatBoxFor(_converse, sender_jid);
  1226. var chatbox = _converse.chatboxes.get(sender_jid);
  1227. chatbox.save('scrolled', true);
  1228. _converse.windowState = 'hidden';
  1229. _converse.chatboxes.onMessage(msgFactory());
  1230. expect(chatbox.get('num_unread')).toBe(1);
  1231. _converse.saveWindowState(null, 'focus');
  1232. expect(chatbox.get('num_unread')).toBe(1);
  1233. done();
  1234. }));
  1235. });
  1236. describe("A RosterView's Unread Message Count", function () {
  1237. it("is updated when message is received and chatbox is scrolled up",
  1238. mock.initConverseWithPromises(
  1239. null, ['rosterGroupsFetched'], {},
  1240. function (done, _converse) {
  1241. test_utils.createContacts(_converse, 'current');
  1242. test_utils.waitUntil(function () {
  1243. return $(_converse.rosterview.el).find('.roster-group').length;
  1244. }, 500)
  1245. .then(function () {
  1246. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1247. test_utils.openChatBoxFor(_converse, sender_jid);
  1248. var chatbox = _converse.chatboxes.get(sender_jid);
  1249. chatbox.save('scrolled', true);
  1250. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1251. _converse.chatboxes.onMessage(msg);
  1252. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1253. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1254. expect($msgIndicator.text()).toBe('1');
  1255. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  1256. _converse.chatboxes.onMessage(msg);
  1257. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1258. expect($msgIndicator.text()).toBe('2');
  1259. done();
  1260. });
  1261. }));
  1262. it("is updated when message is received and chatbox is minimized",
  1263. mock.initConverseWithPromises(
  1264. null, ['rosterGroupsFetched'], {},
  1265. function (done, _converse) {
  1266. test_utils.createContacts(_converse, 'current');
  1267. test_utils.waitUntil(function () {
  1268. return $(_converse.rosterview.el).find('.roster-group').length;
  1269. }, 500)
  1270. .then(function () {
  1271. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1272. test_utils.openChatBoxFor(_converse, sender_jid);
  1273. var chatbox = _converse.chatboxes.get(sender_jid);
  1274. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1275. chatboxview.minimize();
  1276. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1277. _converse.chatboxes.onMessage(msg);
  1278. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1279. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1280. expect($msgIndicator.text()).toBe('1');
  1281. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  1282. _converse.chatboxes.onMessage(msg);
  1283. $msgIndicator = $($(_converse.rosterview.el).find(msgIndicatorSelector));
  1284. expect($msgIndicator.text()).toBe('2');
  1285. done();
  1286. });
  1287. }));
  1288. it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
  1289. mock.initConverseWithPromises(
  1290. null, ['rosterGroupsFetched'], {},
  1291. function (done, _converse) {
  1292. test_utils.createContacts(_converse, 'current');
  1293. test_utils.waitUntil(function () {
  1294. return $(_converse.rosterview.el).find('.roster-group').length;
  1295. }, 500)
  1296. .then(function () {
  1297. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1298. test_utils.openChatBoxFor(_converse, sender_jid);
  1299. var chatbox = _converse.chatboxes.get(sender_jid);
  1300. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1301. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
  1302. var selectMsgsIndicator = function () { return $($(_converse.rosterview.el).find(msgsIndicatorSelector)); };
  1303. var msgFactory = function () {
  1304. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1305. };
  1306. chatboxview.minimize();
  1307. _converse.chatboxes.onMessage(msgFactory());
  1308. expect(selectMsgsIndicator().text()).toBe('1');
  1309. _converse.chatboxes.onMessage(msgFactory());
  1310. expect(selectMsgsIndicator().text()).toBe('2');
  1311. chatboxview.maximize();
  1312. expect(selectMsgsIndicator().length).toBe(0);
  1313. done();
  1314. });
  1315. }));
  1316. it("is cleared when unread messages are viewed which were received in scrolled-up chatbox",
  1317. mock.initConverseWithPromises(
  1318. null, ['rosterGroupsFetched'], {},
  1319. function (done, _converse) {
  1320. test_utils.createContacts(_converse, 'current');
  1321. test_utils.waitUntil(function () {
  1322. return $(_converse.rosterview.el).find('.roster-group').length;
  1323. }, 500)
  1324. .then(function () {
  1325. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1326. test_utils.openChatBoxFor(_converse, sender_jid);
  1327. var chatbox = _converse.chatboxes.get(sender_jid);
  1328. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1329. var msgFactory = function () {
  1330. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1331. };
  1332. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1333. selectMsgsIndicator = function () { return $($(_converse.rosterview.el).find(msgsIndicatorSelector)); };
  1334. chatbox.save('scrolled', true);
  1335. _converse.chatboxes.onMessage(msgFactory());
  1336. expect(selectMsgsIndicator().text()).toBe('1');
  1337. chatboxview.viewUnreadMessages();
  1338. _converse.rosterview.render();
  1339. expect(selectMsgsIndicator().length).toBe(0);
  1340. done();
  1341. });
  1342. }));
  1343. it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
  1344. mock.initConverseWithPromises(
  1345. null, ['rosterGroupsFetched'], {},
  1346. function (done, _converse) {
  1347. test_utils.createContacts(_converse, 'current');
  1348. test_utils.waitUntil(function () {
  1349. return $(_converse.rosterview.el).find('.roster-group').length;
  1350. }, 500)
  1351. .then(function () {
  1352. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1353. test_utils.openChatBoxFor(_converse, sender_jid);
  1354. var chatbox = _converse.chatboxes.get(sender_jid);
  1355. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1356. var msgFactory = function () {
  1357. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1358. };
  1359. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  1360. selectMsgsIndicator = function () { return $($(_converse.rosterview.el).find(msgsIndicatorSelector)); };
  1361. chatbox.save('scrolled', true);
  1362. _converse.chatboxes.onMessage(msgFactory());
  1363. expect(selectMsgsIndicator().text()).toBe('1');
  1364. test_utils.openChatBoxFor(_converse, sender_jid);
  1365. expect(selectMsgsIndicator().text()).toBe('1');
  1366. done();
  1367. });
  1368. }));
  1369. });
  1370. describe("A Minimized ChatBoxView's Unread Message Count", function () {
  1371. it("is displayed when scrolled up chatbox is minimized after receiving unread messages",
  1372. mock.initConverseWithPromises(
  1373. null, ['rosterGroupsFetched'], {},
  1374. function (done, _converse) {
  1375. test_utils.createContacts(_converse, 'current');
  1376. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1377. test_utils.openChatBoxFor(_converse, sender_jid);
  1378. var msgFactory = function () {
  1379. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1380. };
  1381. var selectUnreadMsgCount = function () {
  1382. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  1383. return $(minimizedChatBoxView.el).find('.message-count');
  1384. };
  1385. var chatbox = _converse.chatboxes.get(sender_jid);
  1386. chatbox.save('scrolled', true);
  1387. _converse.chatboxes.onMessage(msgFactory());
  1388. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1389. chatboxview.minimize();
  1390. var $unreadMsgCount = selectUnreadMsgCount();
  1391. expect(u.isVisible($unreadMsgCount[0])).toBeTruthy();
  1392. expect($unreadMsgCount.html()).toBe('1');
  1393. done();
  1394. }));
  1395. it("is incremented when message is received and windows is not focused",
  1396. mock.initConverseWithPromises(
  1397. null, ['rosterGroupsFetched'], {},
  1398. function (done, _converse) {
  1399. test_utils.createContacts(_converse, 'current');
  1400. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1401. test_utils.openChatBoxFor(_converse, sender_jid);
  1402. var msgFactory = function () {
  1403. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  1404. };
  1405. var selectUnreadMsgCount = function () {
  1406. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  1407. return $(minimizedChatBoxView.el).find('.message-count');
  1408. };
  1409. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1410. chatboxview.minimize();
  1411. _converse.chatboxes.onMessage(msgFactory());
  1412. var $unreadMsgCount = selectUnreadMsgCount();
  1413. expect(u.isVisible($unreadMsgCount[0])).toBeTruthy();
  1414. expect($unreadMsgCount.html()).toBe('1');
  1415. done();
  1416. }));
  1417. it("will render Openstreetmap-URL from geo-URI",
  1418. mock.initConverseWithPromises(
  1419. null, ['rosterGroupsFetched'], {},
  1420. function (done, _converse) {
  1421. test_utils.createContacts(_converse, 'current');
  1422. var base_url = document.URL.split(window.location.pathname)[0];
  1423. var message = "geo:37.786971,-122.399677";
  1424. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1425. test_utils.openChatBoxFor(_converse, contact_jid);
  1426. var view = _converse.chatboxviews.get(contact_jid);
  1427. spyOn(view.model, 'sendMessage').and.callThrough();
  1428. test_utils.sendMessage(view, message);
  1429. test_utils.waitUntil(function () {
  1430. return $(view.el).find('.chat-content').find('.chat-msg').length;
  1431. }, 1000).then(function () {
  1432. expect(view.model.sendMessage).toHaveBeenCalled();
  1433. var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg-text');
  1434. expect(msg.html()).toEqual(
  1435. '<a target="_blank" rel="noopener" href="https://www.openstreetmap.org/?mlat=37.786971&amp;'+
  1436. 'mlon=-122.399677#map=18/37.786971/-122.399677">https://www.openstreetmap.org/?mlat=37.7869'+
  1437. '71&amp;mlon=-122.399677#map=18/37.786971/-122.399677</a>');
  1438. done();
  1439. });
  1440. }));
  1441. });
  1442. });
  1443. }));