Tuesday, April 5, 2011

Selenium v2.02b2

Recently, SauceLabs must have upgraded their Remote Control Servers from v1.0 to v2.0b2.  One thing to notice is that for IE browser testing, invoking get_text() on empty elements yielded a "Command execution failure" problem.  The issue was first reported here:

http://code.google.com/p/selenium/issues/detail?id=1163

A similar issue was also reported here:

http://code.google.com/p/selenium/issues/detail?id=1270&q=gettext%20undefined&colspec=ID%20Stars%20Type%20Status%20Priority%20Milestone%20Owner%20Summary

I also confirmed the issue exists on the latest beta release for Selenium (v2.0b3).  If you want to resolve the issue, I posted the patch here. For the more detailed explanation, see below.

How to verify the issue: If you're using SauceLabs and running Selenium tests, you can also set a breakpoint and verify for yourself that this issue exists for blank HTML elements:

(Pdb) sel.get_text('preview_url')
(Pdb) sel.get_eval('selenium.browserbot.getCurrentWindow().jQuery("#preview_url").html("")')
u'http://cnn.com'
(Pdb) sel.get_text('preview_url')
*** Exception: ERROR: Command execution failure. Please search the forum at http://clearspace.openqa.org for error details from the log window.  The error message is: 'undefined' is null or not an object
(Pdb) sel.get_eval('selenium.browserbot.getCurrentWindow().jQuery("#preview_url").html("aa")')
(Pdb) sel.get_text('preview_url')
u'aa'


Why does this issue happen?  The tests provided in http://code.google.com/p/selenium/issues/detail?id=1270&q=gettext%20undefined&colspec=ID%20Stars%20Type%20Status%20Priority%20Milestone%20Owner%20Summary actually for a good basis for how to verify that the problem exists only in IE browsers but not in Firefox.  Here's how you can reproduce the issue yourself:

1. a. Download http://selenium.googlecode.com/issues/attachment?aid=2624640355068451943&name=assert-span-empty.zip&token=b1a0f8e2d362e20eb01ede9614292dfc.

b. Download Selenium RC v2.0(http://seleniumhq.org/download/). Make sure you have Java installed to be able to run Selenium RC locally.

c. Set your environment path in Windows to be c:\Program Files\Mozilla Firefox.  Note: I found that v2.0b3 works with Firefox3 but not v2.0b2 right out of the box.

2. Unzip assert-span-empty.zip and change assert-span-empty.html to point the first set of lines to a URL that will host example-of-empty-span.html (i.e).  The original was expecting a local webserver but you can change it to be a remote one:

<tr>
<td>open</td>
        <td>http://myhost.com/static/example-of-empty-span.html</td>
<td></td>
</tr>

3. Edit the run.bat and edit the URL:

java -jar sele.jar -htmlSuite *iexplore http://myhost.com/static ./assert-span-empty-suite.html ./assert-span-empty-suite.log.html -singleWindow -timeout 10

You can toggle between *iexplore and *firefox to verify that the test suite passes the latter, but the former:

4. FTP the example-of-empty-span.html to the URL. Verify that you can connect to the file before invoking run.bat.

5. Invoke run.bat.

Once the issue is verified, how does one fix the problem?  It turns out Selenium relies on a bunch of JavaScript files that it exposes as an API interface. You can find them within the .jar file (i.e. selenium-server-standalone-2.0b3.jar) in the core/scripts dir. The main ones to trace are selenium-api.js, selenium-browserbot.js, and atoms.js.

Within selenium-api.js, you have this function:
Selenium.prototype.getText = function(locator) {
    /**
   * Gets the text of an element. This works for any element that contains
   * text. This command uses either the textContent (Mozilla-like browsers) or
   * the innerText (IE-like browsers) of the element, which is the rendered
   * text shown to the user.
   *
   * @param locator an element locator
   * @return string the text of the element
   */
    var element = this.browserbot.findElement(locator);
    return core.text.getText(element);
};
It turns out that the findElement() works properly. The problem appears to be inside the core.text.getText() function, which happens to be located in atoms.js. The reason is that for blank elements, the a.innerText in IE will resolve to false and b will therefore never be set.  In Firefox/Chrome/Opera, the getTextContent_() function is invoked and always appears to return a valid string.

The solution then is to initialize b to be an empty string (var b = '') to avoid these issues altogether:
core.text.getText = function(a) {
  a = core.locators.findElement(a);
  var b = '';
  if(goog.userAgent.GECKO && goog.userAgent.VERSION >= "1.8" || goog.userAgent.KONQUEROR || goog.userAgent.SAFARI || goog.userAgent.OPERA) {
    b = core.text.getTextContent_(a)
  }else {
    if(a.textContent) {
      b = a.textContent
    }else {
      if(a.innerText) {
        b = a.innerText
      }
    }
  }
  b = core.text.normalizeNewlines_(b);
  b = core.text.normalizeSpaces_(b);
  return goog.string.trim(b)
};

You can verify this change works by editing atoms.js, re-updating the atoms.js within core/scripts dir of the .jar file, and then re-running run.bat in *iexplore mode.  Once the .jar file has been updated and you run run.bat again, both tests should pass!

The diff for core/scripts/atoms.js can be located here:

http://code.google.com/p/selenium/issues/detail?id=1163

No comments:

Post a Comment