2
0

chatbox.js 141 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553
  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)
  1399. .then(function () {
  1400. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1401. test_utils.openChatBoxFor(_converse, contact_jid);
  1402. var view = _converse.chatboxviews.get(contact_jid);
  1403. expect(view.model.get('chat_state')).toBe('active');
  1404. spyOn(_converse.connection, 'send');
  1405. spyOn(_converse, 'emit');
  1406. view.keyPressed({
  1407. target: view.$el.find('textarea.chat-textarea'),
  1408. keyCode: 1
  1409. });
  1410. expect(view.model.get('chat_state')).toBe('composing');
  1411. expect(_converse.connection.send).toHaveBeenCalled();
  1412. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1413. expect($stanza.attr('to')).toBe(contact_jid);
  1414. expect($stanza.children().get(0).tagName).toBe('composing');
  1415. expect($stanza.children().get(1).tagName).toBe('no-store');
  1416. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1417. // The notification is not sent again
  1418. view.keyPressed({
  1419. target: view.$el.find('textarea.chat-textarea'),
  1420. keyCode: 1
  1421. });
  1422. expect(view.model.get('chat_state')).toBe('composing');
  1423. expect(_converse.emit.calls.count(), 1);
  1424. done();
  1425. });
  1426. }));
  1427. it("will be shown if received",
  1428. mock.initConverseWithPromises(
  1429. null, ['rosterGroupsFetched'], {},
  1430. function (done, _converse) {
  1431. test_utils.createContacts(_converse, 'current');
  1432. test_utils.openControlBox();
  1433. test_utils.openContactsPanel(_converse);
  1434. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  1435. spyOn(_converse, 'emit');
  1436. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1437. // <composing> state
  1438. var msg = $msg({
  1439. from: sender_jid,
  1440. to: _converse.connection.jid,
  1441. type: 'chat',
  1442. id: (new Date()).getTime()
  1443. }).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1444. _converse.chatboxes.onMessage(msg);
  1445. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1446. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1447. expect(chatboxview).toBeDefined();
  1448. // Check that the notification appears inside the chatbox in the DOM
  1449. var $events = chatboxview.$el.find('.chat-event');
  1450. expect($events.text()).toEqual(mock.cur_names[1] + ' is typing');
  1451. done();
  1452. }));
  1453. it("can be a composing carbon message that this user sent from a different client",
  1454. mock.initConverseWithPromises(
  1455. null, ['rosterGroupsFetched'], {},
  1456. function (done, _converse) {
  1457. var contact, sent_stanza, IQ_id, stanza;
  1458. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
  1459. .then(function () {
  1460. return test_utils.waitUntil(function () {
  1461. return _converse.xmppstatus.get('fullname');
  1462. }, 300);
  1463. }).then(function () {
  1464. test_utils.createContacts(_converse, 'current');
  1465. // Send a message from a different resource
  1466. spyOn(_converse, 'log');
  1467. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  1468. test_utils.openChatBoxFor(_converse, recipient_jid);
  1469. var msg = $msg({
  1470. 'from': _converse.bare_jid,
  1471. 'id': (new Date()).getTime(),
  1472. 'to': _converse.connection.jid,
  1473. 'type': 'chat',
  1474. 'xmlns': 'jabber:client'
  1475. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  1476. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  1477. .c('message', {
  1478. 'xmlns': 'jabber:client',
  1479. 'from': _converse.bare_jid+'/another-resource',
  1480. 'to': recipient_jid,
  1481. 'type': 'chat'
  1482. }).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1483. _converse.chatboxes.onMessage(msg);
  1484. // Check that the chatbox and its view now exist
  1485. var chatbox = _converse.chatboxes.get(recipient_jid);
  1486. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  1487. // Check that the message was received and check the message parameters
  1488. expect(chatbox.messages.length).toEqual(1);
  1489. var msg_obj = chatbox.messages.models[0];
  1490. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  1491. expect(msg_obj.get('sender')).toEqual('me');
  1492. expect(msg_obj.get('delayed')).toEqual(false);
  1493. var $chat_content = chatboxview.$el.find('.chat-content');
  1494. var status_text = $chat_content.find('.chat-info.chat-event').text();
  1495. expect(status_text).toBe('Typing from another device');
  1496. done();
  1497. });
  1498. }));
  1499. });
  1500. describe("A paused notification", function () {
  1501. it("is sent if the user has stopped typing since 30 seconds",
  1502. mock.initConverseWithPromises(
  1503. null, ['rosterGroupsFetched'], {},
  1504. function (done, _converse) {
  1505. var view, contact_jid;
  1506. test_utils.createContacts(_converse, 'current');
  1507. test_utils.openControlBox();
  1508. test_utils.openContactsPanel(_converse);
  1509. test_utils.waitUntil(function () {
  1510. return _converse.rosterview.$el.find('.roster-group').length;
  1511. }, 300)
  1512. .then(function () {
  1513. _converse.TIMEOUTS.PAUSED = 200; // Make the timeout shorter so that we can test
  1514. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1515. test_utils.openChatBoxFor(_converse, contact_jid);
  1516. view = _converse.chatboxviews.get(contact_jid);
  1517. spyOn(_converse.connection, 'send');
  1518. spyOn(view, 'setChatState').and.callThrough();
  1519. expect(view.model.get('chat_state')).toBe('active');
  1520. view.keyPressed({
  1521. target: view.$el.find('textarea.chat-textarea'),
  1522. keyCode: 1
  1523. });
  1524. expect(view.model.get('chat_state')).toBe('composing');
  1525. expect(_converse.connection.send).toHaveBeenCalled();
  1526. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1527. expect($stanza.children().get(0).tagName).toBe('composing');
  1528. return test_utils.waitUntil(function () {
  1529. return view.model.get('chat_state') === 'paused';
  1530. }, 500);
  1531. }).then(function () {
  1532. expect(_converse.connection.send).toHaveBeenCalled();
  1533. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  1534. return call.args[0] instanceof Strophe.Builder;
  1535. });
  1536. expect(calls.length).toBe(2);
  1537. var $stanza = $(calls[1].args[0].tree());
  1538. expect($stanza.attr('to')).toBe(contact_jid);
  1539. expect($stanza.children().length).toBe(3);
  1540. expect($stanza.children().get(0).tagName).toBe('paused');
  1541. expect($stanza.children().get(1).tagName).toBe('no-store');
  1542. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1543. // Test #359. A paused notification should not be sent
  1544. // out if the user simply types longer than the
  1545. // timeout.
  1546. view.keyPressed({
  1547. target: view.$el.find('textarea.chat-textarea'),
  1548. keyCode: 1
  1549. });
  1550. expect(view.setChatState).toHaveBeenCalled();
  1551. expect(view.model.get('chat_state')).toBe('composing');
  1552. view.keyPressed({
  1553. target: view.$el.find('textarea.chat-textarea'),
  1554. keyCode: 1
  1555. });
  1556. expect(view.model.get('chat_state')).toBe('composing');
  1557. done();
  1558. });
  1559. }));
  1560. it("will be shown if received",
  1561. mock.initConverseWithPromises(
  1562. null, ['rosterGroupsFetched'], {},
  1563. function (done, _converse) {
  1564. test_utils.createContacts(_converse, 'current');
  1565. test_utils.openControlBox();
  1566. test_utils.openContactsPanel(_converse);
  1567. test_utils.waitUntil(function () {
  1568. return _converse.rosterview.$el.find('.roster-group').length;
  1569. }, 300)
  1570. .then(function () {
  1571. // TODO: only show paused state if the previous state was composing
  1572. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  1573. spyOn(_converse, 'emit');
  1574. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1575. // <paused> state
  1576. var msg = $msg({
  1577. from: sender_jid,
  1578. to: _converse.connection.jid,
  1579. type: 'chat',
  1580. id: (new Date()).getTime()
  1581. }).c('body').c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1582. _converse.chatboxes.onMessage(msg);
  1583. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1584. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1585. var $events = chatboxview.$el.find('.chat-event');
  1586. expect($events.text()).toEqual(mock.cur_names[1] + ' has stopped typing');
  1587. done();
  1588. });
  1589. }));
  1590. it("can be a paused carbon message that this user sent from a different client",
  1591. mock.initConverseWithPromises(
  1592. null, ['rosterGroupsFetched'], {},
  1593. function (done, _converse) {
  1594. var contact, sent_stanza, IQ_id, stanza;
  1595. test_utils.waitUntilFeatureSupportConfirmed(_converse, 'vcard-temp')
  1596. .then(function () {
  1597. return test_utils.waitUntil(function () {
  1598. return _converse.xmppstatus.get('fullname');
  1599. }, 300);
  1600. }).then(function () {
  1601. test_utils.createContacts(_converse, 'current');
  1602. // Send a message from a different resource
  1603. spyOn(_converse, 'log');
  1604. var recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
  1605. test_utils.openChatBoxFor(_converse, recipient_jid);
  1606. var msg = $msg({
  1607. 'from': _converse.bare_jid,
  1608. 'id': (new Date()).getTime(),
  1609. 'to': _converse.connection.jid,
  1610. 'type': 'chat',
  1611. 'xmlns': 'jabber:client'
  1612. }).c('sent', {'xmlns': 'urn:xmpp:carbons:2'})
  1613. .c('forwarded', {'xmlns': 'urn:xmpp:forward:0'})
  1614. .c('message', {
  1615. 'xmlns': 'jabber:client',
  1616. 'from': _converse.bare_jid+'/another-resource',
  1617. 'to': recipient_jid,
  1618. 'type': 'chat'
  1619. }).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1620. _converse.chatboxes.onMessage(msg);
  1621. // Check that the chatbox and its view now exist
  1622. var chatbox = _converse.chatboxes.get(recipient_jid);
  1623. var chatboxview = _converse.chatboxviews.get(recipient_jid);
  1624. // Check that the message was received and check the message parameters
  1625. expect(chatbox.messages.length).toEqual(1);
  1626. var msg_obj = chatbox.messages.models[0];
  1627. expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
  1628. expect(msg_obj.get('sender')).toEqual('me');
  1629. expect(msg_obj.get('delayed')).toEqual(false);
  1630. var $chat_content = chatboxview.$el.find('.chat-content');
  1631. var status_text = $chat_content.find('.chat-info.chat-event').text();
  1632. expect(status_text).toBe('Stopped typing on the other device');
  1633. done();
  1634. });
  1635. }));
  1636. });
  1637. describe("An inactive notifciation", function () {
  1638. it("is sent if the user has stopped typing since 2 minutes",
  1639. mock.initConverseWithPromises(
  1640. null, ['rosterGroupsFetched'], {},
  1641. function (done, _converse) {
  1642. var view, contact_jid;
  1643. test_utils.createContacts(_converse, 'current');
  1644. test_utils.openControlBox();
  1645. test_utils.openContactsPanel(_converse);
  1646. test_utils.waitUntil(function () {
  1647. return _converse.rosterview.$el.find('.roster-group').length;
  1648. }, 500).then(function () {
  1649. // Make the timeouts shorter so that we can test
  1650. _converse.TIMEOUTS.PAUSED = 200;
  1651. _converse.TIMEOUTS.INACTIVE = 200;
  1652. contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1653. test_utils.openChatBoxFor(_converse, contact_jid);
  1654. view = _converse.chatboxviews.get(contact_jid);
  1655. return test_utils.waitUntil(function () {
  1656. return view.model.get('chat_state') === 'active';
  1657. }, 500);
  1658. }).then(function () {
  1659. console.log('chat_state set to active');
  1660. view = _converse.chatboxviews.get(contact_jid);
  1661. expect(view.model.get('chat_state')).toBe('active');
  1662. view.keyPressed({
  1663. target: view.$el.find('textarea.chat-textarea'),
  1664. keyCode: 1
  1665. });
  1666. return test_utils.waitUntil(function () {
  1667. return view.model.get('chat_state') === 'composing';
  1668. }, 500);
  1669. }).then(function () {
  1670. console.log('chat_state set to composing');
  1671. view = _converse.chatboxviews.get(contact_jid);
  1672. expect(view.model.get('chat_state')).toBe('composing');
  1673. spyOn(_converse.connection, 'send');
  1674. return test_utils.waitUntil(function () {
  1675. return view.model.get('chat_state') === 'paused';
  1676. }, 500);
  1677. }).then(function () {
  1678. console.log('chat_state set to paused');
  1679. return test_utils.waitUntil(function () {
  1680. return view.model.get('chat_state') === 'inactive';
  1681. }, 500);
  1682. }).then(function () {
  1683. console.log('chat_state set to inactive');
  1684. expect(_converse.connection.send).toHaveBeenCalled();
  1685. var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
  1686. return call.args[0] instanceof Strophe.Builder;
  1687. });
  1688. expect(calls.length).toBe(2);
  1689. var $stanza = $(calls[0].args[0].tree());
  1690. expect($stanza.attr('to')).toBe(contact_jid);
  1691. expect($stanza.children().length).toBe(3);
  1692. expect($stanza.children().get(0).tagName).toBe('paused');
  1693. expect($stanza.children().get(1).tagName).toBe('no-store');
  1694. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1695. $stanza = $(_converse.connection.send.calls.mostRecent().args[0].tree());
  1696. expect($stanza.attr('to')).toBe(contact_jid);
  1697. expect($stanza.children().length).toBe(3);
  1698. expect($stanza.children().get(0).tagName).toBe('inactive');
  1699. expect($stanza.children().get(1).tagName).toBe('no-store');
  1700. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1701. done();
  1702. }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
  1703. }));
  1704. it("is sent when the user a minimizes a chat box",
  1705. mock.initConverseWithPromises(
  1706. null, ['rosterGroupsFetched'], {},
  1707. function (done, _converse) {
  1708. test_utils.createContacts(_converse, 'current');
  1709. test_utils.openControlBox();
  1710. test_utils.openContactsPanel(_converse);
  1711. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1712. test_utils.openChatBoxFor(_converse, contact_jid);
  1713. var view = _converse.chatboxviews.get(contact_jid);
  1714. spyOn(_converse.connection, 'send');
  1715. view.minimize();
  1716. expect(view.model.get('chat_state')).toBe('inactive');
  1717. expect(_converse.connection.send).toHaveBeenCalled();
  1718. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1719. expect($stanza.attr('to')).toBe(contact_jid);
  1720. expect($stanza.children().get(0).tagName).toBe('inactive');
  1721. done();
  1722. }));
  1723. it("is sent if the user closes a chat box",
  1724. mock.initConverseWithPromises(
  1725. null, ['rosterGroupsFetched'], {},
  1726. function (done, _converse) {
  1727. test_utils.createContacts(_converse, 'current');
  1728. test_utils.openControlBox();
  1729. test_utils.openContactsPanel(_converse);
  1730. test_utils.waitUntil(function () {
  1731. return _converse.rosterview.$el.find('.roster-group').length;
  1732. }, 300).then(function () {
  1733. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1734. test_utils.openChatBoxFor(_converse, contact_jid);
  1735. var view = _converse.chatboxviews.get(contact_jid);
  1736. expect(view.model.get('chat_state')).toBe('active');
  1737. spyOn(_converse.connection, 'send');
  1738. view.close();
  1739. expect(view.model.get('chat_state')).toBe('inactive');
  1740. expect(_converse.connection.send).toHaveBeenCalled();
  1741. var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
  1742. expect($stanza.attr('to')).toBe(contact_jid);
  1743. expect($stanza.children().length).toBe(3);
  1744. expect($stanza.children().get(0).tagName).toBe('inactive');
  1745. expect($stanza.children().get(1).tagName).toBe('no-store');
  1746. expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
  1747. done();
  1748. });
  1749. }));
  1750. it("will clear any other chat status notifications if its received",
  1751. mock.initConverseWithPromises(
  1752. null, ['rosterGroupsFetched'], {},
  1753. function (done, _converse) {
  1754. test_utils.createContacts(_converse, 'current');
  1755. test_utils.openControlBox();
  1756. test_utils.openContactsPanel(_converse);
  1757. // See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
  1758. spyOn(_converse, 'emit');
  1759. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1760. test_utils.openChatBoxFor(_converse, sender_jid);
  1761. var view = _converse.chatboxviews.get(sender_jid);
  1762. expect(view.$el.find('.chat-event').length).toBe(0);
  1763. view.showStatusNotification(sender_jid+' is typing');
  1764. expect(view.$el.find('.chat-event').length).toBe(1);
  1765. var msg = $msg({
  1766. from: sender_jid,
  1767. to: _converse.connection.jid,
  1768. type: 'chat',
  1769. id: (new Date()).getTime()
  1770. }).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1771. _converse.chatboxes.onMessage(msg);
  1772. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1773. expect(view.$el.find('.chat-event').length).toBe(0);
  1774. done();
  1775. }));
  1776. });
  1777. describe("A gone notifciation", function () {
  1778. it("will be shown if received",
  1779. mock.initConverseWithPromises(
  1780. null, ['rosterGroupsFetched'], {},
  1781. function (done, _converse) {
  1782. test_utils.createContacts(_converse, 'current');
  1783. test_utils.openControlBox();
  1784. test_utils.openContactsPanel(_converse);
  1785. spyOn(_converse, 'emit');
  1786. var sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
  1787. // <paused> state
  1788. var msg = $msg({
  1789. from: sender_jid,
  1790. to: _converse.connection.jid,
  1791. type: 'chat',
  1792. id: (new Date()).getTime()
  1793. }).c('body').c('gone', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1794. _converse.chatboxes.onMessage(msg);
  1795. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1796. var chatboxview = _converse.chatboxviews.get(sender_jid);
  1797. var $events = chatboxview.$el.find('.chat-event');
  1798. expect($events.text()).toEqual(mock.cur_names[1] + ' has gone away');
  1799. done();
  1800. }));
  1801. });
  1802. });
  1803. });
  1804. describe("Special Messages", function () {
  1805. it("'/clear' can be used to clear messages in a conversation",
  1806. mock.initConverseWithPromises(
  1807. null, ['rosterGroupsFetched'], {},
  1808. function (done, _converse) {
  1809. test_utils.createContacts(_converse, 'current');
  1810. test_utils.openControlBox();
  1811. test_utils.openContactsPanel(_converse);
  1812. spyOn(_converse, 'emit');
  1813. var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1814. test_utils.openChatBoxFor(_converse, contact_jid);
  1815. var view = _converse.chatboxviews.get(contact_jid);
  1816. var message = 'This message is another sent from this chatbox';
  1817. // Lets make sure there is at least one message already
  1818. // (e.g for when this test is run on its own).
  1819. test_utils.sendMessage(view, message);
  1820. expect(view.model.messages.length > 0).toBeTruthy();
  1821. expect(view.model.messages.browserStorage.records.length > 0).toBeTruthy();
  1822. expect(_converse.emit).toHaveBeenCalledWith('messageSend', message);
  1823. message = '/clear';
  1824. spyOn(view, 'onMessageSubmitted').and.callThrough();
  1825. spyOn(view, 'clearMessages').and.callThrough();
  1826. spyOn(window, 'confirm').and.callFake(function () {
  1827. return true;
  1828. });
  1829. test_utils.sendMessage(view, message);
  1830. expect(view.onMessageSubmitted).toHaveBeenCalled();
  1831. expect(view.clearMessages).toHaveBeenCalled();
  1832. expect(window.confirm).toHaveBeenCalled();
  1833. expect(view.model.messages.length, 0); // The messages must be removed from the chatbox
  1834. expect(view.model.messages.browserStorage.records.length, 0); // And also from browserStorage
  1835. expect(_converse.emit.calls.count(), 1);
  1836. expect(_converse.emit.calls.mostRecent().args, ['messageSend', message]);
  1837. done();
  1838. }));
  1839. });
  1840. describe("A Message Counter", function () {
  1841. it("is incremented when the message is received and the window is not focused",
  1842. mock.initConverseWithPromises(
  1843. null, ['rosterGroupsFetched'], {},
  1844. function (done, _converse) {
  1845. test_utils.createContacts(_converse, 'current');
  1846. test_utils.openControlBox();
  1847. test_utils.openContactsPanel(_converse);
  1848. spyOn(_converse, 'emit');
  1849. expect(_converse.msg_counter).toBe(0);
  1850. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1851. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1852. var previous_state = _converse.windowState;
  1853. var message = 'This message will increment the message counter';
  1854. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1855. msg = $msg({
  1856. from: sender_jid,
  1857. to: _converse.connection.jid,
  1858. type: 'chat',
  1859. id: (new Date()).getTime()
  1860. }).c('body').t(message).up()
  1861. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1862. _converse.windowState = 'hidden';
  1863. _converse.chatboxes.onMessage(msg);
  1864. expect(_converse.incrementMsgCounter).toHaveBeenCalled();
  1865. expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
  1866. expect(_converse.msg_counter).toBe(1);
  1867. expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
  1868. _converse.windowSate = previous_state;
  1869. done();
  1870. }));
  1871. it("is cleared when the window is focused",
  1872. mock.initConverseWithPromises(
  1873. null, ['rosterGroupsFetched'], {},
  1874. function (done, _converse) {
  1875. test_utils.createContacts(_converse, 'current');
  1876. test_utils.openControlBox();
  1877. test_utils.openContactsPanel(_converse);
  1878. _converse.windowState = 'hidden';
  1879. spyOn(_converse, 'clearMsgCounter').and.callThrough();
  1880. _converse.saveWindowState(null, 'focus');
  1881. _converse.saveWindowState(null, 'blur');
  1882. expect(_converse.clearMsgCounter).toHaveBeenCalled();
  1883. done();
  1884. }));
  1885. it("is not incremented when the message is received and the window is focused",
  1886. mock.initConverseWithPromises(
  1887. null, ['rosterGroupsFetched'], {},
  1888. function (done, _converse) {
  1889. test_utils.createContacts(_converse, 'current');
  1890. test_utils.openControlBox();
  1891. test_utils.openContactsPanel(_converse);
  1892. expect(_converse.msg_counter).toBe(0);
  1893. spyOn(_converse, 'incrementMsgCounter').and.callThrough();
  1894. _converse.saveWindowState(null, 'focus');
  1895. var message = 'This message will not increment the message counter';
  1896. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1897. msg = $msg({
  1898. from: sender_jid,
  1899. to: _converse.connection.jid,
  1900. type: 'chat',
  1901. id: (new Date()).getTime()
  1902. }).c('body').t(message).up()
  1903. .c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
  1904. _converse.chatboxes.onMessage(msg);
  1905. expect(_converse.incrementMsgCounter).not.toHaveBeenCalled();
  1906. expect(_converse.msg_counter).toBe(0);
  1907. done();
  1908. }));
  1909. it("is incremented from zero when chatbox was closed after viewing previously received messages and the window is not focused now",
  1910. mock.initConverseWithPromises(
  1911. null, ['rosterGroupsFetched'], {},
  1912. function (done, _converse) {
  1913. test_utils.createContacts(_converse, 'current');
  1914. // initial state
  1915. expect(_converse.msg_counter).toBe(0);
  1916. var message = 'This message will always increment the message counter from zero',
  1917. sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1918. msgFactory = function () {
  1919. return $msg({
  1920. from: sender_jid,
  1921. to: _converse.connection.jid,
  1922. type: 'chat',
  1923. id: (new Date()).getTime()
  1924. })
  1925. .c('body').t(message).up()
  1926. .c('active', {'xmlns': Strophe.NS.CHATSTATES})
  1927. .tree();
  1928. };
  1929. // leave converse-chat page
  1930. _converse.windowState = 'hidden';
  1931. _converse.chatboxes.onMessage(msgFactory());
  1932. expect(_converse.msg_counter).toBe(1);
  1933. // come back to converse-chat page
  1934. _converse.saveWindowState(null, 'focus');
  1935. var view = _converse.chatboxviews.get(sender_jid);
  1936. expect(view.$el.is(':visible')).toBeTruthy();
  1937. expect(_converse.msg_counter).toBe(0);
  1938. // close chatbox and leave converse-chat page again
  1939. view.close();
  1940. _converse.windowState = 'hidden';
  1941. // check that msg_counter is incremented from zero again
  1942. _converse.chatboxes.onMessage(msgFactory());
  1943. view = _converse.chatboxviews.get(sender_jid);
  1944. expect(view.$el.is(':visible')).toBeTruthy();
  1945. expect(_converse.msg_counter).toBe(1);
  1946. done();
  1947. }));
  1948. });
  1949. describe("A ChatBox's Unread Message Count", function () {
  1950. it("is incremented when the message is received and ChatBoxView is scrolled up",
  1951. mock.initConverseWithPromises(
  1952. null, ['rosterGroupsFetched'], {},
  1953. function (done, _converse) {
  1954. test_utils.createContacts(_converse, 'current');
  1955. test_utils.openContactsPanel(_converse);
  1956. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1957. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1958. test_utils.openChatBoxFor(_converse, sender_jid);
  1959. var chatbox = _converse.chatboxes.get(sender_jid);
  1960. chatbox.save('scrolled', true);
  1961. _converse.chatboxes.onMessage(msg);
  1962. expect(chatbox.get('num_unread')).toBe(1);
  1963. done();
  1964. }));
  1965. it("is not incremented when the message is received and ChatBoxView is scrolled down",
  1966. mock.initConverseWithPromises(
  1967. null, ['rosterGroupsFetched'], {},
  1968. function (done, _converse) {
  1969. test_utils.createContacts(_converse, 'current');
  1970. test_utils.openContactsPanel(_converse);
  1971. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  1972. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be read');
  1973. test_utils.openChatBoxFor(_converse, sender_jid);
  1974. var chatbox = _converse.chatboxes.get(sender_jid);
  1975. _converse.chatboxes.onMessage(msg);
  1976. expect(chatbox.get('num_unread')).toBe(0);
  1977. done();
  1978. }));
  1979. it("is incremeted when message is received, chatbox is scrolled down and the window is not focused",
  1980. mock.initConverseWithPromises(
  1981. null, ['rosterGroupsFetched'], {},
  1982. function (done, _converse) {
  1983. test_utils.createContacts(_converse, 'current');
  1984. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  1985. var msgFactory = function () {
  1986. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  1987. };
  1988. test_utils.openChatBoxFor(_converse, sender_jid);
  1989. var chatbox = _converse.chatboxes.get(sender_jid);
  1990. _converse.windowState = 'hidden';
  1991. _converse.chatboxes.onMessage(msgFactory());
  1992. expect(chatbox.get('num_unread')).toBe(1);
  1993. done();
  1994. }));
  1995. it("is incremeted when message is received, chatbox is scrolled up and the window is not focused",
  1996. mock.initConverseWithPromises(
  1997. null, ['rosterGroupsFetched'], {},
  1998. function (done, _converse) {
  1999. test_utils.createContacts(_converse, 'current');
  2000. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2001. var msgFactory = function () {
  2002. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2003. };
  2004. test_utils.openChatBoxFor(_converse, sender_jid);
  2005. var chatbox = _converse.chatboxes.get(sender_jid);
  2006. chatbox.save('scrolled', true);
  2007. _converse.windowState = 'hidden';
  2008. _converse.chatboxes.onMessage(msgFactory());
  2009. expect(chatbox.get('num_unread')).toBe(1);
  2010. done();
  2011. }));
  2012. it("is cleared when ChatBoxView was scrolled down and the window become focused",
  2013. mock.initConverseWithPromises(
  2014. null, ['rosterGroupsFetched'], {},
  2015. function (done, _converse) {
  2016. test_utils.createContacts(_converse, 'current');
  2017. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2018. var msgFactory = function () {
  2019. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2020. };
  2021. test_utils.openChatBoxFor(_converse, sender_jid);
  2022. var chatbox = _converse.chatboxes.get(sender_jid);
  2023. _converse.windowState = 'hidden';
  2024. _converse.chatboxes.onMessage(msgFactory());
  2025. expect(chatbox.get('num_unread')).toBe(1);
  2026. _converse.saveWindowState(null, 'focus');
  2027. expect(chatbox.get('num_unread')).toBe(0);
  2028. done();
  2029. }));
  2030. it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
  2031. mock.initConverseWithPromises(
  2032. null, ['rosterGroupsFetched'], {},
  2033. function (done, _converse) {
  2034. test_utils.createContacts(_converse, 'current');
  2035. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2036. var msgFactory = function () {
  2037. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2038. };
  2039. test_utils.openChatBoxFor(_converse, sender_jid);
  2040. var chatbox = _converse.chatboxes.get(sender_jid);
  2041. chatbox.save('scrolled', true);
  2042. _converse.windowState = 'hidden';
  2043. _converse.chatboxes.onMessage(msgFactory());
  2044. expect(chatbox.get('num_unread')).toBe(1);
  2045. _converse.saveWindowState(null, 'focus');
  2046. expect(chatbox.get('num_unread')).toBe(1);
  2047. done();
  2048. }));
  2049. });
  2050. describe("A RosterView's Unread Message Count", function () {
  2051. it("is updated when message is received and chatbox is scrolled up",
  2052. mock.initConverseWithPromises(
  2053. null, ['rosterGroupsFetched'], {},
  2054. function (done, _converse) {
  2055. test_utils.createContacts(_converse, 'current');
  2056. test_utils.openContactsPanel(_converse);
  2057. test_utils.waitUntil(function () {
  2058. return _converse.rosterview.$el.find('.roster-group').length;
  2059. }, 500)
  2060. .then(function () {
  2061. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2062. test_utils.openChatBoxFor(_converse, sender_jid);
  2063. var chatbox = _converse.chatboxes.get(sender_jid);
  2064. chatbox.save('scrolled', true);
  2065. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2066. _converse.chatboxes.onMessage(msg);
  2067. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2068. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2069. expect($msgIndicator.text()).toBe('1');
  2070. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  2071. _converse.chatboxes.onMessage(msg);
  2072. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2073. expect($msgIndicator.text()).toBe('2');
  2074. done();
  2075. });
  2076. }));
  2077. it("is updated when message is received and chatbox is minimized",
  2078. mock.initConverseWithPromises(
  2079. null, ['rosterGroupsFetched'], {},
  2080. function (done, _converse) {
  2081. test_utils.createContacts(_converse, 'current');
  2082. test_utils.openContactsPanel(_converse);
  2083. test_utils.waitUntil(function () {
  2084. return _converse.rosterview.$el.find('.roster-group').length;
  2085. }, 500)
  2086. .then(function () {
  2087. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2088. test_utils.openChatBoxFor(_converse, sender_jid);
  2089. var chatbox = _converse.chatboxes.get(sender_jid);
  2090. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2091. chatboxview.minimize();
  2092. var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
  2093. _converse.chatboxes.onMessage(msg);
  2094. var msgIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2095. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2096. expect($msgIndicator.text()).toBe('1');
  2097. msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
  2098. _converse.chatboxes.onMessage(msg);
  2099. $msgIndicator = $(_converse.rosterview.$el.find(msgIndicatorSelector));
  2100. expect($msgIndicator.text()).toBe('2');
  2101. done();
  2102. });
  2103. }));
  2104. it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
  2105. mock.initConverseWithPromises(
  2106. null, ['rosterGroupsFetched'], {},
  2107. function (done, _converse) {
  2108. test_utils.createContacts(_converse, 'current');
  2109. test_utils.openContactsPanel(_converse);
  2110. test_utils.waitUntil(function () {
  2111. return _converse.rosterview.$el.find('.roster-group').length;
  2112. }, 500)
  2113. .then(function () {
  2114. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2115. test_utils.openChatBoxFor(_converse, sender_jid);
  2116. var chatbox = _converse.chatboxes.get(sender_jid);
  2117. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2118. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
  2119. var selectMsgsIndicator = function () { return $(_converse.rosterview.$el.find(msgsIndicatorSelector)); };
  2120. var msgFactory = function () {
  2121. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2122. };
  2123. chatboxview.minimize();
  2124. _converse.chatboxes.onMessage(msgFactory());
  2125. expect(selectMsgsIndicator().text()).toBe('1');
  2126. _converse.chatboxes.onMessage(msgFactory());
  2127. expect(selectMsgsIndicator().text()).toBe('2');
  2128. chatboxview.maximize();
  2129. expect(selectMsgsIndicator().length).toBe(0);
  2130. done();
  2131. });
  2132. }));
  2133. it("is cleared when unread messages are viewed which were received in scrolled-up chatbox",
  2134. mock.initConverseWithPromises(
  2135. null, ['rosterGroupsFetched'], {},
  2136. function (done, _converse) {
  2137. test_utils.createContacts(_converse, 'current');
  2138. test_utils.openContactsPanel(_converse);
  2139. test_utils.waitUntil(function () {
  2140. return _converse.rosterview.$el.find('.roster-group').length;
  2141. }, 500)
  2142. .then(function () {
  2143. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2144. test_utils.openChatBoxFor(_converse, sender_jid);
  2145. var chatbox = _converse.chatboxes.get(sender_jid);
  2146. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2147. var msgFactory = function () {
  2148. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2149. };
  2150. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2151. selectMsgsIndicator = function () { return $(_converse.rosterview.$el.find(msgsIndicatorSelector)); };
  2152. chatbox.save('scrolled', true);
  2153. _converse.chatboxes.onMessage(msgFactory());
  2154. expect(selectMsgsIndicator().text()).toBe('1');
  2155. chatboxview.viewUnreadMessages();
  2156. _converse.rosterview.render();
  2157. expect(selectMsgsIndicator().length).toBe(0);
  2158. done();
  2159. });
  2160. }));
  2161. it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
  2162. mock.initConverseWithPromises(
  2163. null, ['rosterGroupsFetched'], {},
  2164. function (done, _converse) {
  2165. test_utils.createContacts(_converse, 'current');
  2166. test_utils.openContactsPanel(_converse);
  2167. test_utils.waitUntil(function () {
  2168. return _converse.rosterview.$el.find('.roster-group').length;
  2169. }, 500)
  2170. .then(function () {
  2171. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2172. test_utils.openChatBoxFor(_converse, sender_jid);
  2173. var chatbox = _converse.chatboxes.get(sender_jid);
  2174. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2175. var msgFactory = function () {
  2176. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2177. };
  2178. var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
  2179. selectMsgsIndicator = function () { return $(_converse.rosterview.$el.find(msgsIndicatorSelector)); };
  2180. chatbox.save('scrolled', true);
  2181. _converse.chatboxes.onMessage(msgFactory());
  2182. expect(selectMsgsIndicator().text()).toBe('1');
  2183. test_utils.openChatBoxFor(_converse, sender_jid);
  2184. expect(selectMsgsIndicator().text()).toBe('1');
  2185. done();
  2186. });
  2187. }));
  2188. });
  2189. describe("A Minimized ChatBoxView's Unread Message Count", function () {
  2190. it("is displayed when scrolled up chatbox is minimized after receiving unread messages",
  2191. mock.initConverseWithPromises(
  2192. null, ['rosterGroupsFetched'], {},
  2193. function (done, _converse) {
  2194. test_utils.createContacts(_converse, 'current');
  2195. test_utils.openContactsPanel(_converse);
  2196. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2197. test_utils.openChatBoxFor(_converse, sender_jid);
  2198. var msgFactory = function () {
  2199. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2200. };
  2201. var selectUnreadMsgCount = function () {
  2202. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  2203. return minimizedChatBoxView.$el.find('.chat-head-message-count');
  2204. };
  2205. var chatbox = _converse.chatboxes.get(sender_jid);
  2206. chatbox.save('scrolled', true);
  2207. _converse.chatboxes.onMessage(msgFactory());
  2208. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2209. chatboxview.minimize();
  2210. var $unreadMsgCount = selectUnreadMsgCount();
  2211. expect($unreadMsgCount.is(':visible')).toBeTruthy();
  2212. expect($unreadMsgCount.html()).toBe('1');
  2213. done();
  2214. }));
  2215. it("is incremented when message is received and windows is not focused",
  2216. mock.initConverseWithPromises(
  2217. null, ['rosterGroupsFetched'], {},
  2218. function (done, _converse) {
  2219. test_utils.createContacts(_converse, 'current');
  2220. test_utils.openContactsPanel(_converse);
  2221. var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
  2222. test_utils.openChatBoxFor(_converse, sender_jid);
  2223. var msgFactory = function () {
  2224. return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
  2225. };
  2226. var selectUnreadMsgCount = function () {
  2227. var minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
  2228. return minimizedChatBoxView.$el.find('.chat-head-message-count');
  2229. };
  2230. var chatboxview = _converse.chatboxviews.get(sender_jid);
  2231. chatboxview.minimize();
  2232. _converse.chatboxes.onMessage(msgFactory());
  2233. var $unreadMsgCount = selectUnreadMsgCount();
  2234. expect($unreadMsgCount.is(':visible')).toBeTruthy();
  2235. expect($unreadMsgCount.html()).toBe('1');
  2236. done();
  2237. }));
  2238. });
  2239. });
  2240. }));