Wednesday, January 5, 2011

PRACTICUM: Selenium 1.0 Testing Tools: Chapter 5: User Extensions and Add-ons

This is the fifth entry for the TESTHEAD PRACTICUM review of PACKT Publishing's book Selenium 1.0 Testing Tools Beginners Guide by David Burns. The emphasis on this type of review is the actual exercises and the practical applications of what we learn. Rather than reprint out the full listing of the exercises in the book (and there are a bunch of them, as that is the format of this particular title), much of these chapters are going to be a summary of the content, some walk-through of application, and my take on what I see and do.



Selenium 1.0 Testing Tools is full of examples, "try it yourself" instructions and explanations of what happened. The book’s cover states “Learn by doing: less theory, more results”. The focus will be less on the concepts and more on the actual implementation and methods suggested.



Two notes: Any and all examples listed can be tried against the book's web site at http://book.theautomatedtester.co.uk/. Make this the Base URL in Selenium IDE for any tests. Also, whenever an Output Log table is shown, that is the format for all Selenium test output and test cases written in Selenese (i.e. it shows the formatting of the Selenium tests in question). Likewise, to help illustrate each example so there is no confusion, I will format the direction steps with regard to the actual command to be created:

(Command: CommandName, Target: TargetName, Value: ValueName )



Chapter 5: User Extensions and Add-ons


The Selenium API covers a lot of possibilities. It allows for identifying values, checking their actions, and creating JavaScript to let us customize commands and add options to make our tests more flexible. That framework goes beyond just creating small enhancements and custom calls. Selenium is designed to be extensible and allow developers to create their own functions. Using the same three-column format, tests can be enhanced and extended with User extensions and add-ons (all of which are written in JavaScript).


Installing a User Extension

So what do you need to do if you want to create your own extension that will exist as a function?


1. Open your favorite text editor.
2. Create an empty method with the following text:

Selenium.prototype.doNothing = function(){

// The do in front of Nothing in the function is what tells the system this is a global function

}


3. Save the file (recommended fine name to save as is "user-extension.js"
4. Click on the Options menu and then click on Options.
5. Place the path of the user-extension.js file in the text box labeled Selenium
Core extensions
(Note: the book says to place this reference in Selenium IDE extensions, but when I followed those instructions and restarted Selenium IDE I received an error that said "error loading Selenium IDE extensions: ReferenceError: Selenium is not defined". If, however, you put the reference in the Selenium Core extensions text box, then this will work).
6. Click OK
7. Restart Selenium IDE.
8. Start typing "nothing" in the command text box and you will see two options appear; "nothing" and "nothingAndWait".

Ta-Da!!! You've just created your first extension to Selenium. Granted, this one doesn't do anything, but we can fix that :).

OK ,this time we'll make a function that does something, stores a random number.


1. Open your favorite text editor and open the user-extension.js file you created earlier.
2. Create a function called storeRandom. The function will look like
the following:

Selenium.prototype.doStoreRandom = function(variableName){
random = Math.floor(Math.random()*10000000);
storedVars[variableName] = random;
}

3. Save the file.
4. Restart Selenium IDE.
5. Create a new step with storeRandom and the variable that will hold the value will
be called random (Command: storeRandom, Target: random).
6. Create an echo statement that will show the value of the variable random (Command: echo, Target: ${random}).
6. Run the test.


Output Log:


[info] Executing: |storeRandom | random | |
[info] Executing: |echo | ${random} | |
[info] echo: 5743243


So now we have an example that stores a variable called random. The variable uses the storedVars dictionary. If we run this command over and over, we'll get a different result each time:

[info] Executing: |storeRandom | random | |
[info] Executing: |echo | ${random} | |
[info] echo: 5743243
[info] Executing: |storeRandom | random | |
[info] Executing: |echo | ${random} | |
[info] echo: 3737288
[info] Executing: |storeRandom | random | |
[info] Executing: |echo | ${random} | |
[info] echo: 9375708
[info] Executing: |storeRandom | random | |
[info] Executing: |echo | ${random} | |
[info] echo: 5861831

So now that we have a function that does something, what else can we do?

Since David presents this so well, I'm going to turn the mic over to him :)...

Imagine that you need to calculate today's date and then type that into a textbox. To do that you can use the type | locator | javascript{…} format, but sometimes it's just neater to have a command that looks like typeTodaysDate | locator. We do this by creating an extension and then calling the relevant Selenium command in the same way that we are creating our functions. To tell it to type in a locator, use:

this.doType(locator,text);

The this in front of the command text is to make sure that it used the doType function
inside of the Selenium object and not one that may be in scope from the user extensions.

Let's see this in action:

1. Use your favorite text editor to edit the user extensions that you were using in the previous examples.
2. Create a new function called doTypeTodaysDate with the following snippet:

Selenium.prototype.doTypeTodaysDate = function(locator){
var dates = new Date();
var day = dates.getDate();
if (day < 10){
day = '0' + day;
}
month = dates.getMonth() + 1;
if (month < 10){
month = '0' + month;
}
var year = dates.getFullYear();
var prettyDay = month + '/' + day + '/' + year;
this.doType(locator, prettyDay);
}
3. Save the file and restart Selenium IDE.
4. Create a step in a test to type this in a textbox (Command: typeTodaysDate, Target: q).
5. Run the test.

[info] Executing: |typeTodaysDate | q | |

