Tag Archives: js

How to unit test your JS and use it in the browser

Intro

Recently, I wanted to add test coverage to Halloween Bash and keep using it in the browser. This doesn’t seem to be an unreasonable request, but it turns out that it involves many things. You have many choices of test runners & testing frameworks, and I didn’t want to setup a [cci]SpecRunner.html[/cci] to unit test my JS.

The setup that I ended up using is:

What you’ll need:

  • Your HTML/JS project (You can use my demo project)
  • NodeJS (I recommend the Installer from the Node homepage – click “Install”)

Try out the demo app that squares your input.

With my demo, my file structure looks like:

index.html
assets/js
assets/css

There are countless ways to organize your non-html assets, and my demo asset structure is intended to be easy to follow.

My demo contains jquery and two unit testable lib functions (multiply & square):

// define multiply()
window.unitTestJsDemo.multiply = function(x, y) {
  return x*y;
};

// define square()
window.unitTestJsDemo.square = function(x) {
  return unitTestJsDemo.multiply(x, x);
};

Setup NodeJS & GulpJS

Once NodeJS is installed on your machine, setup your node environment:

npm init

The [cci]npm init[/cci] command will walk you through your project, ask you a series of questions, and setup your configuration in [cci]package.json[/cci].

Next, setup Gulp via npm on your command line:

// Install gulp globally
npm install -g gulp

// Install gulp in your project devDependencies
npm install --save-dev gulp

// Create a gulpfile.js at the root of your project
touch gulpfile.js

// gulpfile.js file contents
    var gulp = require('gulp');

    gulp.task('default', function() {
      // place code for your default task here
    }); 

// Run gulp default task
gulp

You now have Node & Gulp setup to run your Gulp tasks. The default Gulp task doesn’t do anything, but you can try it out by running [cci]gulp[/cci].

Setup gulp-jasmine

Save gulp-jasmine into your gulpfile.js:

npm install --save-dev gulp-jasmine

Create your tests:

mkdir -p assets/js/spec/lib
touch assets/js/spec/lib/multiply-spec.js
touch assets/js/spec/lib/square-spec.js

[cci]assets/js/spec/lib/multiply-spec.js[/cci] will contain:

/* jslint node: true */
/* global describe, it, expect */

"use strict";

var multiply_lib = require('../../lib/multiply');

describe("#multiply", function () {
  it("returns the correct multiplied value", function () {
    var product = multiply_lib.multiply(2, 3);
    expect(product).toBe(6);
  });
});

[cci]assets/js/spec/lib/square-spec.js[/cci] will contain:

/* jslint node: true */
/* global describe, it, expect */

"use strict";

var square_lib = require('../../lib/square');

describe("#square", function () {
  it("returns the correct squared value", function () {
    var squared = square_lib.square(3);
    expect(squared).toBe(9);
  });
});

Next, we’re going to move the unit testable functions (multiply & square) into node.js-style modules.

mkdir assets/js/lib
touch assets/js/lib/square.js
touch assets/js/lib/multiply.js

[cci]assets/js/lib/multiply.js[/cci] will contain:

exports.multiply = function(x, y) {

  "use strict";

  return x*y;
};

[cci]assets/js/lib/square.js[/cci] will contain:

var multiply_lib = require('./multiply');

exports.square = function(x) {

  "use strict";

  return multiply_lib.multiply(x, x);
};

Update your [cci]gulpfile.js[/cci] to run the tests:

"use strict";

// Include gulp
var gulp = require('gulp');

// Include plugins
var jasmine = require('gulp-jasmine');

// Test JS
gulp.task('specs', function () {
    return gulp.src('assets/js/spec/lib/*.js')
        .pipe(jasmine());
});

// Default Task
gulp.task('default', function() {
  // place code for your default task here
});

gulp.task('default', ['specs']);

You’ve created your libs (multiply & square), the lib specs (multiply-spec.js & square-spec.js), and setup Gulp to run your tests with Jasmine.

[cci]square.js[/cci] is setup to use the [cci]multiply.js[/cci] lib through the Node require module syntax. Woot!

