This page shows Test in action, simulating adding, editing, dragging, and deleting todos in the Todo Demo below. It also provides a walk through of the test code.
This page loads in test mode. Make sure your popup blocker is turned off. You'll notice the JavaScriptMVC Console loads in a separate window.
This demo tests the Controller introduced in the Controller Demo. This guide will be easier after you've read through the Controller Demo code walkthrough. Here's what's covered in this page:
Download the demo
To run the demo, you need to run the collection of functional tests.
A TodoController manages the event handlers for the Todo Demo shown above. The Controller Demo page goes over the TodoController in detail.
To test a controller, use a Controller Test. Controller tests are subclasses of Functional Tests, so they belong in the 'test/functional' directory. This test file is named todos_controller_test.js. Initialize the test as shown.
new Test.Controller('todos',{ ... tests ... });
The TodoController has actions that respond to a user mousing over and out of a todo element. The following displays the behaviors and how they are tested.
| Behavior | How to test |
|---|---|
| Mousing over a todo causes the background color to change | Simulate a mousover and assert the color changed correctly |
| Mousing out of the todo causes the background color to be restored | Simulate a mouseout and assert the color change is gone |
test_mouseover : function(){
// simulate mouseover for the first todo
// params contains an object with the event and element
var params = this.TodoMouseover();
// check the expected color matches the todo's actual background color
this.assert_equal("#8fba3c", params.element.style.backgroundColor);
},
test_mouseout : function(){
// simulate mouseout
var params = this.TodoMouseout();
// check the background color has been removed
this.assert_equal('', params.element.style.backgroundColor);
},
Another TodoController action turns the text color gray whenever the checkbox is clicked. Here's how we'll test it:
| Behavior | How to test |
|---|---|
| Clicking the checkbox turns the text gray | Simulate clicking the checkbox and assert the color changed correctly |
| Unchecking the checkbox restores the color | Simulate clicking the checkbox again and assert the color change is removed |
test_check : function() {
// simulate clicking the checkbox
// params has the todo element
var params = this.TodoCheckClick();
// assert the color change occurs as expected
this.assert_equal("#808080", params.element.parentNode.style.color);
},
test_uncheck : function() {
// click the checkbox again to uncheck it
var params = this.TodoCheckClick();
// assert the color change goes away
this.assert_equal('', params.element.parentNode.style.color);
},
Clicking a todo makes it editable, and clicking out of it saves your changes. This functionality is controlled by several TodoController actions.
| Behavior | How to test |
|---|---|
| Clicking the a todo's text makes it editable | Simulate clicking the label element and check the label has a descendent element (the input) |
| Clicking out of the input saves the changed todo | Modify the todo by writing something in the input. Then simulate a blur event and check the label element was modified. |
test_edit : function(){
// simulate clicking the text for the first todo
var params = this.TodoLabelClick();
// assert the class and nodeType have been changed to an input element
this.assert_equal('working', params.element.className);
this.assert_equal(1, params.element.firstChild.nodeType);
// grab the input element
var input = Query('.todo label input')[0];
// delete the last word in the todo (\b is backspace), then write 'to fly'
// call the next function when Write is complete
this.Write(input, {text: '\b\b\b\b\b\b\b\b\b\b\b\b\bto fly',
callback: this.next_callback()});
},
edit_after_write : function() {
var input = Query('.todo label input')[0];
// has the correct text been written?
this.assert_equal("Learn to fly", input.value);
// simulate a blur action (clicking out of the input)
var params = this.TodoLabelInputBlur();
// check if the text has been saved in the todo
this.assert_equal("Learn to fly", Query('.todo label')[0].innerHTML);
},
The next test is for the interactions that lead to creating a new todo. Several TodoController actions control this functionality.
| Behavior | How to test |
|---|---|
| Clicking the input box makes the "type new todo here" text to disappear | Simulate a focus event on the input, assert the input is blank |
| After writing in the input, clicking out of the input saves the todo | Write text in the input, simulate a blur event, and assert the first todo has the correct text. |
test_new : function(){
// simulate a focus event on the input element
var params = this.TodosNewInputFocus();
// does the text in the input disappear?
this.assert_equal('', params.element.value);
// write some text in the input
this.Write(params.element, {text: 'Walk the dog', callback: this.next_callback()});
},
new_after_write : function() {
var input = Query('#todos .new input')[0];
// does the input have the correct text?
this.assert_equal('Walk the dog', input.value);
// simulate a blur event
var params = this.TodosNewInputBlur();
// was the todo created successfully in the first todo slot
this.assert_equal("Walk the dog", Query('.todo label')[0].innerHTML);
},
The todos in this page's demo have the ability to be sorted by dragging and dropping. Try it yourself.
| Behavior | How to test |
|---|---|
| Dragging a todo past another todo will cause them to flip spots in the list | Simulate dragging a todo to a spot slightly past another todo, then assert they did indeed flip spots |
test_drag: function(){
// grab the first todo
var draggable_todo = Query('#todos .todo')[0];
// get the x, y page coordinates for the second todo
var to = Test.center(todos[1]);
// simulate dragging the first todo from its current location to 30 pixels
// below the second todo
this.Drag(draggable_todo,{from: draggable_todo, to: {x: to[0], y: to[1]+30},
duration: 0.5, callback: this.next_callback() } )
},
// have the todos flipped spots?
done_dragging : function(){
var todo_labels = MVC.Query('#todos .todo label');
// check the first todo contains 'Learn to fly'
this.assert_equal('Learn to fly', todo_labels[0].innerHTML);
// check the second todo contains 'Walk the dog'
this.assert_equal('Walk the dog', todo_labels[1].innerHTML);
},
The final piece of functionality to test is delete, which is controlled by an action called 'img click'.
| Behavior | How to test |
|---|---|
| Clicking the delete icon deletes a todo | Simulate clicking the delete icon, then assert there is one less todo than previously |
test_delete : function() {
// simulate clicking the delete image
var params = this.TodoImgClick();
// there should only be one todo left
this.assert_equal(1, Query('.todo').length);
}});
Step back for a second and put the TodoController test in context. This example shows that you can comprehensively test every interaction you expect users to have with your page. In addition, the code is relatively easy to understand, thanks to the TodoController helper methods automatically created. As you add new features to the todo list, you'd use the tests to verify you haven't broken anything.
Testing is a programming best practice often ignored in JavaScript development. You can run the TodoController test on many different browsers to quickly idenfity and fix any cross browser issues. In this way, Test helps create cross browser, error-free code much faster and more reliably.
This demo has skipped over some important features of Test, such as testing Ajax functionality. Check out the Learn page and the API to continue learning.
Download the demo
Overview
Test's highlights.
Learn
Learn how to use Test for JavaScriptMVC and non-JavaScriptMVC features.
API
Low level documentation for Test's methods.