Describe what happens from the time TestRunner.html or TestRunner.hta is fetched to the time the suite finish assuming auto=true.
TestRunner.html and TestRunner.hta has 3 frames (testSuiteFrame, testFrame, and selenium_myiframe). These frames are not part of a frameset, but are actually iframes embeded inside table cells. This is why these frames are not easily resizable. To make these frames resizable, we must implement code to resize these table cells.
The selenium_myiframe is for the AUT unless multiWindow=true in which case a separate window is opened for AUT. selenium_myiframe is still used to display the result when the suite finish.
The src attribute of testSuiteFrame is TestPrompt.html which is displayed if test=… parameter was not specified. Actually TestPrompt.html is always displayed, at least momentarily. In the case that test=… was specified, TestPrompt.html is unloaded, and the content of the suite is loaded into testSuiteFrame and replace TestPrompt.html. If test=… parameter was not specified, TestPrompt.html stay loaded and you can customize various parameters, and when you submit the form, code in function go() reach into the main window and call loadSuiteFrame() which display the content of the suite in the testSuiteFrame.
If test=… was specified, the code that load the content of test suite into testSuiteFrame starts with onSeleniumLoad().
These frames, according to TestRunner.html and TestRunner.hta, have no customer event handler. The body element of TestRunner.html and TestRunner.hta which contains these iframes has onload handler that call onSeleniumLoad().
TestPrompt.html is always displayed, at least momentarily, because the default onload handler for testSuiteFrame (the frame that has src=TestPrompt.html) must be fired before the onload event handler for the body tag is fired. In other words, the onload handler for the body tag which call onSeleniumLoad() is the last event fired, and there is no race condition so far.
onSeleniumLoad() is defined in selenium-testrunner.js as:
function onSeleniumLoad() {
suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
testFrame = new HtmlTestFrame(getTestFrame());
htmlTestRunner = new HtmlTestRunner();
}
There is not much worth noticing in the constructor of HtmlTestSuiteFrame, HtmlTestFrame, as well as the functions getSuiteFrame() and getTestFrame(). Interesting stuff happens in the constructor of HtmlTestRunner which construct the controlPanel, and also call loadSuiteFrame() with a setTimeout (so that the LOG window is loaded first).
Here are the important points in loadSuiteFrame():
1. call appWindow=this._getApplicationWindow() which check if multiWindow=true ... openSeparateApplicationWindow()
2. create the global variable 'selenium' with Selenium.createForWindow(appWindow)
3. call this._registerCommandHandlers() to manufacture and register all selenium commands
4. call this.controlPanel.getTestSuiteName() to determine if the test=... parameter was specified.
If test=… was specified, call suiteFrame.load(testSuiteName, function() {setTimeout(fnBind(self._onloadTestSuite, self), 50)} );
The variable testSuiteName is misleading. It usually is a URL for the suite. The load() function is defined as a method of SeleniumFrame. The constructor of SeleniumFrame register a onload listener _handleLoad(). The load() function takes 2 arguments, a URL and a callback function. The load() function fetch the URL, therefore trigger off the callback function (in this case _onloadTestSuite).
Here is the definition of _onloadTestSuite():
_onloadTestSuite:function () {
suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
if (! this.getTestSuite().isAvailable()) {
return;
}
if (this.controlPanel.isAutomatedRun()) {
this.controlPanel.reset();
this.metrics.resetMetrics();
this.getTestSuite().reset();
this.runAllTests = true;
this.runNextTest();
} else if (this.controlPanel.getAutoUrl()) {
//todo what is the autourl doing, left to check it out
addLoadListener(this._getApplicationWindow(), fnBind(this._startSingleTest, this));
this._getApplicationWindow().src = this.controlPanel.getAutoUrl();
} else {
var testCaseLoaded = fnBind(function(){this.testCaseLoaded=true;},this);
this.getTestSuite().getSuiteRows()[0].loadTestCase(testCaseLoaded);
}
Notice that _onloadTestSuite() redefine global variable suiteFrame, and check for auto=true via this.controlPanel.isAutomatedRun(). If auto=true was specified, the suite is started.
Here is the definition for runNextTest():
runNextTest: function () {
this.getTestSuite().updateSuiteWithResultOfPreviousTest();
if (!this.runAllTests) {
return;
}
this.getTestSuite().runNextTestInSuite();
}
Here is the definition for runNextTestInSuite():
runNextTestInSuite: function() {
this.currentRowInSuite++;
// If we are done with all of the tests, set the title bar as pass or fail
if (this.currentRowInSuite >= this.suiteRows.length) {
this._onTestSuiteComplete();
} else {
this._startCurrentTestCase();
}
}
Here is the definition of _startCurrentTestCase():
_startCurrentTestCase: function() {
this.getCurrentRow().loadTestCase(fnBind(htmlTestRunner.startTest, htmlTestRunner));
}
loadTestCase() is defined as a method of HtmlTestSuiteRow:
loadTestCase: function(onloadFunction) {
this.htmlTestSuite.unselectCurrentRow();
this.select();
this.htmlTestSuite.currentRowInSuite = this.trElement.rowIndex - 1;
// If the row has a stored results table, use that
var resultsFromPreviousRun = this.trElement.cells[1];
if (resultsFromPreviousRun) {
// todo: delegate to TestFrame, e.g.
// this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
var testBody = this.testFrame.getDocument().body;
testBody.innerHTML = resultsFromPreviousRun.innerHTML;
this.testFrame._onLoad();
if (onloadFunction) {
onloadFunction();
}
} else {
this.testFrame.load(this.link.href, onloadFunction);
}
}
Original definition of startTestSuite() (a method of HtmlTestRunner class):
startTestSuite: function() {
this.controlPanel.reset();
this.metrics.resetMetrics();
var suite = this.getTestSuite();
this.getTestSuite().reset();
this.runAllTests = true;
this.runNextTest();
}
This function was originally called by _onloadTestSuite() when isAutomatedRun() is true. This function was also called by the "start test suite" button. We've modified this function:
startTestSuite: function() {
this.controlPanel.reset();
this.metrics.resetMetrics();
var suite = this.getTestSuite();
// this.getTestSuite().reset();
this.runAllTests = true;
// this.runNextTest();
this.startTest();
}
and only call this function via the "start test suite" button. This is so that the "start test suite" button will start the suite from the current case forward.
Definition of runNextTest() (a method of HtmlTestRunner class):
runNextTest: function () {
this.getTestSuite().updateSuiteWithResultOfPreviousTest();
if (!this.runAllTests) {
return;
}
this.getTestSuite().runNextTestInSuite();
}
Definition of runNextTestInSuite() (a method of HtmlTestSuite):
runNextTestInSuite: function() {
this.currentRowInSuite++;
// If we are done with all of the tests, set the title bar as pass or fail
if (this.currentRowInSuite >= this.suiteRows.length) {
this._onTestSuiteComplete();
} else {
this._startCurrentTestCase();
}
}
Definition of _startCurrentTestCase() (a method of HtmlTestSuite class):
_startCurrentTestCase: function() {
this.getCurrentRow().loadTestCase(fnBind(htmlTestRunner.startTest, htmlTestRunner));
}
Definition of _onTestSuiteComplete() (a method of HtmlTestSuite class):
_onTestSuiteComplete: function() {
this.markDone();
new TestResult(this.failed, this.getTestTable()).post();
}
Definition of updateSuiteWithResultOfPreviousTest() (a method of HtmlTestSuite class):
updateSuiteWithResultOfPreviousTest: function() {
if (this.currentRowInSuite >= 0) {
this.getCurrentRow().saveTestResults();
}
}
Important point to remember is loadTestCase() calls htmlTestRunner.startTest().
Definition of startTest() (a method of HtmlTestRunner class):
startTest: function () {
this.controlPanel.reset();
testFrame.scrollToTop();
//todo: move testFailed and storedVars to TestCase
this.testFailed = false;
storedVars = new Object();
storedVars.nbsp = String.fromCharCode(160);
storedVars.space = ' ';
this.currentTest = new HtmlRunnerTestLoop(testFrame.getCurrentTestCase(), this.metrics, this.commandFactory);
currentTest = this.currentTest;
this.currentTest.start();
}
HtmlRunnerTestLoop class is defined in selenium-testrunner.js and extends TestLoop class (defined in selenium-executionloop.js). The start() method is defined in selenium-executionloop.js.
Definition of start() (a method of TestLoop class):
start : function() {
selenium.reset();
LOG.debug("currentTest.start()");
this.aborted = false;
this.continueTest();
}
Definition of continueTest() (a method of TestLoop class):
continueTest : function() {
/**
* Select the next command and continue the test.
*/
LOG.debug("currentTest.continueTest() - acquire the next command");
if (! this.aborted) {
this.currentCommand = this.nextCommand();
if (! this.requiresCallBack) {
this.continueTestAtCurrentCommand();
} // otherwise, just finish and let the callback invoke continueTestAtCurrentCommand()
} else {
this.requiresCallBack = undefined;
}
}
Definition of continueTestAtCurrentCommand() (a method of TestLoop class):
continueTestAtCurrentCommand : function() {
LOG.debug("currentTest.continueTestAtCurrentCommand()");
if (this.currentCommand) {
// TODO: rename commandStarted to commandSelected, OR roll it into nextCommand
this.commandStarted(this.currentCommand);
this._resumeAfterDelay();
} else {
this._testComplete();
}
}
Definition of _resumeAfterDelay() (a method of TestLoop class):
_resumeAfterDelay : function() {
/**
* Pause, then execute the current command.
*/
// Get the command delay. If a pauseInterval is set, use it once
// and reset it. Otherwise, use the defined command-interval.
var delay = this.pauseInterval || this.getCommandInterval();
this.pauseInterval = undefined;
if (this.currentCommand.isBreakpoint || delay < 0) {
// Pause: enable the "next/continue" button
this.pause();
} else {
window.setTimeout(fnBind(this.resume, this), delay);
}
}
Definition of resume() (a method of TestLoop class):
resume: function() {
/**
* Select the next command and continue the test.
*/
LOG.debug("currentTest.resume() - actually execute");
try {
selenium.browserbot.runScheduledPollers();
this._executeCurrentCommand();
this.continueTestWhenConditionIsTrue();
} catch (e) {
if (!this._handleCommandError(e)) {
this.testComplete();
} else {
this.continueTest();
}
}
}
Definition of _executeCurrentCommand() (a method of TestLoop class):
_executeCurrentCommand : function() {
/**
* Execute the current command.
*
* @return a function which will be used to determine when
* execution can continue, or null if we can continue immediately
*/
var command = this.currentCommand;
LOG.info("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");
var handler = this.commandFactory.getCommandHandler(command.command);
if (handler == null) {
throw new SeleniumError("Unknown command: '" + command.command + "'");
}
command.target = selenium.preprocessParameter(command.target);
command.value = selenium.preprocessParameter(command.value);
LOG.debug("Command found, going to execute " + command.command);
this.result = handler.execute(selenium, command);
this.waitForCondition = this.result.terminationCondition;
}
Definition of continueTestWhenConditionIsTrue() (a method of TestLoop class):
continueTestWhenConditionIsTrue: function () {
/**
* Busy wait for waitForCondition() to become true, and then carry
* on with test. Fail the current test if there's a timeout or an
* exception.
*/
//LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
selenium.browserbot.runScheduledPollers();
try {
if (this.waitForCondition == null) {
LOG.debug("null condition; let's continueTest()");
LOG.debug("Command complete");
this.commandComplete(this.result);
this.continueTest();
} else if (this.waitForCondition()) {
LOG.debug("condition satisfied; let's continueTest()");
this.waitForCondition = null;
LOG.debug("Command complete");
this.commandComplete(this.result);
this.continueTest();
} else {
//LOG.debug("waitForCondition was false; keep waiting!");
window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
}
} catch (e) {
this.result = {};
this.result.failed = true;
this.result.failureMessage = extractExceptionMessage(e);
this.commandComplete(this.result);
this.continueTest();
}
}
Notice that start() is the entry point for the case, then continue(), continueTestAtCurrentCommand(), _resumeAfterDelay(), resume(), and continueTestWhenConditionIsTrue() form a close loop and process all the commands in a test. When a command fail, or the last command of the test is done, testComplete() (a method of HtmlRunnerTestLoop class) is called, which set a timeout to call htmlTestRunner.runNextTest(). This is how the entire suite get processed.
Also notice that continueTestWhenConditionIsTrue implements the magic of "AndWait" (continueTestWhenConditionIsTrue form a close loop with itself with a timeout)
How does selenium detect when a page finish loading?
getCurrentWindow -> _modifyWindow -> modifySeparateTestWindowToDetectPageLoad -> pollForLoad -> reschedulePollers.
Every command in selenium start out by calling getCurrentWindow (need to verify if this is actually the case, and if so, how).
runScheduledPollers() is called in executionloop.js once before a command is executed, and every 10ms until the AndWait condition is satisfied. It seems that runScheduledPollers() always empty the windowPollers array. However, looking closer, the existing pollers are annonymous functions that call pollForLoad(), which call reschedulePollers(), so windowPollers array is only empty momentarily.
Discuss the poll for load mechanism after we tell selenium to open a new page.
Discuss the poll for load mechanism and how it works when dealing with popup window.
open() implies openAndWait(), refresh() on the other hand does not imply refreshAndWait. How did I fix this?
Command Factory. How are other actions and Accessors/Assertions generated from function defined in user-extension.js?
Extending Selenium
Locator strategies
What happens when a command fail?
What happens when a test case finish?
What cause the next test to run when previous test finish?
What happens when you click on a link in the test suite window?
What happens when you click on the pause button?
What happens when you click on the continue button?
What are the javascript objects?
http://wiki.openqa.org/display/SEL/References+and+Citations
http://wiki.openqa.org/display/SEL/Publications+and+Presentations
http://wiki.openqa.org/display/SEL/Selenium+Core+API+Documentation+Standard
http://wiki.openqa.org/display/SEL/Release+Process
http://wiki.openqa.org/display/SEL/Manual+Testing+for+Selenium+Core
http://wiki.openqa.org/pages/viewpage.action?pageId=422
http://wiki.openqa.org/display/SEL/Conference+Homework
http://wiki.openqa.org/display/SEL/Getting+Started+with+Selenium+Core
http://wiki.openqa.org/display/SEL/Integrating+Selenium+And+CruiseControl.Net
http://wiki.openqa.org/display/SEL/Selenium+Core+FAQ
http://wiki.openqa.org/display/SEL/Selenium+Road+Map





