Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the inner workings of the Selenium waitFor mechanism?

I am trying to customize the behavior of Selenium's click command, (via user-extentions.js), by intercepting calls to doClick(locator). Basically I need to delay click actions whenever our application's "busy indicator" is being displayed.

(Now the standard answer for this kind of thing is to insert a waitFor into the script for those situations. Indeed, we currently have zillions of them throughout our scripts. I'm trying to eliminate those.)

Detecting the page element is the trivial part. The tricky part is getting the script to actually wait. My promising looking, but failed attempt looks like this:

var nativeClick = Selenium.prototype.doClick;
Selenium.prototype.doClick = function(locator) {
  this.doWaitForCondition("!selenium.browserbot.findElementOrNull('busy-indicator')", 5000);
  return nativeClick.call(this, locator);
}

The doWaitForCondition gets called before every click, but it does not wait when the condition evaluates to false. nativeClick always gets called immediately, and so no delay is introduced. I suspect that the doWaitForCondition function doesn't actually do any waiting per se, but rather establishes the conditions for it within the command execution loop. And in this case the click command is already in play, and I'm trying to run a command within a command.

Can somebody shed some light on how Selenium command execution and waitFor works, or offer suggestions on how this might be done?

like image 314
Chris Noe Avatar asked Nov 16 '10 14:11

Chris Noe


1 Answers

I have finally solved this. And with an approach that is much better than trying to intercept click processing in its various forms. My refined goal is: to delay execution of script command completion when our application is "busy".

How Selenium command processing works:

Upon completion, each selenium command returns an ActionResult object, (see ActionHandler.prototype.execute). The terminationCondition attribute on this object is a function that determines when it is okay for selenium to proceed to the next command, (TestLoop.prototype.continueTestWhenConditionIsTrue). Basically, selenium repeatedly executes the condition function until it yields true. The result object it quite trivial:

function ActionResult(terminationCondition) {
  this.terminationCondition = terminationCondition;
}

Customizing it:

I want to delay execution any time myAppIsBusy() returns true. Of course all of the standard delays need to remain in place as well, like waiting for page loads, and explicit waitFor conditions as scripted. The solution is to redefine the selenium result object in my user-extensions.js, as follows:

function ActionResult(terminationCondition) {
  this.terminationCondition = function() {
    // a null terminationCondition means okay to continue
    return (!terminationCondition || terminationCondition()) && !myAppIsBusy();
  }
}

The great thing is that this is at a low enough level that it works for the IDE, as well as for RC.

Note that this does not affect Accessor or Assert command types, which return different result objects. But that should be fine, because those commands don't effect the state of the application.

like image 183
Chris Noe Avatar answered Oct 07 '22 00:10

Chris Noe