Archive

Archive for March, 2010

Testing YUI Data Tables with Selenium

March 31st, 2010 1 comment

I’m automating tests of an application that displays data using YUI Data Tables. The data in those tables is refreshed when user sorts or paginates the table. The table object makes an AJAX request to the server and there is no way in Selenium to wait for that request to finish. So how do we know when the table is ready with new data?

We could sleep few seconds in the test script and then verify the data, but that is error prone. What if the request takes more time than our sleep? We can increase the sleep but we increase testing time as well. That is unacceptable.

The solution is to use YUI events. We can subscribe to an event that is fired when the table is rendered and set a flag. Then just loop and wait for that flag to change the state and flip the flag back. Sounds easy. How do we do that?

It’s only possible if the data table object (instance of DataTable) is accessible via variable. The event we have to subscribe to is “postRenderEvent“. Here is the code we have to inject into the site with getEval command.

// render flag
window.document.table_rendered_flag = false;
 
// function that will be called by the event to set the flag
window.document.table_rendered = function(o) {
    window.document.table_rendered_flag = true;
};
 
// function that we will call to check the status of the flag
// and reset the flag if the table has been rendered
window.document.check_table_rendered = function() {
    if (window.document.table_rendered_flag) {
        window.document.table_rendered_flag = false;
        return true;
    }
    return false;
};
 
// subscribe to the event
if (window.ourTableObject.dt) {
    window.ourTableObject.dt.subscribe("postRenderEvent", window.document.table_rendered);
};

After injecting that code we have to perform an action that will force the data change, wait for new data by calling window.document.check_table_rendered() in getEval until it returns True, and verify the data.

In Robot Framework, the code can be injected with “Execute Javascript” keyword and the check done with “Wait For Condition”:

Execute Javascript window.document.table_rendered_flag = false;
window.document.table_rendered = function(o) {
window.document.table_rendered_flag = true;
};
window.document.check_table_rendered = function() {
if (window.document.table_rendered_flag) {
window.document.table_rendered_flag = false;
return true;
}
return false;
};
if (window.ourTableObject.dt) {
window.ourTableObject.dt.subscribe(“postRenderEvent”, window.document.table_rendered);
};
 
Click Link sort  
Wait For Condition window.document.check_table_rendered(); 10s
Page Should Contain some text  
Categories: Robot Framework, Selenium, Testing Tags:

Your Own URL Shortener

March 31st, 2010 No comments

I just installed Your Own URL Shortener or YOURLS on my domain.

Categories: Blog Tags:

Java Script location strategy in Selenium

March 30th, 2010 No comments

Recently I had to solve an interesting problem when testing a web application. I had a table where a single data unit spanned over two rows. Visually, a data unit looked like a single row, but that was only styling. Table consisted of multiple row-pairs like this:

Unit Status Action
One Active Edit
Unit One
Two Active Edit
Unit Two
Three Active Edit
Unit Three

In reality, without styling, the table structure is as follows:

Unit Status Action
One Active Edit
Unit One
Two Active Edit
Unit Two
Three Active Edit
Unit Three

The problem was to locate the “Edit” link for any data unit. Let’s look at “Unit Two”. The cell with the link is the first cell in a pair but depends on the next cell’s text. To find it I have to locate the row with text “Unit Two”, then check if the previous row contains text “Two” and look for the link “Edit” within that row. I don’t know a way to write an XPath expression to do that. I could not figure out how to write a single jQuery expression for this either. I could, however, write a Java Script expression to locate the “Edit” link.

$('table > tbody > tr:has(td:contains("Unit Two"))').prev('tr:has(td:contains("Two"))').find('a:contains("Edit")').get()[0]

The problem here is that we are using :contains selector, which does not match the text from the beginning to the end. jQuery does not provide a selector function that compares an entire string. I extended jQuery to do just that.

$.expr[':'].textEquals = function(a, i, m) { 
    return $(a).text() == m[3];
};
 
$('table > tbody > tr:has(td:contains("Unit Two"))').prev('tr:has(td:textEquals("Two"))').find('a:textEquals("Edit")').get()[0]

