Certified Selenium Professiona Learning Resources User extensions

Learning Resources
 

User extensions


Imagine that you wanted to use a snippet of code that is used in a number of different tests. You could use:

type | locator | javascript{ .... }

However, if you had a bug in the JavaScript you would need to go through all the tests that reused this snippet of code.

This, as we know from software development, is not good practice and is normally corrected with a refactoring of the code. In Selenium, we can create our own function that can then be used throughout the tests.

User extensions are stored in a separate file that we will tell Selenium IDE or Selenium RC to use. Inside there the new function will be written in JavaScript.

Because Selenium's core is developed in JavaScript, creating an extension follows the standard rules for prototypal languages. To create an extension, we create a function in the following design pattern.

Selenium.prototype.doFunctionName = function(){ . . . }

The "do" in front of the function name tells Selenium that this function can be called as a command for a step instead of an internal or private function.

Now that we understand this, let's see this in action.

Time for action – installing a user extension

Now that you have a need for a user extension, let's have a look at installing an extension into Selenium IDE. This will make sure that we can use these functions in future Time for action sections throughout this article.

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

    Selenium.prototype.doNothing = function(){ . . . }

  3. Start Selenium IDE.
  4. Click on the Options menu and then click on Options.
  5. Place the path of the user-extension.js file in the textbox labeled Selenium IDE extensions.
  6. Click on OK.
  7. Restart Selenium IDE.
  8. Start typing in the Command textbox and your new command will be available, as seen in the next screenshot:

We have just seen how to create our first basic extension command and how to get this going in Selenium IDE. You will notice that you had to restart Selenium IDE for the changes to take effect. Selenium has a process that finds all the command functions available to it when it starts up, and does a few things to it to make sure that Selenium can use them without any issues.

 

User extension needs the following as noted by Selenium team, for each proposed extension, please provide
 

  • on this page
    • a name (as a h4 title)
    • a short description
    • a more... link pointing to the dedicated page (see below)
  • on a dedicated child page, following section (using h3 titles)
    • description
    • example of use
    • notes (optional)
    • license : we recommend either the Apache License 2.0 or public domain
    • download : a link to a zip archive which is attached to the page. It contains :
      • a file named user-extensions.js. containing the code to include in user-extensions.js
      • html tests that can be placed under selenium_ROOT/tests and added to testSuite in order to test the extension
      • it is better if those files are contained in a directory with the name of your extension.

If you correct a bug in an extension update the package.

If you change some behaviour to a new one you should post it as a new version for this extension. (adding a note and a link to your archive)
 

Example - An example of selenium user extension is detailed below --

  • The new user extension that you will be creating will be accessed via the Selenium API so the first thing that we need to do is create a new function within the Selenium Object. The first line of the snippet below shows that the function is part Selenium. The "do" in Selenium.prototype.doWaitForConditionshows that this will be picked up as an action within Selenium.

    01.Selenium.prototype.doWaitForCondition = function(script, timeout) {
    02.if (isNaN(timeout)) {
    03.throw new SeleniumError("Timeout is not a number: " + timeout);
    04.}
    05. 
    06.testLoop.waitForCondition = function () {
    07.return eval(script);
    08.};
    09. 
    10.testLoop.waitForConditionStart = new Date().getTime();
    11.testLoop.waitForConditionTimeout = timeout;
    12. 
    13.testLoop.pollUntilConditionIsTrue = function () {
    14.try {
    15.if (this.waitForCondition()) {
    16.this.waitForCondition = null;
    17.this.waitForConditionStart = null;
    18.this.waitForConditionTimeout = null;
    19.this.continueCommandExecutionWithDelay();
    20.} else {
    21.if (this.waitForConditionTimeout != null) {
    22.var now = new Date();
    23.if ((now - this.waitForConditionStart) > this.waitForConditionTimeout) {
    24.throw new SeleniumError("Timed out after " + this.waitForConditionTimeout + "ms");
    25.}
    26.}
    27.window.setTimeout("testLoop.pollUntilConditionIsTrue()", 10);
    28.}
    29.} catch (e) {
    30.var lastResult = new CommandResult();
    31.lastResult.failed = true;
    32.lastResult.failureMessage = e.message;
    33.this.commandComplete(lastResult);
    34.this.testComplete();
    35.}
    36.};
    37.};
  • function(script, timeout) means that your new function can handle the 2 arguments that Selenium Actions need.
  • Now add the code to do the functionality that you need. Once that is done you will need to parse the result back to Selenium. We do this be creating a new variable of the type CommandResult()
  •  The new variable will have access to the .failedand .passedfunctions to say if the test has passed or failed. You only need to do this if you are creating a new function that can influence the Pass/Fail state of the test.

    If your test has failed it will need a meaningful failure message. CommandResult has a property called failureMessagethat will store a message that will then be printed to the Selenium derivative that you are using.
  • Once your newly created extension has come to the end of its code you will need to send a message back to Selenium saying that you command is finished. You will achieve this by adding this.commandComplete(lastResult);. This is also very useful in the catch of a try...catch to say that the action is finished.
  • If your newly created extension will act like an assert instead of verify you will need to make sure that you have this.testComplete();as the last line of the try...catch block. It will terminate the action and the test. If you have this and the line is called your test will stop running and it will move onto the next test in your test suite.

If you follow these basic steps you will be able to create your own user extension for Selenium and call your functions just as if they were built in to Selenium!

 For Support