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

Changes from master

parent 746862f2
......@@ -2,9 +2,10 @@
Contributing to Converse.js
===========================
Contributions to Converse.js are very welcome. Please follow the usual github
workflow. Create your own local fork of this repository, make your changes and
then submit a pull request.
Thanks for contributing to Converse.js_!
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
================================
......@@ -12,11 +13,18 @@ Before submitting a pull request
Add tests for your bugfix or feature
------------------------------------
- Please try to add a test for any bug fixed or feature added. We use Jasmine
for testing.
Add a test for any bug fixed or feature added. We use Jasmine
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 Jasmine BDD tests complete sucessfully. Open test.html in your
browser, and the tests will run automatically.
Check that the Jasmine BDD tests complete sucessfully. Open tests.html in your
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() {
self.index++;
var now = new Date().getTime();
if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
self.env.lastUpdate = now;
if (self.env.updateInterval) {
var timeout;
if (now - self.env.lastUpdate > self.env.updateInterval) {
timeout = 0;
} else {
timeout = self.env.updateInterval;
}
self.env.setTimeout(function() {
self.env.lastUpdate = now;
self.next_();
}, 0);
}, timeout);
} else {
if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
goAgain = true;
......
......@@ -489,7 +489,7 @@
initialize: function (){
this.model.messages.on('add', this.showMessage, 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.$el.appendTo(xmppchat.chatboxesview.$el);
......@@ -539,6 +539,14 @@
return this;
},
hide: function () {
if (xmppchat.animate) {
this.$el.hide('fast');
} else {
this.$el.hide();
}
},
show: function () {
if (this.$el.is(':visible') && this.$el.css('opacity') == "1") {
return this.focus();
......@@ -750,13 +758,8 @@
}, this));
this.model.on('show', this.show, this);
this.model.on('destroy', $.proxy(function (model, response, options) {
this.$el.hide('fast');
}, this));
this.model.on('hide', $.proxy(function (model, response, options) {
this.$el.hide('fast');
}, this));
this.model.on('destroy', this.hide, this);
this.model.on('hide', this.hide, this);
if (this.model.get('visible')) {
this.show();
}
......@@ -1064,7 +1067,7 @@
xmppchat.getVCard(
partner_jid,
$.proxy(function (jid, fullname, image, image_type, url) {
chatbox = this.create({
var chatbox = this.create({
'id': jid,
'jid': jid,
'fullname': fullname,
......@@ -1093,7 +1096,7 @@
initialize: function () {
// boxesviewinit
this.views = {};
this.options.model.on("add", function (item) {
this.model.on("add", function (item) {
var view = this.views[item.get('id')];
if (!view) {
if (item.get('chatroom')) {
......
......@@ -3,8 +3,10 @@
<head>
<meta charset='utf-8' />
<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="converse.css">
<script data-main="main" src="Libraries/require-jquery.js"></script>
<title>Converse.js</title>
</head>
......@@ -26,53 +28,52 @@
<!-- MAIN CONTENT -->
<div id="main_content_wrap" class="outer">
<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>
<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>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>
<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>
<h2>Screencasts</h2>
<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>
<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>
<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>Screencasts</h2>
<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>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>
<h2>Licence</h2>
<div id="chatpanel">
<div id="collective-xmpp-chat-data"></div>
<div id="toggle-controlbox">
<a href="#" class="chat" id="toggle-online-users">
<span class="conn-feedback">Click here to chat</span> <strong style="display: none" id="online-count">(0)</strong>
</a>
</div>
</div>
<!-- FOOTER -->
......@@ -83,15 +84,10 @@
</footer>
</div>
<script type="text/javascript">
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"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-2128260-8");
pageTracker._trackPageview();
} catch(err) {}
</script>
<script type="text/javascript">
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"));
</script>
<script type="text/javascript">try { var pageTracker = _gat._getTracker("UA-2128260-8"); pageTracker._trackPageview(); } catch(err) {}</script>
</body>
</html>
......@@ -10,22 +10,15 @@
return describe("Converse.js", $.proxy(function() {
// Names from http://www.fakenamegenerator.com/
var req_names = [
'Louw Spekman', 'Mohamad Stet', 'Dominik Beyer', 'Dirk Eichel', 'Marco Duerr', 'Ute Schiffer',
'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'
'Louw Spekman', 'Mohamad Stet', 'Dominik Beyer'
];
var pend_names = [
'Suleyman van Beusichem', 'Nicole Diederich', 'Nanja van Yperen', 'Delany Bloemendaal',
'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'
'Suleyman van Beusichem', 'Nicole Diederich', 'Nanja van Yperen'
];
var cur_names = [
'Max Frankfurter', 'Candice van der Knijff', 'Irini Vlastuin', 'Rinse Sommer', 'Annegreet Gomez',
'Robin Schook', 'Marcel Eberhardt', 'Simone Brauer', 'Asmaa Haakman', 'Felix Amsel',
'Lena Grunewald', 'Laura Grunewald', 'Mandy Seiler', 'Sven Bosch', 'Nuriye Cuypers', 'Ben Zomer',
'Leah Weiss', 'Francesca Disseldorp', 'Sven Bumgarner', 'Benjamin Zweig'
'Lena Grunewald', 'Laura Grunewald', 'Mandy Seiler', 'Sven Bosch', 'Nuriye Cuypers'
];
var num_contacts = req_names.length + pend_names.length + cur_names.length;
this.bare_jid = 'dummy@localhost';
......@@ -44,6 +37,16 @@
'get': function () {},
'subscribe': 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 @@
hex_sha1('converse.chatboxes-'+this.bare_jid));
window.localStorage.removeItem(
hex_sha1('converse.xmppstatus-'+this.bare_jid));
window.localStorage.removeItem(
hex_sha1('converse.messages'+cur_names[0].replace(' ','.').toLowerCase() + '@localhost'));
this.prebind = true;
this.onConnected(mock_connection);
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 () {
it("is not shown by default", $.proxy(function () {
expect(this.rosterview.$el.is(':visible')).toEqual(false);
......@@ -112,7 +104,6 @@
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(''));
}
sleep(timeout);
}, xmppchat));
it("will have their own heading once they have been added", $.proxy(function () {
......@@ -141,7 +132,6 @@
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(''));
}
sleep(timeout);
}, xmppchat));
it("will have their own heading once they have been added", $.proxy(function () {
......@@ -151,7 +141,7 @@
it("can change their status to online and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough();
for (i=0; i<5; i++) {
for (i=0; i<3; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough();
......@@ -163,14 +153,13 @@
// 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();
expect(t).toEqual(cur_names.slice(0,i+1).sort().join(''));
sleep(timeout);
}
}, xmppchat));
it("can change their status to busy and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough();
for (i=5; i<10; i++) {
for (i=3; i<6; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough();
......@@ -180,15 +169,14 @@
expect(this.rosterview.render).toHaveBeenCalled();
// 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();
expect(t).toEqual(cur_names.slice(5,i+1).sort().join(''));
sleep(timeout);
expect(t).toEqual(cur_names.slice(3,i+1).sort().join(''));
}
}, xmppchat));
it("can change their status to away and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough();
for (i=10; i<15; i++) {
for (i=6; i<9; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough();
......@@ -199,15 +187,14 @@
// 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();
expect(t).toEqual(cur_names.slice(10,i+1).sort().join(''));
sleep(timeout);
expect(t).toEqual(cur_names.slice(6,i+1).sort().join(''));
}
}, xmppchat));
it("can change their status to unavailable and be sorted alphabetically", $.proxy(function () {
var item, view, jid, t;
spyOn(this.rosterview, 'render').andCallThrough();
for (i=15; i<20; i++) {
for (i=9; i<12; i++) {
jid = cur_names[i].replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.rosteritemviews[jid];
spyOn(view, 'render').andCallThrough();
......@@ -218,8 +205,7 @@
// 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();
expect(t).toEqual(cur_names.slice(15, i+1).sort().join(''));
sleep(timeout);
expect(t).toEqual(cur_names.slice(9, i+1).sort().join(''));
}
}, xmppchat));
......@@ -227,23 +213,23 @@
var contacts = this.rosterview.$el.find('dd.current-xmpp-contact');
var i;
// 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');
}
// 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');
}
// 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');
}
// 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');
}
// 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');
}
}, xmppchat));
......@@ -278,7 +264,6 @@
// be opened.
expect(this.showControlBox).toHaveBeenCalled();
}
sleep(timeout);
}, xmppchat));
it("will have their own heading once they have been added", $.proxy(function () {
......@@ -298,7 +283,6 @@
accept_button.click();
expect(view.acceptRequest).toHaveBeenCalled();
expect(this.connection.roster.authorize).toHaveBeenCalled();
sleep(timeout);
}, xmppchat));
it("can have their requests denied by the user", $.proxy(function () {
......@@ -315,7 +299,6 @@
expect(this.connection.roster.unauthorize).toHaveBeenCalled();
// There should now be one less contact
expect(this.roster.length).toEqual(num_contacts-1);
sleep(timeout);
}, xmppchat));
}, xmppchat));
......@@ -366,9 +349,9 @@
}, 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;
// showControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created.
......@@ -384,52 +367,106 @@
$el.click();
expect(view.openChat).toHaveBeenCalled();
expect(this.chatboxes.length).toEqual(i+2);
sleep(timeout);
}
}, xmppchat));
it("can be saved to, and retrieved from, localStorage", $.proxy(function () {
var old_chatboxes = this.chatboxes;
expect(this.chatboxes.length).toEqual(6);
this.chatboxes = new this.ChatBoxes();
expect(this.chatboxes.length).toEqual(0);
this.chatboxes.onConnected();
expect(this.chatboxes.length).toEqual(6);
// We instantiate a new ChatBoxes collection, which by default
// will be empty.
this.newchatboxes = new this.ChatBoxes();
expect(this.newchatboxes.length).toEqual(0);
// The chatboxes will then be fetched from localStorage inside the
// onConnected method
this.newchatboxes.onConnected();
expect(this.newchatboxes.length).toEqual(6);
// Check that the roster items retrieved from localStorage
// have the same attributes values as the original ones.
attrs = ['id', 'box_id', 'visible'];
for (i=0; i<attrs.length; i++) {
new_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]);
old_attrs = _.pluck(_.pluck(old_chatboxes.models, 'attributes'), attrs[i]);
new_attrs = _.pluck(_.pluck(this.newchatboxes.models, 'attributes'), attrs[i]);
old_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]);
expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
}
this.rosterview.render();
}, xmppchat));
it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () {
var chatbox, view, $el;
for (i=0; i<this.chatboxes.length; i++) {
chatbox = this.chatboxes.models[i];
var chatbox, view, $el,
num_open_chats = this.chatboxes.length;
for (i=0; i<num_open_chats; i++) {
chatbox = this.chatboxes.models[0];
view = this.chatboxesview.views[chatbox.get('id')];
spyOn(view, 'closeChat').andCallThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
view.$el.find('.close-chatbox-button').click();
expect(view.closeChat).toHaveBeenCalled();
sleep(timeout);
}
}, xmppchat));
it("will be removed from localStorage when closed", $.proxy(function () {
var old_chatboxes = this.chatboxes;
expect(this.chatboxes.length).toEqual(6);
this.chatboxes = new this.ChatBoxes();
expect(this.chatboxes.length).toEqual(0);
this.newchatboxes = new this.ChatBoxes();
expect(this.newchatboxes.length).toEqual(0);
// onConnected will fetch chatboxes in localStorage, but
// because there aren't any open chatboxes, there won't be any
// in localStorage either.
this.chatboxes.onConnected();
expect(this.chatboxes.length).toEqual(0);
}, 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));
......
......@@ -2,7 +2,7 @@ require(["jquery", "spec/MainSpec"], function($) {
$(function($) {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
jasmineEnv.updateInterval = 500;
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