Commit 9ee92f24 authored by JC Brand's avatar JC Brand

Changes from master

parent 746862f2
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
Contributing to Converse.js Contributing to Converse.js
=========================== ===========================
Contributions to Converse.js are very welcome. Please follow the usual github Thanks for contributing to Converse.js_!
workflow. Create your own local fork of this repository, make your changes and
then submit a pull request. Please follow the usual github workflow. Create your own local fork of this repository,
make your changes and then submit a pull request.
Before submitting a pull request Before submitting a pull request
================================ ================================
...@@ -12,11 +13,18 @@ Before submitting a pull request ...@@ -12,11 +13,18 @@ Before submitting a pull request
Add tests for your bugfix or feature Add tests for your bugfix or feature
------------------------------------ ------------------------------------
- Please try to add a test for any bug fixed or feature added. We use Jasmine Add a test for any bug fixed or feature added. We use Jasmine
for testing. for testing.
Take a look at ``tests.html`` and ``spec/MainSpec.js`` to see how
the tests are implemented.
Check that the tests run Check that the tests run
------------------------ ------------------------
- Check that the Jasmine BDD tests complete sucessfully. Open test.html in your Check that the Jasmine BDD tests complete sucessfully. Open tests.html in your
browser, and the tests will run automatically. browser, and the tests will run automatically.
You can see the current test output online, here: http://conversejs.org/tests.html
.. _Converse.js: http://conversejs.org
...@@ -2080,11 +2080,17 @@ jasmine.Queue.prototype.next_ = function() { ...@@ -2080,11 +2080,17 @@ jasmine.Queue.prototype.next_ = function() {
self.index++; self.index++;
var now = new Date().getTime(); var now = new Date().getTime();
if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { if (self.env.updateInterval) {
self.env.lastUpdate = now; var timeout;
if (now - self.env.lastUpdate > self.env.updateInterval) {
timeout = 0;
} else {
timeout = self.env.updateInterval;
}
self.env.setTimeout(function() { self.env.setTimeout(function() {
self.env.lastUpdate = now;
self.next_(); self.next_();
}, 0); }, timeout);
} else { } else {
if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
goAgain = true; goAgain = true;
......
...@@ -489,7 +489,7 @@ ...@@ -489,7 +489,7 @@
initialize: function (){ initialize: function (){
this.model.messages.on('add', this.showMessage, this); this.model.messages.on('add', this.showMessage, this);
this.model.on('show', this.show, this); this.model.on('show', this.show, this);
this.model.on('destroy', function (model, response, options) { this.$el.hide('fast'); }, this); this.model.on('destroy', this.hide, this);
this.model.on('change', this.onChange, this); this.model.on('change', this.onChange, this);
this.$el.appendTo(xmppchat.chatboxesview.$el); this.$el.appendTo(xmppchat.chatboxesview.$el);
...@@ -539,6 +539,14 @@ ...@@ -539,6 +539,14 @@
return this; return this;
}, },
hide: function () {
if (xmppchat.animate) {
this.$el.hide('fast');
} else {
this.$el.hide();
}
},
show: function () { show: function () {
if (this.$el.is(':visible') && this.$el.css('opacity') == "1") { if (this.$el.is(':visible') && this.$el.css('opacity') == "1") {
return this.focus(); return this.focus();
...@@ -750,13 +758,8 @@ ...@@ -750,13 +758,8 @@
}, this)); }, this));
this.model.on('show', this.show, this); this.model.on('show', this.show, this);
this.model.on('destroy', $.proxy(function (model, response, options) { this.model.on('destroy', this.hide, this);
this.$el.hide('fast'); this.model.on('hide', this.hide, this);
}, this));
this.model.on('hide', $.proxy(function (model, response, options) {
this.$el.hide('fast');
}, this));
if (this.model.get('visible')) { if (this.model.get('visible')) {
this.show(); this.show();
} }
...@@ -1064,7 +1067,7 @@ ...@@ -1064,7 +1067,7 @@
xmppchat.getVCard( xmppchat.getVCard(
partner_jid, partner_jid,
$.proxy(function (jid, fullname, image, image_type, url) { $.proxy(function (jid, fullname, image, image_type, url) {
chatbox = this.create({ var chatbox = this.create({
'id': jid, 'id': jid,
'jid': jid, 'jid': jid,
'fullname': fullname, 'fullname': fullname,
...@@ -1093,7 +1096,7 @@ ...@@ -1093,7 +1096,7 @@
initialize: function () { initialize: function () {
// boxesviewinit // boxesviewinit
this.views = {}; this.views = {};
this.options.model.on("add", function (item) { this.model.on("add", function (item) {
var view = this.views[item.get('id')]; var view = this.views[item.get('id')];
if (!view) { if (!view) {
if (item.get('chatroom')) { if (item.get('chatroom')) {
......
...@@ -3,8 +3,10 @@ ...@@ -3,8 +3,10 @@
<head> <head>
<meta charset='utf-8' /> <meta charset='utf-8' />
<meta http-equiv="X-UA-Compatible" content="chrome=1" /> <meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="description" content="Converse.js : Browser-based Instant Messaging with Strophe.js and Backbone.js" /> <meta name="description" content="Converse.js: Open Source Browser-Based Instant Messaging" />
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css"> <link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css">
<link rel="stylesheet" type="text/css" media="screen" href="converse.css">
<script data-main="main" src="Libraries/require-jquery.js"></script>
<title>Converse.js</title> <title>Converse.js</title>
</head> </head>
...@@ -26,53 +28,52 @@ ...@@ -26,53 +28,52 @@
<!-- MAIN CONTENT --> <!-- MAIN CONTENT -->
<div id="main_content_wrap" class="outer"> <div id="main_content_wrap" class="outer">
<section id="main_content" class="inner"> <section id="main_content" class="inner">
<p><strong>Converse.js</strong> implements an <a href="http://xmpp.org">XMPP</a> based instant messaging client in the browser.</p> <p><strong>Converse.js</strong> implements an <a href="http://xmpp.org">XMPP</a> based instant messaging client in the browser.</p>
<p>It is used by <a href="http://github.com/collective/collective.xmpp.chat">collective.xmpp.chat</a>, which is a <a href="http://plone.org">Plone</a> instant messaging add-on.</p>
<p>The ultimate goal is to enable anyone to add chat functionality to their websites, regardless of the backend.</p>
<p>This is currently possible, except for adding new contacts, which still makes an XHR call to the (Plone) backend to fetch user info.</p>
<p>It is used by <a href="http://github.com/collective/collective.xmpp.chat">collective.xmpp.chat</a>, which is a <a href="http://plone.org">Plone</a> instant messaging add-on.</p> <h2>Features</h2>
<ul>
<li>Manually or automically subscribe to other users.</li>
<li>Accept or decline contact requests</li>
<li>Chat status (online, busy, away, offline)</li>
<li>Custom status messages</li>
<li>Typing notifications</li>
<li>Third person messages (/me )</li>
<li>Multi-user chat in chatrooms</li>
<li>Chatroom Topics</li>
<li>vCard support</li>
</ul>
<p>The ultimate goal is to enable anyone to add chat functionality to their websites, regardless of the backend.</p> <h2>Screencasts</h2>
<p>This is currently possible, except for adding new contacts, which still makes an XHR call to the (Plone) backend to fetch user info.</p> <ul>
<li><a href="http://opkode.com/media/blog/instant-messaging-for-plone-with-javascript-and-xmpp">Screencast 1</a>:
Integrated into a Plone site via <strong>collective.xmpp.chat</strong>.
</li>
<li><a href="http://opkode.com/media/blog/2013/04/02/converse.js-xmpp-instant-messaging-with-javascript">Screencast 2</a>:
A static HTML page with <em>Converse.js</em>. Here we chat to external XMPP accounts on Jabber.org and Gmail.
</li>
</ul>
<h2>Features</h2> <h2>Dependencies</h2>
<ul> <p><strong>Converse.js</strong> depends on a few third party libraries, including:
<li>Manually or automically subscribe to other users.</li> <ul>
<li>Accept or decline contact requests</li> <li><a href="http://strophe.im/strophejs">strophe.js</a></li>
<li>Chat status (online, busy, away, offline)</li> <li><a href="http:/backbonejs.org">backbone.js</a></li>
<li>Custom status messages</li> <li><a href="http:/requirejs.org">require.js</a></li>
<li>Typing notifications</li> </ul>
<li>Third person messages (/me )</li> </p>
<li>Multi-user chat in chatrooms</li>
<li>Chatroom Topics</li>
<li>vCard support</li>
</ul>
<h2>Screencasts</h2> <h2>Licence</h2>
<div id="chatpanel">
<div id="collective-xmpp-chat-data"></div>
<ul> <div id="toggle-controlbox">
<li><a href="http://opkode.com/media/blog/instant-messaging-for-plone-with-javascript-and-xmpp">Screencast 1</a>: <a href="#" class="chat" id="toggle-online-users">
Integrated into a Plone site via <strong>collective.xmpp.chat</strong>. <span class="conn-feedback">Click here to chat</span> <strong style="display: none" id="online-count">(0)</strong>
</a>
</li> </div>
<li><a href="http://opkode.com/media/blog/2013/04/02/converse.js-xmpp-instant-messaging-with-javascript">Screencast 2</a>:
A static HTML page with <em>Converse.js</em>. Here we chat to external XMPP accounts on Jabber.org and Gmail.
</li>
</ul>
<h2>Dependencies</h2>
<p><strong>Converse.js</strong> depends on a few third party libraries, including:
<ul>
<li><a href="http://strophe.im/strophejs">strophe.js</a></li>
<li><a href="http:/backbonejs.org">backbone.js</a></li>
<li><a href="http:/requirejs.org">require.js</a></li>
</ul>
</p>
<h2>Licence</h2>
<p><strong>Converse.js</strong> is released under both the <a href="http://opensource.org/licenses/mit-license.php">MIT</a> and <a href="http://opensource.org/licenses/gpl-license.php">GPL</a> licenses.</p>
</section>
</div> </div>
<!-- FOOTER --> <!-- FOOTER -->
...@@ -83,15 +84,10 @@ ...@@ -83,15 +84,10 @@
</footer> </footer>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script> </script>
<script type="text/javascript"> <script type="text/javascript">try { var pageTracker = _gat._getTracker("UA-2128260-8"); pageTracker._trackPageview(); } catch(err) {}</script>
try {
var pageTracker = _gat._getTracker("UA-2128260-8");
pageTracker._trackPageview();
} catch(err) {}
</script>
</body> </body>
</html> </html>
...@@ -10,22 +10,15 @@ ...@@ -10,22 +10,15 @@
return describe("Converse.js", $.proxy(function() { return describe("Converse.js", $.proxy(function() {
// Names from http://www.fakenamegenerator.com/ // Names from http://www.fakenamegenerator.com/
var req_names = [ var req_names = [
'Louw Spekman', 'Mohamad Stet', 'Dominik Beyer', 'Dirk Eichel', 'Marco Duerr', 'Ute Schiffer', 'Louw Spekman', 'Mohamad Stet', 'Dominik Beyer'
'Billie Westerhuis', 'Sarah Kuester', 'Sabrina Loewe', 'Laura Duerr', 'Mathias Meyer',
'Tijm Keller', 'Lea Gerste', 'Martin Pfeffer', 'Ulrike Abt', 'Zoubida van Rooij',
'Maylin Hettema', 'Ruwan Bechan', 'Marco Beich', 'Karin Busch', 'Mathias Müller'
]; ];
var pend_names = [ var pend_names = [
'Suleyman van Beusichem', 'Nicole Diederich', 'Nanja van Yperen', 'Delany Bloemendaal', 'Suleyman van Beusichem', 'Nicole Diederich', 'Nanja van Yperen'
'Jannah Hofmeester', 'Christine Trommler', 'Martin Bumgarner', 'Emil Baeten', 'Farshad Brasser',
'Gabriele Fisher', 'Sofiane Schopman', 'Sky Wismans', 'Jeffery Stoelwinder', 'Ganesh Waaijenberg',
'Dani Boldewijn', 'Katrin Propst', 'Martina Kaiser', 'Philipp Kappel', 'Meeke Grootendorst'
]; ];
var cur_names = [ var cur_names = [
'Max Frankfurter', 'Candice van der Knijff', 'Irini Vlastuin', 'Rinse Sommer', 'Annegreet Gomez', 'Max Frankfurter', 'Candice van der Knijff', 'Irini Vlastuin', 'Rinse Sommer', 'Annegreet Gomez',
'Robin Schook', 'Marcel Eberhardt', 'Simone Brauer', 'Asmaa Haakman', 'Felix Amsel', 'Robin Schook', 'Marcel Eberhardt', 'Simone Brauer', 'Asmaa Haakman', 'Felix Amsel',
'Lena Grunewald', 'Laura Grunewald', 'Mandy Seiler', 'Sven Bosch', 'Nuriye Cuypers', 'Ben Zomer', 'Lena Grunewald', 'Laura Grunewald', 'Mandy Seiler', 'Sven Bosch', 'Nuriye Cuypers'
'Leah Weiss', 'Francesca Disseldorp', 'Sven Bumgarner', 'Benjamin Zweig'
]; ];
var num_contacts = req_names.length + pend_names.length + cur_names.length; var num_contacts = req_names.length + pend_names.length + cur_names.length;
this.bare_jid = 'dummy@localhost'; this.bare_jid = 'dummy@localhost';
...@@ -44,6 +37,16 @@ ...@@ -44,6 +37,16 @@
'get': function () {}, 'get': function () {},
'subscribe': function () {}, 'subscribe': function () {},
'registerCallback': function () {} 'registerCallback': function () {}
},
'vcard': {
'get': function (callback, jid) {
var name = jid.split('@')[0].replace('.', ' ').split(' ');
var firstname = name[0].charAt(0).toUpperCase()+name[0].slice(1);
var lastname = name[1].charAt(0).toUpperCase()+name[1].slice(1);
var fullname = firstname+' '+lastname;
var vcard = $iq().c('vCard').c('FN').t(fullname);
callback(vcard.tree());
}
} }
}; };
...@@ -54,24 +57,13 @@ ...@@ -54,24 +57,13 @@
hex_sha1('converse.chatboxes-'+this.bare_jid)); hex_sha1('converse.chatboxes-'+this.bare_jid));
window.localStorage.removeItem( window.localStorage.removeItem(
hex_sha1('converse.xmppstatus-'+this.bare_jid)); hex_sha1('converse.xmppstatus-'+this.bare_jid));
window.localStorage.removeItem(
hex_sha1('converse.messages'+cur_names[0].replace(' ','.').toLowerCase() + '@localhost'));
this.prebind = true; this.prebind = true;
this.onConnected(mock_connection); this.onConnected(mock_connection);
this.animate = false; // don't use animations this.animate = false; // don't use animations
// The timeout is used to slow down the tests so that one can see
// visually what is happening in the page.
var timeout = 0;
var sleep = function (delay) {
// Yes this is blocking and stupid, but these are tests and this is
// the easiest way to delay execution without having to use
// callbacks.
var start = new Date().getTime();
while (new Date().getTime() < start + delay) {
continue;
}
};
describe("The Contacts Roster", $.proxy(function () { describe("The Contacts Roster", $.proxy(function () {
it("is not shown by default", $.proxy(function () { it("is not shown by default", $.proxy(function () {
expect(this.rosterview.$el.is(':visible')).toEqual(false); expect(this.rosterview.$el.is(':visible')).toEqual(false);
...@@ -112,7 +104,6 @@ ...@@ -112,7 +104,6 @@
t = this.rosterview.$el.find('dt#pending-xmpp-contacts').siblings('dd.pending-xmpp-contact').text(); t = this.rosterview.$el.find('dt#pending-xmpp-contacts').siblings('dd.pending-xmpp-contact').text();
expect(t).toEqual(pend_names.slice(0,i+1).sort().join('')); expect(t).toEqual(pend_names.slice(0,i+1).sort().join(''));
} }
sleep(timeout);
}, xmppchat)); }, xmppchat));
it("will have their own heading once they have been added", $.proxy(function () { it("will have their own heading once they have been added", $.proxy(function () {
...@@ -141,7 +132,6 @@ ...@@ -141,7 +132,6 @@
t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.offline').find('a.open-chat').text(); t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.offline').find('a.open-chat').text();
expect(t).toEqual(cur_names.slice(0,i+1).sort().join('')); expect(t).toEqual(cur_names.slice(0,i+1).sort().join(''));
} }
sleep(timeout);
}, xmppchat)); }, xmppchat));
it("will have their own heading once they have been added", $.proxy(function () { it("will have their own heading once they have been added", $.proxy(function () {
...@@ -151,7 +141,7 @@ ...@@ -151,7 +141,7 @@
it("can change their status to online and be sorted alphabetically", $.proxy(function () { it("can change their status to online and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t; var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough(); spyOn(this.rosterview, 'render').andCallThrough();
for (i=0; i<5; i++) { for (i=0; i<3; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost'; jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid]; view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough(); spyOn(view, 'render').andCallThrough();
...@@ -163,14 +153,13 @@ ...@@ -163,14 +153,13 @@
// Check that they are sorted alphabetically // Check that they are sorted alphabetically
t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.online').find('a.open-chat').text(); t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.online').find('a.open-chat').text();
expect(t).toEqual(cur_names.slice(0,i+1).sort().join('')); expect(t).toEqual(cur_names.slice(0,i+1).sort().join(''));
sleep(timeout);
} }
}, xmppchat)); }, xmppchat));
it("can change their status to busy and be sorted alphabetically", $.proxy(function () { it("can change their status to busy and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t; var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough(); spyOn(this.rosterview, 'render').andCallThrough();
for (i=5; i<10; i++) { for (i=3; i<6; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost'; jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid]; view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough(); spyOn(view, 'render').andCallThrough();
...@@ -180,15 +169,14 @@ ...@@ -180,15 +169,14 @@
expect(this.rosterview.render).toHaveBeenCalled(); expect(this.rosterview.render).toHaveBeenCalled();
// Check that they are sorted alphabetically // Check that they are sorted alphabetically
t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.dnd').find('a.open-chat').text(); t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.dnd').find('a.open-chat').text();
expect(t).toEqual(cur_names.slice(5,i+1).sort().join('')); expect(t).toEqual(cur_names.slice(3,i+1).sort().join(''));
sleep(timeout);
} }
}, xmppchat)); }, xmppchat));
it("can change their status to away and be sorted alphabetically", $.proxy(function () { it("can change their status to away and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t; var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough(); spyOn(this.rosterview, 'render').andCallThrough();
for (i=10; i<15; i++) { for (i=6; i<9; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost'; jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid]; view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough(); spyOn(view, 'render').andCallThrough();
...@@ -199,15 +187,14 @@ ...@@ -199,15 +187,14 @@
// Check that they are sorted alphabetically // Check that they are sorted alphabetically
t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.away').find('a.open-chat').text(); t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.away').find('a.open-chat').text();
expect(t).toEqual(cur_names.slice(10,i+1).sort().join('')); expect(t).toEqual(cur_names.slice(6,i+1).sort().join(''));
sleep(timeout);
} }
}, xmppchat)); }, xmppchat));
it("can change their status to unavailable and be sorted alphabetically", $.proxy(function () { it("can change their status to unavailable and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t; var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough(); spyOn(this.rosterview, 'render').andCallThrough();
for (i=15; i<20; i++) { for (i=9; i<12; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost'; jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid]; view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough(); spyOn(view, 'render').andCallThrough();
...@@ -218,8 +205,7 @@ ...@@ -218,8 +205,7 @@
// Check that they are sorted alphabetically // Check that they are sorted alphabetically
t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.unavailable').find('a.open-chat').text(); t = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact.unavailable').find('a.open-chat').text();
expect(t).toEqual(cur_names.slice(15, i+1).sort().join('')); expect(t).toEqual(cur_names.slice(9, i+1).sort().join(''));
sleep(timeout);
} }
}, xmppchat)); }, xmppchat));
...@@ -227,23 +213,23 @@ ...@@ -227,23 +213,23 @@
var contacts = this.rosterview.$el.find('dd.current-xmpp-contact'); var contacts = this.rosterview.$el.find('dd.current-xmpp-contact');
var i; var i;
// The first five contacts are online. // The first five contacts are online.
for (i=0; i<5; i++) { for (i=0; i<3; i++) {
expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('online'); expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('online');
} }
// The next five are busy // The next five are busy
for (i=5; i<10; i++) { for (i=3; i<6; i++) {
expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('dnd'); expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('dnd');
} }
// The next five are away // The next five are away
for (i=10; i<15; i++) { for (i=6; i<9; i++) {
expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('away'); expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('away');
} }
// The next five are unavailable // The next five are unavailable
for (i=15; i<20; i++) { for (i=9; i<12; i++) {
expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('unavailable'); expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('unavailable');
} }
// The next 20 are offline // The next 20 are offline
for (i=20; i<cur_names.length; i++) { for (i=12; i<cur_names.length; i++) {
expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('offline'); expect($(contacts[i]).attr('class').split(' ',1)[0]).toEqual('offline');
} }
}, xmppchat)); }, xmppchat));
...@@ -278,7 +264,6 @@ ...@@ -278,7 +264,6 @@
// be opened. // be opened.
expect(this.showControlBox).toHaveBeenCalled(); expect(this.showControlBox).toHaveBeenCalled();
} }
sleep(timeout);
}, xmppchat)); }, xmppchat));
it("will have their own heading once they have been added", $.proxy(function () { it("will have their own heading once they have been added", $.proxy(function () {
...@@ -298,7 +283,6 @@ ...@@ -298,7 +283,6 @@
accept_button.click(); accept_button.click();
expect(view.acceptRequest).toHaveBeenCalled(); expect(view.acceptRequest).toHaveBeenCalled();
expect(this.connection.roster.authorize).toHaveBeenCalled(); expect(this.connection.roster.authorize).toHaveBeenCalled();
sleep(timeout);
}, xmppchat)); }, xmppchat));
it("can have their requests denied by the user", $.proxy(function () { it("can have their requests denied by the user", $.proxy(function () {
...@@ -315,7 +299,6 @@ ...@@ -315,7 +299,6 @@
expect(this.connection.roster.unauthorize).toHaveBeenCalled(); expect(this.connection.roster.unauthorize).toHaveBeenCalled();
// There should now be one less contact // There should now be one less contact
expect(this.roster.length).toEqual(num_contacts-1); expect(this.roster.length).toEqual(num_contacts-1);
sleep(timeout);
}, xmppchat)); }, xmppchat));
}, xmppchat)); }, xmppchat));
...@@ -366,9 +349,9 @@ ...@@ -366,9 +349,9 @@
}, xmppchat)); }, xmppchat));
}, xmppchat)); }, xmppchat));
describe("Chatboxes", $.proxy(function () { describe("A Chatbox", $.proxy(function () {
it("are created when you click on a roster item", $.proxy(function () { it("is created when you click on a roster item", $.proxy(function () {
var i, $el, click, jid, view; var i, $el, click, jid, view;
// showControlBox was called earlier, so the controlbox is // showControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created. // visible, but no other chat boxes have been created.
...@@ -384,52 +367,106 @@ ...@@ -384,52 +367,106 @@
$el.click(); $el.click();
expect(view.openChat).toHaveBeenCalled(); expect(view.openChat).toHaveBeenCalled();
expect(this.chatboxes.length).toEqual(i+2); expect(this.chatboxes.length).toEqual(i+2);
sleep(timeout);
} }
}, xmppchat)); }, xmppchat));
it("can be saved to, and retrieved from, localStorage", $.proxy(function () { it("can be saved to, and retrieved from, localStorage", $.proxy(function () {
var old_chatboxes = this.chatboxes; // We instantiate a new ChatBoxes collection, which by default
expect(this.chatboxes.length).toEqual(6); // will be empty.
this.chatboxes = new this.ChatBoxes(); this.newchatboxes = new this.ChatBoxes();
expect(this.chatboxes.length).toEqual(0); expect(this.newchatboxes.length).toEqual(0);
// The chatboxes will then be fetched from localStorage inside the
this.chatboxes.onConnected(); // onConnected method
expect(this.chatboxes.length).toEqual(6); this.newchatboxes.onConnected();
expect(this.newchatboxes.length).toEqual(6);
// Check that the roster items retrieved from localStorage // Check that the roster items retrieved from localStorage
// have the same attributes values as the original ones. // have the same attributes values as the original ones.
attrs = ['id', 'box_id', 'visible']; attrs = ['id', 'box_id', 'visible'];
for (i=0; i<attrs.length; i++) { for (i=0; i<attrs.length; i++) {
new_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]); new_attrs = _.pluck(_.pluck(this.newchatboxes.models, 'attributes'), attrs[i]);
old_attrs = _.pluck(_.pluck(old_chatboxes.models, 'attributes'), attrs[i]); old_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]);
expect(_.isEqual(new_attrs, old_attrs)).toEqual(true); expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
} }
this.rosterview.render(); this.rosterview.render();
}, xmppchat)); }, xmppchat));
it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () { it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () {
var chatbox, view, $el; var chatbox, view, $el,
for (i=0; i<this.chatboxes.length; i++) { num_open_chats = this.chatboxes.length;
chatbox = this.chatboxes.models[i]; for (i=0; i<num_open_chats; i++) {
chatbox = this.chatboxes.models[0];
view = this.chatboxesview.views[chatbox.get('id')]; view = this.chatboxesview.views[chatbox.get('id')];
spyOn(view, 'closeChat').andCallThrough(); spyOn(view, 'closeChat').andCallThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
view.$el.find('.close-chatbox-button').click(); view.$el.find('.close-chatbox-button').click();
expect(view.closeChat).toHaveBeenCalled(); expect(view.closeChat).toHaveBeenCalled();
sleep(timeout);
} }
}, xmppchat)); }, xmppchat));
it("will be removed from localStorage when closed", $.proxy(function () { it("will be removed from localStorage when closed", $.proxy(function () {
var old_chatboxes = this.chatboxes; this.newchatboxes = new this.ChatBoxes();
expect(this.chatboxes.length).toEqual(6); expect(this.newchatboxes.length).toEqual(0);
this.chatboxes = new this.ChatBoxes(); // onConnected will fetch chatboxes in localStorage, but
expect(this.chatboxes.length).toEqual(0); // because there aren't any open chatboxes, there won't be any
// in localStorage either.
this.chatboxes.onConnected(); this.chatboxes.onConnected();
expect(this.chatboxes.length).toEqual(0); expect(this.chatboxes.length).toEqual(0);
}, xmppchat)); }, xmppchat));
describe("A Chat Message", $.proxy(function () {
it("can be received which will open a chatbox and be displayed inside it", $.proxy(function () {
var message = 'This is a received message';
var sender_jid = cur_names[0].replace(' ','.').toLowerCase() + '@localhost';
msg = $msg({
from: sender_jid,
to: this.bare_jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').t(message).up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
spyOn(this, 'getVCard').andCallThrough();
// We don't already have an open chatbox for this user
expect(this.chatboxes.get(sender_jid)).not.toBeDefined();
runs($.proxy(function () {
// messageReceived is a handler for received XMPP
// messages
this.chatboxes.messageReceived(msg);
}, xmppchat));
waits(500);
runs($.proxy(function () {
// Since we didn't already have an open chatbox, one
// will asynchronously created inside a callback to
// getVCard
expect(this.getVCard).toHaveBeenCalled();
// Check that the chatbox and its view now exist
var chatbox = this.chatboxes.get(sender_jid);
var chatboxview = this.chatboxesview.views[sender_jid];
expect(chatbox).toBeDefined();
expect(chatboxview).toBeDefined();
// Check that the message was received and check the
// message parameters
expect(chatbox.messages.length).toEqual(1);
var msg_obj = chatbox.messages.models[0];
expect(msg_obj.get('message')).toEqual(message);
// XXX: This is stupid, fullname is actually only the
// users first name
expect(msg_obj.get('fullname')).toEqual(cur_names[0].split(' ')[0]);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(false);
// Now check that the message appears inside the
// chatbox in the DOM
var txt = chatboxview.$el.find('.chat-content').find('.chat-message').find('.chat-message-content').text();
expect(txt).toEqual(message);
}, xmppchat));
}, xmppchat));
it("can be sent from a chatbox, and will appear inside it", $.proxy(function () {
// TODO
}, xmppchat));
}, xmppchat));
}, xmppchat)); }, xmppchat));
}, xmppchat)); }, xmppchat));
......
...@@ -2,7 +2,7 @@ require(["jquery", "spec/MainSpec"], function($) { ...@@ -2,7 +2,7 @@ require(["jquery", "spec/MainSpec"], function($) {
$(function($) { $(function($) {
var jasmineEnv = jasmine.getEnv(); var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000; jasmineEnv.updateInterval = 500;
var htmlReporter = new jasmine.HtmlReporter(); var htmlReporter = new jasmine.HtmlReporter();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment