Overview

How to inject JavaScript using Robot Framework and Selenium

3 Comments

My team has chosen – amongst other TDD approaches – ATDD as the way to go. We are delighted every day by the Robot Framework and the related Selenium Library. We write our tests in a natural language like syntax. For complex web applications, you will sooner or later need to perform assertions for which no standard keywords are available. In this article, I’ll show you how to inject JavaScript into the HTML GUI under test, which enables you to perform complex or unusual assertions

In the case I’m using for this example, we have developed a website on which different insurance products are compared after a couple of calculation steps have been performed. The website is quite smart, thus it is also able to sort the products based on different criteria. Another feature is a filter that allows the customer to – well – filter the products 🙂 If the customer selects one of the filters, certain products will become invisible.

For the tests, this implies that we need to have automated tests for both the sorting and the filter mechanism. I’ll use the filter as an example here. The test case looks roughly like this (I’ve introduced a little abstraction of course):

*** Keyword ***
Behaviour
    [Arguments]  ${Filter}  ${o1}  ${o2}
 
    Given that there is a product page for XYZ 
 
    If the filter is set to ${Filter}
 
    Then product A is optically ${o1}
    And product B is optically ${o2}
 
| *Test Case* | | *Filter*    | *o1*      | *o2*      |
| 1 | Behaviour | no filter   | visible   | invisible |
| 2 | Behaviour | F1          | invisible | visible   |
| 3 | Behaviour | F2          | visible   | invisible |

There is no predefined keyword for visibility checks in the Selenium Library. In the Selenium IDE you can use “assertVisible”, but we prefer to use JavaScript. Our templates use Prototype, which provides quite a couple of very comfortable methods. I had the following idea how I might check the visibility of an element:

  1. Add a hidden input to the DOM tree
  2. Set the hidden input’s value to true or false, which indicates visibility of the targeted element
  3. Finally, check that input’s value using “Text Field Value Should Be”.

Okay, so I wrote the following JavaScript function that performs the hidden input magic:

function prepareHiddenField() {
  if(!$('HIDDENROBOT')) {
    Element.insert(
      $('products'),
      new Element(
        'input',
        {type:'hidden', id:'HIDDENROBOT'}
      )
    );
  }
 
  $('HIDDENROBOT').value = $('product_A').visible();
}

In order to inject this function into the page being tested, it has to be transformed into an anonymous function that immediately calls itself. Also, the entire JS code must be put on a single line in order to avoid line break foobar in the BDD file. Kudos to those who can read this – here is the result, spiced up with a variable “pid” as in “product ID”:

(function(){!$('HIDDENROBOT') && Element.insert($('products'), new Element('input', {type:'hidden', id:'HIDDENROBOT'})); $('HIDDENROBOT').value=$('product_${pid}').visible();})()

So – now we can write the required Robot keywords:

Prepare hidden visibility field for ${product}
	${pid} =  productId for  ${product}
	Call Selenium Api  runScript  theJavaScriptAsShownAbove
 
product ${product} is optically visible
	Prepare hidden visibility field for ${product}
	Text Field Value Should Be  HIDDENROBOT  true
 
product ${product} is optically invisible
	Prepare hidden visibility field for ${product}
	Text Field Value Should Be  HIDDENROBOT  false

This example may seem to lack elegance, but I hope you see the point I’m trying to make. In many cases, CSS or XPath selectors won’t help you much, for example if you intend to check style attributes set by visual effects. To do such things, JavaScript injection is a very handy tool.

I mentioned earlier that we test the re-sorting of the compared products as well, so I’ll give you another example which is a little more complex. Upon sorting, the order of elements inside the DOM tree is changed. Thus, we need to assert that those DOM tree elements have the correct order after they have been sorted.

To do this, we require n hidden inputs, matching n products being compared. Here’s an excerpt from the test case, using hard-coded values:

Then product B is in position ${posB}
And product A is in position ${posA}

So, let’s create a hidden input for each product:

(function(){
  Element.insert(
    $('products'),
    new Element(
      'input',
      {type:'hidden', id:'HIDDENROBOT${index}', value:$$('div.product')[${index}-1].id}
    )
  );
})()

The required position is passed as the variable “index”, 1-based, so that we need to reduce it by one in order to get a correct index for the element array collected by $$(‘div.product’).

Here goes the JavaScript in one line again:

(function(){ Element.insert($('products'), new Element('input', {type:'hidden', id:'HIDDENROBOT${index}', value:$$('div.product')[${index}-1].id})); })()

And the entire keyword for the position check:

product ${product} is in position ${index}
	${pid} =  productId for  ${product}
 
	Call Selenium API  runScript  theJavaScriptAsShownAbove
	Text Field Value Should Be  HIDDENROBOT${index}  product_${pid}

In this case, we did really not have any chance to use the default Selenium or Robot toolbox. As you can see, injecting JavaScript is a great tool in order to build very complex tests.

Kindly let me advise you that we are offering an ATDD training with Elisabeth Hendrickson on June 21/22 2010. There are still seats available – come join us! 😉

Kommentare

  • Ofelia

    Is it possible to trigger a javascript function from the selenium library? (ie.through the execute javascript keyword). What I want to accomplish is to call a javascript function which triggers underlying C# function from a webpage.

    • Robert Spielmann

      Hi Ofelia,

      as you can see from the examples, the injected JS code makes use of JavaScript already loaded along with the page. For example, the JS I inject uses Element.insert(…) which is provided by Prototype, and Prototype has been loaded when the page was rendered. So in your case, if the function you wish to call is accessible, you should be able to inject something like (function(){ yourfunction(); })() and it should work just fine. I hope I’ve understood your requirement correctly – if you have any further questions, feel free to ask again.

      Cheers,
      Robert

      • Ofelia

        Thanks Robert, I’ll give that a try.

        I think my problem was that I was trying to test with a simple alert pop up but I found out later that alert messages don’t show up during Selenium testing.

Comment

Your email address will not be published. Required fields are marked *