You can run the default task, which is setup to run your specs task. It should look like:

$ gulp
[16:29:18] Using gulpfile your/path/unit-test-js-demo/gulpfile.js
[16:29:18] Starting 'specs'...
[16:29:18] Finished 'specs' after 44 ms
[16:29:18] Starting 'default'...
[16:29:18] Finished 'default' after 20 μs
..

Finished in 0.008 seconds
2 tests, 2 assertions, 0 failures

Great! Your modules are unit tested (feel free to add more tests), and you want to use them in the browser.

Browserify your JS

Up to this point, we’ve been using jQuery through our local file at [cci]/assets/js/jquery-1.10.2.min.js[/cci]. We’ll want to get rid of managing jQuery ourselves and let Node manage our jQuery dependency.

Let’s create a new file for our page’s JS to organize itself around:

touch assets/js/app.js

Add jQuery to your dependencies and remove your local copy of jQuery:

npm install --save-dev jquery
rm assets/js/jquery-1.10.2.min.js

[cci]assets/js/app.js[/cci] will contain:

var $           = require('jquery');
var square_lib  = require('./lib/square');

$(function() {

  "use strict";

  $("#squareValue").change(function() {
    var $this       = $(this),
        squareValue = $this.val(),
        squareResult;

    // if squareValue is not numeric
    if (isNaN(squareValue)) {

      $("#squareResult").html('N/A');
      return false;

    // else squareValue is numeric
    } else {

      squareResult = square_lib.square(squareValue);
      $("#squareResult").html(squareResult);
      return true;
    }
  });

});

Now we need to use Browserify to build our JS file with gulp.

Add Browserify related dependencies into your gulpfile and setup your new task:

npm install --save-dev gulp-uglify
npm install --save-dev vinyl-source-stream
npm install --save-dev gulp-streamify
npm install --save-dev browserify

Update your [cci]gulpfile.js[/cci] to include the new browserify task:

"use strict";

// Include gulp
var gulp = require('gulp');

// Include plugins
var jasmine     = require('gulp-jasmine');
var uglify      = require('gulp-uglify');
var source      = require('vinyl-source-stream'); // makes browserify bundle compatible with gulp
var streamify   = require('gulp-streamify');
var browserify  = require('browserify');

// Test JS
gulp.task('specs', function () {
    return gulp.src('assets/js/spec/lib/*.js')
        .pipe(jasmine());
});

// Concatenate, Browserify & Minify JS
gulp.task('scripts', function() {
    return browserify('./assets/js/app.js').bundle()
        .pipe(source('all.min.js'))
        .pipe(streamify(uglify()))
        .pipe(gulp.dest('./public/'));
});

// Default Task
gulp.task('default', function() {
  // place code for your default task here
});

gulp.task('default', ['specs', 'scripts']);

You’ll notice that we did a few things: declare new modules at the top through [cci]require[/cci], add a new gulp task called [cci]scripts[/cci], and update the default task to run our JS specs & scripts tasks.

At [cci]public/all.min.js[/cci], your new JS is ready to use in your browser.

Let’s remove the old file and update our [cci]index.html[/cci] to use our new minified JS:

rm assets/js/main.js 

Remove the following lines from [cci]index.html[/cci]:

< script src="assets/js/jquery-1.10.2.min.js">
< script src="assets/js/main.js">

Add the following line into [cci]index.html[/cci]:

< script src="public/all.min.js">

Voila! Open up [cci]index.html[/cci] in your browser and your [cci]square()[/cci] function is working again.

Conclusion

GulpJS is an amazing tool to run tasks, and the Gulp streaming build system is very easy to read and understand.

There are countless tasks that you can setup on Gulp to lint your JS, compile your sass, etc. Livereload is useful for front end development.

Hopefully, the Unit Test JS demo helped you understand a simple example of using Gulp to run Jasmine unit tests and use the tested JS in your website.

Maintainable Client JavaScript

I’ve been reading Maintainable JavaScript by Nicholas C. Zakas lately. It has been very insightful into best practices across larger teams. When you have a large team and adopt a consistent coding style, it makes working across your codebase easier.