If you look on the main page at http://book.theautomatedtester.co.uk/, you will see a nicely formatted version of today's date (my version's been Americanized :) ).

We created an extension command that uses a locator. We can also
call other commands from within our new command by calling its original
function in Selenium. Next up: browserbot from within an extension method.


1. Open up your favorite text editor and open up the user extensions file that you
created earlier.
2. Create a new function and call it doCheckDate, as seen in the following snippet:

Selenium.prototype.doCheckDate = function(){
var dates = new Date();
var day = dates.getDate();
if (day < 10){ day = '0' + day; } month = dates.getMonth() + 1; if (month < 10){ month = '0' + month; } var year = dates.getFullYear(); var prettyDay = month + '/' + day + '/' + year; this.browserbot.getUserWindow().checkDate(prettyDay); }

3. Save the file and restart Selenium IDE.
4. Create a new step using the new command.
5. Create a new step with verifyText that checks whether the location
with "Answer" has the word Correct.
6. Run the script.

For the record, following the advice in the book, I end up getting the following message when I try to validate the command:

[info] Executing: |checkDate | | |
[error] Unexpected Exception: message -> this.browserbot.getUserWindow().CheckDate is not a function, fileName -> chrome://selenium-ide/content/tools.js -> file:///C:/code/Selenium/user-extension.js, lineNumber -> 13, stack -> ("","")@chrome://selenium-ide/content/tools.js -> file:///C:/code/Selenium/user-extension.js:13 ("","")@chrome://selenium-ide/content/selenium/scripts/htmlutils.js:60 ([object Object],[object Object])@chrome://selenium-ide/content/selenium/scripts/selenium-commandhandlers.js:310 ()@chrome://selenium-ide/content/selenium/scripts/selenium-executionloop.js:112 (-10)@chrome://selenium-ide/content/selenium/scripts/selenium-executionloop.js:78 (-10)@chrome://selenium-ide/content/selenium/scripts/htmlutils.js:60 , name -> TypeError


Creating New Commands to Verify or Assert

Let's make a test that can perform a verification or assert.

1. Open your favorite text editor and the user extensions file that you created earlier.
2. Create a new function called doFireDateAndVerifyText with the
following snippet:
Selenium.prototype.doFireDateAndVerifyText =function(locator,value){
var dates = new Date();
var day = dates.getDate();
if (day < 10){
day = '0' + day;
}
month = dates.getMonth() + 1;
if (month < 10){
month = '0' + month;
}
var year = dates.getFullYear();
var prettyDay = day + '/' + month + '/' + year;
this.browserbot.getUserWindow().checkDate(prettyDay);
var lastResult = new CommandResult();
try{
var realValue = this.getText(locator);
if (realValue === value){
lastResult.failed = false;
}
else
{
lastResult.failed = true;
}
}
catch (e) {
lastResult.failed = true;
lastResult.message = e.message;
} this.commandComplete(lastResult);
}
3. Save the file and restart Selenium IDE.
4. Run the test.

Again, in this instance, I run into the same error as the previous example. Not sure if it's because this is Selenium 1.0.10 or something else unusual is happening, but hey, it's what I'm currently seeing (anyone else who may be more savvy is welcome to let me know where I'm being stupid :) ). Still, we can confirm that it is possible to create user extensions and make them work correctly, so maybe it's just something to do with the way browserbot interacts with the user-extension.js file... or maybe it's just me.  

Add-ons


Adam Goucher, one of the Selenium IDE maintainers, created a new API that allowed anyone to create new add-ons for Selenium IDE. Add-Ons are Mozilla Firefox add-ons that Selenium can access. Firefinder is an add-on that's been implemented in this manner.

Printing out the format for these steps is rather difficult for Blogger to digest, so I'll leave this experiment as an exercise to the reader. Suffice it to say it's a bit involved :).


Extensions and add-ons allow us the flexibility and options to create more specific commands and have them available to us when we want to make more specific and detailed test cases. IF we want to go beyond what Selenium provides by default, we can.

5 comments:

Anonymous said...

This Selenium book has lot of errors. It is a crap to follow the book, honestly.

Colin Piper said...

I've noticed the same errors too, initially thought it was me so at least it looks a bit more like the text is wrong now!

I'm also using IDE (1.0.10) on FF 3.6.3.

Initial ideas:

Basic copying error - isn't the first one so is a possibility.

The IDE David appears to use is 1.0.6. Entirely possible that updates have 'broken' this functionality

the last line that attempts to invoke a checkDate function may be trying to reference a now-defunct function in js.

or that last line may be referencing itself, creating an infinite loop

I'm hoping David may already be aware of these and have some updated examples. From what i've googled so far there doesn't appear to be anything that helps.

Has anyone contacted him to find out? On the assumption that's a no, I'll do it and let you know what he says.

Col

testerinColorado said...

Hello Col

I've noticed the same errors as all of you. Have you had a chance to contact David and get updated scripts? I can't find any updates on the web.

Anonymous said...

It seems there must be some voodoo in your code, sir... At the "Time for Action" where David shows us how to use locators in our extensions, I kept on getting the same syntax error no matter how closely I matched my code to the example in the Guide and the example posted here.

So, I said "Screw it," and copy-pasted the example code here into my extension file and set my own code aside as a comment for future reference. Save, restart Selenium, and hey-presto, suddenly it works. I double-check MY code... and it's exactly the same. Nothing about the syntax is different at all.

I just don't understand. :(

Savita said...

Its very helpful..

Thanks for the article :-)