How to write unit tests for events in backbone.js - javascript

I begin my adventure with write unit tests so please be indulgence. In my app I am using jQuery, backbone.js and underscore.js and for tests mocha.js and chai.js. I have problem because I have no idea how to test events using this tools. Fox example I have function which changes value of input. It call after change checkbox value.
test.html file:
<!DOCTYPE html>
<html>
<head>
<title>Backbone.js Tests</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="js/lib/mocha.css" />
<script src="js/lib/mocha.js"></script>
<script src="js/lib/chai.js"></script>
<!-- <script src="js/lib/sinon.js"></script> -->
<script>
// Setup
var expect = chai.expect;
mocha.setup('bdd');
// Run tests on window load event.
window.onload = function () {
(window.mochaPhantomJS || mocha).run();
};
</script>
<!-- App code -->
<script src="../public/static/js/libs/jquery-2.1.4.min.js"></script>
<script src="../public/static/js/libs/js.cookie-2.1.0.min.js"></script>
<script src="../public/static/js/libs/bootstrap.min.js"></script>
<script src="../public/static/js/libs/underscore-min.js"></script>
<script src="../public/static/js/libs/backbone-min.js"></script>
<script src="../public/static/js/libs/backbone-relational.min.js"></script>
<script src="../public/static/js/libs/backbone-super.min.js"></script>
<script src="../public/static/js/libs/handlebars-v4.0.5.min.js"></script>
<script src="../public/static/js/application.js"></script>
<script src="../public/static/js/main.js"></script>
<script src="../public/static/js/views/register.js"></script>
<script src="../public/static/js/views/modals/sign-in-modal.js"></script>
<script src="../public/static/js/views/modals/sign-up-modal.js"></script>
<script src="../public/static/js/helpers/forms.js"></script>
<script src="../public/static/js/helpers/alerts.js"></script>
<script src="../public/static/js/helpers/inpage-alerts.js"></script>
<script src="../public/static/js/collections.js"></script>
<script src="../public/static/js/models.js"></script>
<script src="../public/static/js/kodilla.lib.js"></script>
<script src="../public/static/js/views/knowledge-base.js"></script>
<script src="../public/static/js/libs/tether.min.js"></script>
<!-- Tests -->
<script src="js/spec/register.spec.js"></script>
</head>
<body>
<div id="mocha"></div>
</body>
</html>
register.js file:
App.Views.Register = Backbone.View.extend({
events: {
'click input[name="terms"]' : 'changeTermsConfirmation',
'click [type=submit]': 'onSubmitForm'
},
initialize: function(options) {
this.$termsConfirmedAtHidden = this.$('input[name="terms_confirmed_at"]');
},
changeTermsConfirmation: function(e) {
if ($(e.currentTarget).is(":checked")) {
this.$termsConfirmedAtHidden.val(Math.floor(Date.now() / 1000));
} else {
this.$termsConfirmedAtHidden.val('');
}
},
onSubmitForm: function() {
if (!this.$el.find('input[name="terms"]').is(':checked')) {
analytics.track('auth_register_not_checked_terms');
}
}
});
register.spec.js file:
$(document).ready(function() {
describe('Register Form', function() {
App.registerView = new App.Views.Register();
describe('initialize() function', function() {
it('Should initialize this.$termsConfirmedAtHidden variable by HTML object', function () {
expect(App.registerView.$termsConfirmedAtHidden).to.be.a('object');
});
});
describe('changeTermsConfirmation() function', function() {
it('Should set value of $termsConfirmedAtHidden element', function () {
//how to test this function
});
});
});
});
My question is how to write sensible unit test for "changeTermsConfirmation()" function. I'll be thankful for others notes, usage tips.

Maybe I can help you out with a few bits of info which might get you started.
Mocha/Chai is modular, so first you need to include a mocking/spying library into your project. The default choice for most people seems to be Sinon (along with Sinon-Chai).
Then you can examine and unit test your event handler:
Create a view instance for your test.
Set up a spy on the view method you want to test (changeTermsConfirmation in your case)
Trigger the event by calling trigger on your view instance
Your expectation can check how often your method has been called, and with which arguments, and what its return value has been.
If you don't care about any of that and just want to test view state changes, you don't need a spy (and you don't need to include a library like Sinon). Just create a view instance, call trigger and examine the result.

Related

Make sure two functions in different files run in the correct order?

So I have two functions in different files.
File 1 looks like this:
function 1() {
do something
}
window.addEventListener('load', () => {
1();
});
and file two looks like:
function 2() {
do something
}
window.addEventListener('load', () => {
2();
});
I want function 1 to run before function 2.
Currently I do this by importing them like this;
<head>
<script src="file1">
<script src="file2">
</head>
The problem with this is it is quite fragile as if someone where to switch the imports then it would stop working, so I am asking if there is a better way of doing it to ensure that function 1 will be run before function 2(and no I can't put them in the same file).
Remove the event listeners from the JS files and wire up the events in the html file.
<head>
<script src="file1">
<script src="file2">
window.addEventListener("DOMContentLoaded", () => {
1()
2()
});
</head>
Better yet, load up the scripts at the end of the <body> which will help with faster load times of the raw html.
<head>
<!-- not much here -->
</head>
<body>
<!-- html -->
<script src="file1">
<script src="file2">
<script>
window.addEventListener("DOMContentLoaded", () => {
1()
2()
});
</script>
</body>

cordova android backbutton behavior [duplicate]

I am overriding my device back button per the documentation that I attach event listener to backbutton with argument of false after the deviceready event.
I have done so like this:
// Smart App object
define([
'routes',
'cordova',
'angular',
'angularRoute',
'angularResource',
'angularTouch',
'config',
'controllers',
'services',
'directives',
'helpers'
], function(appRoute) {
var oApp = {
_app: {},
init: function() {
console.log('init');
document.addEventListener('deviceready', this.onDeviceReady, false);
},
onDeviceReady: function() {
console.log('ok device ready');
document.addEventListener('backbutton', function() {
console.error('deviceback start');
angular.element('.ng-scope').scope().back();
console.error('deviceback end');
}, false);
// ...
}
// ...
I have worked crazily to figure out why hitting the device button is not triggering this backbutton event i attached, i dont even see the console.error messages in the console. I am testing on Android right now I haven't tested on any of the other Phone OS'es yet.
The console.log('ok device ready') does fire and my app works properly, the device backbutton is overridden in that its default functionality is not taking place, however as stated, my function is not taking place either.
I read other stackoverflow topcis and they said the Cordova version landed a fix, but my Cordova version is much after theirs, my .cordova/config.json file tells me:
{
"lib": {
"www": {
"id": "com.tigoenergy.smart",
"version": "3.5.0"
}
}
}
It is 3.5 and then my info.txt tells me it is 5.0.0:
Node version: v0.10.25
Cordova version: 5.0.0
Config.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<!-- config.xml reference: https://build.phonegap.com/docs/config-xml -->
<widget xmlns = "http://www.w3.org/ns/widgets"
xmlns:gap = "http://phonegap.com/ns/1.0"
Does anyone have any ideas?
Try using the normal way of using cordova events.
// device APIs are available
//
function onDeviceReady() {
// Register the event listener
document.addEventListener("backbutton", onBackKeyDown, false);
}
// Handle the back button
//
function onBackKeyDown() {
//Add your back button implementation.
}
official documentation here
Modified code
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<title>Tigo SMART Installation</title>
<script type="text/javascript" src="apps/installation/js/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="apps/installation/js/index.js"></script>
<script type="text/javascript" src="apps/installation/js/fastclick.min.js"></script>
<script type="text/javascript" src="apps/installation/js/sha512.js"></script>
<script type="text/javascript" src="bower_components/uri.js/src/URI.min.js"></script>
<script type="text/javascript" src="js/extlogin.js"></script>
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" type="text/css" href="apps/installation/css/index.css" />
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
//mycode
console.log('ok loaded');
document.addEventListener("deviceready", onDeviceReady, false);
});
// Wait for device API libraries to load
//
/*function onLoad() {
console.warn('ok loaded')
document.addEventListener("deviceready", onDeviceReady, false);
}
*/
// device APIs are available
//
function onDeviceReady() {
// Register the event listener
console.log('ok device ready');
document.addEventListener("backbutton", onBackKeyDown, false);
}
// Handle the back button
//
function onBackKeyDown() {
console.log('ok backey downed');
}
</script>
</head>
<body>
<div ng-view></div>
<script data-main="bin/SmartApp" src="bower_components/requirejs/require.min.js"></script>
<img style="opacity:0;visibility:hidden;" class="loader" src="img/loader.gif" />
</body>
</html>
Few reference links here and here
The issue was I was importing cordova.js in an iframe even though it was already in the parent window scope. I didn't know it would break it and thought I had to import cordova.js in the iframe. Removing the from the iframe fixed it.
Try This
$(document).ready(function () {
document.addEventListener("backbutton", onBackKeyDown, false);
});
function onBackKeyDown() {
alert('ok backey downed');
}
Make sure these files are link in your page
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="scripts/platformOverrides.js"></script>
<script type="text/javascript" src="scripts/index.js"></script>

How can I test the part of code that executes before onready?

I have written a small page to learn BDD/TDD. It is at http://duolingo.howyousay.it/link.html
My tests (only one file at the moment) are at http://duolingo.howyousay.it/tests/test.html?coverage&hidepassed&noglobals
I've managed to get 100% code coverage except for 5 lines in http://duolingo.howyousay.it/duolingo_link_fixer.js
1 var DuolingoLinkFixer = function() {
2 this.data = {};
3 var self = this;
4
5 document.onreadystatechange = function () {
* 6 self.init();
7 }
8 };
9
10 DuolingoLinkFixer.prototype.init = function() {
* 11 if (document.readyState == "complete") {
* 12 this.setOriginalUrl();
* 13 this.setAlternateText();
* 14 this.setFixedUrl();
15 }
16 }
Lines 6 and 11-14 don't get tested, but if I remove them, the code doesn't work. I'm using QUnit, QUnit-BDD and Blanket.js. How can I test the part of code that runs before onready as it seems that the tests only start running after onready?
My test code currently starts like this:
describe("DuolingoLinkFixer", function() {
describe("new", function() {
it("should create a new instance of DuolingoLinkFixer", function() {
expect(new DuolingoLinkFixer instanceof DuolingoLinkFixer).to.be.true();
});
});
});
This is the source for my test HTML page:
<!DOCTYPE html>
<html>
<head>
<title>Duolingo JavaScript tests</title>
<script type="text/javascript" src="/lib/jquery/jquery-3.0.0.js"></script>
<script type="text/javascript">
$.holdReady(true);
</script>
<link type="text/css" rel="stylesheet" href="/lib/qunit/qunit-1.23.1.css">
<script type="text/javascript" src="/lib/qunit/qunit-1.23.1.js"></script>
<script type="text/javascript" src="/lib/qunit-bdd/qunit-bdd.js"></script>
<script type="text/javascript" src="/lib/blanket.js/blanket.min.js"></script>
<script type="text/javascript" data-cover src="../url_helper.js"></script>
<script type="text/javascript" data-cover src="../duolingo_link_fixer.js"></script>
<script type="text/javascript" src="test_url_helper.js"></script>
<script type="text/javascript" src="test_duolingo_link_fixer.js"></script>
</head>
<body>
<div id="qunit"></div>
</body>
</html>
I tried adding jQuery's $.holdReady(true); as suggested in another post but it didn't help. I don't need to use jQuery so I'm trying to avoid it for this project.
It turned out that I only needed to add a single line to my test code to test the previously untestable code:
describe("DuolingoLinkFixer", function() {
describe("new", function() {
it("should create a new instance of DuolingoLinkFixer", function() {
expect(new DuolingoLinkFixer instanceof DuolingoLinkFixer).to.be.true();
document.onreadystatechange();
});
});
});
Adding document.onreadystatechange(); runs the code exactly the same as if it had been triggered by the readyStateChange event.

Jasmine beforeEach not called

Just starting with Jasmine and following the instructions on the Jasmine website for async testing I notice the beforeEach function is never called.
Any idea why not? I don't find any hints relevant anywhere on the web. Thanks.
Code
The code is really simple:
describe("Testing test.php", function()
{
it ("Gets me a coke", function()
{
var asyncResult = null;
// query function with callback
var queryFcn = function(callback)
{
console.log("queryFcn");
$.get("be_com/test.php?coke")
.success(function(data)
{
asyncResult = data.response;
callback(); // notify jasmine
})
.error(function() { callback(); });
};
// Call ajax
beforeEach(function(done) {
console.log('beforeEach');
queryFcn(done); });
// Evaluate response
expect(asyncResult).toBe("a can of coke");
});
});
The HTML is equally simple:
<html>
<head>
<link rel="shortcut icon" type="image/png" href="../fe_com/js/lib/jasmine-standalone-2.3.4/lib/jasmine-2.3.4/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="../fe_com/js/lib/jasmine-standalone-2.3.4/lib/jasmine-2.3.4/jasmine.css">
<script type="text/javascript" src="../fe_com/js/lib/jquery-2.0.0.min.js"></script>
<script type="text/javascript" src="../fe_com/js/lib/jasmine-standalone-2.3.4/lib/jasmine-2.3.4/jasmine.js"></script>
<script type="text/javascript" src="../fe_com/js/lib/jasmine-standalone-2.3.4/lib/jasmine-2.3.4/jasmine-html.js"></script>
<script type="text/javascript" src="../fe_com/js/lib/jasmine-standalone-2.3.4/lib/jasmine-2.3.4/boot.js"></script>
<!-- FILES -->
<script type="text/javascript" src="testSpec.js"></script>
</head>
<body>
<h2>Unit Tests</h2>
</body>
</html>
A beforeEach block should not be inside of an it block.
The idea of a beforeEach is that the code inside it runs before each of the following it blocks. Example:
beforeEach(function () {
// code in here will run 3 times
});
it('does first thing', function () {});
it('does a second thing', function () {});
it('does a third thing', function () {});

javascript dont work with jquery in the same page

I am having a problem with my code, which is a mix of jquery and plain javascript. I use jquery to show and hide some divs and the js to refresh a div by loading a page inside the div. The plain js code doesn't work as is, but if I delete the jquery code it works fine.
My code :
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Ton plans</title>
<link href="template/style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var auto_refresh = setInterval(
function ()
{
$('#refresh').fadeOut().load('last_post.php').fadeIn();
}, 10000);
</script>
<script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
<script type="text/javascript">
//no conflict jquery
jQuery.noConflict();
//jquery stuff
(function($) {
$(document).ready(function()
{
$("#bott_div").click(function() {
$("#div_profile").show(['fast'])
})
$("#page").click(function() {
$("#div_profile").hide(['fast'])
})
})
})
(jQuery);
</script>
</head>
There is a conflict between the jQuery code and the plain javaScript that is preventing it from working properly. I would require help identifying the problem.
Change the order of the first two scripts -- like this
<script type="text/javascript" src="js/jquery-1.9.0.min.js"></script>
<script type="text/javascript">
var auto_refresh = setInterval(
function ()
{
$('#refresh').fadeOut().load('last_post.php').fadeIn();
}, 10000);
</script>
There is a "(" in front of your function in jQuery code. Encapsulate code in a
$(document).ready(function() {
$("sampleSelect").click(function() {
//code
}
});

Categories

Resources