I want to start writing Unit tests for my JS code, i have never done this before, never had the need, but as my projects are ever expanding think it's time to start.
Now writing the tests themselves i don't think will be an issue for functional javascript code (a function that adds two numbers together), i don't have that, all of those things are server side (as it's much more complex than adding two digits), what i do have (and a lot of it) is dynamic Javascript (and jQuery) which is intertwined with HTML and my question is, how (and can i) write tests for that? Same code bellow (completely lorem ispum)
MyHelperClass = function () {
var _someBool = false;
return {
Initialize: function () {
MyHelperClass.ChangeALotOfThings("init");
},
ChangeALotOfThings: function (arg1) {
var $someID = $("#myID");
var _anotherBool = true;
if ($someID.lenght) {
var moreHTML = MyOtherHelperClass.ThisFunctionWillReturnSomeHTML();
$someID.find(".myClass").append(moreHTML);
$(".someClass").on("click", function(){
// do some code | i.e. basic Accordion
});
if (_anotherBool && arg1) {
var result = $.ajax({
url: "test.html",
context: document.body
});
$someID.prepend(result);
}
} else {
// do other similar stuff
}
}
}
}
MyHelperClass.Initialize();
This is a random example i wrote in 5min (some .js/jQuery syntax isn't even correct) and think the code doesn't even make a lot of sense, bottom line is, can something like this be Unit Tested?
I was looking into Tape.js and Jasmine.js but couldn't find much.
Thank you for any comments and/or advice regarding writing Unit Test for .js/jQuery.
Related
I want to tidy up the js-code used on my php-website to increase the loading speed. For the moment i include in every website the required js-file.
My plan is to merge all js-files into one big one. Not every page uses every js-code, so i started something but don't know if this makes any sense.
I have already read the article One JS File for Multiple Pages but the method of Paul Irish is way to complicated for me (for the moment) as a beginner.
This is my approach:
I create the file core.js and call it on every website like..
<script src="js/core.js"></script>
In core.js i first get the name of the corresponding page.
var path = window.location.pathname;
var page = path.split("/").pop();
var page_name = page.slice(0, -4);
Then i check which site requires which js-script (pseudo-code).
if (page_name == 'xyz'){
execute this code which is only used on this site
}
if (page_name == 'abc' || 'xyz' || 'def'){
execute another code which is used on multiple sites
}
if (page_name == 'ghi' || 'jkl' || 'mno' || 'xyz'){
include jquery for multiple sites
}
...
...
This means a lot of work for me, because i have a lot of js, so i wanted to ask first if this is a good solution to tidy up.
By the way: The js code i place on my website doesn't change often.
Thank you
Misch
A solution for your problem could be something like:
if(selector) {
//run code
}
This runs the code inside the block only if a particular selector exists. This way you don't have to go through all the trouble of getting the name of the page, splitting and slicing the string etc (this is also prone to errors).
So let's say you want to add some innerHTML on some node it will look something like this:
function bar (text) {
alert(text)
}
if(document.getElementById('#foo')) {
bar('#foo exists!')
}
This way bar is only called when a node with id #foo exists.
Split your javascript into sensible groups. You may have an admin section to your site, so have admin.js.
It's also worth noting that most browsers will only download the javascript file once and then cache it. You said that your code does not change very often so you may find that putting it all in one file doesn't actually have that much of an affect.
Lets say you have pages like page1, page2, page3 etc.
Then your core.js will include all the codes of all the pages and then just initialize the code which you want to use
var page1= (function () {
var Init = function (){
//write the codes used by page 1
};
return {
Initialize: function () {
Init();
}
};
})();
var page2= (function () {
var Init = function (){
//write the codes used by page 2
};
return {
Initialize: function () {
Init();
}
};
})();
var page3= (function () {......});
var page = path.split("/").pop();
var path = window.location.pathname;
var page = path.split("/").pop();
var page_name = page.slice(0, -4);
if (page_name == 'pg1'){
page1.Initialize();
}
if (page_name == 'pg2' || 'pg3'){
page2.Initialize();
page3.Initialize();
}
if (page_name == 'pg4' ){
page4.Initialize();
}
To be honest, this is just going to slow down your performance. If your users stay a long time on the website, then one single file reduces a bit of clutter in your code. But, if the user is visiting only a few pages, single file is just extra burden on bandwidth. There is a possibility, most of your users might not even need more than half of your js.
Plus, those extra conditions aren't really helping anyone. So, I would say, don't use single file option.
I am creating a small project that heavily relies on JavaScript. I come from php/mysql and now stepping into node.js/javascript/mongodb, and I hve to say it's quite a mindswitch.
I want to create a simple object that has some special function that I can use in the page. I have been looking at some tutorial, and looking at the libraries such as jquery and backbone, but I need some final advice on my decision.
I only need some small functions, and no cross-browser support, that's why I don't choose something like backbone. Maybe ill change to that later when I have a better crasp on JavaScript programming.
What is confusing me is whether to use the new, or maybe wrapping the code into a self-invoking function.
I see jquery creates an object inside the window and than exposes that, but I have no idea how that works.
Enough intro, now to the point. I have created something like this:
var $s = Object.create({
page: Object.create({
title: 'pagetitle',
html: '',
data: {},
render: function(){
// Basic render function
}
}),
socket: Object.create({
// My websocket connection
}),
store: function(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
retrieve: function(key) {
var value = localStorage.getItem(key);
return value && JSON.parse(value);
},
slugify: function(slug){
return slug.replace(/[^a-zA-Z 0-9-]+/g,'').toLowerCase().replace(/ /g,'-');
}
});
This are just a few random functions I put in.
I haven't tested this yet, it is a draft, I want to know if this is any good.
Now I was thinking i can do some stuff like this:
$s.page.html = 'somehtml';
$s.page.render();
// Maybe
$s.store( $s.page.title, $s.page.html );
I do use jQuery and jQuery templating, so something like this could be possible:
$.tmpl( $s.page.html, $s.page.data ).appendTo( "#content" );
Nothing fancy is needed here. You can create a global javascript object with a method like this:
var myGlobalObject = {};
myGlobalObject.testFunction = function() {
// put your code here
};
You can then call that like this:
myGlobalObject.testFunction();
One slightly more flexible design pattern you will often seen used is this:
var myGlobalObject = myGlobalObject || {};
myGlobalObject.testFunction = function() {
// put your code here
};
This is used when there might be lots of different pieces of code contributing to myGlobalObject and they all want to make sure that it's properly declared before adding properties to it. This way of doing it, creates it if it doesn't already exist and if it does already exist, leaves the methods and properties on it that might already be there. This allows multiple modules to each contribute initialization to myGlobalObject without regards for the order they load.
I have recently built an single-page ASP.NET MVC 3 with a JS/jQuery UI (on top of the view's HTML), the general idea of the Javascript work is below. I am encountering issues with GC not properly freeing memory and leaving a large number of elements (24,000 for the biggest form, 15-20k, and 1k depending on which form is loaded/unloaded) in Detached DOM (viewable in Chrome's Developer tools Heap Profiler).
var MainApp = function () {
var meBase = this;
this.activeObject = undefined;
this.someFunc = function (val1, val2, etc) {
//Some operation here
}
this.GetView(x, y, z)
{
if (meBase.activeObject != null) {
meBase.BustActive(x, y, z);
} else {
if (condition) {
//Load static html via $.get
} else {
switch (activeObjectSelector) {
case CASEHERE:
self.activeObject = new SomeObject();
self.activeObject.BeginInit();
break;
case .....
}
}
}
}
this.BustActive = function (x, y, z) {
if (meBase.activeObject.Destroy()) {
meBase.activeObject = null;
meBase.GetView(x, y, z);
}
}
}
var SomeObject = function () {
var meBase = this;
this.Bindings = [];
this.Container = "#somecontainer";
//Some Properties
this.Unbind = function () {
$("#Somecontainer .bound").each(function () {
if ($(this)["click"] && $.isFunction($(this)["click"])) {
$(this).unbind('click');
}
if ($(this)["blur"] && $.isFunction($(this)["blur"])) {
$(this).unbind('blur');
} if ($(this)["change"] && $.isFunction($(this)["change"])) {
$(this).unbind('change');
}
if ($(this)["mouseenter"] && $.isFunction($(this)["mouseenter"])) {
$(this).unbind('mouseenter');
} if ($(this)["mouseleave"] && $.isFunction($(this)["mouseleave"])) {
$(this).unbind('mouseleave');
}
});
//iterate through meBase.Bindings to remove any 'special' bindings such as 'live/die'
}
this.MapEvents = function () {
//For Example
$("#Somecontainer #element").click(meBase.SomeAction).addClass('bound');
// create object with removal function for 'special' bindings such as 'live/die'
// and push it into meBase.Bindings;
}
this.InitUI = function () {
//Setup tabs, datepickers, etc
}
this.Destroy = function () {
meBase.Unbind();
//remove object fields and methods
delete meBase.someProp;
$(meBase.Container).empty();
delete meBase.BeginInit;
delete meBase.InitUI;
delete meBase.MapEvents;
delete meBase.SomeAction;
delete meBase;
return true;
}
this.SomeAction = function () {
//Do something productive..hopefully
}
this.ProcessView = function (data) {
$("#MainContainer").fadeOut(150, "swing", function () {
$(this).empty().append(data);
});
}
this.LoadView = function () {
$.ajax({
url: '/somewhere/something',
type: 'GET',
success: meBase.ProccessView, error: SomeGlobalObject.LogAjaxError
});
}
this.BeginInit = function () {
//Load pages via ajax
meBase.LoadView();
meBase.InitUI();
meBase.MapEvents();
return true;
}
}
I have tried doing iterations with javascript to remove events and elements in .Destroy() function, and it substantially reduced the number of elements left in Detached DOM versus $(container).empty() or $(container).remove(). But my memory is never properly collecting back down, it just continually rises during each load/unload. There are drops at random intervals, but not the amount I would expect. Is it normal for so many elements to remain hung-up, or is there some fundamental issue with the way my code is functioning?
Thanks for taking the time to read this!
First post, please be gentle...
I've also recently been building some single-page apps in .Net MVC3. I suspect your problems are arising because Microsoft, in their attempts to keep developers out of JS and in C#, mucks around with the Javascript and Jquery on your page pretty badly.
The best advice I can give you is that you need to ditch all of Microsoft's cruft, and build the html/js part of your app as though it were totally platform independent. This means that you'll mostly be using the M in the MVC, and you'll only need enough Cs to manage your Ms. If the View is all HTML and Javascript, life gets a lot simpler indeed. Here's how to get started:
Delete all pre-packaged server-side code, including the Razor or ASPX pages.
Switch to static HTML files, static JS files
(Optional) Use Require.js to manage your JS dependencies (read the docs carefully, it seems weird at first, but it's incredibly powerful)
(Optional) Use Spine.js to give your JS code some structure
(Optional) Use Handlebars.js for your client-side templating engine
Require and Spine have quickly become my favorite tools for building single-page apps. They give you some very powerful and flexible tools to help you manage the increased volume of Javascript code you'll be writing in any single-page app.
Once you've got your client-side code completely disconnected from Microsoft's attempts to ruin Javascript, then you can focus on your data, which should use JSON-based Rest Services in MVC3. You can get help with this here and here.
Good luck!
I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)
We have an application with a good amount of jQuery JSON calls to server side code. Because of this, we have a large amount of binding code to parse responses and bind the appropriate values to the form. This is a two part question.
What is the reccomended approach for dealing with a large number of forms that all have different data. Right now were are trying to take a structured approach in setting up a js "class" for each page, with an init, wireClickEvents etc.. to try to have everything conformed.
Is there any "best practices" with creating repetitive jQuery code or any type of reccomended structure other than just throwing a bunch of functions in a js file?
You should probably look into a framework like knockout.js This way you can just update your models and the forms will update automatically.
Not 100% sure example what you are asking, but personally, and I use MochiKit, I create JavaScript "classes" (or widgets, if you prefer) for every significant client-side UI structure. These know, of course, how to populate themselves with data.
I don't know what more there is to say - writing UI code for the browser in JavaScript is no different than writing UI code for other types of apps, as far as I am concerned. Build classes and instantiate them as needed, populate them with data, have them throw events, etc. etc.
Am I up in the night on this? :)
EDIT: In other words, yes - do what you are doing, for the most part. I see too many novice JavaScript hackers write a bunch of poorly-cohesive functions that don't appear to be a part of anything specific other than they are all in a single file. Hope that makes sense.
I think there are multiple challanges for you. The first question is how to structure javascript code, i.e. how to build namespaces so that you don't fight name clashes or have to name your functions like
form1validate
form1aftersubmit
form2validate
form2aftersubmit
One of the proven patterns for modules in javascript is to use an anonymous function to build a new naming scope. The basic idea is shon in the following code
(function() {
var foo = 1;
})();
(function() {
if(foo == 1) alert("namespace separation failed!")
})();
I think this blog entry is a good introduction.
The second question you face is how to avoid all the repetition in javascript code.
You have a couple of weapons against this.
functions - this seams obvious but it's often forgotten to refactor common code into functions where it can be done. In you case this will be functions to copy values from the json response into the forms and like that
higher order function - or functions as data - or callback, as they are often called in javascript. These are the mightiest weapon in javascript. In case for form and ajax handling you can use callback to avoid repetition in the control flow of your forms.
Let me construct an example out of my head (using jquery for convinence)
// this is a validator for one form
var form1validator = function() {
if($("input[name=name]",this).attr("value").length < 1 &&
$("input[name=organisation]",this).attr("value").length < 1)
return "Either name or organisation required"
}
// and this for a second form
var form2validator = function() {
if($("input[name=age]",this).attr("value").length < 21
return "Age of 21 required"
}
// and a function to display a validation result
var displayResult = function(r) {
$(this).prepend("<span></span>").text(r);
}
// we use them as higher order functions like that
$("#form1").onSubmit(validator(form1validator, displayResult, function() {
//on submit
...send some xhr request or like that
});
$("#form2").onSubmit(validator(form2validator, displayResult, function() {
this.submit() // simply submit form
});
$("#form1b").onSubmit(validator(form1validator, function(r) {
alert("There was an validation error " + r);
}, function() {
//on submit
...send some xhr request or like that
});
// the validator function itself would be defined as
function validator(formValidator, displayResult, onSubmit) {
var r = formValidator.apply(this)
if(typeof(r) === 'undefined')
onSubmit(this)
else
displayResult(r)
}