In order to use that is Selenium I had to create a new location strategy. Here is the function to add to user-extensions.js.

Selenium.prototype.locateElementByJavaScript = function(locator, inDocument, inWindow) {
    try {
        var document = inDocument;
        var window = inWindow;
        var element = eval(locator);
        if (element != null) {
            return element;
        } else {
            return null;
        }
    } catch (e) {
        throw new SeleniumError("Threw an exception: " + extractExceptionMessage(e));
    }
}
Categories: Selenium, Testing Tags:

jQuery location strategy in Robot Framework Selenium Library revisited

March 30th, 2010 No comments

I wrote about jQuery location strategy in Selenium some time ago. I want to update on the topic a bit.

I proposed before to place jQuery and locator function inside selenium-server.jar. Later I found out that it is a waste of time. Selenium RC can be started with ‘-userExtensions’ option and passed a file with all extra Java Script code. Therefore, I placed downloaded jQuery code along with locator function inside users-extensions.js.

My locator function has changed a bit. I added some exception checks and it is now compatible single-window mode that selenium allows.

Selenium.prototype.locateElementByJQuerySelector = function(locator, inDocument, inWindow) {
    var loc = locator.replace(/>/g, '>');
    loc = loc.replace(/&lt;/g, '<');
    var element;
    try {
        element = $(inDocument).find(loc);
    } catch (e) {
        return null;
    }
    if (element.length == 1 ) {
        return element[0];
    } else if(element.length > 1) {
        return element.get();
    } else {
        return null;
    }
}

I was allowed to make changes to RF Selenium Library and the keyword Add Location Strategy is available in the newest version.

In order to use jQuery locator in RF, the Selenium Server must be started with user extensions. Then, after opening the browser window, location strategy must be registered.

| Start Selenium Server | -userExtensions | ${CURDIR}${/}user-extensions.js |
| Add Location Strategy | jquery | return Selenium.prototype.locateElementByJQuerySelector(locator, inDocument, inWindow); |

Use the new locator like this:

| Page Should Contain Element | jquery=div.#data-table > table |
Categories: Robot Framework, Selenium, Testing Tags:

Moving to a new Mac

March 3rd, 2010 No comments

After many years of owning laptops I decided to get a desktop. I needed more space and more memory. I got an iMac – the basic 21.5″ model with upgraded memory to 8GB.

After admiring the iMac for few minutes I started thinking about transferring the data and software from the MacBook.

The easiest way would be to restore everything from the Time Machine. This would add all the garbage accumulated over the years to the new system so I quickly forgot about that option. I think Time Machine is great for restoring the data on the same computer – not transferring to the new one.

Another option was given to my by Apple at the time I turned the iMac on – the Migration Assistant. I connected both machines to the 1Gbit network and started the process. The Migration Assistant allowed me to choose what to transfer. This option would also transfer old application settings and caches, shortly unnecessary garbage. I exited the application and skipped that option too.

I decided to build and install everything from scratch transferring only what I wanted. I used APF share as it’s faster than a USB hard drive. I also used MobileMe synchronization.

Once I enabled MobileMe synchronization on the new iMac it gave me few options to overwrite or merge the settings. I told it to merge the keystore (the file that holds all the passwords and security certificates) and overwrite the rest. It worked great for Safari and all the passwords. The iMail had trouble with exchange setup and was missing all local mailboxes I had on the MacBook. Another method was necessary.

I run the Backup program from Apple and archived iMail and iTunes. Restoring on the iMac was a breeze. iTunes worked as before. iMail had trouble with exchange again but local mailboxes were there. I had to re-create the exchange account and all worked great.

The rest of the software I moved manually. I downloaded new versions and only moved preferences files and folders using a network share. I copied the files from ~/Library/Preferences and ~/Library/Application Support. It worked for Skype, Adium, Eclipse and iMovie. I also copied Safari cookies from ~/Library/Cookies so all my web sessions were untouched.

As for the photos I moved them manually using network share and did a fresh Aperture installation.

I’m back online.

Categories: Home, iMac Tags: