Thursday, January 29, 2015

Testing your jquery implementation with Jasmine framework

We tend to write lot of javascript /jquery implementations but it is not that easy to test them since not many stable frameworks are available, and not much documentation is provided. Today we will look into one of those framework named Jasmine, and how to write unit tests using it.

1. Jasmine Framework
Jasmine framework supports Behavior Based Development (BDD) . That is in this type of testing, we describe our code and tell the test what it should be doing. Then we expect the code to act in the described way and see the results. Following code segment brings out this style in the most basic manner.

// describe your  implementation objective
describe(' shopping cart', function () {

// what your test should check
it( ' add items to cart' ,function (){

//expect this to happen at the end
   expect(shoppingCart).toEqual(set of items);
});

});

Note that words highlighted are key words.
Above is the basic set of specification you would need to create your unit tests. Section 'describe' is basically similar as to defining a test suite while section 'it' is similar to defining a single test case. You can add multiple sets of 'it' cases inside your 'describe' block. Before we get into more detail with writing tests, we will first look into how to setup jasmine framework.

2. Setting up Jasmine Framework

   2.1 Download jasmine standalone package (I have used jasmine 2.1.3) from https://github.com/jasmine/jasmine/releases and unzip it .
2.1 Copy following file and folders , SpecRunner.html, lib, spec and src.
2.3 Create a new folder (I'll call it shoppingcart) and add those folders to it .


Now you have done the basic setup required. Now you need to add all your .js (source files) under src folder. Next you can create your test specification files (where you will write your test cases) into spec folder. lib folder is where you can add all your dependencies required. By default it will have jasmine. If you are to test jquery related coding, you need to download jasmine-jquery ( https://github.com/velesin/jasmine-jquery) and include that file inside lib folder. SpecRunner.html is the file where you get to define all the source ,specs and libraries that you need to use in your tests. In that file, you can add your links as follows.
    <!-- include library files here... -->

   <script src="lib/jasmine-2.1.3/jasmine.js"></script>
        <script src="lib/jasmine-2.1.3/jasmine-html.js"></script>
        <script src="lib/jasmine-2.1.3/boot.js"></script>
        <script src="lib/jquery.min.js"></script>
         <script src="lib/jasmine-jquery.js"></script>
             
    <!-- include source files here... -->

        <script src="src/addItems.js"></script>
    
      
        <!-- include spec files here... -->

        <script src="spec/shoppingCart_spec.js"></script>

Note that in order for jquery-jasmine to work, you need to include jquery dependency as well, and you need to add it before where you specify jasmine-jquery. Else you will get a jquery undefined error. 
   

Jasmine framework supports lot of matcher keywords that you can use to test your jquery code. You can also write your own custom matchers as well. Refer to jasmine documentation for more information on this.

We will look into few basic unit tests of jquery so you can get an idea on how to start. Following is the addItems. js source file which contains the implementation I need to test.


var itemList = []; 

function addItem ( item) {

itemList.push(item);
}

function removeItem(item)
{
  itemList.splice(item,1);
}

function findItemOnList(item) {
for (var i =0 ;i <itemList.length; i++){
if( item  === itemList[i].item){
return true;
}
else{
return false;
}
}

Now we will look into how to write unit tests for above functionalities.  Note that I have highlighted keywords and matchers in blue. This is how your shoppingCart_spec.js would look like.

describe('Shopping Cart' , function () {

var defaultItem;
var defaultName = "groceries";

beforeEach(function(){

   defaultItem = createNewItem(1,defaultName);
   addItem(defaultItem);
});

afterEach(function() {
        itemList = [];
    });

it('Add item to shopping cart',function () {
var itemName = "stationary";
      var item = createNewItem(2, itemName); 
      addItem(item);

   expect(itemList.length).toEqual(2);

});

it('Remove item from shopping cart' ,function () {
    removeItem(defaultItem);

expect(itemList).toContain(null);
});

it('Search for  valid item on shopping cart' , function () {
    var result = findItemOnList(defaultItem); 
     expect(result).toBe(true);
});

it( 'Search for invalid item on shopping cart', function () {
var itemName = "stationary";
  var newItem = createNewItem(2,itemName);
  var result = findItemOnList(newItem);
expect(result).toBe(false);

});
});

Now I add additional createNewItem method into my SpecRunner.html as below .

 <script type="text/javascript">

function createNewItem(id, name) {
 var item = $(<div>);

item.attr({
id:id,
name:name
};

return item; 
}

</script>

Note that I have used different matchers under expect sections so you can get an idea on how they can be used. beforeEach and afterEach code blocks can contain any resetting or functionality that can happen before and after every individual 'it ' block. Note how i have used the functions and variables in addItem.js source code.  Finally once your test cases are completed, you can launch SpecRunner.html in your favourite browser and run the specifications.  This is a very basic start up guide for you. Jasmine framework supports lots of functionalities such as testing ajax calls , triggering mouse events, testing css properties.




Thursday, January 8, 2015

Creating a basic drawing toolbox using jsPlumb part 2

As we looked into drag and drop feature, cloning elements in the previous post creating-basic-drawing-toolbox1     , in this post we will look into adding event handlers for any element added to a canvas. Here the main concern would be on how to draw connections between elements which is a basic feature requirement in a drawing toolbox.

Below are the steps to be followed in this post.

1. Adding endpoints
2. Adding anchors
3. Adding connectors
4 Define source and target
5. How to get connection details

Please note that jsPlumb is used once again same as the previous post.

Connections can be added  programmatically (static) or dynamically. jsPlumb provides following basic method to create a connection between two elements.

jsPlumb.connect( { source: "node1", target:"node2"});


This line of code is quite simple, where we tell jsPlumb to draw a connection between node1 and node2, where the connection source would be node1 and connection target is node2.  But in a real world scenario, we would need to allow the user to draw connections between elements as they wish. Also you would want to customize the type of connection ( flow chart arrows, curved arrows etc) , connecting endpoints ,arrow labels and so on. jsPlumb supports all these modifications which can be easily defined. First we will look into how we can define connector endpoints to an element.

1.Adding endpoints

The basic endpoint types provided by jsPlumb would be as follows.

  • Dot
  • Rectangle
  • Image
Again you can customize the look and feel of the endpoint by providing additional attributes to it. For example, following code displays on how to define a dot endpoint that has a radius of 4.

 endpoint: ["Dot", {
                radius: 4
            }];

2. Adding anchors

You can define where you would want the endpoint to reside in your element. Following are some basic anchor types provided by jsPlumb.

  • Top
  • Bottom
  • Left
  • Right
Other than these,users can always define dynamic endpoints giving their x,y coordinate locations. For example following code displays a anchor which has a dynamically set location on the left side of the element.

 anchors: [
                [0.2, 0.5], "Left"
            ];

3. Adding connectors

jsPlumb includes some predefined connector types for different diagrams. Two mainly used connector types as below.

  • Straight : Draws a line  between elements
  • Flowchart :  Draws a line that is standard to flow charts.
You can customize this connector with different css classes and attributes.Following code segment displays a flowchart connector definition with additional styling properties included.

connector: ["Flowchart"],
            connectorStyle: {
                strokeStyle: "#5c96bc",
                lineWidth: 1,
                outlineColor: "transparent",
                outlineWidth: 4
            };

4.  Define source and target

As mentioned before , there might be scenarios where you need to allow the user to decide which element would be the source of the connection and end the connection at a different element as he wish. To support this, you can define a common variable as below, with all endpoint,anchor and connector types defined. Here you can include following lines(isSource:true, isTarget:true)  to say that any element can be a source or a target. Therefore jsPlumb would allow the user to draw connections between any element that he wish for.

Finally you can add the common variable as an endpoint to your elements. For example the following code segment will add an endpoint at the top of shape 'element' , and the user will be able to draw connections to/from any shape 'element' since both isSource and isTarget is set to true. Note that I have set maxConnections property to -1.This will enable the user to draw unlimited amount of connections between elements.



Following is a screenshot on how connections drawn will be displayed. The endpoints are displayed as black dots. 

5. How to get connection details

In an application you would want to save the diagrams that are drawn for future purposes. jsPlumb has made it easy to derive all the information of any connections that are drawn by using following code segments. There are different properties you can access about the connections that are drawn, such as endpoint details, anchor details etc. In following example I am extracting the source element id , target element id  and endpoint details of each connection .