chatbox.js 141 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551
  1. (function (root, factory) {
  2. define([
  3. "jquery.noconflict",
  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 Promise = converse.env.Promise;
  17. var moment = converse.env.moment;
  18. return describe("Chatboxes", function() {
  19. describe("A Chatbox", function () {
  20. it("supports the /me command",
  21. mock.initConverseWithPromises(
  22. null, ['rosterGroupsFetched'], {},
  23. function (done, _converse) {
  24. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
  25. .then(function () {
  26. return test_utils.waitUntil(function () {
  27. return _converse.xmppstatus.get('fullname');
  28. }, 300);
  29. }).then(function () {
  30. test_utils.createContacts(_converse, 'current');
  31. test_utils.openControlBox();
  32. test_utils.openContactsPanel(_converse);
  33. expect(_converse.chatboxes.length).toEqual(1);
  34. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  35. var message = '/me is tired';
  36. var msg = $msg({
  37. from: sender_jid,
  38. to: _converse.connection.jid,
  39. type: 'chat',
  40. id: (new Date()).getTime()
  41. }).c('body').t(message).up()
  42. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  43. _converse.chatboxes.onMessage(msg);
  44. var view = _converse.chatboxviews.get(sender_jid);
  45. expect(_.includes(view.$el.find('.chat-msg-author').text(), '**Max Frankfurter')).toBeTruthy();
  46. expect(view.$el.find('.chat-msg-content').text()).toBe(' is tired');
  47. message = '/me is as well';
  48. test_utils.sendMessage(view, message);
  49. expect(_.includes(view.$el.find('.chat-msg-author:last').text(), '**Max Mustermann')).toBeTruthy();
  50. expect(view.$el.find('.chat-msg-content:last').text()).toBe(' is as well');
  51. done();
  52. });
  53. }));
  54. it("is created when you click on a roster item",
  55. mock.initConverseWithPromises(
  56. null, ['rosterGroupsFetched'], {},
  57. function (done, _converse) {
  58. test_utils.createContacts(_converse, 'current');
  59. test_utils.openControlBox();
  60. test_utils.openContactsPanel(_converse);
  61. var i, $el, jid, chatboxview;
  62. // openControlBox was called earlier, so the controlbox is
  63. // visible, but no other chat boxes have been created.
  64. expect(_converse.chatboxes.length).toEqual(1);
  65. spyOn(_converse.chatboxviews, 'trimChats');
  66. expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
  67. var online_contacts = _converse.rosterview.$el.find('dt.roster-group').siblings('dd.current-xmpp-contact').find('a.open-chat');
  68. for (i=0; i<online_contacts.length; i++) {
  69. $el = $(online_contacts[i]);
  70. jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost';
  71. $el.click();
  72. chatboxview = _converse.chatboxviews.get(jid);
  73. expect(_converse.chatboxes.length).toEqual(i+2);
  74. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  75. // Check that new chat boxes are created to the left of the
  76. // controlbox (but to the right of all existing chat boxes)
  77. expect($("#conversejs .chatbox").length).toBe(i+2);
  78. expect($("#conversejs .chatbox")[1].id).toBe(chatboxview.model.get('box_id'));
  79. }
  80. done();
  81. }));
  82. it("can be trimmed to conserve space",
  83. mock.initConverseWithPromises(
  84. null, ['rosterGroupsFetched'], {},
  85. function (done, _converse) {
  86. test_utils.createContacts(_converse, 'current');
  87. test_utils.openControlBox();
  88. test_utils.openContactsPanel(_converse);
  89. var i, $el, jid, chatbox, chatboxview, trimmedview;
  90. // openControlBox was called earlier, so the controlbox is
  91. // visible, but no other chat boxes have been created.
  92. var trimmed_chatboxes = _converse.minimized_chats;
  93. expect(_converse.chatboxes.length).toEqual(1);
  94. spyOn(_converse.chatboxviews, 'trimChats');
  95. spyOn(trimmed_chatboxes, 'addChat').and.callThrough();
  96. spyOn(trimmed_chatboxes, 'removeChat').and.callThrough();
  97. expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
  98. _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
  99. test_utils.waitUntil(function () {
  100. return _converse.rosterview.$el.find('.roster-group').length;
  101. }, 300)
  102. .then(function () {
  103. // Test that they can be maximized again
  104. var online_contacts = _converse.rosterview.$el.find('dt.roster-group').siblings('dd.current-xmpp-contact').find('a.open-chat');
  105. for (i=0; i<online_contacts.length; i++) {
  106. $el = $(online_contacts[i]);
  107. jid = _.trim($el.text()).replace(/ /g,'.').toLowerCase() + '@localhost';
  108. $el.click();
  109. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  110. chatboxview = _converse.chatboxviews.get(jid);
  111. spyOn(chatboxview, 'minimize').and.callThrough();
  112. chatboxview.model.set({'minimized': true});
  113. expect(trimmed_chatboxes.addChat).toHaveBeenCalled();
  114. expect(chatboxview.minimize).toHaveBeenCalled();
  115. }
  116. return test_utils.waitUntil(function () {
  117. return _converse.chatboxviews.keys().length > 1;
  118. }, 500);
  119. }).then(function () {
  120. var key = _converse.chatboxviews.keys()[1];
  121. trimmedview = trimmed_chatboxes.get(key);
  122. chatbox = trimmedview.model;
  123. spyOn(chatbox, 'maximize').and.callThrough();
  124. spyOn(trimmedview, 'restore').and.callThrough();
  125. trimmedview.delegateEvents();
  126. trimmedview.$("a.restore-chat").click();
  127. expect(trimmedview.restore).toHaveBeenCalled();
  128. expect(chatbox.maximize).toHaveBeenCalled();
  129. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  130. done();
  131. });
  132. done();
  133. }));
  134. it("can be opened in minimized mode initially",
  135. mock.initConverseWithPromises(
  136. null, ['rosterGroupsFetched'], {},
  137. function (done, _converse) {
  138. test_utils.createContacts(_converse, 'current');
  139. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  140. var chat = _converse.api.chats.open(sender_jid, {
  141. minimized: true
  142. });
  143. var chatBoxView = _converse.chatboxviews.get(sender_jid);
  144. expect(chatBoxView.$el.is(':visible')).toBeFalsy();
  145. var minimized_chat = _converse.minimized_chats.get(sender_jid);
  146. expect(minimized_chat).toBeTruthy();
  147. expect(minimized_chat.$el.is(':visible')).toBeTruthy();
  148. done();
  149. }));
  150. it("is focused if its already open and you click on its corresponding roster item",
  151. mock.initConverseWithPromises(
  152. null, ['rosterGroupsFetched'], {},
  153. function (done, _converse) {
  154. test_utils.createContacts(_converse, 'current');
  155. _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
  156. test_utils.openControlBox();
  157. test_utils.openContactsPanel(_converse);
  158. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  159. var $el, jid, chatbox;
  160. // openControlBox was called earlier, so the controlbox is
  161. // visible, but no other chat boxes have been created.
  162. expect(_converse.chatboxes.length).toEqual(1);
  163. spyOn(_converse.ChatBoxView.prototype, 'focus');
  164. chatbox = test_utils.openChatBoxFor(_converse, contact_jid);
  165. $el = _converse.rosterview.$el.find('a.open-chat:contains("'+chatbox.get('fullname')+'")');
  166. jid = $el.text().replace(/ /g,'.').toLowerCase() + '@localhost';
  167. $el.click();
  168. expect(_converse.chatboxes.length).toEqual(2);
  169. var chatboxview = _converse.chatboxviews.get(contact_jid);
  170. expect(chatboxview.focus).toHaveBeenCalled();
  171. done();
  172. }));
  173. it("can be saved to, and retrieved from, browserStorage",
  174. mock.initConverseWithPromises(
  175. null, ['rosterGroupsFetched'], {},
  176. function (done, _converse) {
  177. test_utils.createContacts(_converse, 'current');
  178. test_utils.openControlBox();
  179. test_utils.openContactsPanel(_converse);
  180. spyOn(_converse, 'emit');
  181. spyOn(_converse.chatboxviews, 'trimChats');
  182. test_utils.openControlBox();
  183. test_utils.openChatBoxes(_converse, 6);
  184. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  185. // We instantiate a new ChatBoxes collection, which by default
  186. // will be empty.
  187. var newchatboxes = new _converse.ChatBoxes();
  188. expect(newchatboxes.length).toEqual(0);
  189. // The chatboxes will then be fetched from browserStorage inside the
  190. // onConnected method
  191. newchatboxes.onConnected();
  192. expect(newchatboxes.length).toEqual(7);
  193. // Check that the chatboxes items retrieved from browserStorage
  194. // have the same attributes values as the original ones.
  195. var attrs = ['id', 'box_id', 'visible'];
  196. var new_attrs, old_attrs;
  197. for (var i=0; i<attrs.length; i++) {
  198. new_attrs = _.map(_.map(newchatboxes.models, 'attributes'), attrs[i]);
  199. old_attrs = _.map(_.map(_converse.chatboxes.models, 'attributes'), attrs[i]);
  200. expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
  201. }
  202. _converse.rosterview.render();
  203. done();
  204. }));
  205. it("can be closed by clicking a DOM element with class 'close-chatbox-button'",
  206. mock.initConverseWithPromises(
  207. null, ['rosterGroupsFetched'], {},
  208. function (done, _converse) {
  209. test_utils.createContacts(_converse, 'current');
  210. test_utils.openControlBox();
  211. test_utils.openContactsPanel(_converse);
  212. test_utils.waitUntil(function () {
  213. return _converse.rosterview.$el.find('.roster-group').length;
  214. }, 300)
  215. .then(function () {
  216. var chatbox = test_utils.openChatBoxes(_converse, 1)[0],
  217. controlview = _converse.chatboxviews.get('controlbox'), // The controlbox is currently open
  218. chatview = _converse.chatboxviews.get(chatbox.get('jid'));
  219. spyOn(chatview, 'close').and.callThrough();
  220. spyOn(controlview, 'close').and.callThrough();
  221. spyOn(_converse, 'emit');
  222. // We need to rebind all events otherwise our spy won't be called
  223. controlview.delegateEvents();
  224. chatview.delegateEvents();
  225. controlview.$el.find('.close-chatbox-button').click();
  226. expect(controlview.close).toHaveBeenCalled();
  227. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  228. expect(_converse.emit.calls.count(), 1);
  229. chatview.$el.find('.close-chatbox-button').click();
  230. expect(chatview.close).toHaveBeenCalled();
  231. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  232. expect(_converse.emit.calls.count(), 2);
  233. done();
  234. });
  235. }));
  236. it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'",
  237. mock.initConverseWithPromises(
  238. null, ['rosterGroupsFetched'], {},
  239. function (done, _converse) {
  240. var chatview;
  241. test_utils.createContacts(_converse, 'current');
  242. test_utils.openControlBox();
  243. test_utils.openContactsPanel(_converse);
  244. test_utils.waitUntil(function () {
  245. return _converse.rosterview.$el.find('.roster-group').length;
  246. }, 300)
  247. .then(function () {
  248. var chatbox = test_utils.openChatBoxes(_converse, 1)[0],
  249. trimmed_chatboxes = _converse.minimized_chats,
  250. trimmedview;
  251. chatview = _converse.chatboxviews.get(chatbox.get('jid'));
  252. spyOn(chatview, 'minimize').and.callThrough();
  253. spyOn(_converse, 'emit');
  254. // We need to rebind all events otherwise our spy won't be called
  255. chatview.delegateEvents();
  256. chatview.$el.find('.toggle-chatbox-button').click();
  257. expect(chatview.minimize).toHaveBeenCalled();
  258. expect(_converse.emit).toHaveBeenCalledWith('chatBoxMinimized', jasmine.any(Object));
  259. expect(_converse.emit.calls.count(), 2);
  260. expect(chatview.$el.is(':visible')).toBeFalsy();
  261. expect(chatview.model.get('minimized')).toBeTruthy();
  262. chatview.$el.find('.toggle-chatbox-button').click();
  263. trimmedview = trimmed_chatboxes.get(chatview.model.get('id'));
  264. spyOn(trimmedview, 'restore').and.callThrough();
  265. trimmedview.delegateEvents();
  266. trimmedview.$("a.restore-chat").click();
  267. expect(trimmedview.restore).toHaveBeenCalled();
  268. expect(_converse.emit).toHaveBeenCalledWith('chatBoxMaximized', jasmine.any(Object));
  269. return test_utils.waitUntil(function () {
  270. return chatview.$el.find('.chat-body').is(':visible');
  271. }, 500);
  272. }).then(function () {
  273. expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeTruthy();
  274. expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeFalsy();
  275. expect(chatview.model.get('minimized')).toBeFalsy();
  276. done();
  277. });
  278. }));
  279. it("will be removed from browserStorage when closed",
  280. mock.initConverseWithPromises(
  281. null, ['rosterGroupsFetched'], {},
  282. function (done, _converse) {
  283. test_utils.createContacts(_converse, 'current');
  284. test_utils.openControlBox();
  285. test_utils.openContactsPanel(_converse);
  286. test_utils.waitUntil(function () {
  287. return _converse.rosterview.$el.find('.roster-group').length;
  288. }, 300)
  289. .then(function () {
  290. spyOn(_converse, 'emit');
  291. spyOn(_converse.chatboxviews, 'trimChats');
  292. _converse.chatboxes.browserStorage._clear();
  293. test_utils.closeControlBox();
  294. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  295. expect(_converse.chatboxes.length).toEqual(1);
  296. expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
  297. test_utils.openChatBoxes(_converse, 6);
  298. expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
  299. expect(_converse.chatboxes.length).toEqual(7);
  300. expect(_converse.emit).toHaveBeenCalledWith('chatBoxOpened', jasmine.any(Object));
  301. test_utils.closeAllChatBoxes(_converse);
  302. expect(_converse.chatboxes.length).toEqual(1);
  303. expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
  304. expect(_converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
  305. var newchatboxes = new _converse.ChatBoxes();
  306. expect(newchatboxes.length).toEqual(0);
  307. expect(_converse.chatboxes.pluck('id')).toEqual(['controlbox']);
  308. // onConnected will fetch chatboxes in browserStorage, but
  309. // because there aren't any open chatboxes, there won't be any
  310. // in browserStorage either. XXX except for the controlbox
  311. newchatboxes.onConnected();
  312. expect(newchatboxes.length).toEqual(1);
  313. expect(newchatboxes.models[0].id).toBe("controlbox");
  314. done();
  315. });
  316. }));
  317. describe("A chat toolbar", function () {
  318. it("can be found on each chat box",
  319. mock.initConverseWithPromises(
  320. null, ['rosterGroupsFetched'], {},
  321. function (done, _converse) {
  322. test_utils.createContacts(_converse, 'current');
  323. test_utils.openControlBox();
  324. test_utils.openContactsPanel(_converse);
  325. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  326. test_utils.openChatBoxFor(_converse, contact_jid);
  327. var chatbox = _converse.chatboxes.get(contact_jid);
  328. var view = _converse.chatboxviews.get(contact_jid);
  329. expect(chatbox).toBeDefined();
  330. expect(view).toBeDefined();
  331. var $toolbar = view.$el.find('ul.chat-toolbar');
  332. expect($toolbar.length).toBe(1);
  333. expect($toolbar.children('li').length).toBe(3);
  334. done();
  335. }));
  336. it("contains a button for inserting emojis",
  337. mock.initConverseWithPromises(
  338. null, ['rosterGroupsFetched'], {},
  339. function (done, _converse) {
  340. test_utils.createContacts(_converse, 'current');
  341. test_utils.openControlBox();
  342. test_utils.openContactsPanel(_converse);
  343. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  344. test_utils.openChatBoxFor(_converse, contact_jid);
  345. var view = _converse.chatboxviews.get(contact_jid);
  346. var toolbar = view.el.querySelector('ul.chat-toolbar');
  347. expect(toolbar.querySelectorAll('li.toggle-smiley').length).toBe(1);
  348. // Register spies
  349. spyOn(view, 'toggleEmojiMenu').and.callThrough();
  350. spyOn(view, 'insertEmoji').and.callThrough();
  351. view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
  352. toolbar.querySelector('li.toggle-smiley').click();
  353. test_utils.waitUntil(function () {
  354. return utils.isVisible(view.el.querySelector('.toggle-smiley .emoji-picker-container'));
  355. }, 150).then(function () {
  356. var picker = view.el.querySelector('.toggle-smiley .emoji-picker-container');
  357. var items = picker.querySelectorAll('.emoji-picker li');
  358. items[0].click()
  359. expect(view.insertEmoji).toHaveBeenCalled();
  360. toolbar.querySelector('li.toggle-smiley').click(); // Close the panel again
  361. return test_utils.waitUntil(function () {
  362. return !view.el.querySelector('.toggle-smiley .toolbar-menu').offsetHeight;
  363. }, 300);
  364. }).then(function () {
  365. toolbar.querySelector('li.toggle-smiley').click();
  366. expect(view.toggleEmojiMenu).toHaveBeenCalled();
  367. return test_utils.waitUntil(function () {
  368. var $picker = view.$el.find('.toggle-smiley .emoji-picker-container');
  369. return $picker.is(':visible');
  370. }, 300);
  371. }).then(function () {
  372. var nodes = view.el.querySelectorAll('.toggle-smiley ul li');
  373. nodes[nodes.length-1].click();
  374. expect(view.el.querySelector('textarea.chat-textarea').value).toBe(':grinning: ');
  375. expect(view.insertEmoji).toHaveBeenCalled();
  376. done();
  377. }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
  378. }));
  379. it("contains a button for starting an encrypted chat session",
  380. mock.initConverseWithPromises(
  381. null, ['rosterGroupsFetched'], {},
  382. function (done, _converse) {
  383. test_utils.createContacts(_converse, 'current');
  384. test_utils.openControlBox();
  385. test_utils.openContactsPanel(_converse);
  386. test_utils.waitUntil(function () {
  387. return _converse.rosterview.$el.find('.roster-group').length;
  388. }, 300)
  389. .then(function () {
  390. // TODO: More tests can be added here...
  391. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  392. test_utils.openChatBoxFor(_converse, contact_jid);
  393. var view = _converse.chatboxviews.get(contact_jid);
  394. var $toolbar = view.$el.find('ul.chat-toolbar');
  395. expect($toolbar.children('li.toggle-otr').length).toBe(1);
  396. // Register spies
  397. spyOn(view, 'toggleOTRMenu').and.callThrough();
  398. view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
  399. $toolbar.children('li.toggle-otr').click();
  400. expect(view.toggleOTRMenu).toHaveBeenCalled();
  401. done();
  402. });
  403. }));
  404. it("can contain a button for starting a call",
  405. mock.initConverseWithPromises(
  406. null, ['rosterGroupsFetched'], {},
  407. function (done, _converse) {
  408. test_utils.createContacts(_converse, 'current');
  409. test_utils.openControlBox();
  410. test_utils.openContactsPanel(_converse);
  411. var view, callButton, $toolbar;
  412. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  413. spyOn(_converse, 'emit');
  414. // First check that the button doesn't show if it's not enabled
  415. // via "visible_toolbar_buttons"
  416. _converse.visible_toolbar_buttons.call = false;
  417. test_utils.openChatBoxFor(_converse, contact_jid);
  418. view = _converse.chatboxviews.get(contact_jid);
  419. $toolbar = view.$el.find('ul.chat-toolbar');
  420. callButton = $toolbar.find('.toggle-call');
  421. expect(callButton.length).toBe(0);
  422. view.close();
  423. // Now check that it's shown if enabled and that it emits
  424. // callButtonClicked
  425. _converse.visible_toolbar_buttons.call = true; // enable the button
  426. test_utils.openChatBoxFor(_converse, contact_jid);
  427. view = _converse.chatboxviews.get(contact_jid);
  428. $toolbar = view.$el.find('ul.chat-toolbar');
  429. callButton = $toolbar.find('.toggle-call');
  430. expect(callButton.length).toBe(1);
  431. callButton.click();
  432. expect(_converse.emit).toHaveBeenCalledWith('callButtonClicked', jasmine.any(Object));
  433. done();
  434. }));
  435. it("can contain a button for clearing messages",
  436. mock.initConverseWithPromises(
  437. null, ['rosterGroupsFetched'], {},
  438. function (done, _converse) {
  439. test_utils.createContacts(_converse, 'current');
  440. test_utils.openControlBox();
  441. test_utils.openContactsPanel(_converse);
  442. var view, clearButton, $toolbar;
  443. var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  444. // First check that the button doesn't show if it's not enabled
  445. // via "visible_toolbar_buttons"
  446. _converse.visible_toolbar_buttons.clear = false;
  447. test_utils.openChatBoxFor(_converse, contact_jid);
  448. view = _converse.chatboxviews.get(contact_jid);
  449. view = _converse.chatboxviews.get(contact_jid);
  450. $toolbar = view.$el.find('ul.chat-toolbar');
  451. clearButton = $toolbar.find('.toggle-clear');
  452. expect(clearButton.length).toBe(0);
  453. view.close();
  454. // Now check that it's shown if enabled and that it calls
  455. // clearMessages
  456. _converse.visible_toolbar_buttons.clear = true; // enable the button
  457. test_utils.openChatBoxFor(_converse, contact_jid);
  458. view = _converse.chatboxviews.get(contact_jid);
  459. $toolbar = view.$el.find('ul.chat-toolbar');
  460. clearButton = $toolbar.find('.toggle-clear');
  461. expect(clearButton.length).toBe(1);
  462. spyOn(view, 'clearMessages');
  463. view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
  464. clearButton.click();
  465. expect(view.clearMessages).toHaveBeenCalled();
  466. done();
  467. }));
  468. });
  469. describe("A Chat Message", function () {
  470. describe("when received from someone else", function () {
  471. it("will open a chatbox and be displayed inside it",
  472. mock.initConverseWithPromises(
  473. null, ['rosterGroupsFetched'], {},
  474. function (done, _converse) {
  475. test_utils.createContacts(_converse, 'current');
  476. test_utils.openControlBox();
  477. test_utils.openContactsPanel(_converse);
  478. test_utils.waitUntil(function () {
  479. return _converse.rosterview.$el.find('.roster-group').length;
  480. }, 300)
  481. .then(function () {
  482. spyOn(_converse, 'emit');
  483. var message = 'This is a received message';
  484. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  485. var msg = $msg({
  486. from: sender_jid,
  487. to: _converse.connection.jid,
  488. type: 'chat',
  489. id: (new Date()).getTime()
  490. }).c('body').t(message).up()
  491. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  492. // We don't already have an open chatbox for this user
  493. expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
  494. // onMessage is a handler for received XMPP messages
  495. _converse.chatboxes.onMessage(msg);
  496. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  497. // Check that the chatbox and its view now exist
  498. var chatbox = _converse.chatboxes.get(sender_jid);
  499. var chatboxview = _converse.chatboxviews.get(sender_jid);
  500. expect(chatbox).toBeDefined();
  501. expect(chatboxview).toBeDefined();
  502. // Check that the message was received and check the message parameters
  503. expect(chatbox.messages.length).toEqual(1);
  504. var msg_obj = chatbox.messages.models[0];
  505. expect(msg_obj.get('message')).toEqual(message);
  506. expect(msg_obj.get('fullname')).toEqual(mock.cur_names[0]);
  507. expect(msg_obj.get('sender')).toEqual('them');
  508. expect(msg_obj.get('delayed')).toEqual(false);
  509. // Now check that the message appears inside the chatbox in the DOM
  510. var $chat_content = chatboxview.$el.find('.chat-content');
  511. var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
  512. expect(msg_txt).toEqual(message);
  513. var sender_txt = $chat_content.find('span.chat-msg-them').text();
  514. expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
  515. done();
  516. });
  517. }));
  518. describe("when a chatbox is opened for someone who is not in the roster", function () {
  519. it("the VCard for that user is fetched and the chatbox updated with the results",
  520. mock.initConverseWithPromises(
  521. null, ['rosterGroupsFetched'], {},
  522. function (done, _converse) {
  523. _converse.allow_non_roster_messaging = true;
  524. spyOn(_converse, 'emit').and.callThrough();
  525. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  526. var vcard_fetched = false;
  527. spyOn(_converse.api.vcard, "get").and.callFake(function () {
  528. vcard_fetched = true;
  529. return Promise.resolve({
  530. 'fullname': mock.cur_names[0],
  531. 'vcard_updated': moment().format(),
  532. 'jid': sender_jid
  533. });
  534. });
  535. var message = 'This is a received message from someone not on the roster';
  536. var msg = $msg({
  537. from: sender_jid,
  538. to: _converse.connection.jid,
  539. type: 'chat',
  540. id: (new Date()).getTime()
  541. }).c('body').t(message).up()
  542. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  543. // We don't already have an open chatbox for this user
  544. expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
  545. _converse.chatboxes.onMessage(msg);
  546. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  547. // Check that the chatbox and its view now exist
  548. var chatbox = _converse.chatboxes.get(sender_jid);
  549. var chatboxview = _converse.chatboxviews.get(sender_jid);
  550. expect(chatbox).toBeDefined();
  551. expect(chatboxview).toBeDefined();
  552. // XXX: I don't really like the convention of
  553. // setting "fullname" to the JID if there's
  554. // no fullname. Should ideally be null if
  555. // there's no fullname.
  556. expect(chatbox.get('fullname') === sender_jid);
  557. test_utils.waitUntil(function () { return vcard_fetched; }, 100)
  558. .then(function () {
  559. expect(_converse.api.vcard.get).toHaveBeenCalled();
  560. return test_utils.waitUntil(function () {
  561. return chatbox.get('fullname') === mock.cur_names[0];
  562. }, 100);
  563. }).then(function () {
  564. done();
  565. });
  566. }));
  567. });
  568. describe("who is not on the roster", function () {
  569. it("will open a chatbox and be displayed inside it if allow_non_roster_messaging is true",
  570. mock.initConverseWithPromises(
  571. null, ['rosterGroupsFetched'], {},
  572. function (done, _converse) {
  573. _converse.allow_non_roster_messaging = false;
  574. spyOn(_converse, 'emit');
  575. var message = 'This is a received message from someone not on the roster';
  576. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  577. var msg = $msg({
  578. from: sender_jid,
  579. to: _converse.connection.jid,
  580. type: 'chat',
  581. id: (new Date()).getTime()
  582. }).c('body').t(message).up()
  583. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  584. // We don't already have an open chatbox for this user
  585. expect(_converse.chatboxes.get(sender_jid)).not.toBeDefined();
  586. // onMessage is a handler for received XMPP messages
  587. _converse.chatboxes.onMessage(msg);
  588. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  589. var chatbox = _converse.chatboxes.get(sender_jid);
  590. expect(chatbox).not.toBeDefined();
  591. // onMessage is a handler for received XMPP messages
  592. _converse.allow_non_roster_messaging =true;
  593. _converse.chatboxes.onMessage(msg);
  594. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  595. // Check that the chatbox and its view now exist
  596. chatbox = _converse.chatboxes.get(sender_jid);
  597. var chatboxview = _converse.chatboxviews.get(sender_jid);
  598. expect(chatbox).toBeDefined();
  599. expect(chatboxview).toBeDefined();
  600. // Check that the message was received and check the message parameters
  601. expect(chatbox.messages.length).toEqual(1);
  602. var msg_obj = chatbox.messages.models[0];
  603. expect(msg_obj.get('message')).toEqual(message);
  604. expect(msg_obj.get('fullname')).toEqual(sender_jid);
  605. expect(msg_obj.get('sender')).toEqual('them');
  606. expect(msg_obj.get('delayed')).toEqual(false);
  607. // Now check that the message appears inside the chatbox in the DOM
  608. var $chat_content = chatboxview.$el.find('.chat-content');
  609. var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
  610. expect(msg_txt).toEqual(message);
  611. var sender_txt = $chat_content.find('span.chat-msg-them').text();
  612. expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
  613. done();
  614. }));
  615. });
  616. describe("and for which then an error message is received from the server", function () {
  617. it("will have the error message displayed after itself",
  618. mock.initConverseWithPromises(
  619. null, ['rosterGroupsFetched'], {},
  620. function (done, _converse) {
  621. test_utils.createContacts(_converse, 'current');
  622. test_utils.openControlBox();
  623. test_utils.openContactsPanel(_converse);
  624. // TODO: what could still be done for error
  625. // messages... if the <error> element has type
  626. // "cancel", then we know the messages wasn't sent,
  627. // and can give the user a nicer indication of
  628. // that.
  629. /* <message from="scotty@enterprise.com/_converse.js-84843526"
  630. * to="kirk@enterprise.com.com"
  631. * type="chat"
  632. * id="82bc02ce-9651-4336-baf0-fa04762ed8d2"
  633. * xmlns="jabber:client">
  634. * <body>yo</body>
  635. * <active xmlns="http://jabber.org/protocol/chatstates"/>
  636. * </message>
  637. */
  638. var sender_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  639. var fullname = _converse.xmppstatus.get('fullname');
  640. fullname = _.isEmpty(fullname)? _converse.bare_jid: fullname;
  641. _converse.api.chats.open(sender_jid);
  642. var msg_text = 'This message will not be sent, due to an error';
  643. var view = _converse.chatboxviews.get(sender_jid);
  644. var message = view.model.messages.create({
  645. 'msgid': '82bc02ce-9651-4336-baf0-fa04762ed8d2',
  646. 'fullname': fullname,
  647. 'sender': 'me',
  648. 'time': moment().format(),
  649. 'message': msg_text
  650. });
  651. view.sendMessage(message);
  652. var $chat_content = view.$el.find('.chat-content');
  653. var msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text();
  654. expect(msg_txt).toEqual(msg_text);
  655. // We send another message, for which an error will
  656. // not be received, to test that errors appear
  657. // after the relevant message.
  658. msg_text = 'This message will be sent, and not receive an error';
  659. message = view.model.messages.create({
  660. 'msgid': '6fcdeee3-000f-4ce8-a17e-9ce28f0ae104',
  661. 'fullname': fullname,
  662. 'sender': 'me',
  663. 'time': moment().format(),
  664. 'message': msg_text
  665. });
  666. view.sendMessage(message);
  667. msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text();
  668. expect(msg_txt).toEqual(msg_text);
  669. /* <message xmlns="jabber:client"
  670. * to="scotty@enterprise.com/_converse.js-84843526"
  671. * type="error"
  672. * id="82bc02ce-9651-4336-baf0-fa04762ed8d2"
  673. * from="kirk@enterprise.com.com">
  674. * <error type="cancel">
  675. * <remote-server-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
  676. * <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Server-to-server connection failed: Connecting failed: connection timeout</text>
  677. * </error>
  678. * </message>
  679. */
  680. var error_txt = 'Server-to-server connection failed: Connecting failed: connection timeout';
  681. var stanza = $msg({
  682. 'to': _converse.connection.jid,
  683. 'type':'error',
  684. 'id':'82bc02ce-9651-4336-baf0-fa04762ed8d2',
  685. 'from': sender_jid
  686. })
  687. .c('error', {'type': 'cancel'})
  688. .c('remote-server-not-found', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" }).up()
  689. .c('text', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" })
  690. .t('Server-to-server connection failed: Connecting failed: connection timeout');
  691. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  692. expect($chat_content.find('.chat-error').text()).toEqual(error_txt);
  693. /* Incoming error messages that are not tied to a
  694. * certain show message (via the msgid attribute),
  695. * are not shown at all. The reason for this is
  696. * that we may get error messages for chat state
  697. * notifications as well.
  698. */
  699. stanza = $msg({
  700. 'to': _converse.connection.jid,
  701. 'type':'error',
  702. 'id':'some-other-unused-id',
  703. 'from': sender_jid
  704. })
  705. .c('error', {'type': 'cancel'})
  706. .c('remote-server-not-found', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" }).up()
  707. .c('text', { 'xmlns': "urn:ietf:params:xml:ns:xmpp-stanzas" })
  708. .t('Server-to-server connection failed: Connecting failed: connection timeout');
  709. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  710. expect($chat_content.find('.chat-error').length).toEqual(1);
  711. done();
  712. }));
  713. });
  714. it("will cause the chat area to be scrolled down only if it was at the bottom originally",
  715. mock.initConverseWithPromises(
  716. null, ['rosterGroupsFetched'], {},
  717. function (done, _converse) {
  718. test_utils.createContacts(_converse, 'current');
  719. test_utils.openControlBox();
  720. test_utils.openContactsPanel(_converse);
  721. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  722. test_utils.openChatBoxFor(_converse, sender_jid);
  723. var chatboxview = _converse.chatboxviews.get(sender_jid);
  724. spyOn(chatboxview, 'scrollDown').and.callThrough();
  725. // Create enough messages so that there's a scrollbar.
  726. var message = 'This message is received while the chat area is scrolled up';
  727. for (var i=0; i<20; i++) {
  728. _converse.chatboxes.onMessage($msg({
  729. from: sender_jid,
  730. to: _converse.connection.jid,
  731. type: 'chat',
  732. id: (new Date()).getTime()
  733. }).c('body').t('Message: '+i).up()
  734. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
  735. }
  736. test_utils.waitUntil(function () {
  737. return chatboxview.$content.scrollTop();
  738. }, 1000)
  739. .then(function () {
  740. return test_utils.waitUntil(function () {
  741. return !chatboxview.model.get('auto_scrolled');
  742. }, 500);
  743. }).then(function () {
  744. chatboxview.$content.scrollTop(0);
  745. return test_utils.waitUntil(function () {
  746. return chatboxview.model.get('scrolled');
  747. }, 900);
  748. }).then(function () {
  749. _converse.chatboxes.onMessage($msg({
  750. from: sender_jid,
  751. to: _converse.connection.jid,
  752. type: 'chat',
  753. id: (new Date()).getTime()
  754. }).c('body').t(message).up()
  755. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
  756. // Now check that the message appears inside the chatbox in the DOM
  757. var $chat_content = chatboxview.$el.find('.chat-content');
  758. var msg_txt = $chat_content.find('.chat-message:last').find('.chat-msg-content').text();
  759. expect(msg_txt).toEqual(message);
  760. return test_utils.waitUntil(function () {
  761. return chatboxview.$('.new-msgs-indicator').is(':visible');
  762. }, 500);
  763. }).then(function () {
  764. expect(chatboxview.model.get('scrolled')).toBe(true);
  765. expect(chatboxview.$content.scrollTop()).toBe(0);
  766. expect(chatboxview.$('.new-msgs-indicator').is(':visible')).toBeTruthy();
  767. // Scroll down again
  768. chatboxview.$content.scrollTop(chatboxview.$content[0].scrollHeight);
  769. return test_utils.waitUntil(function () {
  770. return !chatboxview.$('.new-msgs-indicator').is(':visible');
  771. }, 500);
  772. }).then(done);
  773. }));
  774. it("is ignored if it's intended for a different resource and filter_by_resource is set to true",
  775. mock.initConverseWithPromises(
  776. null, ['rosterGroupsFetched'], {},
  777. function (done, _converse) {
  778. test_utils.createContacts(_converse, 'current');
  779. test_utils.openControlBox();
  780. test_utils.openContactsPanel(_converse);
  781. test_utils.waitUntil(function () {
  782. return _converse.rosterview.$el.find('.roster-group').length;
  783. }, 300)
  784. .then(function () {
  785. // Send a message from a different resource
  786. var message, sender_jid, msg;
  787. spyOn(_converse, 'log');
  788. spyOn(_converse.chatboxes, 'getChatBox').and.callThrough();
  789. _converse.filter_by_resource = true;
  790. sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  791. msg = $msg({
  792. from: sender_jid,
  793. to: _converse.bare_jid+"/some-other-resource",
  794. type: 'chat',
  795. id: (new Date()).getTime()
  796. }).c('body').t("This message will not be shown").up()
  797. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  798. _converse.chatboxes.onMessage(msg);
  799. expect(_converse.log).toHaveBeenCalledWith(
  800. "onMessage: Ignoring incoming message intended for a different resource: dummy@localhost/some-other-resource",
  801. Strophe.LogLevel.INFO);
  802. expect(_converse.chatboxes.getChatBox).not.toHaveBeenCalled();
  803. _converse.filter_by_resource = false;
  804. message = "This message sent to a different resource will be shown";
  805. msg = $msg({
  806. from: sender_jid,
  807. to: _converse.bare_jid+"/some-other-resource",
  808. type: 'chat',
  809. id: '134234623462346'
  810. }).c('body').t(message).up()
  811. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  812. _converse.chatboxes.onMessage(msg);
  813. expect(_converse.chatboxes.getChatBox).toHaveBeenCalled();
  814. var chatboxview = _converse.chatboxviews.get(sender_jid);
  815. var $chat_content = chatboxview.$el.find('.chat-content:last');
  816. var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
  817. expect(msg_txt).toEqual(message);
  818. done();
  819. });
  820. }));
  821. });
  822. it("is ignored if it's a malformed headline message",
  823. mock.initConverseWithPromises(
  824. null, ['rosterGroupsFetched'], {},
  825. function (done, _converse) {
  826. test_utils.createContacts(_converse, 'current');
  827. test_utils.openControlBox();
  828. test_utils.openContactsPanel(_converse);
  829. /* Ideally we wouldn't have to filter out headline
  830. * messages, but Prosody gives them the wrong 'type' :(
  831. */
  832. sinon.spy(_converse, 'log');
  833. sinon.spy(_converse.chatboxes, 'getChatBox');
  834. sinon.spy(utils, 'isHeadlineMessage');
  835. var msg = $msg({
  836. from: 'localhost',
  837. to: _converse.bare_jid,
  838. type: 'chat',
  839. id: (new Date()).getTime()
  840. }).c('body').t("This headline message will not be shown").tree();
  841. _converse.chatboxes.onMessage(msg);
  842. expect(_converse.log.calledWith(
  843. "onMessage: Ignoring incoming headline message sent with type 'chat' from JID: localhost",
  844. Strophe.LogLevel.INFO
  845. )).toBeTruthy();
  846. expect(utils.isHeadlineMessage.called).toBeTruthy();
  847. expect(utils.isHeadlineMessage.returned(true)).toBeTruthy();
  848. expect(_converse.chatboxes.getChatBox.called).toBeFalsy();
  849. // Remove sinon spies
  850. _converse.log.restore();
  851. _converse.chatboxes.getChatBox.restore();
  852. utils.isHeadlineMessage.restore();
  853. done();
  854. }));
  855. it("can be a carbon message, as defined in XEP-0280",
  856. mock.initConverseWithPromises(
  857. null, ['rosterGroupsFetched'], {},
  858. function (done, _converse) {
  859. test_utils.createContacts(_converse, 'current');
  860. test_utils.openControlBox();
  861. test_utils.openContactsPanel(_converse);
  862. // Send a message from a different resource
  863. spyOn(_converse, 'log');
  864. var msgtext = 'This is a carbon message';
  865. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  866. var msg = $msg({
  867. 'from': sender_jid,
  868. 'id': (new Date()).getTime(),
  869. 'to': _converse.connection.jid,
  870. 'type': 'chat',
  871. 'xmlns': 'jabber:client'
  872. }).c('received', {'xmlns': 'urn:xmpp:carbons:2'})
  873. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  874. .c('message', {
  875. 'xmlns': 'jabber:client',
  876. 'from': sender_jid,
  877. 'to': _converse.bare_jid+'/another-resource',
  878. 'type': 'chat'
  879. }).c('body').t(msgtext).tree();
  880. _converse.chatboxes.onMessage(msg);
  881. // Check that the chatbox and its view now exist
  882. var chatbox = _converse.chatboxes.get(sender_jid);
  883. var chatboxview = _converse.chatboxviews.get(sender_jid);
  884. expect(chatbox).toBeDefined();
  885. expect(chatboxview).toBeDefined();
  886. // Check that the message was received and check the message parameters
  887. expect(chatbox.messages.length).toEqual(1);
  888. var msg_obj = chatbox.messages.models[0];
  889. expect(msg_obj.get('message')).toEqual(msgtext);
  890. expect(msg_obj.get('fullname')).toEqual(mock.cur_names[1]);
  891. expect(msg_obj.get('sender')).toEqual('them');
  892. expect(msg_obj.get('delayed')).toEqual(false);
  893. // Now check that the message appears inside the chatbox in the DOM
  894. var $chat_content = chatboxview.$el.find('.chat-content');
  895. var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
  896. expect(msg_txt).toEqual(msgtext);
  897. var sender_txt = $chat_content.find('span.chat-msg-them').text();
  898. expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
  899. done();
  900. }));
  901. it("can be a carbon message that this user sent from a different client, as defined in XEP-0280",
  902. mock.initConverseWithPromises(
  903. null, ['rosterGroupsFetched'], {},
  904. function (done, _converse) {
  905. var contact, sent_stanza, IQ_id, stanza;
  906. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
  907. .then(function () {
  908. return test_utils.waitUntil(function () {
  909. return _converse.xmppstatus.get('fullname');
  910. }, 300);
  911. }).then(function () {
  912. test_utils.createContacts(_converse, 'current');
  913. test_utils.openControlBox();
  914. test_utils.openContactsPanel(_converse);
  915. // Send a message from a different resource
  916. spyOn(_converse, 'log');
  917. var msgtext = 'This is a sent carbon message';
  918. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  919. var msg = $msg({
  920. 'from': _converse.bare_jid,
  921. 'id': (new Date()).getTime(),
  922. 'to': _converse.connection.jid,
  923. 'type': 'chat',
  924. 'xmlns': 'jabber:client'
  925. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  926. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  927. .c('message', {
  928. 'xmlns': 'jabber:client',
  929. 'from': _converse.bare_jid+'/another-resource',
  930. 'to': recipient_jid,
  931. 'type': 'chat'
  932. }).c('body').t(msgtext).tree();
  933. _converse.chatboxes.onMessage(msg);
  934. // Check that the chatbox and its view now exist
  935. var chatbox = _converse.chatboxes.get(recipient_jid);
  936. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  937. expect(chatbox).toBeDefined();
  938. expect(chatboxview).toBeDefined();
  939. // Check that the message was received and check the message parameters
  940. expect(chatbox.messages.length).toEqual(1);
  941. var msg_obj = chatbox.messages.models[0];
  942. expect(msg_obj.get('message')).toEqual(msgtext);
  943. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  944. expect(msg_obj.get('sender')).toEqual('me');
  945. expect(msg_obj.get('delayed')).toEqual(false);
  946. // Now check that the message appears inside the chatbox in the DOM
  947. var $chat_content = chatboxview.$el.find('.chat-content');
  948. var msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
  949. expect(msg_txt).toEqual(msgtext);
  950. done();
  951. });
  952. }));
  953. it("will be discarded if it's a malicious message meant to look like a carbon copy",
  954. mock.initConverseWithPromises(
  955. null, ['rosterGroupsFetched'], {},
  956. function (done, _converse) {
  957. test_utils.createContacts(_converse, 'current');
  958. test_utils.openControlBox();
  959. test_utils.openContactsPanel(_converse);
  960. /* <message from="mallory@evil.example" to="b@xmpp.example">
  961. * <received xmlns='urn:xmpp:carbons:2'>
  962. * <forwarded xmlns='urn:xmpp:forward:0'>
  963. * <message from="alice@xmpp.example" to="bob@xmpp.example/client1">
  964. * <body>Please come to Creepy Valley tonight, alone!</body>
  965. * </message>
  966. * </forwarded>
  967. * </received>
  968. * </message>
  969. */
  970. spyOn(_converse, 'log');
  971. var msgtext = 'Please come to Creepy Valley tonight, alone!';
  972. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  973. var impersonated_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
  974. var msg = $msg({
  975. 'from': sender_jid,
  976. 'id': (new Date()).getTime(),
  977. 'to': _converse.connection.jid,
  978. 'type': 'chat',
  979. 'xmlns': 'jabber:client'
  980. }).c('received', {'xmlns': 'urn:xmpp:carbons:2'})
  981. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  982. .c('message', {
  983. 'xmlns': 'jabber:client',
  984. 'from': impersonated_jid,
  985. 'to': _converse.connection.jid,
  986. 'type': 'chat'
  987. }).c('body').t(msgtext).tree();
  988. _converse.chatboxes.onMessage(msg);
  989. // Check that chatbox for impersonated user is not created.
  990. var chatbox = _converse.chatboxes.get(impersonated_jid);
  991. expect(chatbox).not.toBeDefined();
  992. // Check that the chatbox for the malicous user is not created
  993. chatbox = _converse.chatboxes.get(sender_jid);
  994. expect(chatbox).not.toBeDefined();
  995. done();
  996. }));
  997. it("received for a minimized chat box will increment a counter on its header",
  998. mock.initConverseWithPromises(
  999. null, ['rosterGroupsFetched'], {},
  1000. function (done, _converse) {
  1001. test_utils.createContacts(_converse, 'current');
  1002. test_utils.openControlBox();
  1003. test_utils.openContactsPanel(_converse);
  1004. test_utils.waitUntil(function () {
  1005. return _converse.rosterview.$el.find('.roster-group').length;
  1006. }, 300)
  1007. .then(function () {
  1008. var contact_name = mock.cur_names[0];
  1009. var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost';
  1010. spyOn(_converse, 'emit').and.callThrough();
  1011. test_utils.openChatBoxFor(_converse, contact_jid);
  1012. var chatview = _converse.chatboxviews.get(contact_jid);
  1013. expect(chatview.$el.is(':visible')).toBeTruthy();
  1014. expect(chatview.model.get('minimized')).toBeFalsy();
  1015. chatview.$el.find('.toggle-chatbox-button').click();
  1016. expect(chatview.model.get('minimized')).toBeTruthy();
  1017. var message = 'This message is sent to a minimized chatbox';
  1018. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1019. var msg = $msg({
  1020. from: sender_jid,
  1021. to: _converse.connection.jid,
  1022. type: 'chat',
  1023. id: (new Date()).getTime()
  1024. }).c('body').t(message).up()
  1025. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  1026. _converse.chatboxes.onMessage(msg);
  1027. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1028. var trimmed_chatboxes = _converse.minimized_chats;
  1029. var trimmedview = trimmed_chatboxes.get(contact_jid);
  1030. var $count = trimmedview.$el.find('.chat-head-message-count');
  1031. expect(chatview.$el.is(':visible')).toBeFalsy();
  1032. expect(trimmedview.model.get('minimized')).toBeTruthy();
  1033. expect($count.is(':visible')).toBeTruthy();
  1034. expect($count.html()).toBe('1');
  1035. _converse.chatboxes.onMessage(
  1036. $msg({
  1037. from: mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1038. to: _converse.connection.jid,
  1039. type: 'chat',
  1040. id: (new Date()).getTime()
  1041. }).c('body').t('This message is also sent to a minimized chatbox').up()
  1042. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
  1043. );
  1044. expect(chatview.$el.is(':visible')).toBeFalsy();
  1045. expect(trimmedview.model.get('minimized')).toBeTruthy();
  1046. $count = trimmedview.$el.find('.chat-head-message-count');
  1047. expect($count.is(':visible')).toBeTruthy();
  1048. expect($count.html()).toBe('2');
  1049. trimmedview.$el.find('.restore-chat').click();
  1050. expect(trimmed_chatboxes.keys().length).toBe(0);
  1051. done();
  1052. });
  1053. }));
  1054. it("will indicate when it has a time difference of more than a day between it and its predecessor",
  1055. mock.initConverseWithPromises(
  1056. null, ['rosterGroupsFetched'], {},
  1057. function (done, _converse) {
  1058. test_utils.createContacts(_converse, 'current');
  1059. test_utils.openControlBox();
  1060. test_utils.openContactsPanel(_converse);
  1061. test_utils.waitUntil(function () {
  1062. return _converse.rosterview.$el.find('.roster-group').length;
  1063. }, 300)
  1064. .then(function () {
  1065. spyOn(_converse, 'emit');
  1066. var contact_name = mock.cur_names[1];
  1067. var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost';
  1068. test_utils.openChatBoxFor(_converse, contact_jid);
  1069. test_utils.clearChatBoxMessages(_converse, contact_jid);
  1070. var one_day_ago = moment();
  1071. one_day_ago.subtract('days', 1);
  1072. var message = 'This is a day old message';
  1073. var chatbox = _converse.chatboxes.get(contact_jid);
  1074. var chatboxview = _converse.chatboxviews.get(contact_jid);
  1075. var $chat_content = chatboxview.$el.find('.chat-content');
  1076. var msg_obj;
  1077. var msg_txt;
  1078. var sender_txt;
  1079. var msg = $msg({
  1080. from: contact_jid,
  1081. to: _converse.connection.jid,
  1082. type: 'chat',
  1083. id: one_day_ago.unix()
  1084. }).c('body').t(message).up()
  1085. .c('delay', { xmlns:'urn:xmpp:delay', from: 'localhost', stamp: one_day_ago.format() })
  1086. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  1087. _converse.chatboxes.onMessage(msg);
  1088. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1089. expect(chatbox.messages.length).toEqual(1);
  1090. msg_obj = chatbox.messages.models[0];
  1091. expect(msg_obj.get('message')).toEqual(message);
  1092. expect(msg_obj.get('fullname')).toEqual(contact_name);
  1093. expect(msg_obj.get('sender')).toEqual('them');
  1094. expect(msg_obj.get('delayed')).toEqual(true);
  1095. msg_txt = $chat_content.find('.chat-message').find('.chat-msg-content').text();
  1096. expect(msg_txt).toEqual(message);
  1097. sender_txt = $chat_content.find('span.chat-msg-them').text();
  1098. expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
  1099. var $time = $chat_content.find('time');
  1100. expect($time.length).toEqual(1);
  1101. expect($time.attr('class')).toEqual('chat-info chat-date');
  1102. expect($time.data('isodate')).toEqual(moment(one_day_ago.startOf('day')).format());
  1103. expect($time.text()).toEqual(moment(one_day_ago.startOf('day')).format("dddd MMM Do YYYY"));
  1104. message = 'This is a current message';
  1105. msg = $msg({
  1106. from: contact_jid,
  1107. to: _converse.connection.jid,
  1108. type: 'chat',
  1109. id: new Date().getTime()
  1110. }).c('body').t(message).up()
  1111. .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
  1112. _converse.chatboxes.onMessage(msg);
  1113. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1114. // Check that there is a <time> element, with the required
  1115. // props.
  1116. $time = $chat_content.find('time');
  1117. expect($time.length).toEqual(2); // There are now two time elements
  1118. $time = $chat_content.find('time:last'); // We check the last one
  1119. var message_date = new Date();
  1120. expect($time.attr('class')).toEqual('chat-info chat-date');
  1121. expect($time.data('isodate')).toEqual(moment(message_date).startOf('day').format());
  1122. expect($time.text()).toEqual(moment(message_date).startOf('day').format("dddd MMM Do YYYY"));
  1123. // Normal checks for the 2nd message
  1124. expect(chatbox.messages.length).toEqual(2);
  1125. msg_obj = chatbox.messages.models[1];
  1126. expect(msg_obj.get('message')).toEqual(message);
  1127. expect(msg_obj.get('fullname')).toEqual(contact_name);
  1128. expect(msg_obj.get('sender')).toEqual('them');
  1129. expect(msg_obj.get('delayed')).toEqual(false);
  1130. msg_txt = $chat_content.find('.chat-message').last().find('.chat-msg-content').text();
  1131. expect(msg_txt).toEqual(message);
  1132. sender_txt = $chat_content.find('span.chat-msg-them').last().text();
  1133. expect(sender_txt.match(/^[0-9][0-9]:[0-9][0-9] /)).toBeTruthy();
  1134. done();
  1135. });
  1136. }));
  1137. it("can be sent from a chatbox, and will appear inside it",
  1138. mock.initConverseWithPromises(
  1139. null, ['rosterGroupsFetched'], {},
  1140. function (done, _converse) {
  1141. test_utils.createContacts(_converse, 'current');
  1142. test_utils.openControlBox();
  1143. test_utils.openContactsPanel(_converse);
  1144. spyOn(_converse, 'emit');
  1145. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1146. test_utils.openChatBoxFor(_converse, contact_jid);
  1147. expect(_converse.emit).toHaveBeenCalledWith('chatBoxFocused', jasmine.any(Object));
  1148. var view = _converse.chatboxviews.get(contact_jid);
  1149. var message = 'This message is sent from this chatbox';
  1150. spyOn(view, 'sendMessage').and.callThrough();
  1151. test_utils.sendMessage(view, message);
  1152. expect(view.sendMessage).toHaveBeenCalled();
  1153. expect(view.model.messages.length, 2);
  1154. expect(_converse.emit.calls.mostRecent().args, ['messageSend', message]);
  1155. expect(view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content').text()).toEqual(message);
  1156. done();
  1157. }));
  1158. it("is sanitized to prevent Javascript injection attacks",
  1159. mock.initConverseWithPromises(
  1160. null, ['rosterGroupsFetched'], {},
  1161. function (done, _converse) {
  1162. test_utils.createContacts(_converse, 'current');
  1163. test_utils.openControlBox();
  1164. test_utils.openContactsPanel(_converse);
  1165. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1166. test_utils.openChatBoxFor(_converse, contact_jid);
  1167. var view = _converse.chatboxviews.get(contact_jid);
  1168. var message = '<p>This message contains <em>some</em> <b>markup</b></p>';
  1169. spyOn(view, 'sendMessage').and.callThrough();
  1170. test_utils.sendMessage(view, message);
  1171. expect(view.sendMessage).toHaveBeenCalled();
  1172. var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1173. expect(msg.text()).toEqual(message);
  1174. expect(msg.html()).toEqual('&lt;p&gt;This message contains &lt;em&gt;some&lt;/em&gt; &lt;b&gt;markup&lt;/b&gt;&lt;/p&gt;');
  1175. done();
  1176. }));
  1177. it("can contain hyperlinks, which will be clickable",
  1178. mock.initConverseWithPromises(
  1179. null, ['rosterGroupsFetched'], {},
  1180. function (done, _converse) {
  1181. test_utils.createContacts(_converse, 'current');
  1182. test_utils.openControlBox();
  1183. test_utils.openContactsPanel(_converse);
  1184. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1185. test_utils.openChatBoxFor(_converse, contact_jid);
  1186. var view = _converse.chatboxviews.get(contact_jid);
  1187. var message = 'This message contains a hyperlink: www.opkode.com';
  1188. spyOn(view, 'sendMessage').and.callThrough();
  1189. test_utils.sendMessage(view, message);
  1190. expect(view.sendMessage).toHaveBeenCalled();
  1191. var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1192. expect(msg.text()).toEqual(message);
  1193. expect(msg.html()).toEqual('This message contains a hyperlink: <a target="_blank" rel="noopener" href="http://www.opkode.com">www.opkode.com</a>');
  1194. done();
  1195. }));
  1196. it("will have properly escaped URLs",
  1197. mock.initConverseWithPromises(
  1198. null, ['rosterGroupsFetched'], {},
  1199. function (done, _converse) {
  1200. test_utils.createContacts(_converse, 'current');
  1201. test_utils.openControlBox();
  1202. test_utils.openContactsPanel(_converse);
  1203. var message, msg;
  1204. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1205. test_utils.openChatBoxFor(_converse, contact_jid);
  1206. var view = _converse.chatboxviews.get(contact_jid);
  1207. spyOn(view, 'sendMessage').and.callThrough();
  1208. message = "http://www.opkode.com/'onmouseover='alert(1)'whatever";
  1209. test_utils.sendMessage(view, message);
  1210. expect(view.sendMessage).toHaveBeenCalled();
  1211. msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1212. expect(msg.text()).toEqual(message);
  1213. expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="http://www.opkode.com/%27onmouseover=%27alert%281%29%27whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>');
  1214. message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever';
  1215. test_utils.sendMessage(view, message);
  1216. expect(view.sendMessage).toHaveBeenCalled();
  1217. msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1218. expect(msg.text()).toEqual(message);
  1219. expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>');
  1220. message = "https://en.wikipedia.org/wiki/Ender's_Game";
  1221. test_utils.sendMessage(view, message);
  1222. expect(view.sendMessage).toHaveBeenCalled();
  1223. msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1224. expect(msg.text()).toEqual(message);
  1225. expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender\'s_Game</a>');
  1226. message = "https://en.wikipedia.org/wiki/Ender%27s_Game";
  1227. test_utils.sendMessage(view, message);
  1228. expect(view.sendMessage).toHaveBeenCalled();
  1229. msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1230. expect(msg.text()).toEqual(message);
  1231. expect(msg.html()).toEqual('<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender%27s_Game</a>');
  1232. done();
  1233. }));
  1234. it("will render images from their URLs",
  1235. mock.initConverseWithPromises(
  1236. null, ['rosterGroupsFetched'], {},
  1237. function (done, _converse) {
  1238. test_utils.createContacts(_converse, 'current');
  1239. var base_url = document.URL.split(window.location.pathname)[0];
  1240. var message = base_url+"/logo/conversejs.svg";
  1241. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1242. test_utils.openChatBoxFor(_converse, contact_jid);
  1243. var view = _converse.chatboxviews.get(contact_jid);
  1244. spyOn(view, 'sendMessage').and.callThrough();
  1245. test_utils.sendMessage(view, message);
  1246. test_utils.waitUntil(function () {
  1247. return view.$el.find('.chat-content').find('.chat-message img').length;
  1248. }, 500).then(function () {
  1249. expect(view.sendMessage).toHaveBeenCalled();
  1250. var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1251. expect(msg.html()).toEqual(
  1252. '<a target="_blank" rel="noopener" href="'+base_url+'/logo/conversejs.svg"><img src="' +
  1253. message + '" class="chat-image"></a>');
  1254. message += "?param1=val1&param2=val2";
  1255. test_utils.sendMessage(view, message);
  1256. return test_utils.waitUntil(function () {
  1257. return view.$el.find('.chat-content').find('.chat-message img').length === 2;
  1258. }, 500);
  1259. }).then(function () {
  1260. expect(view.sendMessage).toHaveBeenCalled();
  1261. var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1262. expect(msg.html()).toEqual(
  1263. '<a target="_blank" rel="noopener" href="'+base_url+'/logo/conversejs.svg?param1=val1&amp;param2=val2"><img src="'+
  1264. message.replace(/&/g, '&amp;') +
  1265. '" class="chat-image"></a>')
  1266. // Test now with two images in one message
  1267. message += ' hello world '+base_url+"/logo/conversejs.svg";
  1268. test_utils.sendMessage(view, message);
  1269. return test_utils.waitUntil(function () {
  1270. return view.$el.find('.chat-content').find('.chat-message img').length === 4;
  1271. }, 500);
  1272. }).then(function () {
  1273. expect(view.sendMessage).toHaveBeenCalled();
  1274. var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
  1275. expect(msg.html()).toEqual(
  1276. '<a target="_blank" rel="noopener" href="'+base_url+'/logo/conversejs.svg?param1=val1&amp;param2=val2">'+
  1277. '<img src="'+base_url+'/logo/conversejs.svg?param1=val1&amp;param2=val2" class="chat-image"></a> hello world '+
  1278. '<a target="_blank" rel="noopener" href="'+base_url+'/logo/conversejs.svg">'+
  1279. '<img src="'+base_url+'/logo/conversejs.svg" class="chat-image"></a>'
  1280. )
  1281. done();
  1282. });
  1283. }));
  1284. it("will render the message time as configured",
  1285. mock.initConverseWithPromises(
  1286. null, ['rosterGroupsFetched'], {},
  1287. function (done, _converse) {
  1288. test_utils.createContacts(_converse, 'current');
  1289. _converse.time_format = 'hh:mm';
  1290. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1291. test_utils.openChatBoxFor(_converse, contact_jid);
  1292. var view = _converse.chatboxviews.get(contact_jid);
  1293. var message = 'This message is sent from this chatbox';
  1294. test_utils.sendMessage(view, message);
  1295. var chatbox = _converse.chatboxes.get(contact_jid);
  1296. expect(chatbox.messages.models.length, 1);
  1297. var msg_object = chatbox.messages.models[0];
  1298. var msg_time_author = view.$el.find('.chat-content').find('.chat-message')
  1299. .last().find('.chat-msg-author.chat-msg-me').text();
  1300. var msg_time_rendered = msg_time_author.split(" ",1);
  1301. var msg_time = moment(msg_object.get('time')).format(_converse.time_format);
  1302. expect(msg_time_rendered[0]).toBe(msg_time);
  1303. done();
  1304. }));
  1305. });
  1306. describe("A Chat Status Notification", function () {
  1307. it("does not open automatically if a chat state notification is received",
  1308. mock.initConverseWithPromises(
  1309. null, ['rosterGroupsFetched'], {},
  1310. function (done, _converse) {
  1311. test_utils.createContacts(_converse, 'current');
  1312. test_utils.openControlBox();
  1313. test_utils.openContactsPanel(_converse);
  1314. spyOn(_converse, 'emit');
  1315. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1316. // <composing> state
  1317. var msg = $msg({
  1318. from: sender_jid,
  1319. to: _converse.connection.jid,
  1320. type: 'chat',
  1321. id: (new Date()).getTime()
  1322. }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1323. _converse.chatboxes.onMessage(msg);
  1324. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1325. done();
  1326. }));
  1327. describe("An active notification", function () {
  1328. it("is sent when the user opens a chat box",
  1329. mock.initConverseWithPromises(
  1330. null, ['rosterGroupsFetched'], {},
  1331. function (done, _converse) {
  1332. test_utils.createContacts(_converse, 'current');
  1333. test_utils.openControlBox();
  1334. test_utils.openContactsPanel(_converse);
  1335. test_utils.waitUntil(function () {
  1336. return _converse.rosterview.$el.find('.roster-group').length;
  1337. }, 300).then(function () {
  1338. spyOn(_converse.connection, 'send');
  1339. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1340. test_utils.openChatBoxFor(_converse, contact_jid);
  1341. var view = _converse.chatboxviews.get(contact_jid);
  1342. expect(view.model.get('chat_state')).toBe('active');
  1343. expect(_converse.connection.send).toHaveBeenCalled();
  1344. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1345. expect($stanza.attr('to')).toBe(contact_jid);
  1346. expect($stanza.children().length).toBe(3);
  1347. expect($stanza.children().get(0).tagName).toBe('active');
  1348. expect($stanza.children().get(1).tagName).toBe('no-store');
  1349. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1350. done();
  1351. });
  1352. }));
  1353. it("is sent when the user maximizes a minimized a chat box", mock.initConverseWithPromises(
  1354. null, ['rosterGroupsFetched'], {},
  1355. function (done, _converse) {
  1356. test_utils.createContacts(_converse, 'current');
  1357. test_utils.openControlBox();
  1358. test_utils.openContactsPanel(_converse);
  1359. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1360. test_utils.waitUntil(function () {
  1361. return _converse.rosterview.$el.find('.roster-group').length;
  1362. }, 500).then(function () {
  1363. test_utils.openChatBoxFor(_converse, contact_jid);
  1364. var view = _converse.chatboxviews.get(contact_jid);
  1365. view.model.minimize();
  1366. expect(view.model.get('chat_state')).toBe('inactive');
  1367. spyOn(_converse.connection, 'send');
  1368. view.model.maximize();
  1369. return test_utils.waitUntil(function () {
  1370. return view.model.get('chat_state') === 'active';
  1371. }, 500);
  1372. }).then(function () {
  1373. expect(_converse.connection.send).toHaveBeenCalled();
  1374. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  1375. return call.args[0] instanceof Strophe.Builder;
  1376. });
  1377. expect(calls.length).toBe(1);
  1378. var $stanza = $(calls[0].args[0].tree());
  1379. expect($stanza.attr('to')).toBe(contact_jid);
  1380. expect($stanza.children().length).toBe(3);
  1381. expect($stanza.children().get(0).tagName).toBe('active');
  1382. expect($stanza.children().get(1).tagName).toBe('no-store');
  1383. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1384. done();
  1385. });
  1386. }));
  1387. });
  1388. describe("A composing notification", function () {
  1389. it("is sent as soon as the user starts typing a message which is not a command",
  1390. mock.initConverseWithPromises(
  1391. null, ['rosterGroupsFetched'], {},
  1392. function (done, _converse) {
  1393. test_utils.createContacts(_converse, 'current');
  1394. test_utils.openControlBox();
  1395. test_utils.openContactsPanel(_converse);
  1396. test_utils.waitUntil(function () {
  1397. return _converse.rosterview.$el.find('.roster-group').length;
  1398. }, 300).then(function () {
  1399. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1400. test_utils.openChatBoxFor(_converse, contact_jid);
  1401. var view = _converse.chatboxviews.get(contact_jid);
  1402. expect(view.model.get('chat_state')).toBe('active');
  1403. spyOn(_converse.connection, 'send');
  1404. spyOn(_converse, 'emit');
  1405. view.keyPressed({
  1406. target: view.$el.find('textarea.chat-textarea'),
  1407. keyCode: 1
  1408. });
  1409. expect(view.model.get('chat_state')).toBe('composing');
  1410. expect(_converse.connection.send).toHaveBeenCalled();
  1411. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1412. expect($stanza.attr('to')).toBe(contact_jid);
  1413. expect($stanza.children().get(0).tagName).toBe('composing');
  1414. expect($stanza.children().get(1).tagName).toBe('no-store');
  1415. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1416. // The notification is not sent again
  1417. view.keyPressed({
  1418. target: view.$el.find('textarea.chat-textarea'),
  1419. keyCode: 1
  1420. });
  1421. expect(view.model.get('chat_state')).toBe('composing');
  1422. expect(_converse.emit.calls.count(), 1);
  1423. done();
  1424. });
  1425. }));
  1426. it("will be shown if received",
  1427. mock.initConverseWithPromises(
  1428. null, ['rosterGroupsFetched'], {},
  1429. function (done, _converse) {
  1430. test_utils.createContacts(_converse, 'current');
  1431. test_utils.openControlBox();
  1432. test_utils.openContactsPanel(_converse);
  1433. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  1434. spyOn(_converse, 'emit');
  1435. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1436. // <composing> state
  1437. var msg = $msg({
  1438. from: sender_jid,
  1439. to: _converse.connection.jid,
  1440. type: 'chat',
  1441. id: (new Date()).getTime()
  1442. }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1443. _converse.chatboxes.onMessage(msg);
  1444. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1445. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1446. expect(chatboxview).toBeDefined();
  1447. // Check that the notification appears inside the chatbox in the DOM
  1448. var $events = chatboxview.$el.find('.chat-event');
  1449. expect($events.text()).toEqual(mock.cur_names[1] + ' is typing');
  1450. done();
  1451. }));
  1452. it("can be a composing carbon message that this user sent from a different client",
  1453. mock.initConverseWithPromises(
  1454. null, ['rosterGroupsFetched'], {},
  1455. function (done, _converse) {
  1456. var contact, sent_stanza, IQ_id, stanza;
  1457. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
  1458. .then(function () {
  1459. return test_utils.waitUntil(function () {
  1460. return _converse.xmppstatus.get('fullname');
  1461. }, 300);
  1462. }).then(function () {
  1463. test_utils.createContacts(_converse, 'current');
  1464. // Send a message from a different resource
  1465. spyOn(_converse, 'log');
  1466. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  1467. test_utils.openChatBoxFor(_converse, recipient_jid);
  1468. var msg = $msg({
  1469. 'from': _converse.bare_jid,
  1470. 'id': (new Date()).getTime(),
  1471. 'to': _converse.connection.jid,
  1472. 'type': 'chat',
  1473. 'xmlns': 'jabber:client'
  1474. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  1475. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  1476. .c('message', {
  1477. 'xmlns': 'jabber:client',
  1478. 'from': _converse.bare_jid+'/another-resource',
  1479. 'to': recipient_jid,
  1480. 'type': 'chat'
  1481. }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1482. _converse.chatboxes.onMessage(msg);
  1483. // Check that the chatbox and its view now exist
  1484. var chatbox = _converse.chatboxes.get(recipient_jid);
  1485. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  1486. // Check that the message was received and check the message parameters
  1487. expect(chatbox.messages.length).toEqual(1);
  1488. var msg_obj = chatbox.messages.models[0];
  1489. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  1490. expect(msg_obj.get('sender')).toEqual('me');
  1491. expect(msg_obj.get('delayed')).toEqual(false);
  1492. var $chat_content = chatboxview.$el.find('.chat-content');
  1493. var status_text = $chat_content.find('.chat-info.chat-event').text();
  1494. expect(status_text).toBe('Typing from another device');
  1495. done();
  1496. });
  1497. }));
  1498. });
  1499. describe("A paused notification", function () {
  1500. it("is sent if the user has stopped typing since 30 seconds",
  1501. mock.initConverseWithPromises(
  1502. null, ['rosterGroupsFetched'], {},
  1503. function (done, _converse) {
  1504. var view, contact_jid;
  1505. test_utils.createContacts(_converse, 'current');
  1506. test_utils.openControlBox();
  1507. test_utils.openContactsPanel(_converse);
  1508. test_utils.waitUntil(function () {
  1509. return _converse.rosterview.$el.find('.roster-group').length;
  1510. }, 300)
  1511. .then(function () {
  1512. _converse.TIMEOUTS.PAUSED = 200; // Make the timeout shorter so that we can test
  1513. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1514. test_utils.openChatBoxFor(_converse, contact_jid);
  1515. view = _converse.chatboxviews.get(contact_jid);
  1516. spyOn(_converse.connection, 'send');
  1517. spyOn(view, 'setChatState').and.callThrough();
  1518. expect(view.model.get('chat_state')).toBe('active');
  1519. view.keyPressed({
  1520. target: view.$el.find('textarea.chat-textarea'),
  1521. keyCode: 1
  1522. });
  1523. expect(view.model.get('chat_state')).toBe('composing');
  1524. expect(_converse.connection.send).toHaveBeenCalled();
  1525. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1526. expect($stanza.children().get(0).tagName).toBe('composing');
  1527. return test_utils.waitUntil(function () {
  1528. return view.model.get('chat_state') === 'paused';
  1529. }, 500);
  1530. }).then(function () {
  1531. expect(_converse.connection.send).toHaveBeenCalled();
  1532. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  1533. return call.args[0] instanceof Strophe.Builder;
  1534. });
  1535. expect(calls.length).toBe(2);
  1536. var $stanza = $(calls[1].args[0].tree());
  1537. expect($stanza.attr('to')).toBe(contact_jid);
  1538. expect($stanza.children().length).toBe(3);
  1539. expect($stanza.children().get(0).tagName).toBe('paused');
  1540. expect($stanza.children().get(1).tagName).toBe('no-store');
  1541. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1542. // Test #359. A paused notification should not be sent
  1543. // out if the user simply types longer than the
  1544. // timeout.
  1545. view.keyPressed({
  1546. target: view.$el.find('textarea.chat-textarea'),
  1547. keyCode: 1
  1548. });
  1549. expect(view.setChatState).toHaveBeenCalled();
  1550. expect(view.model.get('chat_state')).toBe('composing');
  1551. view.keyPressed({
  1552. target: view.$el.find('textarea.chat-textarea'),
  1553. keyCode: 1
  1554. });
  1555. expect(view.model.get('chat_state')).toBe('composing');
  1556. done();
  1557. });
  1558. }));
  1559. it("will be shown if received",
  1560. mock.initConverseWithPromises(
  1561. null, ['rosterGroupsFetched'], {},
  1562. function (done, _converse) {
  1563. test_utils.createContacts(_converse, 'current');
  1564. test_utils.openControlBox();
  1565. test_utils.openContactsPanel(_converse);
  1566. test_utils.waitUntil(function () {
  1567. return _converse.rosterview.$el.find('.roster-group').length;
  1568. }, 300)
  1569. .then(function () {
  1570. // TODO: only show paused state if the previous state was composing
  1571. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  1572. spyOn(_converse, 'emit');
  1573. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1574. // <paused> state
  1575. var msg = $msg({
  1576. from: sender_jid,
  1577. to: _converse.connection.jid,
  1578. type: 'chat',
  1579. id: (new Date()).getTime()
  1580. }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1581. _converse.chatboxes.onMessage(msg);
  1582. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1583. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1584. var $events = chatboxview.$el.find('.chat-event');
  1585. expect($events.text()).toEqual(mock.cur_names[1] + ' has stopped typing');
  1586. done();
  1587. });
  1588. }));
  1589. it("can be a paused carbon message that this user sent from a different client",
  1590. mock.initConverseWithPromises(
  1591. null, ['rosterGroupsFetched'], {},
  1592. function (done, _converse) {
  1593. var contact, sent_stanza, IQ_id, stanza;
  1594. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
  1595. .then(function () {
  1596. return test_utils.waitUntil(function () {
  1597. return _converse.xmppstatus.get('fullname');
  1598. }, 300);
  1599. }).then(function () {
  1600. test_utils.createContacts(_converse, 'current');
  1601. // Send a message from a different resource
  1602. spyOn(_converse, 'log');
  1603. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  1604. test_utils.openChatBoxFor(_converse, recipient_jid);
  1605. var msg = $msg({
  1606. 'from': _converse.bare_jid,
  1607. 'id': (new Date()).getTime(),
  1608. 'to': _converse.connection.jid,
  1609. 'type': 'chat',
  1610. 'xmlns': 'jabber:client'
  1611. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  1612. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  1613. .c('message', {
  1614. 'xmlns': 'jabber:client',
  1615. 'from': _converse.bare_jid+'/another-resource',
  1616. 'to': recipient_jid,
  1617. 'type': 'chat'
  1618. }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1619. _converse.chatboxes.onMessage(msg);
  1620. // Check that the chatbox and its view now exist
  1621. var chatbox = _converse.chatboxes.get(recipient_jid);
  1622. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  1623. // Check that the message was received and check the message parameters
  1624. expect(chatbox.messages.length).toEqual(1);
  1625. var msg_obj = chatbox.messages.models[0];
  1626. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  1627. expect(msg_obj.get('sender')).toEqual('me');
  1628. expect(msg_obj.get('delayed')).toEqual(false);
  1629. var $chat_content = chatboxview.$el.find('.chat-content');
  1630. var status_text = $chat_content.find('.chat-info.chat-event').text();
  1631. expect(status_text).toBe('Stopped typing on the other device');
  1632. done();
  1633. });
  1634. }));
  1635. });
  1636. describe("An inactive notifciation", function () {
  1637. it("is sent if the user has stopped typing since 2 minutes",
  1638. mock.initConverseWithPromises(
  1639. null, ['rosterGroupsFetched'], {},
  1640. function (done, _converse) {
  1641. var view, contact_jid;
  1642. test_utils.createContacts(_converse, 'current');
  1643. test_utils.openControlBox();
  1644. test_utils.openContactsPanel(_converse);
  1645. test_utils.waitUntil(function () {
  1646. return _converse.rosterview.$el.find('.roster-group').length;
  1647. }, 500).then(function () {
  1648. // Make the timeouts shorter so that we can test
  1649. _converse.TIMEOUTS.PAUSED = 200;
  1650. _converse.TIMEOUTS.INACTIVE = 200;
  1651. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1652. test_utils.openChatBoxFor(_converse, contact_jid);
  1653. view = _converse.chatboxviews.get(contact_jid);
  1654. return test_utils.waitUntil(function () {
  1655. return view.model.get('chat_state') === 'active';
  1656. }, 500);
  1657. }).then(function () {
  1658. console.log('chat_state set to active');
  1659. view = _converse.chatboxviews.get(contact_jid);
  1660. expect(view.model.get('chat_state')).toBe('active');
  1661. view.keyPressed({
  1662. target: view.$el.find('textarea.chat-textarea'),
  1663. keyCode: 1
  1664. });
  1665. return test_utils.waitUntil(function () {
  1666. return view.model.get('chat_state') === 'composing';
  1667. }, 500);
  1668. }).then(function () {
  1669. console.log('chat_state set to composing');
  1670. view = _converse.chatboxviews.get(contact_jid);
  1671. expect(view.model.get('chat_state')).toBe('composing');
  1672. spyOn(_converse.connection, 'send');
  1673. return test_utils.waitUntil(function () {
  1674. return view.model.get('chat_state') === 'paused';
  1675. }, 500);
  1676. }).then(function () {
  1677. console.log('chat_state set to paused');
  1678. return test_utils.waitUntil(function () {
  1679. return view.model.get('chat_state') === 'inactive';
  1680. }, 500);
  1681. }).then(function () {
  1682. console.log('chat_state set to inactive');
  1683. expect(_converse.connection.send).toHaveBeenCalled();
  1684. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  1685. return call.args[0] instanceof Strophe.Builder;
  1686. });
  1687. expect(calls.length).toBe(2);
  1688. var $stanza = $(calls[0].args[0].tree());
  1689. expect($stanza.attr('to')).toBe(contact_jid);
  1690. expect($stanza.children().length).toBe(3);
  1691. expect($stanza.children().get(0).tagName).toBe('paused');
  1692. expect($stanza.children().get(1).tagName).toBe('no-store');
  1693. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1694. $stanza = $(_converse.connection.send.calls.mostRecent().args[0].tree());
  1695. expect($stanza.attr('to')).toBe(contact_jid);
  1696. expect($stanza.children().length).toBe(3);
  1697. expect($stanza.children().get(0).tagName).toBe('inactive');
  1698. expect($stanza.children().get(1).tagName).toBe('no-store');
  1699. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1700. }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
  1701. .then(done);
  1702. }));
  1703. it("is sent when the user a minimizes a chat box",
  1704. mock.initConverseWithPromises(
  1705. null, ['rosterGroupsFetched'], {},
  1706. function (done, _converse) {
  1707. test_utils.createContacts(_converse, 'current');
  1708. test_utils.openControlBox();
  1709. test_utils.openContactsPanel(_converse);
  1710. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1711. test_utils.openChatBoxFor(_converse, contact_jid);
  1712. var view = _converse.chatboxviews.get(contact_jid);
  1713. spyOn(_converse.connection, 'send');
  1714. view.minimize();
  1715. expect(view.model.get('chat_state')).toBe('inactive');
  1716. expect(_converse.connection.send).toHaveBeenCalled();
  1717. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1718. expect($stanza.attr('to')).toBe(contact_jid);
  1719. expect($stanza.children().get(0).tagName).toBe('inactive');
  1720. done();
  1721. }));
  1722. it("is sent if the user closes a chat box",
  1723. mock.initConverseWithPromises(
  1724. null, ['rosterGroupsFetched'], {},
  1725. function (done, _converse) {
  1726. test_utils.createContacts(_converse, 'current');
  1727. test_utils.openControlBox();
  1728. test_utils.openContactsPanel(_converse);
  1729. test_utils.waitUntil(function () {
  1730. return _converse.rosterview.$el.find('.roster-group').length;
  1731. }, 300).then(function () {
  1732. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1733. test_utils.openChatBoxFor(_converse, contact_jid);
  1734. var view = _converse.chatboxviews.get(contact_jid);
  1735. expect(view.model.get('chat_state')).toBe('active');
  1736. spyOn(_converse.connection, 'send');
  1737. view.close();
  1738. expect(view.model.get('chat_state')).toBe('inactive');
  1739. expect(_converse.connection.send).toHaveBeenCalled();
  1740. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1741. expect($stanza.attr('to')).toBe(contact_jid);
  1742. expect($stanza.children().length).toBe(3);
  1743. expect($stanza.children().get(0).tagName).toBe('inactive');
  1744. expect($stanza.children().get(1).tagName).toBe('no-store');
  1745. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1746. done();
  1747. });
  1748. }));
  1749. it("will clear any other chat status notifications if its received",
  1750. mock.initConverseWithPromises(
  1751. null, ['rosterGroupsFetched'], {},
  1752. function (done, _converse) {
  1753. test_utils.createContacts(_converse, 'current');
  1754. test_utils.openControlBox();
  1755. test_utils.openContactsPanel(_converse);
  1756. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  1757. spyOn(_converse, 'emit');
  1758. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1759. test_utils.openChatBoxFor(_converse, sender_jid);
  1760. var view = _converse.chatboxviews.get(sender_jid);
  1761. expect(view.$el.find('.chat-event').length).toBe(0);
  1762. view.showStatusNotification(sender_jid+' is typing');
  1763. expect(view.$el.find('.chat-event').length).toBe(1);
  1764. var msg = $msg({
  1765. from: sender_jid,
  1766. to: _converse.connection.jid,
  1767. type: 'chat',
  1768. id: (new Date()).getTime()
  1769. }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1770. _converse.chatboxes.onMessage(msg);
  1771. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1772. expect(view.$el.find('.chat-event').length).toBe(0);
  1773. done();
  1774. }));
  1775. });
  1776. describe("A gone notifciation", function () {
  1777. it("will be shown if received",
  1778. mock.initConverseWithPromises(
  1779. null, ['rosterGroupsFetched'], {},
  1780. function (done, _converse) {
  1781. test_utils.createContacts(_converse, 'current');
  1782. test_utils.openControlBox();
  1783. test_utils.openContactsPanel(_converse);
  1784. spyOn(_converse, 'emit');
  1785. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1786. // <paused> state
  1787. var msg = $msg({
  1788. from: sender_jid,
  1789. to: _converse.connection.jid,
  1790. type: 'chat',
  1791. id: (new Date()).getTime()
  1792. }).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1793. _converse.chatboxes.onMessage(msg);
  1794. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1795. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1796. var $events = chatboxview.$el.find('.chat-event');
  1797. expect($events.text()).toEqual(mock.cur_names[1] + ' has gone away');
  1798. done();
  1799. }));
  1800. });
  1801. });
  1802. });
  1803. describe("Special Messages", function () {
  1804. it("'/clear' can be used to clear messages in a conversation",
  1805. mock.initConverseWithPromises(
  1806. null, ['rosterGroupsFetched'], {},
  1807. function (done, _converse) {
  1808. test_utils.createContacts(_converse, 'current');
  1809. test_utils.openControlBox();
  1810. test_utils.openContactsPanel(_converse);
  1811. spyOn(_converse, 'emit');
  1812. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1813. test_utils.openChatBoxFor(_converse, contact_jid);
  1814. var view = _converse.chatboxviews.get(contact_jid);
  1815. var message = 'This message is another sent from this chatbox';
  1816. // Lets make sure there is at least one message already
  1817. // (e.g for when this test is run on its own).
  1818. test_utils.sendMessage(view, message);
  1819. expect(view.model.messages.length > 0).toBeTruthy();
  1820. expect(view.model.messages.browserStorage.records.length > 0).toBeTruthy();
  1821. expect(_converse.emit).toHaveBeenCalledWith('messageSend', message);
  1822. message = '/clear';
  1823. spyOn(view, 'onMessageSubmitted').and.callThrough();
  1824. spyOn(view, 'clearMessages').and.callThrough();
  1825. spyOn(window, 'confirm').and.callFake(function () {
  1826. return true;
  1827. });
  1828. test_utils.sendMessage(view, message);
  1829. expect(view.onMessageSubmitted).toHaveBeenCalled();
  1830. expect(view.clearMessages).toHaveBeenCalled();
  1831. expect(window.confirm).toHaveBeenCalled();
  1832. expect(view.model.messages.length, 0); // The messages must be removed from the chatbox
  1833. expect(view.model.messages.browserStorage.records.length, 0); // And also from browserStorage
  1834. expect(_converse.emit.calls.count(), 1);
  1835. expect(_converse.emit.calls.mostRecent().args, ['messageSend', message]);
  1836. done();
  1837. }));
  1838. });
  1839. describe("A Message Counter", function () {
  1840. it("is incremented when the message is received and the window is not focused",
  1841. mock.initConverseWithPromises(
  1842. null, ['rosterGroupsFetched'], {},
  1843. function (done, _converse) {
  1844. test_utils.createContacts(_converse, 'current');
  1845. test_utils.openControlBox();
  1846. test_utils.openContactsPanel(_converse);
  1847. spyOn(_converse, 'emit');
  1848. expect(_converse.msg_counter).toBe(0);
  1849. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1850. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1851. var previous_state = _converse.windowState;
  1852. var message = 'This message will increment the message counter';
  1853. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1854. msg = $msg({
  1855. from: sender_jid,
  1856. to: _converse.connection.jid,
  1857. type: 'chat',
  1858. id: (new Date()).getTime()
  1859. }).c('body').t(message).up()
  1860. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1861. _converse.windowState = 'hidden';
  1862. _converse.chatboxes.onMessage(msg);
  1863. expect(_converse.incrementMsgCounter).toHaveBeenCalled();
  1864. expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
  1865. expect(_converse.msg_counter).toBe(1);
  1866. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1867. _converse.windowSate = previous_state;
  1868. done();
  1869. }));
  1870. it("is cleared when the window is focused",
  1871. mock.initConverseWithPromises(
  1872. null, ['rosterGroupsFetched'], {},
  1873. function (done, _converse) {
  1874. test_utils.createContacts(_converse, 'current');
  1875. test_utils.openControlBox();
  1876. test_utils.openContactsPanel(_converse);
  1877. _converse.windowState = 'hidden';
  1878. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1879. _converse.saveWindowState(null, 'focus');
  1880. _converse.saveWindowState(null, 'blur');
  1881. expect(_converse.clearMsgCounter).toHaveBeenCalled();
  1882. done();
  1883. }));
  1884. it("is not incremented when the message is received and the window is focused",
  1885. mock.initConverseWithPromises(
  1886. null, ['rosterGroupsFetched'], {},
  1887. function (done, _converse) {
  1888. test_utils.createContacts(_converse, 'current');
  1889. test_utils.openControlBox();
  1890. test_utils.openContactsPanel(_converse);
  1891. expect(_converse.msg_counter).toBe(0);
  1892. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1893. _converse.saveWindowState(null, 'focus');
  1894. var message = 'This message will not increment the message counter';
  1895. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1896. msg = $msg({
  1897. from: sender_jid,
  1898. to: _converse.connection.jid,
  1899. type: 'chat',
  1900. id: (new Date()).getTime()
  1901. }).c('body').t(message).up()
  1902. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1903. _converse.chatboxes.onMessage(msg);
  1904. expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
  1905. expect(_converse.msg_counter).toBe(0);
  1906. done();
  1907. }));
  1908. it("is incremented from zero when chatbox was closed after viewing previously received messages and the window is not focused now",
  1909. mock.initConverseWithPromises(
  1910. null, ['rosterGroupsFetched'], {},
  1911. function (done, _converse) {
  1912. test_utils.createContacts(_converse, 'current');
  1913. // initial state
  1914. expect(_converse.msg_counter).toBe(0);
  1915. var message = 'This message will always increment the message counter from zero',
  1916. sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1917. msgFactory = function () {
  1918. return $msg({
  1919. from: sender_jid,
  1920. to: _converse.connection.jid,
  1921. type: 'chat',
  1922. id: (new Date()).getTime()
  1923. })
  1924. .c('body').t(message).up()
  1925. .c('active', {'xmlns': Strophe.NS.CHATSTATES})
  1926. .tree();
  1927. };
  1928. // leave converse-chat page
  1929. _converse.windowState = 'hidden';
  1930. _converse.chatboxes.onMessage(msgFactory());
  1931. expect(_converse.msg_counter).toBe(1);
  1932. // come back to converse-chat page
  1933. _converse.saveWindowState(null, 'focus');
  1934. var view = _converse.chatboxviews.get(sender_jid);
  1935. expect(view.$el.is(':visible')).toBeTruthy();
  1936. expect(_converse.msg_counter).toBe(0);
  1937. // close chatbox and leave converse-chat page again
  1938. view.close();
  1939. _converse.windowState = 'hidden';
  1940. // check that msg_counter is incremented from zero again
  1941. _converse.chatboxes.onMessage(msgFactory());
  1942. view = _converse.chatboxviews.get(sender_jid);
  1943. expect(view.$el.is(':visible')).toBeTruthy();
  1944. expect(_converse.msg_counter).toBe(1);
  1945. done();
  1946. }));
  1947. });
  1948. describe("A ChatBox's Unread Message Count", function () {
  1949. it("is incremented when the message is received and ChatBoxView is scrolled up",
  1950. mock.initConverseWithPromises(
  1951. null, ['rosterGroupsFetched'], {},
  1952. function (done, _converse) {
  1953. test_utils.createContacts(_converse, 'current');
  1954. test_utils.openContactsPanel(_converse);
  1955. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1956. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1957. test_utils.openChatBoxFor(_converse, sender_jid);
  1958. var chatbox = _converse.chatboxes.get(sender_jid);
  1959. chatbox.save('scrolled', true);
  1960. _converse.chatboxes.onMessage(msg);
  1961. expect(chatbox.get('num_unread')).toBe(1);
  1962. done();
  1963. }));
  1964. it("is not incremented when the message is received and ChatBoxView is scrolled down",
  1965. mock.initConverseWithPromises(
  1966. null, ['rosterGroupsFetched'], {},
  1967. function (done, _converse) {
  1968. test_utils.createContacts(_converse, 'current');
  1969. test_utils.openContactsPanel(_converse);
  1970. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1971. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be read');
  1972. test_utils.openChatBoxFor(_converse, sender_jid);
  1973. var chatbox = _converse.chatboxes.get(sender_jid);
  1974. _converse.chatboxes.onMessage(msg);
  1975. expect(chatbox.get('num_unread')).toBe(0);
  1976. done();
  1977. }));
  1978. it("is incremeted when message is received, chatbox is scrolled down and the window is not focused",
  1979. mock.initConverseWithPromises(
  1980. null, ['rosterGroupsFetched'], {},
  1981. function (done, _converse) {
  1982. test_utils.createContacts(_converse, 'current');
  1983. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1984. var msgFactory = function () {
  1985. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1986. };
  1987. test_utils.openChatBoxFor(_converse, sender_jid);
  1988. var chatbox = _converse.chatboxes.get(sender_jid);
  1989. _converse.windowState = 'hidden';
  1990. _converse.chatboxes.onMessage(msgFactory());
  1991. expect(chatbox.get('num_unread')).toBe(1);
  1992. done();
  1993. }));
  1994. it("is incremeted when message is received, chatbox is scrolled up and the window is not focused",
  1995. mock.initConverseWithPromises(
  1996. null, ['rosterGroupsFetched'], {},
  1997. function (done, _converse) {
  1998. test_utils.createContacts(_converse, 'current');
  1999. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2000. var msgFactory = function () {
  2001. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2002. };
  2003. test_utils.openChatBoxFor(_converse, sender_jid);
  2004. var chatbox = _converse.chatboxes.get(sender_jid);
  2005. chatbox.save('scrolled', true);
  2006. _converse.windowState = 'hidden';
  2007. _converse.chatboxes.onMessage(msgFactory());
  2008. expect(chatbox.get('num_unread')).toBe(1);
  2009. done();
  2010. }));
  2011. it("is cleared when ChatBoxView was scrolled down and the window become focused",
  2012. mock.initConverseWithPromises(
  2013. null, ['rosterGroupsFetched'], {},
  2014. function (done, _converse) {
  2015. test_utils.createContacts(_converse, 'current');
  2016. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2017. var msgFactory = function () {
  2018. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2019. };
  2020. test_utils.openChatBoxFor(_converse, sender_jid);
  2021. var chatbox = _converse.chatboxes.get(sender_jid);
  2022. _converse.windowState = 'hidden';
  2023. _converse.chatboxes.onMessage(msgFactory());
  2024. expect(chatbox.get('num_unread')).toBe(1);
  2025. _converse.saveWindowState(null, 'focus');
  2026. expect(chatbox.get('num_unread')).toBe(0);
  2027. done();
  2028. }));
  2029. it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
  2030. mock.initConverseWithPromises(
  2031. null, ['rosterGroupsFetched'], {},
  2032. function (done, _converse) {
  2033. test_utils.createContacts(_converse, 'current');
  2034. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2035. var msgFactory = function () {
  2036. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2037. };
  2038. test_utils.openChatBoxFor(_converse, sender_jid);
  2039. var chatbox = _converse.chatboxes.get(sender_jid);
  2040. chatbox.save('scrolled', true);
  2041. _converse.windowState = 'hidden';
  2042. _converse.chatboxes.onMessage(msgFactory());
  2043. expect(chatbox.get('num_unread')).toBe(1);
  2044. _converse.saveWindowState(null, 'focus');
  2045. expect(chatbox.get('num_unread')).toBe(1);
  2046. done();
  2047. }));
  2048. });
  2049. describe("A RosterView's Unread Message Count", function () {
  2050. it("is updated when message is received and chatbox is scrolled up",
  2051. mock.initConverseWithPromises(
  2052. null, ['rosterGroupsFetched'], {},
  2053. function (done, _converse) {
  2054. test_utils.createContacts(_converse, 'current');
  2055. test_utils.openContactsPanel(_converse);
  2056. test_utils.waitUntil(function () {
  2057. return _converse.rosterview.$el.find('.roster-group').length;
  2058. }, 500)
  2059. .then(function () {
  2060. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2061. test_utils.openChatBoxFor(_converse, sender_jid);
  2062. var chatbox = _converse.chatboxes.get(sender_jid);
  2063. chatbox.save('scrolled', true);
  2064. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2065. _converse.chatboxes.onMessage(msg);
  2066. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2067. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2068. expect($msgIndicator.text()).toBe('1');
  2069. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  2070. _converse.chatboxes.onMessage(msg);
  2071. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2072. expect($msgIndicator.text()).toBe('2');
  2073. done();
  2074. });
  2075. }));
  2076. it("is updated when message is received and chatbox is minimized",
  2077. mock.initConverseWithPromises(
  2078. null, ['rosterGroupsFetched'], {},
  2079. function (done, _converse) {
  2080. test_utils.createContacts(_converse, 'current');
  2081. test_utils.openContactsPanel(_converse);
  2082. test_utils.waitUntil(function () {
  2083. return _converse.rosterview.$el.find('.roster-group').length;
  2084. }, 500)
  2085. .then(function () {
  2086. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2087. test_utils.openChatBoxFor(_converse, sender_jid);
  2088. var chatbox = _converse.chatboxes.get(sender_jid);
  2089. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2090. chatboxview.minimize();
  2091. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2092. _converse.chatboxes.onMessage(msg);
  2093. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2094. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2095. expect($msgIndicator.text()).toBe('1');
  2096. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  2097. _converse.chatboxes.onMessage(msg);
  2098. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2099. expect($msgIndicator.text()).toBe('2');
  2100. done();
  2101. });
  2102. }));
  2103. it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
  2104. mock.initConverseWithPromises(
  2105. null, ['rosterGroupsFetched'], {},
  2106. function (done, _converse) {
  2107. test_utils.createContacts(_converse, 'current');
  2108. test_utils.openContactsPanel(_converse);
  2109. test_utils.waitUntil(function () {
  2110. return _converse.rosterview.$el.find('.roster-group').length;
  2111. }, 500)
  2112. .then(function () {
  2113. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2114. test_utils.openChatBoxFor(_converse, sender_jid);
  2115. var chatbox = _converse.chatboxes.get(sender_jid);
  2116. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2117. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
  2118. var selectMsgsIndicator = function () { return $(_converse.rosterview.$el.find(msgsIndicatorSelector)); };
  2119. var msgFactory = function () {
  2120. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2121. };
  2122. chatboxview.minimize();
  2123. _converse.chatboxes.onMessage(msgFactory());
  2124. expect(selectMsgsIndicator().text()).toBe('1');
  2125. _converse.chatboxes.onMessage(msgFactory());
  2126. expect(selectMsgsIndicator().text()).toBe('2');
  2127. chatboxview.maximize();
  2128. expect(selectMsgsIndicator().length).toBe(0);
  2129. done();
  2130. });
  2131. }));
  2132. it("is cleared when unread messages are viewed which were received in scrolled-up chatbox",
  2133. mock.initConverseWithPromises(
  2134. null, ['rosterGroupsFetched'], {},
  2135. function (done, _converse) {
  2136. test_utils.createContacts(_converse, 'current');
  2137. test_utils.openContactsPanel(_converse);
  2138. test_utils.waitUntil(function () {
  2139. return _converse.rosterview.$el.find('.roster-group').length;
  2140. }, 500)
  2141. .then(function () {
  2142. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2143. test_utils.openChatBoxFor(_converse, sender_jid);
  2144. var chatbox = _converse.chatboxes.get(sender_jid);
  2145. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2146. var msgFactory = function () {
  2147. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2148. };
  2149. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2150. selectMsgsIndicator = function () { return $(_converse.rosterview.$el.find(msgsIndicatorSelector)); };
  2151. chatbox.save('scrolled', true);
  2152. _converse.chatboxes.onMessage(msgFactory());
  2153. expect(selectMsgsIndicator().text()).toBe('1');
  2154. chatboxview.viewUnreadMessages();
  2155. _converse.rosterview.render();
  2156. expect(selectMsgsIndicator().length).toBe(0);
  2157. done();
  2158. });
  2159. }));
  2160. it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
  2161. mock.initConverseWithPromises(
  2162. null, ['rosterGroupsFetched'], {},
  2163. function (done, _converse) {
  2164. test_utils.createContacts(_converse, 'current');
  2165. test_utils.openContactsPanel(_converse);
  2166. test_utils.waitUntil(function () {
  2167. return _converse.rosterview.$el.find('.roster-group').length;
  2168. }, 500)
  2169. .then(function () {
  2170. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2171. test_utils.openChatBoxFor(_converse, sender_jid);
  2172. var chatbox = _converse.chatboxes.get(sender_jid);
  2173. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2174. var msgFactory = function () {
  2175. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2176. };
  2177. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2178. selectMsgsIndicator = function () { return $(_converse.rosterview.$el.find(msgsIndicatorSelector)); };
  2179. chatbox.save('scrolled', true);
  2180. _converse.chatboxes.onMessage(msgFactory());
  2181. expect(selectMsgsIndicator().text()).toBe('1');
  2182. test_utils.openChatBoxFor(_converse, sender_jid);
  2183. expect(selectMsgsIndicator().text()).toBe('1');
  2184. done();
  2185. });
  2186. }));
  2187. });
  2188. describe("A Minimized ChatBoxView's Unread Message Count", function () {
  2189. it("is displayed when scrolled up chatbox is minimized after receiving unread messages",
  2190. mock.initConverseWithPromises(
  2191. null, ['rosterGroupsFetched'], {},
  2192. function (done, _converse) {
  2193. test_utils.createContacts(_converse, 'current');
  2194. test_utils.openContactsPanel(_converse);
  2195. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2196. test_utils.openChatBoxFor(_converse, sender_jid);
  2197. var msgFactory = function () {
  2198. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2199. };
  2200. var selectUnreadMsgCount = function () {
  2201. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  2202. return minimizedChatBoxView.$el.find('.chat-head-message-count');
  2203. };
  2204. var chatbox = _converse.chatboxes.get(sender_jid);
  2205. chatbox.save('scrolled', true);
  2206. _converse.chatboxes.onMessage(msgFactory());
  2207. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2208. chatboxview.minimize();
  2209. var $unreadMsgCount = selectUnreadMsgCount();
  2210. expect($unreadMsgCount.is(':visible')).toBeTruthy();
  2211. expect($unreadMsgCount.html()).toBe('1');
  2212. done();
  2213. }));
  2214. it("is incremented when message is received and windows is not focused",
  2215. mock.initConverseWithPromises(
  2216. null, ['rosterGroupsFetched'], {},
  2217. function (done, _converse) {
  2218. test_utils.createContacts(_converse, 'current');
  2219. test_utils.openContactsPanel(_converse);
  2220. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2221. test_utils.openChatBoxFor(_converse, sender_jid);
  2222. var msgFactory = function () {
  2223. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2224. };
  2225. var selectUnreadMsgCount = function () {
  2226. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  2227. return minimizedChatBoxView.$el.find('.chat-head-message-count');
  2228. };
  2229. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2230. chatboxview.minimize();
  2231. _converse.chatboxes.onMessage(msgFactory());
  2232. var $unreadMsgCount = selectUnreadMsgCount();
  2233. expect($unreadMsgCount.is(':visible')).toBeTruthy();
  2234. expect($unreadMsgCount.html()).toBe('1');
  2235. done();
  2236. }));
  2237. });
  2238. });
  2239. }));