In Chapter 5 of the book, Zakas covers the UI as a mix of HTML, CSS, and Javascript. He mentions that tightly coupling these three layers makes it “impossible to make small changes without changing one or two other layers.

Also, I’ve been working with AngularJS recently. I understand the benefits of a front end framework to keep data in sync throughout your client. Angular fans tout the benefits of a SPA (single page application) framework.

As someone who strives to separate the structure (HTML) from the scripting (JS), Angular feels too tightly coupled to me. Angular works by tagging everything with [cci]ng-[/cci] and letting the magic work behind the scenes. The application dependencies are hardcoded everywhere in your HTML, and there is no way to swap your framework without changing your HTML drastically.

I’ve worked with Backbone in the past, and now I’m trying out Angular. At some point, I’ll probably try out Ember. I’d like a front end framework that plays well with Rails, so perhaps Ember will be fun.

Leaflet JS with Open MapQuest Tiles

With CloudMade changing their API to focus on enterprise customers, I had to find an alternative for hosted map tiles.

There’s a good gist from mourner that shows Leaflet.js TileLayer shortcuts at https://gist.github.com/mourner/1804938

Instead of the old CloudMade tile tutorial used on Leaflet:

[cc]

L.tileLayer(‘http://{s}.tile.cloudmade.com/API-key/997/256/{z}/{x}/{y}.png’, {
attribution: ‘proper attribution goes here’
}).addTo(map);

[/cc]

You can use Open MapQuest:

[cc]

L.tileLayer(‘http://{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png’, {
attribution: ‘proper attribution goes here’,
subdomains: [‘otile1’, ‘otile2’, ‘otile3’, ‘otile4’]
}).addTo(map);

[/cc]

Just make sure your attribution properly reflects all your sources.

How jQuery text method works in JavaScript

jQuery has a popular method for accessing the text contents of a targeted DOM node named [cci].text()[/cci]. It allows you to either get the text content or set the text content.

