roster.js 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. (function (root, factory) {
  2. define(["jquery.noconflict", "jasmine", "mock", "converse-core", "test-utils"], factory);
  3. } (this, function ($, jasmine, mock, converse, test_utils) {
  4. var _ = converse.env._;
  5. var $pres = converse.env.$pres;
  6. var $msg = converse.env.$msg;
  7. var $iq = converse.env.$iq;
  8. var u = converse.env.utils;
  9. var checkHeaderToggling = function ($group) {
  10. var $toggle = $group.find('a.group-toggle');
  11. expect(u.isVisible($group[0])).toBeTruthy();
  12. expect($group.find('ul.collapsed').length).toBe(0);
  13. expect($toggle.hasClass('icon-closed')).toBeFalsy();
  14. expect($toggle.hasClass('icon-opened')).toBeTruthy();
  15. $toggle.click();
  16. return test_utils.waitUntil(function () {
  17. return $group.find('ul.collapsed').length === 1;
  18. }, 500).then(function () {
  19. expect($toggle.hasClass('icon-closed')).toBeTruthy();
  20. expect($toggle.hasClass('icon-opened')).toBeFalsy();
  21. $toggle.click();
  22. return test_utils.waitUntil(function () {
  23. return $group.find('li').length === $group.find('li:visible').length
  24. }, 500);
  25. }).then(function () {
  26. expect($toggle.hasClass('icon-closed')).toBeFalsy();
  27. expect($toggle.hasClass('icon-opened')).toBeTruthy();
  28. });
  29. };
  30. describe("The Contacts Roster", function () {
  31. describe("The live filter", function () {
  32. it("will only appear when roster contacts flow over the visible area",
  33. mock.initConverseWithPromises(
  34. null, ['rosterGroupsFetched'], {},
  35. function (done, _converse) {
  36. var $filter = _converse.rosterview.$('.roster-filter');
  37. var names = mock.cur_names;
  38. test_utils.openControlBox();
  39. _converse.rosterview.update(); // XXX: Will normally called as event handler
  40. expect($filter.length).toBe(1);
  41. test_utils.waitUntil(function () {
  42. return !$filter.is(':visible');
  43. }).then(function () {
  44. for (var i=0; i<names.length; i++) {
  45. _converse.roster.create({
  46. ask: null,
  47. fullname: names[i],
  48. jid: names[i].replace(/ /g,'.').toLowerCase() + '@localhost',
  49. requesting: 'false',
  50. subscription: 'both'
  51. });
  52. _converse.rosterview.update(); // XXX: Will normally called as event handler
  53. }
  54. $.fn.hasScrollBar = function() {
  55. if (!$.contains(document, this.get(0))) {
  56. return false;
  57. }
  58. if(this.parent().height() < this.get(0).scrollHeight) {
  59. return true;
  60. }
  61. return false;
  62. };
  63. return test_utils.waitUntil(function () {
  64. if ($(_converse.rosterview.roster_el).hasScrollBar()) {
  65. return $filter.is(':visible');
  66. } else {
  67. return !$filter.is(':visible');
  68. }
  69. }).then(function () {
  70. done();
  71. });
  72. });
  73. }));
  74. it("can be used to filter the contacts shown",
  75. mock.initConverseWithPromises(
  76. null, ['rosterGroupsFetched'], {},
  77. function (done, _converse) {
  78. _converse.roster_groups = true;
  79. test_utils.openControlBox();
  80. test_utils.createGroupedContacts(_converse);
  81. var $filter = _converse.rosterview.$('.roster-filter');
  82. var $roster = $(_converse.rosterview.roster_el);
  83. _converse.rosterview.filter_view.delegateEvents();
  84. var promise = test_utils.waitUntil(function () {
  85. return $roster.find('li:visible').length === 15;
  86. }, 600).then(function (contacts) {
  87. expect($roster.find('ul.roster-group-contacts:visible').length).toBe(5);
  88. $filter.val("candice");
  89. $filter.trigger('keydown');
  90. return test_utils.waitUntil(function () {
  91. return $roster.find('li:visible').length === 1;
  92. }, 600);
  93. }).then(function (contacts) {
  94. // Only one roster contact is now visible
  95. expect($roster.find('li:visible').length).toBe(1);
  96. expect($roster.find('li:visible').eq(0).text().trim()).toBe('Candice van der Knijff');
  97. // Only one foster group is still visible
  98. expect($roster.find('.roster-group:visible').length).toBe(1);
  99. expect(_.trim($roster.find('.roster-group:visible a.group-toggle').eq(0).text())).toBe('colleagues');
  100. $filter = _converse.rosterview.$('.roster-filter');
  101. $filter.val("an");
  102. $filter.trigger('keydown');
  103. return test_utils.waitUntil(function () {
  104. return $roster.find('li:visible').length === 5;
  105. }, 600)
  106. }).then(function (contacts) {
  107. // Five roster contact is now visible
  108. expect($roster.find('li:visible').length).toBe(5);
  109. // Four groups are still visible
  110. var $groups = $roster.find('.roster-group:visible a.group-toggle');
  111. expect($groups.length).toBe(4);
  112. expect(_.trim($groups.eq(0).text())).toBe('colleagues');
  113. expect(_.trim($groups.eq(1).text())).toBe('Family');
  114. expect(_.trim($groups.eq(2).text())).toBe('friends & acquaintences');
  115. expect(_.trim($groups.eq(3).text())).toBe('ænemies');
  116. $filter = _converse.rosterview.$('.roster-filter');
  117. $filter.val("xxx");
  118. $filter.trigger('keydown');
  119. return test_utils.waitUntil(function () {
  120. return $roster.find('li:visible').length === 0;
  121. }, 600)
  122. }).then(function () {
  123. expect($roster.find('ul.roster-group-contacts:visible a.group-toggle').length).toBe(0);
  124. $filter = _converse.rosterview.$('.roster-filter');
  125. $filter.val(""); // Check that contacts are shown again, when the filter string is cleared.
  126. $filter.trigger('keydown');
  127. return test_utils.waitUntil(function () {
  128. return $roster.find('li:visible').length === 15;
  129. }, 600)
  130. }).then(function () {
  131. expect($roster.find('ul.roster-group-contacts:visible').length).toBe(5);
  132. _converse.roster_groups = false;
  133. done();
  134. });
  135. }));
  136. it("will also filter out contacts added afterwards",
  137. mock.initConverseWithPromises(
  138. null, ['rosterGroupsFetched'], {},
  139. function (done, _converse) {
  140. test_utils.openControlBox();
  141. test_utils.createGroupedContacts(_converse);
  142. var $filter = _converse.rosterview.$('.roster-filter');
  143. var $roster = $(_converse.rosterview.roster_el);
  144. _converse.rosterview.filter_view.delegateEvents();
  145. test_utils.waitUntil(function () {
  146. return $roster.find('li:visible').length === 15;
  147. }, 300).then(function (contacts) {
  148. $filter.val("an");
  149. $filter.trigger('keydown');
  150. return test_utils.waitUntil(function () {
  151. return $roster.find('li:visible').length === 5;
  152. }, 500)
  153. }).then(function (contacts) {
  154. // Five roster contact is now visible
  155. expect($roster.find('li:visible').length).toBe(5);
  156. // Four groups are still visible
  157. var $groups = $roster.find('.roster-group:visible a.group-toggle');
  158. expect($groups.length).toBe(4);
  159. expect(_.trim($groups.eq(0).text())).toBe('colleagues');
  160. expect(_.trim($groups.eq(1).text())).toBe('Family');
  161. expect(_.trim($groups.eq(2).text())).toBe('friends & acquaintences');
  162. expect(_.trim($groups.eq(3).text())).toBe('ænemies');
  163. _converse.roster.create({
  164. jid: 'latecomer@localhost',
  165. subscription: 'both',
  166. ask: null,
  167. groups: ['newgroup'],
  168. fullname: 'Marty McLatecomer'
  169. });
  170. return test_utils.waitUntil(function () {
  171. return $roster.find('.roster-group[data-group="newgroup"] li').length;
  172. }, 300);
  173. }).then(function (contacts) {
  174. // The "newgroup" group doesn't appear
  175. expect($roster.find('.roster-group:visible').length).toBe(4);
  176. expect($roster.find('.roster-group').length).toBe(6);
  177. done();
  178. });
  179. }));
  180. it("can be used to filter the groups shown",
  181. mock.initConverseWithPromises(
  182. null, ['rosterGroupsFetched'], {},
  183. function (done, _converse) {
  184. var $filter;
  185. var $type;
  186. _converse.roster_groups = true;
  187. test_utils.openControlBox();
  188. test_utils.createGroupedContacts(_converse);
  189. _converse.rosterview.filter_view.delegateEvents();
  190. $filter = _converse.rosterview.$('.roster-filter');
  191. var $roster = $(_converse.rosterview.roster_el);
  192. $type = _converse.rosterview.$('.filter-type');
  193. $type.val('groups');
  194. test_utils.waitUntil(function () {
  195. return $roster.find('li:visible').length === 15;
  196. }, 600).then(function () {
  197. expect($roster.find('div.roster-group:visible a.group-toggle').length).toBe(5);
  198. $filter.val("colleagues");
  199. $filter.trigger('keydown');
  200. return test_utils.waitUntil(function () {
  201. return $roster.find('div.roster-group:not(.collapsed) a.group-toggle').length === 1;
  202. }, 600);
  203. }).then(function () {
  204. expect(_.trim($roster.find('div.roster-group:not(.collapsed) a').eq(0).text())).toBe('colleagues');
  205. expect($roster.find('div.roster-group:not(.collapsed) li:visible').length).toBe(3);
  206. // Check that all contacts under the group are shown
  207. expect($roster.find('div.roster-group:not(.collapsed) li:hidden').length).toBe(0);
  208. $filter = _converse.rosterview.$('.roster-filter');
  209. $filter.val("xxx").trigger('keydown');
  210. return test_utils.waitUntil(function () {
  211. return $roster.find('div.roster-group.collapsed a.group-toggle').length === 5;
  212. }, 700);
  213. }).then(function () {
  214. expect($roster.find('div.roster-group:not(.collapsed) a').length).toBe(0);
  215. $filter = _converse.rosterview.$('.roster-filter');
  216. $filter.val(""); // Check that groups are shown again, when the filter string is cleared.
  217. $filter.trigger('keydown');
  218. return test_utils.waitUntil(function () {
  219. return $roster.find('div.roster-group.collapsed a.group-toggle').length === 0;
  220. }, 600);
  221. }).then(function () {
  222. expect($roster.find('div.roster-group:not(collapsed)').length).toBe(5);
  223. expect($roster.find('div.roster-group:not(collapsed) li').length).toBe(15);
  224. done();
  225. });
  226. }));
  227. it("has a button with which its contents can be cleared",
  228. mock.initConverseWithPromises(
  229. null, ['rosterGroupsFetched'], {},
  230. function (done, _converse) {
  231. _converse.roster_groups = true;
  232. test_utils.openControlBox();
  233. test_utils.createGroupedContacts(_converse);
  234. var $filter = _converse.rosterview.$('.roster-filter');
  235. _converse.rosterview.filter_view.delegateEvents();
  236. $filter.val("xxx");
  237. $filter.trigger('keydown');
  238. expect($filter.hasClass("x")).toBeFalsy();
  239. $filter = _converse.rosterview.$('.roster-filter');
  240. test_utils.waitUntil(function () {
  241. return _converse.rosterview.$('.roster-filter').hasClass("x");
  242. }, 900).then(function () {
  243. var $filter = _converse.rosterview.$('.roster-filter');
  244. $filter.addClass("onX").click();
  245. return test_utils.waitUntil(function () {
  246. return !_converse.rosterview.$('.roster-filter').hasClass("x");
  247. }, 900)
  248. }).then(function () {
  249. expect(document.querySelector('.roster-filter').value).toBe("");
  250. done();
  251. });
  252. }));
  253. it("can be used to filter contacts by their chat state",
  254. mock.initConverseWithPromises(
  255. null, ['rosterGroupsFetched'], {},
  256. function (done, _converse) {
  257. var $filter;
  258. _converse.roster_groups = true;
  259. test_utils.createGroupedContacts(_converse);
  260. var jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
  261. _converse.roster.get(jid).set('chat_status', 'online');
  262. test_utils.openControlBox();
  263. _converse.rosterview.filter_view.delegateEvents();
  264. var $type = _converse.rosterview.$('.filter-type');
  265. $type.val('state').trigger('change');
  266. $filter = _converse.rosterview.$('.state-type');
  267. var $roster = $(_converse.rosterview.roster_el);
  268. test_utils.waitUntil(function () {
  269. return $roster.find('li:visible').length === 15;
  270. }, 500).then(function () {
  271. expect($roster.find('ul.roster-group-contacts:visible').length).toBe(5);
  272. $filter.val("online");
  273. $filter.trigger('change');
  274. return test_utils.waitUntil(function () {
  275. return $roster.find('li:visible').length === 1;
  276. }, 500)
  277. }).then(function () {
  278. expect($roster.find('li:visible').eq(0).text().trim()).toBe('Rinse Sommer');
  279. expect($roster.find('ul.roster-group-contacts:visible').length).toBe(1);
  280. var $type = _converse.rosterview.$('.filter-type');
  281. $type.val('contacts').trigger('change');
  282. done();
  283. });
  284. }));
  285. });
  286. describe("A Roster Group", function () {
  287. it("can be used to organize existing contacts",
  288. mock.initConverseWithPromises(
  289. null, ['rosterGroupsFetched'], {},
  290. function (done, _converse) {
  291. _converse.roster_groups = true;
  292. spyOn(_converse, 'emit');
  293. spyOn(_converse.rosterview, 'update').and.callThrough();
  294. _converse.rosterview.render();
  295. test_utils.openControlBox();
  296. test_utils.createContacts(_converse, 'pending');
  297. test_utils.createContacts(_converse, 'requesting');
  298. test_utils.createGroupedContacts(_converse);
  299. // Check that the groups appear alphabetically and that
  300. // requesting and pending contacts are last.
  301. test_utils.waitUntil(function () {
  302. return _converse.rosterview.$el.find('.roster-group:visible a.group-toggle').length;
  303. }, 500).then(function () {
  304. var group_titles = $.map(
  305. _converse.rosterview.$el.find('.roster-group:visible a.group-toggle'),
  306. function (o) { return $(o).text().trim(); }
  307. );
  308. expect(group_titles).toEqual([
  309. "Contact requests",
  310. "colleagues",
  311. "Family",
  312. "friends & acquaintences",
  313. "ænemies",
  314. "Ungrouped",
  315. "Pending contacts"
  316. ]);
  317. // Check that usernames appear alphabetically per group
  318. _.each(_.keys(mock.groups), function (name) {
  319. var $contacts = _converse.rosterview.$('.roster-group[data-group="'+name+'"] ul');
  320. var names = $.map($contacts, function (o) { return $(o).text().trim(); });
  321. expect(names).toEqual(_.clone(names).sort());
  322. });
  323. done();
  324. });
  325. }));
  326. it("gets created when a contact's \"groups\" attribute changes",
  327. mock.initConverseWithPromises(
  328. null, ['rosterGroupsFetched'], {},
  329. function (done, _converse) {
  330. _converse.roster_groups = true;
  331. spyOn(_converse, 'emit');
  332. spyOn(_converse.rosterview, 'update').and.callThrough();
  333. _converse.rosterview.render();
  334. test_utils.openControlBox();
  335. _converse.roster.create({
  336. jid: 'groupchanger@localhost',
  337. subscription: 'both',
  338. ask: null,
  339. groups: ['firstgroup'],
  340. fullname: 'George Groupchanger'
  341. });
  342. // Check that the groups appear alphabetically and that
  343. // requesting and pending contacts are last.
  344. test_utils.waitUntil(function () {
  345. return _converse.rosterview.$el.find('.roster-group:visible a.group-toggle').length;
  346. }, 500).then(function () {
  347. var group_titles = $.map(
  348. _converse.rosterview.$el.find('.roster-group:visible a.group-toggle'),
  349. function (o) { return $(o).text().trim(); }
  350. );
  351. expect(group_titles).toEqual(['firstgroup']);
  352. var contact = _converse.roster.get('groupchanger@localhost');
  353. contact.set({'groups': ['secondgroup']});
  354. return test_utils.waitUntil(function () {
  355. return _converse.rosterview.$el.find('.roster-group[data-group="secondgroup"]:visible a.group-toggle').length;
  356. }, 500);
  357. }).then(function () {
  358. var group_titles = $.map(
  359. _converse.rosterview.$el.find('.roster-group:visible a.group-toggle'),
  360. function (o) { return $(o).text().trim(); }
  361. );
  362. expect(group_titles).toEqual(['secondgroup']);
  363. done();
  364. });
  365. }));
  366. it("can share contacts with other roster groups",
  367. mock.initConverseWithPromises(
  368. null, ['rosterGroupsFetched'], {},
  369. function (done, _converse) {
  370. _converse.roster_groups = true;
  371. var groups = ['colleagues', 'friends'];
  372. spyOn(_converse, 'emit');
  373. spyOn(_converse.rosterview, 'update').and.callThrough();
  374. test_utils.openControlBox();
  375. _converse.rosterview.render();
  376. for (var i=0; i<mock.cur_names.length; i++) {
  377. _converse.roster.create({
  378. jid: mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost',
  379. subscription: 'both',
  380. ask: null,
  381. groups: groups,
  382. fullname: mock.cur_names[i]
  383. });
  384. }
  385. test_utils.waitUntil(function () {
  386. return _converse.rosterview.$el.find('li:visible').length === 30;
  387. }, 600).then(function () {
  388. // Check that usernames appear alphabetically per group
  389. _.each(groups, function (name) {
  390. var $contacts = _converse.rosterview.$('.roster-group[data-group="'+name+'"] li');
  391. var names = $.map($contacts, function (o) { return $(o).text().trim(); });
  392. expect(names).toEqual(_.clone(names).sort());
  393. expect(names.length).toEqual(mock.cur_names.length);
  394. });
  395. done();
  396. });
  397. }));
  398. it("remembers whether it is closed or opened",
  399. mock.initConverseWithPromises(
  400. null, ['rosterGroupsFetched'], {},
  401. function (done, _converse) {
  402. _converse.roster_groups = true;
  403. test_utils.openControlBox();
  404. var i=0, j=0;
  405. var groups = {
  406. 'colleagues': 3,
  407. 'friends & acquaintences': 3,
  408. 'Ungrouped': 2
  409. };
  410. _.each(_.keys(groups), function (name) {
  411. j = i;
  412. for (i=j; i<j+groups[name]; i++) {
  413. _converse.roster.create({
  414. jid: mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost',
  415. subscription: 'both',
  416. ask: null,
  417. groups: name === 'ungrouped'? [] : [name],
  418. fullname: mock.cur_names[i]
  419. });
  420. }
  421. });
  422. var view = _converse.rosterview.get('colleagues');
  423. var $toggle = view.$el.find('a.group-toggle');
  424. expect(view.model.get('state')).toBe('opened');
  425. $toggle.click();
  426. return test_utils.waitUntil(function () {
  427. return view.model.get('state') === 'closed';
  428. }, 500).then(function () {
  429. $toggle.click();
  430. return test_utils.waitUntil(function () {
  431. return view.model.get('state') === 'opened';
  432. }, 500)
  433. }).then(function () {
  434. done();
  435. });
  436. }));
  437. });
  438. describe("Pending Contacts", function () {
  439. function _addContacts (_converse) {
  440. // Must be initialized, so that render is called and documentFragment set up.
  441. test_utils.createContacts(_converse, 'pending');
  442. test_utils.openControlBox();
  443. test_utils.openContactsPanel(_converse);
  444. }
  445. it("can be collapsed under their own header",
  446. mock.initConverseWithPromises(
  447. null, ['rosterGroupsFetched'], {},
  448. function (done, _converse) {
  449. _addContacts(_converse);
  450. test_utils.waitUntil(function () {
  451. return _converse.rosterview.$el.find('.roster-group:visible li').length;
  452. }, 500).then(function () {
  453. checkHeaderToggling.apply(
  454. _converse,
  455. [_converse.rosterview.get('Pending contacts').$el]
  456. ).then(done);
  457. });
  458. }));
  459. it("can be added to the roster",
  460. mock.initConverseWithPromises(
  461. null, ['rosterGroupsFetched'], {},
  462. function (done, _converse) {
  463. spyOn(_converse, 'emit');
  464. spyOn(_converse.rosterview, 'update').and.callThrough();
  465. test_utils.openControlBox();
  466. _converse.roster.create({
  467. jid: mock.pend_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
  468. subscription: 'none',
  469. ask: 'subscribe',
  470. fullname: mock.pend_names[0]
  471. });
  472. expect(_converse.rosterview.update).toHaveBeenCalled();
  473. done();
  474. }));
  475. it("are shown in the roster when show_only_online_users",
  476. mock.initConverseWithPromises(
  477. null, ['rosterGroupsFetched'], {},
  478. function (done, _converse) {
  479. _converse.show_only_online_users = true;
  480. test_utils.openControlBox();
  481. spyOn(_converse.rosterview, 'update').and.callThrough();
  482. _addContacts(_converse);
  483. test_utils.waitUntil(function () {
  484. return _converse.rosterview.$el.find('li:visible').length;
  485. }, 700).then(function () {
  486. expect(_converse.rosterview.$el.is(':visible')).toEqual(true);
  487. expect(_converse.rosterview.update).toHaveBeenCalled();
  488. expect(_converse.rosterview.$el.find('li:visible').length).toBe(3);
  489. expect(_converse.rosterview.$el.find('ul.roster-group-contacts:visible').length).toBe(1);
  490. done();
  491. });
  492. }));
  493. it("are shown in the roster when hide_offline_users",
  494. mock.initConverseWithPromises(
  495. null, ['rosterGroupsFetched'], {},
  496. function (done, _converse) {
  497. _converse.hide_offline_users = true;
  498. spyOn(_converse.rosterview, 'update').and.callThrough();
  499. _addContacts(_converse);
  500. test_utils.waitUntil(function () {
  501. return _converse.rosterview.$el.find('li:visible').length;
  502. }, 500)
  503. .then(function () {
  504. expect(_converse.rosterview.update).toHaveBeenCalled();
  505. expect(_converse.rosterview.$el.is(':visible')).toBe(true);
  506. expect(_converse.rosterview.$el.find('li:visible').length).toBe(3);
  507. expect(_converse.rosterview.$el.find('ul.roster-group-contacts:visible').length).toBe(1);
  508. done();
  509. });
  510. }));
  511. it("can be removed by the user",
  512. mock.initConverseWithPromises(
  513. null, ['rosterGroupsFetched'], {},
  514. function (done, _converse) {
  515. _addContacts(_converse);
  516. var name = mock.pend_names[0];
  517. var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
  518. var contact = _converse.roster.get(jid);
  519. spyOn(window, 'confirm').and.returnValue(true);
  520. spyOn(contact, 'unauthorize').and.callFake(function () { return contact; });
  521. spyOn(contact, 'removeFromRoster');
  522. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
  523. if (typeof callback === "function") { return callback(); }
  524. });
  525. test_utils.waitUntil(function () {
  526. return _converse.rosterview.$el.find(".pending-contact-name:contains('"+name+"')").length;
  527. }, 700).then(function () {
  528. _converse.rosterview.$el.find(".pending-contact-name:contains('"+name+"')")
  529. .parent().siblings('.remove-xmpp-contact').click();
  530. return test_utils.waitUntil(function () {
  531. return _converse.rosterview.$el.find(".pending-contact-name:contains('"+name+"')").length === 0
  532. }, 700)
  533. }).then(function () {
  534. expect(window.confirm).toHaveBeenCalled();
  535. expect(_converse.connection.sendIQ).toHaveBeenCalled();
  536. expect(contact.removeFromRoster).toHaveBeenCalled();
  537. expect(_converse.connection.sendIQ).toHaveBeenCalled();
  538. done();
  539. });
  540. }));
  541. it("do not have a header if there aren't any",
  542. mock.initConverseWithPromises(
  543. null, ['rosterGroupsFetched'], {},
  544. function (done, _converse) {
  545. test_utils.openControlBox();
  546. var name = mock.pend_names[0];
  547. _converse.roster.create({
  548. jid: name.replace(/ /g,'.').toLowerCase() + '@localhost',
  549. subscription: 'none',
  550. ask: 'subscribe',
  551. fullname: name
  552. });
  553. spyOn(window, 'confirm').and.returnValue(true);
  554. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
  555. if (typeof callback === "function") { return callback(); }
  556. });
  557. test_utils.waitUntil(function () {
  558. var $pending_contacts = _converse.rosterview.get('Pending contacts').$el;
  559. return $pending_contacts.is(':visible') && $pending_contacts.find('li:visible').length;
  560. }, 700).then(function () {
  561. _converse.rosterview.$el.find(".pending-contact-name:contains('"+name+"')")
  562. .parent().siblings('.remove-xmpp-contact').click();
  563. expect(window.confirm).toHaveBeenCalled();
  564. expect(_converse.connection.sendIQ).toHaveBeenCalled();
  565. expect(_converse.rosterview.get('Pending contacts').$el.is(':visible')).toEqual(false);
  566. done();
  567. });
  568. }));
  569. it("is shown when a new private message is received",
  570. mock.initConverseWithPromises(
  571. null, ['rosterGroupsFetched'], {},
  572. function (done, _converse) {
  573. _addContacts(_converse);
  574. var name;
  575. spyOn(window, 'confirm').and.returnValue(true);
  576. for (var i=0; i<mock.pend_names.length; i++) {
  577. name = mock.pend_names[i];
  578. _converse.rosterview.$el.find(".pending-contact-name:contains('"+name+"')")
  579. .parent().siblings('.remove-xmpp-contact').click();
  580. }
  581. expect(_converse.rosterview.$el.find('#pending-xmpp-contacts').is(':visible')).toBeFalsy();
  582. done();
  583. }));
  584. it("can be added to the roster and they will be sorted alphabetically",
  585. mock.initConverseWithPromises(
  586. null, ['rosterGroupsFetched'], {},
  587. function (done, _converse) {
  588. var i, t;
  589. test_utils.openControlBox();
  590. spyOn(_converse, 'emit');
  591. spyOn(_converse.rosterview, 'update').and.callThrough();
  592. for (i=0; i<mock.pend_names.length; i++) {
  593. _converse.roster.create({
  594. jid: mock.pend_names[i].replace(/ /g,'.').toLowerCase() + '@localhost',
  595. subscription: 'none',
  596. ask: 'subscribe',
  597. fullname: mock.pend_names[i]
  598. });
  599. expect(_converse.rosterview.update).toHaveBeenCalled();
  600. }
  601. return test_utils.waitUntil(function () {
  602. return _converse.rosterview.get('Pending contacts').$el.find('li:visible').length;
  603. }, 700).then(function () {
  604. // Check that they are sorted alphabetically
  605. t = _.reduce(_converse.rosterview.get('Pending contacts').$el.find('.pending-xmpp-contact span'),
  606. function (result, value) {
  607. return result + _.trim(value.textContent);
  608. }, '');
  609. expect(t).toEqual(mock.pend_names.slice(0,i+1).sort().join(''));
  610. done();
  611. });
  612. }));
  613. });
  614. describe("Existing Contacts", function () {
  615. var _addContacts = function (_converse) {
  616. test_utils.createContacts(_converse, 'current')
  617. .openControlBox()
  618. .openContactsPanel(_converse);
  619. };
  620. it("can be collapsed under their own header",
  621. mock.initConverseWithPromises(
  622. null, ['rosterGroupsFetched'], {},
  623. function (done, _converse) {
  624. _addContacts(_converse);
  625. test_utils.waitUntil(function () {
  626. return _converse.rosterview.$el.find('li:visible').length;
  627. }, 500).then(function () {
  628. checkHeaderToggling.apply(
  629. _converse,
  630. [_converse.rosterview.$el.find('.roster-group')]
  631. ).then(done);
  632. });
  633. }));
  634. it("will be hidden when appearing under a collapsed group",
  635. mock.initConverseWithPromises(
  636. null, ['rosterGroupsFetched'], {},
  637. function (done, _converse) {
  638. _converse.roster_groups = false;
  639. _addContacts(_converse);
  640. test_utils.waitUntil(function () {
  641. return _converse.rosterview.$el.find('li:visible').length;
  642. }, 500)
  643. .then(function () {
  644. _converse.rosterview.$el.find('.roster-group a.group-toggle').click();
  645. var name = "Max Mustermann";
  646. var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
  647. _converse.roster.create({
  648. ask: null,
  649. fullname: name,
  650. jid: jid,
  651. requesting: false,
  652. subscription: 'both'
  653. });
  654. var view = _converse.rosterview.get('My contacts').get(jid);
  655. expect(view.$el.is(':visible')).toBe(false);
  656. done();
  657. });
  658. }));
  659. it("can be added to the roster and they will be sorted alphabetically",
  660. mock.initConverseWithPromises(
  661. null, ['rosterGroupsFetched'], {},
  662. function (done, _converse) {
  663. test_utils.openControlBox();
  664. spyOn(_converse.rosterview, 'update').and.callThrough();
  665. for (var i=0; i<mock.cur_names.length; i++) {
  666. _converse.roster.create({
  667. jid: mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost',
  668. subscription: 'both',
  669. ask: null,
  670. fullname: mock.cur_names[i]
  671. });
  672. expect(_converse.rosterview.update).toHaveBeenCalled();
  673. }
  674. test_utils.waitUntil(function () {
  675. return _converse.rosterview.$el.find('li').length;
  676. }, 600).then(function () {
  677. // Check that they are sorted alphabetically
  678. var t = _.reduce(_converse.rosterview.$('.roster-group')
  679. .find('.current-xmpp-contact.offline a.open-chat'),
  680. function (result, value) {
  681. return result + _.trim(value.textContent);
  682. }, '');
  683. expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
  684. done();
  685. });
  686. }));
  687. it("can be removed by the user",
  688. mock.initConverseWithPromises(
  689. null, ['rosterGroupsFetched'], {},
  690. function (done, _converse) {
  691. _addContacts(_converse);
  692. test_utils.waitUntil(function () {
  693. return _converse.rosterview.$el.find('li').length;
  694. }, 500).then(function () {
  695. var name = mock.cur_names[0];
  696. var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
  697. var contact = _converse.roster.get(jid);
  698. spyOn(window, 'confirm').and.returnValue(true);
  699. spyOn(contact, 'removeFromRoster');
  700. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
  701. if (typeof callback === "function") { return callback(); }
  702. });
  703. _converse.rosterview.$el.find(".open-chat:contains('"+name+"')")
  704. .parent().find('.remove-xmpp-contact').click();
  705. expect(window.confirm).toHaveBeenCalled();
  706. expect(_converse.connection.sendIQ).toHaveBeenCalled();
  707. expect(contact.removeFromRoster).toHaveBeenCalled();
  708. expect(_converse.rosterview.$el.find(".open-chat:contains('"+name+"')").length).toEqual(0);
  709. done();
  710. });
  711. }));
  712. it("do not have a header if there aren't any",
  713. mock.initConverseWithPromises(
  714. null, ['rosterGroupsFetched'], {},
  715. function (done, _converse) {
  716. test_utils.openControlBox();
  717. var name = mock.cur_names[0];
  718. var contact;
  719. contact = _converse.roster.create({
  720. jid: name.replace(/ /g,'.').toLowerCase() + '@localhost',
  721. subscription: 'both',
  722. ask: null,
  723. fullname: name
  724. });
  725. test_utils.waitUntil(function () {
  726. return _converse.rosterview.$el.find('.roster-group:visible li').length;
  727. }, 700).then(function () {
  728. spyOn(window, 'confirm').and.returnValue(true);
  729. spyOn(contact, 'removeFromRoster');
  730. spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback) {
  731. if (typeof callback === "function") { return callback(); }
  732. });
  733. expect(_converse.rosterview.$el.find('.roster-group').css('display')).toEqual('block');
  734. _converse.rosterview.$el.find(".open-chat:contains('"+name+"')")
  735. .parent().find('.remove-xmpp-contact').click();
  736. expect(window.confirm).toHaveBeenCalled();
  737. expect(_converse.connection.sendIQ).toHaveBeenCalled();
  738. expect(contact.removeFromRoster).toHaveBeenCalled();
  739. expect(_converse.rosterview.$el.find('.roster-group').length).toEqual(0);
  740. done();
  741. });
  742. }));
  743. it("can change their status to online and be sorted alphabetically",
  744. mock.initConverseWithPromises(
  745. null, ['rosterGroupsFetched'], {},
  746. function (done, _converse) {
  747. _addContacts(_converse);
  748. test_utils.waitUntil(function () {
  749. return _converse.rosterview.$el.find('.roster-group li').length;
  750. }, 700).then(function () {
  751. var jid, t;
  752. spyOn(_converse, 'emit');
  753. spyOn(_converse.rosterview, 'update').and.callThrough();
  754. var $roster = _converse.rosterview.$el;
  755. for (var i=0; i<mock.cur_names.length; i++) {
  756. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  757. _converse.roster.get(jid).set('chat_status', 'online');
  758. expect(_converse.rosterview.update).toHaveBeenCalled();
  759. // Check that they are sorted alphabetically
  760. t = _.reduce($roster.find('.roster-group').find('.current-xmpp-contact.online a.open-chat'), function (result, value) {
  761. return result + _.trim(value.textContent);
  762. }, '');
  763. expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
  764. }
  765. done();
  766. });
  767. }));
  768. it("can change their status to busy and be sorted alphabetically",
  769. mock.initConverseWithPromises(
  770. null, ['rosterGroupsFetched'], {},
  771. function (done, _converse) {
  772. _addContacts(_converse);
  773. test_utils.waitUntil(function () {
  774. return _converse.rosterview.$el.find('.roster-group li').length;
  775. }, 700).then(function () {
  776. var jid, t;
  777. spyOn(_converse, 'emit');
  778. spyOn(_converse.rosterview, 'update').and.callThrough();
  779. var $roster = _converse.rosterview.$el;
  780. for (var i=0; i<mock.cur_names.length; i++) {
  781. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  782. _converse.roster.get(jid).set('chat_status', 'dnd');
  783. expect(_converse.rosterview.update).toHaveBeenCalled();
  784. // Check that they are sorted alphabetically
  785. t = _.reduce($roster.find('.roster-group .current-xmpp-contact.dnd a.open-chat'),
  786. function (result, value) {
  787. return result + _.trim(value.textContent);
  788. }, '');
  789. expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
  790. }
  791. done();
  792. });
  793. }));
  794. it("can change their status to away and be sorted alphabetically",
  795. mock.initConverseWithPromises(
  796. null, ['rosterGroupsFetched'], {},
  797. function (done, _converse) {
  798. _addContacts(_converse);
  799. test_utils.waitUntil(function () {
  800. return _converse.rosterview.$el.find('.roster-group li').length;
  801. }, 700).then(function () {
  802. var jid, t;
  803. spyOn(_converse, 'emit');
  804. spyOn(_converse.rosterview, 'update').and.callThrough();
  805. var $roster = _converse.rosterview.$el;
  806. for (var i=0; i<mock.cur_names.length; i++) {
  807. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  808. _converse.roster.get(jid).set('chat_status', 'away');
  809. expect(_converse.rosterview.update).toHaveBeenCalled();
  810. // Check that they are sorted alphabetically
  811. t = _.reduce($roster.find('.roster-group .current-xmpp-contact.away a.open-chat'),
  812. function (result, value) {
  813. return result + _.trim(value.textContent);
  814. }, '');
  815. expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
  816. }
  817. done();
  818. });
  819. }));
  820. it("can change their status to xa and be sorted alphabetically",
  821. mock.initConverseWithPromises(
  822. null, ['rosterGroupsFetched'], {},
  823. function (done, _converse) {
  824. _addContacts(_converse);
  825. test_utils.waitUntil(function () {
  826. return _converse.rosterview.$el.find('.roster-group li').length;
  827. }, 700).then(function () {
  828. var jid, t;
  829. spyOn(_converse, 'emit');
  830. spyOn(_converse.rosterview, 'update').and.callThrough();
  831. var $roster = _converse.rosterview.$el;
  832. for (var i=0; i<mock.cur_names.length; i++) {
  833. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  834. _converse.roster.get(jid).set('chat_status', 'xa');
  835. expect(_converse.rosterview.update).toHaveBeenCalled();
  836. // Check that they are sorted alphabetically
  837. t = _.reduce($roster.find('.roster-group .current-xmpp-contact.xa a.open-chat'),
  838. function (result, value) {
  839. return result + _.trim(value.textContent);
  840. }, '');
  841. expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
  842. }
  843. done();
  844. });
  845. }));
  846. it("can change their status to unavailable and be sorted alphabetically",
  847. mock.initConverseWithPromises(
  848. null, ['rosterGroupsFetched'], {},
  849. function (done, _converse) {
  850. _addContacts(_converse);
  851. test_utils.waitUntil(function () {
  852. return _converse.rosterview.$el.find('.roster-group li').length;
  853. }, 500)
  854. .then(function () {
  855. var jid, t;
  856. spyOn(_converse, 'emit');
  857. spyOn(_converse.rosterview, 'update').and.callThrough();
  858. var $roster = _converse.rosterview.$el;
  859. for (var i=0; i<mock.cur_names.length; i++) {
  860. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  861. _converse.roster.get(jid).set('chat_status', 'unavailable');
  862. expect(_converse.rosterview.update).toHaveBeenCalled();
  863. // Check that they are sorted alphabetically
  864. t = _.reduce($roster.find('.roster-group .current-xmpp-contact.unavailable a.open-chat'),
  865. function (result, value) {
  866. return result + _.trim(value.textContent);
  867. }, '');
  868. expect(t).toEqual(mock.cur_names.slice(0,i+1).sort().join(''));
  869. }
  870. done();
  871. });
  872. }));
  873. it("are ordered according to status: online, busy, away, xa, unavailable, offline",
  874. mock.initConverseWithPromises(
  875. null, ['rosterGroupsFetched'], {},
  876. function (done, _converse) {
  877. _addContacts(_converse);
  878. test_utils.waitUntil(function () {
  879. return _converse.rosterview.$el.find('.roster-group li').length;
  880. }, 700).then(function () {
  881. var i, jid;
  882. for (i=0; i<3; i++) {
  883. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  884. _converse.roster.get(jid).set('chat_status', 'online');
  885. }
  886. for (i=3; i<6; i++) {
  887. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  888. _converse.roster.get(jid).set('chat_status', 'dnd');
  889. }
  890. for (i=6; i<9; i++) {
  891. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  892. _converse.roster.get(jid).set('chat_status', 'away');
  893. }
  894. for (i=9; i<12; i++) {
  895. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  896. _converse.roster.get(jid).set('chat_status', 'xa');
  897. }
  898. for (i=12; i<15; i++) {
  899. jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
  900. _converse.roster.get(jid).set('chat_status', 'unavailable');
  901. }
  902. return test_utils.waitUntil(function () {
  903. return _converse.rosterview.$el.find('li.online').length
  904. })
  905. }).then(function () {
  906. return test_utils.waitUntil(function () {
  907. return _converse.rosterview.$el.find('li:first').text().trim() === 'Candice van der Knijff'
  908. }, 900);
  909. }).then(function () {
  910. var i;
  911. var contacts = _converse.rosterview.$el.find('.current-xmpp-contact');
  912. for (i=0; i<3; i++) {
  913. expect($(contacts[i]).hasClass('online')).toBeTruthy();
  914. expect($(contacts[i]).hasClass('both')).toBeTruthy();
  915. expect($(contacts[i]).hasClass('dnd')).toBeFalsy();
  916. expect($(contacts[i]).hasClass('away')).toBeFalsy();
  917. expect($(contacts[i]).hasClass('xa')).toBeFalsy();
  918. expect($(contacts[i]).hasClass('unavailable')).toBeFalsy();
  919. expect($(contacts[i]).hasClass('offline')).toBeFalsy();
  920. }
  921. for (i=3; i<6; i++) {
  922. expect($(contacts[i]).hasClass('dnd')).toBeTruthy();
  923. expect($(contacts[i]).hasClass('both')).toBeTruthy();
  924. expect($(contacts[i]).hasClass('online')).toBeFalsy();
  925. expect($(contacts[i]).hasClass('away')).toBeFalsy();
  926. expect($(contacts[i]).hasClass('xa')).toBeFalsy();
  927. expect($(contacts[i]).hasClass('unavailable')).toBeFalsy();
  928. expect($(contacts[i]).hasClass('offline')).toBeFalsy();
  929. }
  930. for (i=6; i<9; i++) {
  931. expect($(contacts[i]).hasClass('away')).toBeTruthy();
  932. expect($(contacts[i]).hasClass('both')).toBeTruthy();
  933. expect($(contacts[i]).hasClass('online')).toBeFalsy();
  934. expect($(contacts[i]).hasClass('dnd')).toBeFalsy();
  935. expect($(contacts[i]).hasClass('xa')).toBeFalsy();
  936. expect($(contacts[i]).hasClass('unavailable')).toBeFalsy();
  937. expect($(contacts[i]).hasClass('offline')).toBeFalsy();
  938. }
  939. for (i=9; i<12; i++) {
  940. expect($(contacts[i]).hasClass('xa')).toBeTruthy();
  941. expect($(contacts[i]).hasClass('both')).toBeTruthy();
  942. expect($(contacts[i]).hasClass('online')).toBeFalsy();
  943. expect($(contacts[i]).hasClass('dnd')).toBeFalsy();
  944. expect($(contacts[i]).hasClass('away')).toBeFalsy();
  945. expect($(contacts[i]).hasClass('unavailable')).toBeFalsy();
  946. expect($(contacts[i]).hasClass('offline')).toBeFalsy();
  947. }
  948. for (i=12; i<15; i++) {
  949. expect($(contacts[i]).hasClass('unavailable')).toBeTruthy();
  950. expect($(contacts[i]).hasClass('both')).toBeTruthy();
  951. expect($(contacts[i]).hasClass('online')).toBeFalsy();
  952. expect($(contacts[i]).hasClass('dnd')).toBeFalsy();
  953. expect($(contacts[i]).hasClass('away')).toBeFalsy();
  954. expect($(contacts[i]).hasClass('xa')).toBeFalsy();
  955. expect($(contacts[i]).hasClass('offline')).toBeFalsy();
  956. }
  957. for (i=15; i<mock.cur_names.length; i++) {
  958. expect($(contacts[i]).hasClass('offline')).toBeTruthy();
  959. expect($(contacts[i]).hasClass('both')).toBeTruthy();
  960. expect($(contacts[i]).hasClass('online')).toBeFalsy();
  961. expect($(contacts[i]).hasClass('dnd')).toBeFalsy();
  962. expect($(contacts[i]).hasClass('away')).toBeFalsy();
  963. expect($(contacts[i]).hasClass('xa')).toBeFalsy();
  964. expect($(contacts[i]).hasClass('unavailable')).toBeFalsy();
  965. }
  966. done();
  967. });
  968. }));
  969. });
  970. describe("Requesting Contacts", function () {
  971. it("can be added to the roster and they will be sorted alphabetically",
  972. mock.initConverseWithPromises(
  973. null, ['rosterGroupsFetched'], {},
  974. function (done, _converse) {
  975. var i, children;
  976. var names = [];
  977. var addName = function (idx, item) {
  978. if (!$(item).hasClass('request-actions')) {
  979. names.push($(item).text().replace(/^\s+|\s+$/g, ''));
  980. }
  981. };
  982. test_utils.openContactsPanel(_converse);
  983. spyOn(_converse, 'emit');
  984. spyOn(_converse.rosterview, 'update').and.callThrough();
  985. spyOn(_converse.controlboxtoggle, 'showControlBox').and.callThrough();
  986. for (i=0; i<mock.req_names.length; i++) {
  987. _converse.roster.create({
  988. jid: mock.req_names[i].replace(/ /g,'.').toLowerCase() + '@localhost',
  989. subscription: 'none',
  990. ask: null,
  991. requesting: true,
  992. fullname: mock.req_names[i]
  993. });
  994. }
  995. test_utils.waitUntil(function () {
  996. return _converse.rosterview.get('Contact requests').$el.find('li').length;
  997. }, 700).then(function () {
  998. expect(_converse.rosterview.update).toHaveBeenCalled();
  999. // Check that they are sorted alphabetically
  1000. children = _converse.rosterview.get('Contact requests').$el.find('.requesting-xmpp-contact span');
  1001. names = [];
  1002. children.each(addName);
  1003. expect(names.join('')).toEqual(mock.req_names.slice(0,mock.req_names.length+1).sort().join(''));
  1004. done();
  1005. });
  1006. }));
  1007. it("do not have a header if there aren't any",
  1008. mock.initConverseWithPromises(
  1009. null, ['rosterGroupsFetched'], {},
  1010. function (done, _converse) {
  1011. test_utils.openContactsPanel(_converse);
  1012. var name = mock.req_names[0];
  1013. spyOn(window, 'confirm').and.returnValue(true);
  1014. _converse.roster.create({
  1015. jid: name.replace(/ /g,'.').toLowerCase() + '@localhost',
  1016. subscription: 'none',
  1017. ask: null,
  1018. requesting: true,
  1019. fullname: name
  1020. });
  1021. test_utils.waitUntil(function () {
  1022. return _converse.rosterview.$el.find('.roster-group:visible li').length;
  1023. }, 700).then(function () {
  1024. expect(_converse.rosterview.get('Contact requests').$el.is(':visible')).toEqual(true);
  1025. _converse.rosterview.$el.find(".req-contact-name:contains('"+name+"')")
  1026. .parent().siblings('.request-actions')
  1027. .find('.decline-xmpp-request').click();
  1028. expect(window.confirm).toHaveBeenCalled();
  1029. expect(_converse.rosterview.get('Contact requests').$el.is(':visible')).toEqual(false);
  1030. done();
  1031. });
  1032. }));
  1033. it("can be collapsed under their own header",
  1034. mock.initConverseWithPromises(
  1035. null, ['rosterGroupsFetched'], {},
  1036. function (done, _converse) {
  1037. test_utils.createContacts(_converse, 'requesting').openControlBox();
  1038. test_utils.waitUntil(function () {
  1039. return _converse.rosterview.$el.find('.roster-group:visible li').length;
  1040. }, 700).then(function () {
  1041. checkHeaderToggling.apply(
  1042. _converse,
  1043. [_converse.rosterview.get('Contact requests').$el]
  1044. ).then(done);
  1045. });
  1046. }));
  1047. it("can have their requests accepted by the user",
  1048. mock.initConverseWithPromises(
  1049. null, ['rosterGroupsFetched'], {},
  1050. function (done, _converse) {
  1051. test_utils.createContacts(_converse, 'requesting').openControlBox();
  1052. test_utils.waitUntil(function () {
  1053. return _converse.rosterview.$el.find('.roster-group li').length;
  1054. }, 700).then(function () {
  1055. // TODO: Testing can be more thorough here, the user is
  1056. // actually not accepted/authorized because of
  1057. // mock_connection.
  1058. var name = mock.req_names.sort()[0];
  1059. var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
  1060. var contact = _converse.roster.get(jid);
  1061. spyOn(_converse.roster, 'sendContactAddIQ').and.callFake(function (jid, fullname, groups, callback) {
  1062. callback();
  1063. });
  1064. spyOn(contact, 'authorize').and.callFake(function () { return contact; });
  1065. _converse.rosterview.$el.find(".req-contact-name:contains('"+name+"')")
  1066. .parent().siblings('.request-actions')
  1067. .find('.accept-xmpp-request').click();
  1068. expect(_converse.roster.sendContactAddIQ).toHaveBeenCalled();
  1069. expect(contact.authorize).toHaveBeenCalled();
  1070. done();
  1071. });
  1072. }));
  1073. it("can have their requests denied by the user",
  1074. mock.initConverseWithPromises(
  1075. null, ['rosterGroupsFetched'], {},
  1076. function (done, _converse) {
  1077. test_utils.createContacts(_converse, 'requesting').openControlBox();
  1078. test_utils.waitUntil(function () {
  1079. return _converse.rosterview.$el.find('.roster-group li').length;
  1080. }, 700).then(function () {
  1081. _converse.rosterview.update(); // XXX: Hack to make sure $roster element is attaced.
  1082. var name = mock.req_names.sort()[1];
  1083. var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
  1084. var contact = _converse.roster.get(jid);
  1085. spyOn(window, 'confirm').and.returnValue(true);
  1086. spyOn(contact, 'unauthorize').and.callFake(function () { return contact; });
  1087. _converse.rosterview.$el.find(".req-contact-name:contains('"+name+"')")
  1088. .parent().siblings('.request-actions')
  1089. .find('.decline-xmpp-request').click();
  1090. expect(window.confirm).toHaveBeenCalled();
  1091. expect(contact.unauthorize).toHaveBeenCalled();
  1092. // There should now be one less contact
  1093. expect(_converse.roster.length).toEqual(mock.req_names.length-1);
  1094. done();
  1095. });
  1096. }));
  1097. it("are persisted even if other contacts' change their presence ", mock.initConverseWithPromises(
  1098. null, ['rosterGroupsFetched'], {}, function (done, _converse) {
  1099. /* This is a regression test.
  1100. * https://github.com/jcbrand/_converse.js/issues/262
  1101. */
  1102. expect(_converse.roster.pluck('jid').length).toBe(0);
  1103. var stanza = $pres({from: 'data@enterprise/resource', type: 'subscribe'});
  1104. _converse.connection._dataRecv(test_utils.createRequest(stanza));
  1105. test_utils.waitUntil(function () {
  1106. return $('a:contains("Contact requests")').length;
  1107. }, 700).then(function () {
  1108. expect(_converse.roster.pluck('jid').length).toBe(1);
  1109. expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
  1110. // Taken from the spec
  1111. // http://xmpp.org/rfcs/rfc3921.html#rfc.section.7.3
  1112. stanza = $iq({
  1113. to: _converse.connection.jid,
  1114. type: 'result',
  1115. id: 'roster_1'
  1116. }).c('query', {
  1117. xmlns: 'jabber:iq:roster',
  1118. }).c('item', {
  1119. jid: 'romeo@example.net',
  1120. name: 'Romeo',
  1121. subscription:'both'
  1122. }).c('group').t('Friends').up().up()
  1123. .c('item', {
  1124. jid: 'mercutio@example.org',
  1125. name: 'Mercutio',
  1126. subscription:'from'
  1127. }).c('group').t('Friends').up().up()
  1128. .c('item', {
  1129. jid: 'benvolio@example.org',
  1130. name: 'Benvolio',
  1131. subscription:'both'
  1132. }).c('group').t('Friends');
  1133. _converse.roster.onReceivedFromServer(stanza.tree());
  1134. expect(_.includes(_converse.roster.pluck('jid'), 'data@enterprise')).toBeTruthy();
  1135. done();
  1136. });
  1137. }));
  1138. });
  1139. describe("All Contacts", function () {
  1140. it("are saved to, and can be retrieved from browserStorage",
  1141. mock.initConverseWithPromises(
  1142. null, ['rosterGroupsFetched'], {},
  1143. function (done, _converse) {
  1144. test_utils.createContacts(_converse, 'all').openControlBox();
  1145. test_utils.openContactsPanel(_converse);
  1146. var new_attrs, old_attrs, attrs;
  1147. var num_contacts = _converse.roster.length;
  1148. var new_roster = new _converse.RosterContacts();
  1149. // Roster items are yet to be fetched from browserStorage
  1150. expect(new_roster.length).toEqual(0);
  1151. new_roster.browserStorage = _converse.roster.browserStorage;
  1152. new_roster.fetch();
  1153. expect(new_roster.length).toEqual(num_contacts);
  1154. // Check that the roster items retrieved from browserStorage
  1155. // have the same attributes values as the original ones.
  1156. attrs = ['jid', 'fullname', 'subscription', 'ask'];
  1157. for (var i=0; i<attrs.length; i++) {
  1158. new_attrs = _.map(_.map(new_roster.models, 'attributes'), attrs[i]);
  1159. old_attrs = _.map(_.map(_converse.roster.models, 'attributes'), attrs[i]);
  1160. // Roster items in storage are not necessarily sorted,
  1161. // so we have to sort them here to do a proper
  1162. // comparison
  1163. expect(_.isEqual(new_attrs.sort(), old_attrs.sort())).toEqual(true);
  1164. }
  1165. done();
  1166. }));
  1167. it("will show fullname and jid properties on tooltip",
  1168. mock.initConverseWithPromises(
  1169. null, ['rosterGroupsFetched'], {},
  1170. function (done, _converse) {
  1171. test_utils.createContacts(_converse, 'all').openControlBox();
  1172. test_utils.openContactsPanel(_converse);
  1173. test_utils.waitUntil(function () {
  1174. return _converse.rosterview.$el.find('.roster-group li').length;
  1175. }, 700).then(function () {
  1176. var jid, name, i;
  1177. for (i=0; i<mock.cur_names.length; i++) {
  1178. name = mock.cur_names[i];
  1179. jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
  1180. var $dd = _converse.rosterview.$el.find("li:contains('"+name+"')").children().first();
  1181. var dd_text = $dd.text();
  1182. var dd_title = $dd.attr('title');
  1183. expect(_.trim(dd_text)).toBe(name);
  1184. expect(dd_title).toContain(name);
  1185. expect(dd_title).toContain(jid);
  1186. }
  1187. done();
  1188. });
  1189. }));
  1190. });
  1191. });
  1192. }));