Book Image

Learning jQuery, Third Edition

Book Image

Learning jQuery, Third Edition

Overview of this book

To build interesting, interactive sites, developers are turning to JavaScript libraries such as jQuery to automate common tasks and simplify complicated ones. Because many web developers have more experience with HTML and CSS than with JavaScript, the library's design lends itself to a quick start for designers with little programming experience. Experienced programmers will also be aided by its conceptual consistency.Learning jQuery Third Edition is revised and updated for version 1.6 of jQuery. You will learn the basics of jQuery for adding interactions and animations to your pages. Even if previous attempts at writing JavaScript have left you baffled, this book will guide you past the pitfalls associated with AJAX, events, effects, and advanced JavaScript language features.Starting with an introduction to jQuery, you will first be shown how to write a functioning jQuery program in just three lines of code. Learn how to add impact to your actions through a set of simple visual effects and to create, copy, reassemble, and embellish content using jQuery's DOM modification methods. The book will step you through many detailed, real-world examples, and even equip you to extend the jQuery library itself with your own plug-ins.
Table of Contents (24 chapters)
Learning jQuery Third Edition
Credits
Foreword
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Adding and running tests


In test-driven development , we write tests before writing code. This way, we can observe that a test fails, add new code, and then see that the test passes, verifying that our change has the intended effect.

Let's start by testing the child selector that we used in Chapter 2 to add a horizontal class to all <li> elements that are children of <ul id="selected-plays">:

test('Child Selector', function() {
  expect(1);
  var topLis = $('#selected-plays > li.horizontal');
  equal(topLis.length, 3, 'Top LIs have horizontal class');
});

Here, we have actually introduced two tests. We begin with the expect() test, which instructs QUnit how many tests we expect to run in this set. Then, because we are testing our ability to select elements on the page, we use the equal() test to compare the number of top-level <li> elements against the number 3. If the two are equal, the test passes:

Of course, the test fails because we have not yet written the code to add the horizontal class. It is simple to add that code, though. We do so in the main script file for the page, which we named B.js:

$(document).ready(function() {
  $('#selected-plays > li').addClass('horizontal');
});

When we run the test now, it passes:

Now the Selecting: Child Selector test shows no failing tests and one passing test out of a total of one test. We can add a couple of attribute selector tests as well:

module('Selecting', {
  setup: function() {
    this.topLis = $('#selected-plays > li.horizontal');
  }
});

test('Child Selector', function() {
  expect(1);
  equal(this.topLis.length, 3,
    'Top LIs have horizontal class');
});

test('Attribute Selectors', function() {
  expect(2);
  ok(this.topLis.find('.mailto').length == 1, 'a.mailto');
  equal(this.topLis.find('.pdflink').length, 1, 'a.pdflink');
});

Here we have introduced another type of test: ok(). This one takes two arguments: an expression that should evaluate to a "truthy" value, and a description. Also, note that we have moved the local topLis variable out of the Child Selector test and into the module's setup() callback function. The module() takes an optional second argument, which is a map that can include a setup() and a teardown() function. Within these functions, we can use the this keyword to assign variables once for all of a module's tests.

Again, the new tests will fail without the corresponding working code, so let's include that in the script as follows:

$(document).ready(function() {
  $('#selected-plays > li').addClass('horizontal');
  $('a[href^="mailto:"]').addClass('mailto');
  $('a[href$=".pdf"]').addClass('pdflink');
});

If we expand the newly passing test set, we can see the difference in output between the ok() and equal() tests, as shown in the following screenshot:

In addition to indicating that both of the tests have passed with the green color, the equal() test details the expected result. As it provides more information, the equal() test is typically preferred over the ok() test.

Asynchronous testing

Testing asynchronous JavaScript, such as Ajax requests, presents an additional challenge. The rest of the tests must pause while the asynchronous test occurs and then they must begin again when it is complete. This type of scenario is by now very familiar; we have seen such asynchronous operations in effects queues, Ajax callback functions, and promise objects. In QUnit, we use a special test set named asyncTest(). It looks like the regular test() set, except that it will pause the running of tests until we resume them with a call to the special start() function:

asyncTest('JSON', function() {
  $.getJSON('B.json', function(json, textStatus) {
    // add tests here
  }).always(function() {
    start();
  });
});

Here we are simply requesting JSON from B.js and allowing the tests to continue once the request has completed, whether it succeeds or fails. For actual tests, we are going to check the textStatus to ensure that the request is successful and check the value of one of the objects within the response JSON array as follows:

asyncTest('JSON', function() {
  expect(2);
  var backbite = {
    "term": "BACKBITE",
    "part": "v.t.",
    "definition": "To speak of a man as you find him when he can't find you."
  };

  $.getJSON('B.json', function(json, textStatus) {
    equal(textStatus, 'success', 'Request successful');
    deepEqual(json[1], backbite,
      'result array matches "backbite" map');
  }).always(function() {
    start();
  });
});

For testing the response value, we use yet another test function, deepEqual() . Normally, when two objects are compared, they are considered not equal unless they actually refer to the same memory. If we want to compare the object contents instead, deepEqual() meets the need. This function walks through two objects to ensure that they have the same properties and that those properties have the same values.