Could not convert JavaScript argument arg 0 [nsIDOMHTMLDivElement.appendChild]

You may face this error when you add a Dojo component to a normal DOM element using appendChild as follows:

<div id="container"></div>

<script type="text/javascript">
	dojo.addOnLoad(function(){
		var dojoInputField = new dijit.form.TextBox({id: "txtName"});
		document.getElementById("container").appendChild(dojoInputField);
	});
</script>

This error appears because you try to add the Dojo element itself not the DOM node that it wraps. In order to fix this issue, you can do this by adding the component DOM node as follows:

<div id="container"></div>

<script type="text/javascript">
	dojo.addOnLoad(function(){
		var dojoInputField = new dijit.form.TextBox({id: "txtName"});
		document.getElementById("container").appendChild(dojoInputField.domNode);
	});
</script>	

Preventing backspace from navigating back in all the browsers

One of the requirements which you may face is preventing the backspace key from navigating back in the browser. This requirement can be achieved by listening on the keyboard keys in the browser and acting when the backspace key is pressed under the condition that the mouse cursor is NOT inside an input or a text area elements. Actually, you have to take care of the IE broken event model when you are implementing this feature. for example, the event.stopPropagation() method is not working in IE, adding to this, you have to get the keycode from the window.event not from the passed event object to the event handler.

I wrote the following code which works on all the common browsers (IE, Safari, Chrome, and Firefox) perfectly, and I wish it can be useful for you:

document.onkeydown = function (event) {
	
	if (!event) { /* This will happen in IE */
		event = window.event;
	}
		
	var keyCode = event.keyCode;
	
	if (keyCode == 8 &&
		((event.target || event.srcElement).tagName != "TEXTAREA") && 
		((event.target || event.srcElement).tagName != "INPUT")) { 
		
		if (navigator.userAgent.toLowerCase().indexOf("msie") == -1) {
			event.stopPropagation();
		} else {
			alert("prevented");
			event.returnValue = false;
		}
		
		return false;
	}
};	

Using this code inside a <script> tag of the HTML <body> element can do this job for you.

Developing Weather Application using Dojo Mobile

Abstract

Dojo mobile (dojox/mobile) is one of the most powerful JavaScript frameworks that enable you to build cross mobiles web applications. It does not only contain mobile components but also it mimics the interface of the different mobile devices. Adding to this it responds to the mobile orientations. In this article, I will illustrate how to use the framework for building a weather application on the iPhone mobile.

Application Story

The application story is simple. In the weather mobile application, the user can get the current weather forecasting of a specific location. The user can do this by selecting one city from a list of available cities in the drop down and then click the “Get Information” button to get the weather information as shown in the screenshot below.

Building the weather application

In order to create the weather forecasting page, we need to know the main parts of the Dojo mobile page:

  • The Doc Type and META tags.
  • The mobile script and style includes.
  • The application pages.

The Doc Type and META Tags

The Doc type that is recommended to be used is the HTML5 doctype as shown below:

<!DOCTYPE html>

For the META tags, it is important to use the "viewport" META tag to setup cross-device layout as shown below:

<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/>

The other important META tag is an iPhone specific one which is the “apple-mobile-web-app-capable”. It is recommend to be set to “yes” to make the web application runs in full-screen mode as shown below:

<meta name="apple-mobile-web-app-capable" content="yes"/>

The script and style includes

Now, we come to the script and style includes. I’m using Dojo 1.7.1 for this application; in order to include the script and the style files; this can be done as shown below:

<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/mobile/themes/iphone/iphone.css" rel="stylesheet"></link>

<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js" data-dojo-config="async: true"></script>

I only included the iPhone CSS file, and the dojo.js file which is loaded asynchronously for including only the resources we need to keep the application optimized.

After including the script and the style, we need to use the require statements for including the Dojo modules. I use the new Asynchronous Modules (AMD) convention for making asynchronous loading of the modules as shown below:

require(["dojox/mobile/parser", "dojo/_base/xhr", "dojox/mobile", "dojox/mobile/deviceTheme",
         "dojox/mobile/TextBox", "dojox/mobile/Button", "dojox/mobile/compat", 
	 "dijit/form/DataList", "dojox/mobile/ComboBox",
         "dojo/domReady!"], function(parser) {
			
		//...			
		parser.parse();
	});