For example, using [cci]text()[/cci] on [cci]$(‘#test’)[/cci] returns [cci]foobar[/cci] below:
[cc]

foobar

$(‘#test’).text(); //=> returns ‘foobar’
[/cc]

By using [cci]text()[/cci] with a parameter, you can set the text to the passed in parameter.
[cc]

foobar

$(‘#test’).text(“cupcakes”); // sets the value to ‘cupcakes’
$(‘#test’).text(); //=> returns ‘cupcakes’
[/cc]

There is a similar jQuery method named [cci]html()[/cci]. It returns the html contents within the targeted DOM node.
[cc]

foobar

$(‘#test’).html(); //=> returns ‘foobar
[/cc]

Before we dive into the jQuery source, we can guess that jQuery uses textContent.

In the jQuery source, you can see on line 5702:
[cc]
jQuery.fn.extend({
text: function( value ) {
return jQuery.access( this, function( value ) {
return value === undefined ?
jQuery.text( this ) :
this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
}, null, value, arguments.length );
},
[/cc]

jQuery checks the passed in parameter [cci]value[/cci] using a ternary operator. If [cci]value[/cci] is [cci]undefined[/cci], then jQuery calls [cci]text[/cci]. Else, jQuery sets the value with [cci]createTextNode[/cci].

[cci]jQuery.access[/cci] on line 784 allows jQuery to determine if the value is defined or undefined.

If undefined, [cci]jQuery.text( this )[/cci] runs.

[cc]
// line 5348:
jQuery.text = Sizzle.getText;
[/cc]

Sizzle’s getText looks like:

[cc]
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
var node,
ret = “”,
i = 0,
nodeType = elem.nodeType;

if ( nodeType ) {
if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (see #11153)
if ( typeof elem.textContent === “string” ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
} else {

// If no nodeType, this is expected to be an array
for ( ; (node = elem[i]); i++ ) {
// Do not traverse comment nodes
ret += getText( node );
}
}
return ret;
};
[/cc]

You can see that jQuery either returns the string with [cci]textContent[/cci]

[cc]
// Use textContent for elements
// innerText usage removed for consistency of new lines (see #11153)
if ( typeof elem.textContent === “string” ) {
return elem.textContent;
}
[/cc]

or jQuery will traverse the DOM and append them into the [cci]ret[/cci] variable (initialized as as empty string) to return.

[cc]
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
[/cc]

If you are passing in a param value into [cci]text()[/cci], then jQuery will empty out the contents of the targeted DOM node and use createTextNode to allow your passed in param to show up.

[cc]
this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
[/cc]

If you are interested in using Core JavaScript to replicate jQuery’s [cci]text()[/cci], you could try the following:

[cc]

lion

var div = document.querySelectorAll(“#test”);
div[0].textContent; //=> returns ‘lion’
[/cc]

This targets the [cci]#test[/cci] div and get the text content.

[cc]

var div = document.querySelectorAll(“#test”);
div[0].appendChild(document.createTextNode(‘pizza’));
[/cc]

This is one way to set the div contents to display ‘pizza’.

Thanks for tuning into to another jQuery without jQuery. Diving into the source code is great for helping you understand how it all works.

jQuery without jQuery is a series that aims to open up the jQuery black box. jQuery is just JavaScript, so you should feel comfortable working at lower levels of abstraction and looking at the jQuery source code written in JavaScript.

jQuery DOM ready handlers

As you work with JavaScript & jQuery, you will quickly get accustomed to wrapping your JS scripts with a DOM ready check. You generally want to do this, because you don’t want your DOM-manipulating JS to run before the DOM is fully loaded.

The standard jQuery ready function is:

[cc]
$(document).ready(function(){
// Your code here
});
[/cc]

If you’ve seen the jQuery ready shortcut, you can also use this (equivalent to the above):

[cc]
$(function() {
// Your code here
});
[/cc]

How does this work? Diving into jQuery source, you can see on line 173 that jQuery checks if what is passed in is a function and applies [cci]ready[/cci] to it:

[cc]
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
[/cc]

Also, I want to show how you could wrap your JS code without jQuery. Traditionally, it looks like this:

[cc]
window.onload = function(){
// Your code here
}
[/cc]

Note the traditional way using [cci]window.onload[/cci] will happen later than jQuery’s [cci]ready[/cci] event. Code snippets to run your JS upon DOM ready without jQuery are available online.

Finally, if you want to wait for both the DOM and complete page (including all frames, objects and images) to be loaded using jQuery, you can use:

[cc]
$(window).load(function() {
// Your code here
});
[/cc]

Running JS with jQuery upon DOM ready is simple. Using the jQuery ready shortcut is easy and works well.

jQuery without jQuery is a series that aims to open up the jQuery black box. jQuery is just JavaScript, so you should feel comfortable working at lower levels of abstraction and looking at the jQuery source code written in JavaScript.

How jQuery CSS selector works

Diving into jQuery source code, you’ll find attribution for [cci]Sizzle.js[/cci] near the top. What is Sizzle? Sizzle is jQuery’s spinoff CSS selector engine.

jQuery is optimized for easy DOM manipulation and allows method chaining by returning the jQuery object. This means that the [cci]jQuery[/cci] (or [cci]$[/cci] shorthand syntax) allows you to target DOM elements with CSS syntax.

You can select a paragraph with the id “dino” in jQuery:

[cc]$(‘p#dino’)[/cc]

Pretty easy, right? How does jQuery do this?

BigBinary has a great article walking through the process:

  • If querySelectorAll works, use querySelectorAll.
  • Else, use Sizzle.

To use [cci]querySelectorAll[/cci] instead of jQuery, you could use:

[cc]document.querySelectorAll(‘p#dino’)[/cc]

This goes to show that anything jQuery can do, core JavaScript can do (since jQuery is JavaScript). With that said, I would recommend jQuery over writing your own JavaScript from scratch as jQuery is highly tested for edge cases and offers amazing cross browser support.

jQuery without jQuery is a series that aims to open up the jQuery black box. jQuery is just JavaScript, so you should feel comfortable working at lower levels of abstraction and looking at the jQuery source code written in JavaScript.