Selenium and XHTML

Last month I blogged about Selenium, which is an open source project that let’s you test web applications running in a variety of browser. Unfortunately, Selenium doesn’t work out of the box with XHTML – any XPath expressions you use stop working.

I fixed this last month in my local copy, but I’ve noticed other people are starting to have the same issue. The problem is that Selenium does not implement a namespace resolver as described in the Mozilla XPath documentation. For html documents, XPath expressions look like this:

div/p[@id="foo"]

For XHTML documents, they must include a namespace prefix like this:

x:div/x:p[@id="foo"]

The choice of “x” is random, however, its what XPath Checker (a Firefox extension) uses.

Luckily, Selenium is easily extensible since JavaScript is a language that gets
out of your way
. The fix is to add the following code into your user-extensions.js file:

PageBot.prototype.namespaceResolver = 
function(prefix)
{
  if (prefix == 'html' ||
      prefix == 'xhtml' ||
      prefix == 'x')
  {
    return 'http://www.w3.org/1999/xhtml';
  }
  else if (prefix == 'mathml')
  {
    return 'http://www.w3.org/1998/Math/MathML'
  }
  else
  {
    throw new Error("Unknown namespace: " + prefix + ".")
  }
}

PageBot.prototype.findElementUsingFullXPath = 
function(xpath, inDocument) {
    if (browserVersion.isIE && !inDocument.evaluate) {
        addXPathSupport(inDocument);
    }

    // HUGE hack - remove namespace from xpath for IE
    if (browserVersion.isIE)
        xpath = xpath.replace(/x:/g,'')

    // Use document.evaluate() if it's available
    if (inDocument.evaluate) {
        // cfis
        //return inDocument.evaluate(xpath, 
              inDocument, null, 0, null).iterateNext();
        return inDocument.evaluate(xpath,
          inDocument, this.namespaceResolver, 0, null).iterateNext();
    }

    // If not, fall back to slower JavaScript implementation
    var context = new XPathContext();
    context.expressionContextNode = inDocument;
    var xpathResult = new XPathParser().parse(xpath).evaluate(context);
    if (xpathResult && xpathResult.toArray) {
        return xpathResult.toArray()[0];
    }
    return null;
};

There are two big hacks. First, the hard-coded “x” prefix. And second, Internet
Explorer does not support XHTML so the code strips out any namespace prefixes.

Last, if you are using the Firefox Selenium IDE, make sure to point it at your updated user-extensions.js file (do this using the options menu).

Leave a Reply

Your email address will not be published.

Top