The following modules are loaded:

  • The Dojo mobile parser "dojox/mobile/parser" for converting the HTML elements to Dojo mobile components.
  • The Dojo XHR module "dojo/_base/xhr" for allowing performing Ajax requests.
  • The Dojo mobile device theme "dojox/mobile/deviceTheme", for allowing detecting the mobile type and loading the proper theme correctly.
  • "dojox/mobile/TextBox", "dojox/mobile/Button", "dijit/form/DataList", "dojox/mobile/ComboBox", for loading the mentioned components.
  • The "dojox/mobile/compat" module is required for allowing having cross mobiles CSS3 animations.
  • "dojo/domReady!" module is a replacement for the old dojo.ready API. The exclamation mark is required to tell Dojo not to execute the callback until the DOM is ready.

Finally in the callback, I’m using only the first parameter which is the parser object. The parser.parse statement is called for allowing the Dojo mobile parser to convert the HTML elements to Dojo mobile components.

The Application Pages

In our weather application, we have mainly two pages; the weather forecasting page and the about page. In order to make the pages scroll to the left and the right while navigation, we need to use the Dojo mobile views. The Dojo mobile views are normal HTML elements with the dojo type "dojox.mobile.View". To define the two views, we can do this as shown below:

<div id="weather" dojoType="dojox.mobile.View" selected="true">
	...
</div>
			
<div id="about" dojoType="dojox.mobile.View">
	...
</div>		

The main view should be marked as selected by setting the selected attribute to "true".

In the weather forecasting page, there are three main elements:

  • Heading element.
  • Combo Box of the available cities.
  • The “Get Information” button.

In order to create the heading element, you can mark an HTML element h1 with the “dojox.mobile.Heading” dojo type as follows:

<h1 dojoType="dojox.mobile.Heading">Weather
	<div dojoType="dojox.mobile.ToolBarButton" moveTo="about">About</div>		
</h1>

I included a toolbar button for moving to the other view. The moveTo attribute can have the id of the view to switch to, in our case it is the about view id.

In order to create the combo box element, we need to use two tags, one for the data which is the dijit.form.DataList, and the other for the widget itself which is the dojox.mobile.ComboBox as follows:

<select data-dojo-type="dijit.form.DataList" data-dojo-props="id:'locationList'" >
  <option selected>Cairo, Egypt</option>
  <option>Stockholm, Sweden</option>	
  <option>Vienna, Austria</option>
  <option>Madrid, Spain</option>	
  <option>Paris, France</option>
  <option>New York, USA</option>
  <option>Lima, Peru</option>
</select>

<input id="location" class="ninety" type="text" data-dojo-type="dojox.mobile.ComboBox" data-dojo-props="list:'locationList'" />

The “location” combo box is referring to the “locationList” using the list attribute.

The “Get Information” button calls the getWeatherInformation function to get the weather information as follows:

<button class="mblBlueButton full" dojoType="dojox.mobile.Button" onClick="getWeatherInformation();">Get Information</button>

The getWeatherInformation JavaScript function is calling a proxy Java Servlet that interacts with the Yahoo weather service API using dojo xhrGet.

function getWeatherInformation() {
	var selectedCode = cityMap[dijit.byId("location").value];
	
	dojo.xhrGet({
		url: "/mobileSamples/weatherProxyServlet?w=" + selectedCode,
		handleAs: "text",
		load: function(data) {
			document.getElementById("weatherResult").innerHTML = data;
		}
	});
}

The location value is read from the “location” combo box and the code of the location is retrieved from a custom JavaScript Map that I created for mapping the WOEID (Where on earth identification) to the locations.

When the HTML response is returned from the server, it is displayed directly in the "weatherResult" span.

Finally in the weather forecasting view, I embed the combo box and the button in the "dojox.mobile.RoundRect" div as follows:

<div dojoType="dojox.mobile.RoundRect" shadow="true">
	<label for="txtLocation">Select location</label> <br/>
	
	<select data-dojo-type="dijit.form.DataList" data-dojo-props="id:'locationList'" >
	  <option selected>Cairo, Egypt</option>
	  <option>Stockholm, Sweden</option>	
	  <option>Vienna, Austria</option>
	  <option>Madrid, Spain</option>	
	  <option>Paris, France</option>
	  <option>New York, USA</option>
	  <option>Lima, Peru</option>
	</select>
	
	<input id="location" class="ninety" type="text" data-dojo-type="dojox.mobile.ComboBox" data-dojo-props="list:'locationList'" /><br/><br/>
	
	<button class="mblBlueButton full" dojoType="dojox.mobile.Button" onClick="getWeatherInformation();">Get Information</button><br/><br/>
	
	<span id="weatherResult"/>
</div>

The other “about” view element is a simple one that contains description for the sample as follows:

<div id="about" dojoType="dojox.mobile.View">
	<h1 dojoType="dojox.mobile.Heading" back="back" moveTo="weather">About</h1>
	<div dojoType="dojox.mobile.RoundRect">This sample is developed by TechnicalAdvices.COM. It is powered by Dojo Mobile.</div>
</div>

It contains a heading that includes a back button using the back attribute. The back attribute should be set to id of the view element that the application navigates to when the back button is clicked.

The next code listing shows the complete code of the weather forecasting page:

<!DOCTYPE html>
<html>

<head>
	<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/>
	<meta name="apple-mobile-web-app-capable" content="yes"/>
	<title>Welcome to the weather application</title> 
	
	<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/mobile/themes/iphone/iphone.css" rel="stylesheet"></link>
	<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojo/dojo.js" data-dojo-config="async: true"></script>
	
	<script type="text/javascript">
		var cityMap = {};
		
		require(["dojox/mobile/parser", "dojo/_base/xhr", "dojox/mobile", "dojox/mobile/deviceTheme",
		         "dojox/mobile/TextBox", "dojox/mobile/Button", "dojox/mobile/compat", 
		         "dijit/form/DataList", "dojox/mobile/ComboBox",
		         "dojo/domReady!"], function(parser) {
			
					// initialize WOIDs ...
					cityMap["Cairo, Egypt"] = "1521894";
					cityMap["Stockholm, Sweden"] = "906057";
					cityMap["Vienna, Austria"] = "551801"
					cityMap["Madrid, Spain"] = "766273";
					cityMap["Paris, France"] = "615702";
					cityMap["New York, USA"] = "2459115";
					cityMap["Lima, Peru"] = "418440";
			
					parser.parse();
				});


		function getWeatherInformation() {
			var selectedCode = cityMap[dijit.byId("location").value];
			
			dojo.xhrGet({
			    url: "/mobileSamples/weatherProxyServlet?w=" + selectedCode,
			    handleAs: "text",
			    load: function(data) {
			    	document.getElementById("weatherResult").innerHTML = data;
			    }
			});
		}
	</script> 
	
	<style>
		.full {
			width:100%
		}
		
		.ninety {
			width:90%
		}		
	</style>
</head>

<body>
	<div id="weather" dojoType="dojox.mobile.View" selected="true">
		<h1 dojoType="dojox.mobile.Heading">Weather
			<div dojoType="dojox.mobile.ToolBarButton" moveTo="about">About</div>		
		</h1>
		
		<div dojoType="dojox.mobile.RoundRect" shadow="true">
			<label for="txtLocation">Select location</label> <br/>
			
			<select data-dojo-type="dijit.form.DataList" data-dojo-props="id:'locationList'" >
			  <option selected>Cairo, Egypt</option>
			  <option>Stockholm, Sweden</option>	
			  <option>Vienna, Austria</option>
			  <option>Madrid, Spain</option>	
			  <option>Paris, France</option>
			  <option>New York, USA</option>
			  <option>Lima, Peru</option>
			</select>
			
			<input id="location" class="ninety" type="text" data-dojo-type="dojox.mobile.ComboBox" data-dojo-props="list:'locationList'" /><br/><br/>
			
			<button class="mblBlueButton full" dojoType="dojox.mobile.Button" onClick="getWeatherInformation();">Get Information</button><br/><br/>
			
			<span id="weatherResult"/>
		</div>
	</div>
				
	<div id="about" dojoType="dojox.mobile.View">
		<h1 dojoType="dojox.mobile.Heading" back="back" moveTo="weather">About</h1>
		<div dojoType="dojox.mobile.RoundRect">This sample is developed by TechnicalAdvices.COM. It is powered by Dojo Mobile.</div>
	</div>					

</body>
</html>

The Backend Java Servlet

Finally, the backend Java Servlet reads the WOEID parameter and opens a URL connection to the yahoo API weather forecasting URL for getting the weather forecasting of the location as shown in the code listing below.

package servlets;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;

/**
 * <code>WeatherProxyServlet</code> is a proxy servlet that wraps Yahoo weather REST service.
 * @author hazems
 *
 */
public class WeatherProxyServlet extends HttpServlet {
	private static final long serialVersionUID = 8732370454506907957L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		
		String WOEID = request.getParameter("w");
		HttpURLConnection connection = null;
		
		response.setContentType("text/html");

		PrintWriter writer = null;
		
		try {
			URL url = new URL("http://weather.yahooapis.com/forecastrss?u=c&w=" + WOEID);
			
			connection = createHTTPConnection(url);
			
			String output = getHTTPOutputResponse(connection);
			
			SyndFeedInput input = new SyndFeedInput();
			SyndFeed feed = input.build(new InputStreamReader(new ByteArrayInputStream(output.getBytes("UTF-8"))));  		
			
		    if (feed.getEntries() != null && feed.getEntries().size() > 0) {
	            SyndEntry entry = ((SyndEntry) feed.getEntries().get(0));
	                        
	            String title = entry.getTitle();
	            String description = entry.getDescription().getValue();
	            
	            if (description.contains("Invalid")) {
	            	throw new Exception(description);
	            }
	    		
	    		writer = response.getWriter();	            
	            writer.print(title + "<br/>" + description);
	        }	
		} catch (Exception exception) {
			throw new ServletException(exception.getMessage());
		} finally {
			if (connection != null) {
				connection.disconnect();
			}
			
			if (writer != null) {
				writer.flush();
	        	writer.close();		
			}
		}
	}
	
	private static HttpURLConnection createHTTPConnection(URL url) throws IOException, ProtocolException {
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		
		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setRequestMethod("GET"); 
		conn.setRequestProperty("Accept", "text/html"); 
		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		
		return conn;
	}

	private static String getHTTPOutputResponse(HttpURLConnection conn) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));

		String httpOutputResponse = ""; 
		String outputLine;
		
		while ((outputLine = br.readLine()) != null) {
			httpOutputResponse += outputLine;
		}
		
		return httpOutputResponse;
	}
}

You can try the application from your iPhone by going to the following URL:
http://www.mashups4jsf.com/mobileSamples/weather.jsp

You can download the complete Eclipse project from the following URL:
https://www.ibm.com/developerworks/mydeveloperworks/blogs/hazem/resource/29052012/mobileSamples.zip

Conclusion

In this article, you learned how to build a mobile application using the Dojo mobile APIs. You know how to use the framework views, headings, and components for building the weather forecasting application.

“Efficient JavaScript unit testing” video session in CONFESS 2012

This video provides a presentation about how to make efficient JavaScript unit testing. I presented it in the CON-FESS 2012 international conference which was held in Leogang, Austria. I wish it can be useful.

See the presentation with the slides on Parleys.com

See the presentation with the slides on Parleys.com

and I included below the presentation:

Converting the JsTestDriver coverage files to HTML Reports

In this post, I will show you the detailed steps that are required to convert the JsTestDriver LCOV coverage files to HTML reports. Before reading this post, I recommend you to read this post first “Generating Test Reports using JsTestDriver“.

First of all, you need to download the LCOV visualizer from: http://ltp.sourceforge.net/coverage/lcov.php.

Then install the downloaded rpm file in your Linux Redhat environment using the following command line:

rpm -i lcov-1.9-1.noarch.rpm

To check that the tool is installed correctly, run the genhtml command, you should see the following output:

After that you can start running the HTML report generation command:

genhtml jsTestDriver.conf-coverage.dat

Note that you have to make sure that the paths of the JavaScript source files are correct in the jsTestDriver.conf-coverage.dat file, otherwise you will face the genhtml: ERROR: cannot create directory !

You should see the LCOV visualizer tool is generating the testing report as indicated in the screenshot below:

After the successful generation of the HTML report, click the index.html file in the parent folder to see the HTML report:

As shown in the previous screenshot, the report covers the three main code coverage criteria:
1. Line coverage (statement coverage) which represents the percentage of the source code statements that are tested.
2. Function coverage which represents the percentage of the functions that are tested.
3. Branch coverage which represents the percentage of code branches that are tested.

I attached here the test report files for your reference.

Resolution of genhtml: ERROR: cannot create directory !

When using the LCOV Visualizer tool to generate the HTML reports from the JsTestDriver lcov coverage (.dat) file (usually jsTestDriver.conf-coverage.dat) using the genhtml command:

genhtml jsTestDriver.conf-coverage.dat

You may find the following error:

mkdir: cannot create directory `': No such file or directory
genhtml: ERROR: cannot create directory !

This error happens because the paths of the JavaScript files are not correct in the (.dat) file. To fix this error, open the (.dat) file, you will find its content as follows:

SF:<<Path1>>
DA:1,1
DA:2,1
DA:5,1
...
end_of_record
SF:<<Path2>>
DA:1,1
DA:2,0
DA:5,1
...
end_of_record

Then edit the SF: values with the correct paths of the JavaScript source (and testing) files and execute the genhtml command again to generate the HTML testing report.

Modern Web Architecture: The HTML5 Web Storage

Abstract

One of the most important features of HTML5 is the Web Storage feature. Using the Web Storage feature, the client state can be saved on the browser level without consuming the network bandwidth or the server memory. The Web Storage increases the scalability of the web applications on the server level and on the network level. In this article, I will show you how to work with the HTML5 Web Storage feature.

Difference between the old cookies and the HTML5 Web Storage

Unlike cookies, the HTML5 Web Storage is more scalable. The HTML5 Web Storage content is not transferred between the client and the server per every request. This means using the HTML5 Web Storage, you can store unlimited amount of information on the browser without affecting the network bandwidth. While the web cookies have a limited size, and are transferred between the server and the client per every request.

Checking if the browser is supporting the HTML5 Web Storage

HTML5 Web Storage is supported in most of the modern browsers (IE8, Firefox, Safari, Chrome, and Opera). To check if the Web Storage is supported, check if the web storage objects are defined as follows.

function isStorageSupported() {
	if (typeof(localStorage) === "undefined" || typeof(sessionStorage) === "undefined") {
		alert("Web Storage is no suppored ...");
		return false;
	}
	
	return true;
}

The HTML5 Web Storage Objects

The Web Storage is represented by two main objects the localStorage object and the sessionStorage object. The main difference between them is that the localStorage object is stored in the browser with no expiration date, however, the sessionStorage object is stored in the browser and is removed once the user session ends (i.e., its life time is the user session). It is important to know that there is another difference which is localStorage can work in local mode while the sessionStorage can work only inside a hosted page on the server.

If you try to run the sessionStorage in local mode, you will face an error. For example, the error message in Firefox is:

Operation is not supported

I created an example that illustrates how to use the HTML5 Web Storage objects. In this example, there is a text area “information” field and four buttons for (saving the “information” field value in localStorage, retrieving the stored “information” field value from localStorage, saving the “information” field value in sessionStorage, retrieving the stored “information” field value from sessionStorage).

To store any data in the localStorage object, just add the information in the (key, value) form as follows:

localStorage.key = value;

In the example to store the “information” field data in the localStorage object:

localStorage.information = document.getElementById("information").value;

To retrieve the data from the localStorage object, you can retrieve it using the key as follows:

alert(localStorage.key);

In the example to get the “information” data from the localStorage object:

alert("Data from local storage is: " + localStorage.information);
document.getElementById("information").value = localStorage.information;

Using the sessionStorage object is pretty the same as localStorage. In the example to store the “information” field data in the sessionStorage object:

sessionStorage.information = document.getElementById("information").value;

To get the “information” data from the sessionStorage object:

alert("Data from session storage is: " + sessionStorage.information);
document.getElementById("information").value = sessionStorage.information;

I attach below, the complete example code:

<!DOCTYPE html>
<HTML>
<HEAD>
	<TITLE>Welcome to the WebStorage test</TITLE>
	
	<SCRIPT type="text/javascript">
	
		function isStorageSupported() {
			if (typeof(localStorage) === "undefined" || typeof(sessionStorage) === "undefined") {
				alert("Web Storage is no suppored ...");
				return false;
			}
			
			return true;
		}
	
		function storeInformationInLocalStorage() {
			if (isStorageSupported()) {
				localStorage.information = document.getElementById("information").value;
			}
		}
		
		function readInformationFromLocalStorage() {
			if (isStorageSupported()) {
				alert("Data from local storage is: " + localStorage.information);
				document.getElementById("information").value = localStorage.information;
			}			
		}		
	
		function storeInformationInSessionStorage() {
			if (isStorageSupported()) {
				sessionStorage.information = document.getElementById("information").value;
			}
		}
		
		function readInformationFromSessionStorage() {
			if (isStorageSupported()) {
				alert("Data from session storage is: " + sessionStorage.information);
				document.getElementById("information").value = sessionStorage.information;
			}				
		}	
	
	</SCRIPT>
</HEAD>
<BODY>
	<label for="information">Enter some information in the textArea: </label><br/>
	<textarea id="information" rows="2" cols="20"></textarea>	
	<br/><br/>

	<INPUT TYPE="button" value="Store in the localStorage" onclick="javascript:storeInformationInLocalStorage()"/>
	<INPUT TYPE="button" value="Read from the localStorage" onclick="javascript:readInformationFromLocalStorage()"/>

	<br/>

	<INPUT TYPE="button" value="Store in the sessionStorage" onclick="javascript:storeInformationInSessionStorage()"/>
	<INPUT TYPE="button" value="Read from the sessionStorage" onclick="javascript:readInformationFromSessionStorage()"/>	
</BODY>
</HTML>

You can delete any of the key/value pairs in the Web Storage using the sessionStorage.removeItem(key) and the localStorage.removeItem(key). If you want to delete all of the key/value pairs, you can use the sessionStorage.clear() and localStorage.clear().

I attach the example for your reference here.

Testing JavaScript code using Jasmine (Kickstart)

Jasmine is one of the JavaScript unit testing frameworks. It has a nice advantage that its syntax is readable and a very human friendly. In this post, I will give you a kickstart to help you start working with Jasmine.

First of all, Let’s create a simple JavaScript object that we need to perform some unit testing on it:

/* Basics.js file */
function Basics() {
}

Basics.prototype.add = function(x, y) {
	return x + y;
};

As shown in the Basics.js file, Basics is a simple JavaScript object that contains one method that adds the x and y parameters and finally returns the result to the caller.

To start working with the Jasmine framework, download the framework from http://pivotal.github.com/jasmine/download.html. Make sure that you have the following folder structure after extracting the downloaded zip file:
jasmine structure

You are having three folders and one html file.
1. lib folder: contains the source files of the Jasmine framework.
2. src folder: contains the source JavaScript files that will be tested.
3. spec folder: contains the testing JavaScript files.
4. SpecRunner.html file: is used for running the JavaScript test suites.

Place the Basics.js in the src folder after making sure that you remove all the old contents of the src and the spec folders.

Now, Let’s write the test suite in the BasicsSpec.js file.

describe("Basics", function() {
  var basics = new Basics();  

  it("should be able to make correct addition", function() {
    expect(basics.add(3, 2)).toEqual(5);
  });

});

As we see here, the testing syntax is very human friendly, it means “describe Basics object which should be able to make correct addition”. The describe keyword represents a test suite that can contain one or more specs. Every spec is represented by the it keyword.

Inside the spec, there is an expectation that expects the basics.add(3, 2) is equal to 5. In Jasmine, the expectation is represented by the expect keyword and the equality checking is represented by a matcher called toEquals.

In Jasmine, there are many other built-in matchers; you can find all of them here: https://github.com/pivotal/jasmine/wiki/Matchers.

Finally, do not forget to remove the old source and spec JS includes from the SpecRunner.html and to add the new includes instead:

  <!-- include source files here... -->
  <script type="text/javascript" src="src/Basics.js"></script>  
  
  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/BasicsSpec.js"></script>

After running the SpecRunner.html file, you will find the following screenshot telling you that the test spec runs successfully.

screenshot

I wish this kickstart can be useful for you. For your reference, download the kickstart example from here.

“Ajax Status Zero” Resolution

“Ajax status is zero” is one of the hardest errors to debug especially when the Ajax readyState is 4. If we looked at the XMLHTTPRequest documentation, we will find that the readyState attribute can have one of the following values:

  • 0 “The object has been constructed.”
  • 1 “The open() method has been successfully invoked. During this state request headers can be set using setRequestHeader() and the request can be made using the send() method.”
  • 2 “All redirects (if any) have been followed and all HTTP headers of the final response have been received. Several response members of the object are now available.”
  • 3 “The response entity body is being received.”
  • 4 “The data transfer has been completed.”

This means that although the data transfer is completed, the status is not OK (not 200).

After some hours of investigation, I found that the XHR (XMLHTTPRequest) zero status means that there is a permission problem of the Ajax request. In other words, the Ajax request is trying to access a domain that the request is not authorized to access.

The actual problem was that my JavaScript JsTestDriver asynchronous test case was running on the JsTestDriver server which is using a different port other than the one that is used by the application server which hosts the Ajax servlet.

The other interesting point to mention is that I only faced this issue on Firefox 10 while in Internet Explorer 8, the Ajax request worked without any problems.

To solve this problem, I authorized the Ajax request that comes from the JsTestDriver server to access the application server by setting the "Access-Control-Allow-Origin" header from my servlet as follows.

response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:42442");

The "http://127.0.0.1:42442" is the JsTestDriver server address from which the Ajax request is constructed and sent to the application server.

After making this workaround the scenario worked fine on all the browsers, I wish that this tip can be helpful for you.