Menu Test

In this article we will take a look at simple tips how you can use the library to test your web-page functionality. For this purpose we will develop a simple menu which has a dynamic submenus which will getting shown/hidden by mouse click events.

Our code for the example will be working on the Prototype framework. It is not necessary to use the framework, in real live you can use whatever you want. We just using it for example. The end result of the article you can see on the page. And the full version of test-case is here.

In the description we will try more or less follow the "test first" idiom, this might be not necessary for your cases, but we are trying to follow good habits. Another point is that the number and value of the tests might look overdescriptive, don't pay lot of attention on it, we are just trying to show how does it work in general.


Initial Conditions Test

For the beginning, surely, we need the menu element, so we doing a predictable thing. Write a test.

Initial test

var MenuTest = TestCase.create({
name: 'MenuTest',

testMenuElementExistance: function() {
this.assertExists('ul#main-menu');
}
});

We put the code into the menu_test.js file and save it next to the rest of the javascript stuffs. Then we create a web-page where we will work.

Working page

<html>
<head>
<script src="/javascripts/prototype.js"></script>
<script src="/javascripts/testcase.js"></script>
<script src="/javascripts/menu_test.js"></script>
<script>
MenuTest.runOnLoad();
</script>
</head>
</html>

Then we open the page in a browser and see the broken test report which tells us that there is no menu element. So we create one. <body>
<ul id="main-menu">
</ul>
</body>
Then we run the test again and getting sure that it works.


Testing Menu Element Structure

Now our menu should have some labels with links and each label should contain a submenu. Writting a test.

Menu Structure Test

...
testMenuStructure: function() {
this.assertExists('ul#main-menu > li');
this.assertExists('ul#main-menu > li > a');

$$('ul#main-menu > li').each(function(label) {
this.assertHasChild(label, 'a');
this.assertHasChild(label, 'ul');
this.assertHasChild(label, 'ul > li');
this.assertHasChild(label, 'ul > li > a');
}, this);
}
...
NOTE: we use CSS Level 2 constructions in our assertions. They are handled virtually so this will work with any browser. The library supports CSS Level 2 and most of CSS Level 3 constructions. So feel free yourself with the stuff.

Ok, we run the test and see that there are no menu labels. Lets create some.

Menu Basic Structure

<ul id="main-menu">
<li><a href="#">Some Label</a>
<ul>
<li><a href="">1 Label</a></li>
<li><a href="">2 Label</a></li>
<li><a href="">3 Label</a></li>
</ul>
</li>
<li><a href="#">Some Label</a>
<ul>
<li><a href="">1 Label</a></li>
<li><a href="">2 Label</a></li>
<li><a href="">3 Label</a></li>
</ul>
</li>
</ul>
After this, reload the page and get sure that the tests are passed successfully.


Testing Styles

Naturally our menu elements should have some particular styles. The submenus should be hidden and absolutely positioned.

Style Test

...
testSubmenuStyle: function() {
$$('ul#main-menu > li > ul').each(function(element) {
this.assertHidden(element);
this.assertStyle(element, {position: 'absolute'});
}, this);
}
...

Run the test and get sure it is failed. Now we add some styles on the page so the test was passed.

Basic Menu Style

<head>
...
<style>
ul#main-menu li ul {
display: none;
position: absolute;
}
</style>
</head>


Testing Events

Now we want to bring some dynamic to the menu. Say we want that by clicking on any link in the first level labels, a linked submenu was appeared and all the rest submenus were get hidden. We use the .fireClick() method to initiate the mouse click events.

Events Test

testSubmenusDisplaying: function() {
var submenus = $$('ul#main-menu ul');

$$('ul#main-menu > li').each(function(label) {
var link = label.down('a');
var menu = label.down('ul');

this.fireClick(link);
this.assertVisible(menu);

for (var i=0; i < submenus.length; i++) {
if (submenus[i] != menu) {
this.assertHidden(submenus[i]);
}
}
}, this);
}

Run the test and get sure that the test is failed. Now the time to write the script we wanted. As possible implementation we write down an in-line javascript right below the menu element on the page. That is not much beautiful solution, but it is going to be a working one.

Events Handling Code

<script>
$$('ul#main-menu > li').each(function(label) {
var link = label.down('a');
var menu = label.down('ul');

link.onclick = function() {
// hidding all the submenus
$$('ul#main-menu ul').each(function(menu) {
menu.style.display = 'none';
});

// displaying the current one
menu.style.display = 'block';
};
});
</script>
Pretty simple. Reload the browser and get sure all the tests get passed.

Now we probably want that by clicking somewhere on the page, but not on the menu elements all the submenus were get hidden. No problem, write a test!

Document Event

testHideAll: function() {
var label = $$('ul#main-menu li').first();
var link = label.down('a');
var menu = label.down('ul');

this.fireClick(link);
this.assertVisible(menu);

this.fireClick(document.body);
this.assertHidden(menu);
}
Run the test, get sure it's failed.

This is a little bit tricky one. Because the menu by itself is a part of the page, if we just wire the document.body.onclick event to hide all the menus you will not be able to see any submenus case by clicking on the links you will hide them all. So we need to fix a little our link.onclick handler. And we will take the opportunity and do some refactoring on the code.

Document Onclick handling

<script>
var hide_all_submenus = function() {
$$('ul#main-menu ul').each(function(menu) {
menu.style.display = 'none';
});
};

$$('ul#main-menu > li').each(function(label) {
var link = label.down('a');
var menu = label.down('ul');

link.onclick = function(event) {
event.stop();
hide_all_submenus();
menu.style.display = 'block';
};
});

Event.observe(document.body, 'click', hide_all_submenus);
</script>
Reload the page and get sure that everything works properly.


Effects Mocking

NOTE: this last chapter is for Prototype and MooTools users. This is a special feature for the frameworks only.

There is another point which should be discussed. Say we want some nice visual effect which will cover the submenus appearance. We have included the effects.js library and changed our handler code.

Effected Submenus Handling

...
hide_all_submenus();
Effect.BlindDown(menu);
...

Because an effect applying takes some time, the tests will not work anymore. To handle that situations TestCase have got special functionality which called 'effects mocking'. When you call the method .mockEffects() all the visual effects will get happened immediately. Another method called .undoEffectsMock() will bring the original effects behavour back.

So we add the effects mocking to the .beforeAll() and .afterAll() methods of the test case.

Effects Mocking Call

beforeAll: function() {
this.mockEffects();
},

afterAll: function() {
this.undoEffectsMock();
}
After the small fix all the tests should get passing again.

This is pretty much it. But there are lots of another handy assertions in the library which can be used for your pages functionality tests. Take a look at the API-Documentation for more details.