summaryrefslogtreecommitdiff
path: root/tests/test_tools/selenium/core
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_tools/selenium/core')
-rw-r--r--tests/test_tools/selenium/core/SeleniumLog.html14
-rw-r--r--tests/test_tools/selenium/core/TestRunner-splash.html1
-rw-r--r--tests/test_tools/selenium/core/scripts/find_matching_child.js16
-rw-r--r--tests/test_tools/selenium/core/scripts/htmlutils.js411
-rw-r--r--tests/test_tools/selenium/core/scripts/injection.html19
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-api.js1454
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-browserbot.js1456
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-browserdetect.js48
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js375
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-executionloop.js63
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-logging.js30
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-testrunner.js516
-rw-r--r--tests/test_tools/selenium/core/scripts/selenium-version.js4
-rw-r--r--tests/test_tools/selenium/core/selenium.css44
14 files changed, 2747 insertions, 1704 deletions
diff --git a/tests/test_tools/selenium/core/SeleniumLog.html b/tests/test_tools/selenium/core/SeleniumLog.html
index dfa0080a..dffa184f 100644
--- a/tests/test_tools/selenium/core/SeleniumLog.html
+++ b/tests/test_tools/selenium/core/SeleniumLog.html
@@ -2,6 +2,7 @@
<head>
<title>Selenium Log Console</title>
+<link id="cssLink" rel="stylesheet" href="selenium.css" />
</head>
<body id="logging-console">
@@ -15,8 +16,6 @@ var logLevels = {
error: 3
};
-var logLevelThreshold = null;
-
function getThresholdLevel() {
var buttons = document.getElementById('logLevelChooser').level;
for (var i = 0; i < buttons.length; i++) {
@@ -27,10 +26,9 @@ function getThresholdLevel() {
}
function setThresholdLevel(logLevel) {
- logLevelThreshold = logLevel;
var buttons = document.getElementById('logLevelChooser').level;
for (var i = 0; i < buttons.length; i++) {
- if (buttons[i].value==logLevel) {
+ if (buttons[i].value==logLevel) {
buttons[i].checked = true;
}
else {
@@ -40,9 +38,7 @@ function setThresholdLevel(logLevel) {
}
function append(message, logLevel) {
- if (logLevelThreshold==null) {
- logLevelThreshold = getThresholdLevel();
- }
+ var logLevelThreshold = getThresholdLevel();
if (logLevels[logLevel] < logLevels[logLevelThreshold]) {
return;
}
@@ -64,9 +60,9 @@ function append(message, logLevel) {
value="error" /><label for="level-error">Error</label>
<input id="level-warn" type="radio" name="level"
value="warn" /><label for="level-warn">Warn</label>
- <input id="level-info" type="radio" name="level" checked="yes"
+ <input id="level-info" type="radio" name="level"
value="info" /><label for="level-info">Info</label>
- <input id="level-debug" type="radio" name="level"
+ <input id="level-debug" type="radio" name="level" checked="yes"
value="debug" /><label for="level-debug">Debug</label>
</form>
<h1>Selenium Log Console</h1>
diff --git a/tests/test_tools/selenium/core/TestRunner-splash.html b/tests/test_tools/selenium/core/TestRunner-splash.html
index 205bb8ef..1c32dd79 100644
--- a/tests/test_tools/selenium/core/TestRunner-splash.html
+++ b/tests/test_tools/selenium/core/TestRunner-splash.html
@@ -15,6 +15,7 @@ Copyright 2005 ThoughtWorks, Inc
-->
<html>
+<link rel="stylesheet" type="text/css" href="selenium.css" />
<body>
<table width="100%">
diff --git a/tests/test_tools/selenium/core/scripts/find_matching_child.js b/tests/test_tools/selenium/core/scripts/find_matching_child.js
index 197d1032..fbf35b75 100644
--- a/tests/test_tools/selenium/core/scripts/find_matching_child.js
+++ b/tests/test_tools/selenium/core/scripts/find_matching_child.js
@@ -15,8 +15,8 @@
*
*/
-Element.findMatchingChildren = function(element, selector) {
- var matches = $A([]);
+elementFindMatchingChildren = function(element, selector) {
+ var matches = [];
var childCount = element.childNodes.length;
for (var i=0; i<childCount; i++) {
@@ -24,7 +24,7 @@ Element.findMatchingChildren = function(element, selector) {
if (selector(child)) {
matches.push(child);
} else {
- childMatches = Element.findMatchingChildren(child, selector);
+ childMatches = elementFindMatchingChildren(child, selector);
matches.push(childMatches);
}
}
@@ -34,7 +34,7 @@ Element.findMatchingChildren = function(element, selector) {
ELEMENT_NODE_TYPE = 1;
-Element.findFirstMatchingChild = function(element, selector) {
+elementFindFirstMatchingChild = function(element, selector) {
var childCount = element.childNodes.length;
for (var i=0; i<childCount; i++) {
@@ -43,7 +43,7 @@ Element.findFirstMatchingChild = function(element, selector) {
if (selector(child)) {
return child;
}
- result = Element.findFirstMatchingChild(child, selector);
+ result = elementFindFirstMatchingChild(child, selector);
if (result) {
return result;
}
@@ -52,7 +52,7 @@ Element.findFirstMatchingChild = function(element, selector) {
return null;
}
-Element.findFirstMatchingParent = function(element, selector) {
+elementFindFirstMatchingParent = function(element, selector) {
var current = element.parentNode;
while (current != null) {
if (selector(current)) {
@@ -63,7 +63,7 @@ Element.findFirstMatchingParent = function(element, selector) {
return current;
}
-Element.findMatchingChildById = function(element, id) {
- return Element.findFirstMatchingChild(element, function(element){return element.id==id} );
+elementFindMatchingChildById = function(element, id) {
+ return elementFindFirstMatchingChild(element, function(element){return element.id==id} );
}
diff --git a/tests/test_tools/selenium/core/scripts/htmlutils.js b/tests/test_tools/selenium/core/scripts/htmlutils.js
index 4d78e1a6..a3cd3dd9 100644
--- a/tests/test_tools/selenium/core/scripts/htmlutils.js
+++ b/tests/test_tools/selenium/core/scripts/htmlutils.js
@@ -18,6 +18,93 @@
// This script contains a badly-organised collection of miscellaneous
// functions that really better homes.
+function classCreate() {
+ return function() {
+ this.initialize.apply(this, arguments);
+ }
+}
+
+function objectExtend(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+function $() {
+ var results = [], element;
+ for (var i = 0; i < arguments.length; i++) {
+ element = arguments[i];
+ if (typeof element == 'string')
+ element = document.getElementById(element);
+ results[results.length] = element;
+ }
+ return results.length < 2 ? results[0] : results;
+}
+
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) {
+ return iterable.toArray();
+ } else {
+ var results = [];
+ for (var i = 0; i < iterable.length; i++)
+ results.push(iterable[i]);
+ return results;
+ }
+}
+
+function fnBind() {
+ var args = $A(arguments), __method = args.shift(), object = args.shift();
+ var retval = function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ retval.__method = __method;
+ return retval;
+}
+
+function fnBindAsEventListener(fn, object) {
+ var __method = fn;
+ return function(event) {
+ return __method.call(object, event || window.event);
+ }
+}
+
+function removeClassName(element, name) {
+ var re = new RegExp("\\b" + name + "\\b", "g");
+ element.className = element.className.replace(re, "");
+}
+
+function addClassName(element, name) {
+ element.className = element.className + ' ' + name;
+}
+
+function elementSetStyle(element, style) {
+ for (var name in style) {
+ var value = style[name];
+ if (value == null) value = "";
+ element.style[name] = value;
+ }
+}
+
+function elementGetStyle(element, style) {
+ var value = element.style[style];
+ if (!value) {
+ if (document.defaultView && document.defaultView.getComputedStyle) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css.getPropertyValue(style) : null;
+ } else if (element.currentStyle) {
+ value = element.currentStyle[style];
+ }
+ }
+
+ /** DGF necessary?
+ if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
+ if (Element.getStyle(element, 'position') == 'static') value = 'auto'; */
+
+ return value == 'auto' ? null : value;
+ }
+
String.prototype.trim = function() {
var result = this.replace(/^\s+/g, "");
// strip leading
@@ -134,31 +221,49 @@ function xmlDecode(text) {
// Sets the text in this element
function setText(element, text) {
- if (element.textContent) {
+ if (element.textContent != null) {
element.textContent = text;
- } else if (element.innerText) {
+ } else if (element.innerText != null) {
element.innerText = text;
}
}
// Get the value of an <input> element
function getInputValue(inputElement) {
- if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
- inputElement.type.toUpperCase() == 'RADIO')
- {
- return (inputElement.checked ? 'on' : 'off');
+ if (inputElement.type) {
+ if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
+ inputElement.type.toUpperCase() == 'RADIO')
+ {
+ return (inputElement.checked ? 'on' : 'off');
+ }
+ }
+ if (inputElement.value == null) {
+ throw new SeleniumError("This element has no value; is it really a form field?");
}
return inputElement.value;
}
/* Fire an event in a browser-compatible manner */
-function triggerEvent(element, eventType, canBubble) {
+function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
- element.fireEvent('on' + eventType);
+ var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
+ element.fireEvent('on' + eventType, evt);
}
else {
var evt = document.createEvent('HTMLEvents');
+
+ try {
+ evt.shiftKey = shiftKeyDown;
+ evt.metaKey = metaKeyDown;
+ evt.altKey = altKeyDown;
+ evt.ctrlKey = controlKeyDown;
+ } catch (e) {
+ // On Firefox 1.0, you can only set these during initMouseEvent or initKeyEvent
+ // we'll have to ignore them here
+ LOG.exception(e);
+ }
+
evt.initEvent(eventType, canBubble, true);
element.dispatchEvent(evt);
}
@@ -179,14 +284,23 @@ function getKeyCodeFromKeySequence(keySequence) {
if (match != null) {
return match[0];
}
- throw SeleniumError("invalid keySequence");
+ throw new SeleniumError("invalid keySequence");
}
-function triggerKeyEvent(element, eventType, keySequence, canBubble) {
+function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
+ var evt = element.ownerDocument.createEventObject();
+ evt.shiftKey = shiftKeyDown;
+ evt.metaKey = metaKeyDown;
+ evt.altKey = altKeyDown;
+ evt.ctrlKey = controlKeyDown;
+ return evt;
+}
+
+function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
var keycode = getKeyCodeFromKeySequence(keySequence);
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
- keyEvent = element.ownerDocument.createEventObject();
+ var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
keyEvent.keyCode = keycode;
element.fireEvent('on' + eventType, keyEvent);
}
@@ -194,82 +308,24 @@ function triggerKeyEvent(element, eventType, keySequence, canBubble) {
var evt;
if (window.KeyEvent) {
evt = document.createEvent('KeyEvents');
- evt.initKeyEvent(eventType, true, true, window, false, false, false, false, keycode, keycode);
+ evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode);
} else {
evt = document.createEvent('UIEvents');
+
+ evt.shiftKey = shiftKeyDown;
+ evt.metaKey = metaKeyDown;
+ evt.altKey = altKeyDown;
+ evt.ctrlKey = controlKeyDown;
+
evt.initUIEvent(eventType, true, true, window, 1);
evt.keyCode = keycode;
+ evt.which = keycode;
}
element.dispatchEvent(evt);
}
}
-/* Fire a mouse event in a browser-compatible manner */
-function triggerMouseEvent(element, eventType, canBubble, clientX, clientY) {
- clientX = clientX ? clientX : 0;
- clientY = clientY ? clientY : 0;
-
- // TODO: set these attributes -- they don't seem to be needed by the initial test cases, but that could change...
- var screenX = 0;
- var screenY = 0;
-
- canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
- if (element.fireEvent) {
- LOG.error("element has fireEvent");
- if (!screenX && !screenY && !clientX && !clientY) {
- element.fireEvent('on' + eventType);
- }
- else {
- var ieEvent = element.ownerDocument.createEventObject();
- ieEvent.detail = 0;
- ieEvent.screenX = screenX;
- ieEvent.screenY = screenY;
- ieEvent.clientX = clientX;
- ieEvent.clientY = clientY;
- ieEvent.ctrlKey = false;
- ieEvent.altKey = false;
- ieEvent.shiftKey = false;
- ieEvent.metaKey = false;
- ieEvent.button = 1;
- ieEvent.relatedTarget = null;
-
- // when we go this route, window.event is never set to contain the event we have just created.
- // ideally we could just slide it in as follows in the try-block below, but this normally
- // doesn't work. This is why I try to avoid this code path, which is only required if we need to
- // set attributes on the event (e.g., clientX).
- try {
- window.event = ieEvent;
- }
- catch(e) {
- // getting an "Object does not support this action or property" error. Save the event away
- // for future reference.
- // TODO: is there a way to update window.event?
-
- // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
- selenium.browserbot.getCurrentWindow().selenium_event = ieEvent;
- }
- element.fireEvent('on' + eventType, ieEvent);
- }
- }
- else {
- LOG.error("element doesn't have fireEvent");
- var evt = document.createEvent('MouseEvents');
- if (evt.initMouseEvent)
- {
- LOG.error("element has initMouseEvent");
- //Safari
- evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY, false, false, false, false, 0, null)
- }
- else {
- LOG.error("element doesen't has initMouseEvent");
- // TODO we should be initialising other mouse-event related attributes here
- evt.initEvent(eventType, canBubble, true);
- }
- element.dispatchEvent(evt);
- }
-}
-
function removeLoadListener(element, command) {
LOG.info('Removing loadListenter for ' + element + ', ' + command);
if (window.removeEventListener)
@@ -280,10 +336,13 @@ function removeLoadListener(element, command) {
function addLoadListener(element, command) {
LOG.info('Adding loadListenter for ' + element + ', ' + command);
+ var augmentedCommand = function() {
+ command.call(this, element);
+ }
if (window.addEventListener && !browserVersion.isOpera)
- element.addEventListener("load", command, true);
+ element.addEventListener("load", augmentedCommand, true);
else if (window.attachEvent)
- element.attachEvent("onload", command);
+ element.attachEvent("onload", augmentedCommand);
}
/**
@@ -306,10 +365,160 @@ function getDocumentBase(doc) {
return "";
}
+function getTagName(element) {
+ var tagName;
+ if (element && element.tagName && element.tagName.toLowerCase) {
+ tagName = element.tagName.toLowerCase();
+ }
+ return tagName;
+}
+
+function absolutify(url, baseUrl) {
+ /** returns a relative url in its absolute form, given by baseUrl.
+ *
+ * This function is a little odd, because it can take baseUrls that
+ * aren't necessarily directories. It uses the same rules as the HTML
+ * &lt;base&gt; tag; if the baseUrl doesn't end with "/", we'll assume
+ * that it points to a file, and strip the filename off to find its
+ * base directory.
+ *
+ * So absolutify("foo", "http://x/bar") will return "http://x/foo" (stripping off bar),
+ * whereas absolutify("foo", "http://x/bar/") will return "http://x/bar/foo" (preserving bar).
+ * Naturally absolutify("foo", "http://x") will return "http://x/foo", appropriately.
+ *
+ * @param url the url to make absolute; if this url is already absolute, we'll just return that, unchanged
+ * @param baseUrl the baseUrl from which we'll absolutify, following the rules above.
+ * @return 'url' if it was already absolute, or the absolutized version of url if it was not absolute.
+ */
+
+ // DGF isn't there some library we could use for this?
+
+ if (/^\w+:/.test(url)) {
+ // it's already absolute
+ return url;
+ }
+
+ var loc;
+ try {
+ loc = parseUrl(baseUrl);
+ } catch (e) {
+ // is it an absolute windows file path? let's play the hero in that case
+ if (/^\w:\\/.test(baseUrl)) {
+ baseUrl = "file:///" + baseUrl.replace(/\\/g, "/");
+ loc = parseUrl(baseUrl);
+ } else {
+ throw new SeleniumError("baseUrl wasn't absolute: " + baseUrl);
+ }
+ }
+ loc.search = null;
+ loc.hash = null;
+
+ // if url begins with /, then that's the whole pathname
+ if (/^\//.test(url)) {
+ loc.pathname = url;
+ var result = reassembleLocation(loc);
+ return result;
+ }
+
+ // if pathname is null, then we'll just append "/" + the url
+ if (!loc.pathname) {
+ loc.pathname = "/" + url;
+ var result = reassembleLocation(loc);
+ return result;
+ }
+
+ // if pathname ends with /, just append url
+ if (/\/$/.test(loc.pathname)) {
+ loc.pathname += url;
+ var result = reassembleLocation(loc);
+ return result;
+ }
+
+ // if we're here, then the baseUrl has a pathname, but it doesn't end with /
+ // in that case, we replace everything after the final / with the relative url
+ loc.pathname = loc.pathname.replace(/[^\/\\]+$/, url);
+ var result = reassembleLocation(loc);
+ return result;
+
+}
+
+var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/;
+
+function parseUrl(url) {
+ var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash'];
+ var result = URL_REGEX.exec(url);
+ if (!result) {
+ throw new SeleniumError("Invalid URL: " + url);
+ }
+ var loc = new Object();
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ if (field == null) {
+ continue;
+ }
+ loc[field] = result[i];
+ }
+ return loc;
+}
+
+function reassembleLocation(loc) {
+ if (!loc.protocol) {
+ throw new Error("Not a valid location object: " + o2s(loc));
+ }
+ var protocol = loc.protocol;
+ protocol = protocol.replace(/:$/, "");
+ var url = protocol + "://";
+ if (loc.username) {
+ url += loc.username;
+ if (loc.password) {
+ url += ":" + loc.password;
+ }
+ url += "@";
+ }
+ if (loc.host) {
+ url += loc.host;
+ }
+
+ if (loc.port) {
+ url += ":" + loc.port;
+ }
+
+ if (loc.pathname) {
+ url += loc.pathname;
+ }
+
+ if (loc.search) {
+ url += "?" + loc.search;
+ }
+ if (loc.hash) {
+ var hash = loc.hash;
+ hash = loc.hash.replace(/^#/, "");
+ url += "#" + hash;
+ }
+ return url;
+}
+
+function canonicalize(url) {
+ var tempLink = window.document.createElement("link");
+ tempLink.href = url; // this will canonicalize the href
+ return tempLink.href;
+}
+
+function extractExceptionMessage(ex) {
+ if (ex == null) return "null exception";
+ if (ex.message != null) return ex.message;
+ if (ex.toString && ex.toString() != null) return ex.toString();
+}
+
+
function describe(object, delimiter) {
var props = new Array();
for (var prop in object) {
- props.push(prop + " -> " + object[prop]);
+ try {
+ props.push(prop + " -> " + object[prop]);
+ } catch (e) {
+ props.push(prop + " -> [htmlutils: ack! couldn't read this property! (Permission Denied?)]");
+ }
}
return props.join(delimiter || '\n');
}
@@ -502,28 +711,27 @@ function SeleniumError(message) {
return error;
}
-var Effect = new Object();
-
-Object.extend(Effect, {
- highlight : function(element) {
- var highLightColor = "yellow";
- if (element.originalColor == undefined) { // avoid picking up highlight
- element.originalColor = Element.getStyle(element, "background-color");
- }
- Element.setStyle(element, {"background-color" : highLightColor});
- window.setTimeout(function() {
+function highlight(element) {
+ var highLightColor = "yellow";
+ if (element.originalColor == undefined) { // avoid picking up highlight
+ element.originalColor = elementGetStyle(element, "background-color");
+ }
+ elementSetStyle(element, {"backgroundColor" : highLightColor});
+ window.setTimeout(function() {
+ try {
//if element is orphan, probably page of it has already gone, so ignore
if (!element.parentNode) {
return;
}
- Element.setStyle(element, {"background-color" : element.originalColor});
- }, 200);
- }
-});
+ elementSetStyle(element, {"backgroundColor" : element.originalColor});
+ } catch (e) {} // DGF unhighlighting is very dangerous and low priority
+ }, 200);
+}
+
// for use from vs.2003 debugger
-function objToString(obj) {
+function o2s(obj) {
var s = "";
for (key in obj) {
var line = key + "->" + obj[key];
@@ -535,7 +743,7 @@ function objToString(obj) {
var seenReadyStateWarning = false;
-function openSeparateApplicationWindow(url) {
+function openSeparateApplicationWindow(url, suppressMozillaWarning) {
// resize the Selenium window itself
window.resizeTo(1200, 500);
window.moveTo(window.screenX, 0);
@@ -560,7 +768,7 @@ function openSeparateApplicationWindow(url) {
}
- if (window.document.readyState == null && !seenReadyStateWarning) {
+ if (!suppressMozillaWarning && window.document.readyState == null && !seenReadyStateWarning) {
alert("Beware! Mozilla bug 300992 means that we can't always reliably detect when a new page has loaded. Install the Selenium IDE extension or the readyState extension available from selenium.openqa.org to make page load detection more reliable.");
seenReadyStateWarning = true;
}
@@ -568,8 +776,8 @@ function openSeparateApplicationWindow(url) {
return appWindow;
}
-var URLConfiguration = Class.create();
-Object.extend(URLConfiguration.prototype, {
+var URLConfiguration = classCreate();
+objectExtend(URLConfiguration.prototype, {
initialize: function() {
},
_isQueryParameterTrue: function (name) {
@@ -615,6 +823,11 @@ Object.extend(URLConfiguration.prototype, {
isMultiWindowMode:function() {
return this._isQueryParameterTrue('multiWindow');
+ },
+
+ getBaseUrl:function() {
+ return this._getQueryParameter('baseUrl');
+
}
});
diff --git a/tests/test_tools/selenium/core/scripts/injection.html b/tests/test_tools/selenium/core/scripts/injection.html
index d41fbe69..a75c7211 100644
--- a/tests/test_tools/selenium/core/scripts/injection.html
+++ b/tests/test_tools/selenium/core/scripts/injection.html
@@ -1,21 +1,22 @@
<script language="JavaScript">
if (window["selenium_has_been_loaded_into_this_window"]==null)
{
-__SELENIUM_JS__
+
+ __SELENIUM_JS__
// Some background on the code below: broadly speaking, where we are relative to other windows
// when running in proxy injection mode depends on whether we are in a frame set file or not.
-//
+//
// In regular HTML files, the selenium JavaScript is injected into an iframe called "selenium"
// in order to reduce its impact on the JavaScript environment (through namespace pollution,
// etc.). So in regular HTML files, we need to look at the parent of the current window when we want
// a handle to, e.g., the application window.
-//
+//
// In frame set files, we can't use an iframe, so we put the JavaScript in the head element and share
// the window with the frame set. So in this case, we need to look at the current window, not the
-// parent when looking for, e.g., the application window. (TODO: Perhaps I should have just
+// parent when looking for, e.g., the application window. (TODO: Perhaps I should have just
// assigned a regular frame for selenium?)
-//
+//
BrowserBot.prototype.getContentWindow = function() {
if (window["seleniumInSameWindow"] != null) return window;
return window.parent;
@@ -38,7 +39,7 @@ LOG.openLogWindow = function(message, className) {
BrowserBot.prototype.relayToRC = function(name) {
var object = eval(name);
var s = 'state:' + serializeObject(name, object) + "\n";
- sendToRC(s);
+ sendToRC(s,"state=true");
}
BrowserBot.prototype.relayBotToRC = function(s) {
@@ -58,10 +59,16 @@ function seleniumOnLoad() {
runSeleniumTest();
}
+function seleniumOnUnload() {
+ sendToRC("OK"); // just in case some poor PI server thread is waiting for a response
+}
+
if (window.addEventListener) {
window.addEventListener("load", seleniumOnLoad, false); // firefox
+ window.addEventListener("unload", seleniumOnUnload, false); // firefox
} else if (window.attachEvent){
window.attachEvent("onload", seleniumOnLoad); // IE
+ window.attachEvent("onunload", seleniumOnUnload); // IE
}
else {
throw "causing a JavaScript error to tell the world that I did not arrange to be run on load";
diff --git a/tests/test_tools/selenium/core/scripts/selenium-api.js b/tests/test_tools/selenium/core/scripts/selenium-api.js
index e8e587f7..1646236f 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-api.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-api.js
@@ -15,157 +15,179 @@
*
*/
-// TODO: stop navigating this.page().document() ... it breaks encapsulation
+// TODO: stop navigating this.browserbot.document() ... it breaks encapsulation
var storedVars = new Object();
function Selenium(browserbot) {
- /**
- * Defines an object that runs Selenium commands.
- *
- * <h3><a name="locators"></a>Element Locators</h3>
- * <p>
- * Element Locators tell Selenium which HTML element a command refers to.
- * The format of a locator is:</p>
- * <blockquote>
- * <em>locatorType</em><strong>=</strong><em>argument</em>
- * </blockquote>
- *
- * <p>
- * We support the following strategies for locating elements:
- * </p>
- * <blockquote>
- * <dl>
- * <dt><strong>identifier</strong>=<em>id</em></dt>
- * <dd>Select the element with the specified &#64;id attribute. If no match is
- * found, select the first element whose &#64;name attribute is <em>id</em>.
- * (This is normally the default; see below.)</dd>
- * <dt><strong>id</strong>=<em>id</em></dt>
- * <dd>Select the element with the specified &#64;id attribute.</dd>
- *
- * <dt><strong>name</strong>=<em>name</em></dt>
- * <dd>Select the first element with the specified &#64;name attribute.</dd>
- * <dd><ul class="first last simple">
- * <li>username</li>
- * <li>name=username</li>
- * </ul>
- * </dd>
- * <dd>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace. If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</dd>
- *
- * <dd><ul class="first last simple">
- * <li>name=flavour value=chocolate</li>
- * </ul>
- * </dd>
- * <dt><strong>dom</strong>=<em>javascriptExpression</em></dt>
- *
- * <dd>
- *
- * <dd>Find an element using JavaScript traversal of the HTML Document Object
- * Model. DOM locators <em>must</em> begin with &quot;document.&quot;.
- * <ul class="first last simple">
- * <li>dom=document.forms['myForm'].myDropdown</li>
- * <li>dom=document.images[56]</li>
- * </ul>
- * </dd>
- *
- * </dd>
- *
- * <dt><strong>xpath</strong>=<em>xpathExpression</em></dt>
- * <dd>Locate an element using an XPath expression.
- * <ul class="first last simple">
- * <li>xpath=//img[&#64;alt='The image alt text']</li>
- * <li>xpath=//table[&#64;id='table1']//tr[4]/td[2]</li>
- *
- * </ul>
- * </dd>
- * <dt><strong>link</strong>=<em>textPattern</em></dt>
- * <dd>Select the link (anchor) element which contains text matching the
- * specified <em>pattern</em>.
- * <ul class="first last simple">
- * <li>link=The link text</li>
- * </ul>
- *
- * </dd>
- *
- * <dt><strong>css</strong>=<em>cssSelectorSyntax</em></dt>
- * <dd>Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
- * <ul class="first last simple">
- * <li>css=a[href="#id3"]</li>
- * <li>css=span#firstChild + span</li>
- * </ul>
- * </dd>
- * <dd>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </dd>
- * </dl>
- * </blockquote>
- * <p>
- * Without an explicit locator prefix, Selenium uses the following default
- * strategies:
- * </p>
- *
- * <ul class="simple">
- * <li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
- * <li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
- * <li><strong>identifier</strong>, otherwise</li>
- * </ul>
- *
- * <h3><a name="element-filters">Element Filters</a></h3>
- * <blockquote>
- * <p>Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.</p>
- * <p>Filters look much like locators, ie.</p>
- * <blockquote>
- * <em>filterType</em><strong>=</strong><em>argument</em></blockquote>
- *
- * <p>Supported element-filters are:</p>
- * <p><strong>value=</strong><em>valuePattern</em></p>
- * <blockquote>
- * Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
- * <p><strong>index=</strong><em>index</em></p>
- * <blockquote>
- * Selects a single element based on its position in the list (offset from zero).</blockquote>
- * </blockquote>
- *
- * <h3><a name="patterns"></a>String-match Patterns</h3>
- *
- * <p>
- * Various Pattern syntaxes are available for matching string values:
- * </p>
- * <blockquote>
- * <dl>
- * <dt><strong>glob:</strong><em>pattern</em></dt>
- * <dd>Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
- * kind of limited regular-expression syntax typically used in command-line
- * shells. In a glob pattern, "*" represents any sequence of characters, and "?"
- * represents any single character. Glob patterns match against the entire
- * string.</dd>
- * <dt><strong>regexp:</strong><em>regexp</em></dt>
- * <dd>Match a string using a regular-expression. The full power of JavaScript
- * regular-expressions is available.</dd>
- * <dt><strong>exact:</strong><em>string</em></dt>
- *
- * <dd>Match a string exactly, verbatim, without any of that fancy wildcard
- * stuff.</dd>
- * </dl>
- * </blockquote>
- * <p>
- * If no pattern prefix is specified, Selenium assumes that it's a "glob"
- * pattern.
- * </p>
- */
+ /**
+ * Defines an object that runs Selenium commands.
+ *
+ * <h3><a name="locators"></a>Element Locators</h3>
+ * <p>
+ * Element Locators tell Selenium which HTML element a command refers to.
+ * The format of a locator is:</p>
+ * <blockquote>
+ * <em>locatorType</em><strong>=</strong><em>argument</em>
+ * </blockquote>
+ *
+ * <p>
+ * We support the following strategies for locating elements:
+ * </p>
+ *
+ * <ul>
+ * <li><strong>identifier</strong>=<em>id</em>:
+ * Select the element with the specified &#64;id attribute. If no match is
+ * found, select the first element whose &#64;name attribute is <em>id</em>.
+ * (This is normally the default; see below.)</li>
+ * <li><strong>id</strong>=<em>id</em>:
+ * Select the element with the specified &#64;id attribute.</li>
+ *
+ * <li><strong>name</strong>=<em>name</em>:
+ * Select the first element with the specified &#64;name attribute.
+ * <ul class="first last simple">
+ * <li>username</li>
+ * <li>name=username</li>
+ * </ul>
+ *
+ * <p>The name may optionally be followed by one or more <em>element-filters</em>, separated from the name by whitespace. If the <em>filterType</em> is not specified, <strong>value</strong> is assumed.</p>
+ *
+ * <ul class="first last simple">
+ * <li>name=flavour value=chocolate</li>
+ * </ul>
+ * </li>
+ * <li><strong>dom</strong>=<em>javascriptExpression</em>:
+ *
+ * Find an element by evaluating the specified string. This allows you to traverse the HTML Document Object
+ * Model using JavaScript. Note that you must not return a value in this string; simply make it the last expression in the block.
+ * <ul class="first last simple">
+ * <li>dom=document.forms['myForm'].myDropdown</li>
+ * <li>dom=document.images[56]</li>
+ * <li>dom=function foo() { return document.links[1]; }; foo();</li>
+ * </ul>
+ *
+ * </li>
+ *
+ * <li><strong>xpath</strong>=<em>xpathExpression</em>:
+ * Locate an element using an XPath expression.
+ * <ul class="first last simple">
+ * <li>xpath=//img[&#64;alt='The image alt text']</li>
+ * <li>xpath=//table[&#64;id='table1']//tr[4]/td[2]</li>
+ * <li>xpath=//a[contains(&#64;href,'#id1')]</li>
+ * <li>xpath=//a[contains(&#64;href,'#id1')]/&#64;class</li>
+ * <li>xpath=(//table[&#64;class='stylee'])//th[text()='theHeaderText']/../td</li>
+ * <li>xpath=//input[&#64;name='name2' and &#64;value='yes']</li>
+ * <li>xpath=//*[text()="right"]</li>
+ *
+ * </ul>
+ * </li>
+ * <li><strong>link</strong>=<em>textPattern</em>:
+ * Select the link (anchor) element which contains text matching the
+ * specified <em>pattern</em>.
+ * <ul class="first last simple">
+ * <li>link=The link text</li>
+ * </ul>
+ *
+ * </li>
+ *
+ * <li><strong>css</strong>=<em>cssSelectorSyntax</em>:
+ * Select the element using css selectors. Please refer to <a href="http://www.w3.org/TR/REC-CSS2/selector.html">CSS2 selectors</a>, <a href="http://www.w3.org/TR/2001/CR-css3-selectors-20011113/">CSS3 selectors</a> for more information. You can also check the TestCssLocators test in the selenium test suite for an example of usage, which is included in the downloaded selenium core package.
+ * <ul class="first last simple">
+ * <li>css=a[href="#id3"]</li>
+ * <li>css=span#firstChild + span</li>
+ * </ul>
+ * <p>Currently the css selector locator supports all css1, css2 and css3 selectors except namespace in css3, some pseudo classes(:nth-of-type, :nth-last-of-type, :first-of-type, :last-of-type, :only-of-type, :visited, :hover, :active, :focus, :indeterminate) and pseudo elements(::first-line, ::first-letter, ::selection, ::before, ::after). </p>
+ * </li>
+ * </ul>
+ *
+ * <p>
+ * Without an explicit locator prefix, Selenium uses the following default
+ * strategies:
+ * </p>
+ *
+ * <ul class="simple">
+ * <li><strong>dom</strong>, for locators starting with &quot;document.&quot;</li>
+ * <li><strong>xpath</strong>, for locators starting with &quot;//&quot;</li>
+ * <li><strong>identifier</strong>, otherwise</li>
+ * </ul>
+ *
+ * <h3><a name="element-filters">Element Filters</a></h3>
+ * <blockquote>
+ * <p>Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.</p>
+ * <p>Filters look much like locators, ie.</p>
+ * <blockquote>
+ * <em>filterType</em><strong>=</strong><em>argument</em></blockquote>
+ *
+ * <p>Supported element-filters are:</p>
+ * <p><strong>value=</strong><em>valuePattern</em></p>
+ * <blockquote>
+ * Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.</blockquote>
+ * <p><strong>index=</strong><em>index</em></p>
+ * <blockquote>
+ * Selects a single element based on its position in the list (offset from zero).</blockquote>
+ * </blockquote>
+ *
+ * <h3><a name="patterns"></a>String-match Patterns</h3>
+ *
+ * <p>
+ * Various Pattern syntaxes are available for matching string values:
+ * </p>
+ * <ul>
+ * <li><strong>glob:</strong><em>pattern</em>:
+ * Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a
+ * kind of limited regular-expression syntax typically used in command-line
+ * shells. In a glob pattern, "*" represents any sequence of characters, and "?"
+ * represents any single character. Glob patterns match against the entire
+ * string.</li>
+ * <li><strong>regexp:</strong><em>regexp</em>:
+ * Match a string using a regular-expression. The full power of JavaScript
+ * regular-expressions is available.</li>
+ * <li><strong>exact:</strong><em>string</em>:
+ *
+ * Match a string exactly, verbatim, without any of that fancy wildcard
+ * stuff.</li>
+ * </ul>
+ * <p>
+ * If no pattern prefix is specified, Selenium assumes that it's a "glob"
+ * pattern.
+ * </p>
+ */
this.browserbot = browserbot;
this.optionLocatorFactory = new OptionLocatorFactory();
+ // DGF for backwards compatibility
this.page = function() {
- return browserbot.getCurrentPage();
+ return browserbot;
};
this.defaultTimeout = Selenium.DEFAULT_TIMEOUT;
+ this.mouseSpeed = 10;
}
Selenium.DEFAULT_TIMEOUT = 30 * 1000;
+Selenium.DEFAULT_MOUSE_SPEED = 10;
+
+Selenium.decorateFunctionWithTimeout = function(f, timeout) {
+ if (f == null) {
+ return null;
+ }
+ var timeoutValue = parseInt(timeout);
+ if (isNaN(timeoutValue)) {
+ throw new SeleniumError("Timeout is not a number: '" + timeout + "'");
+ }
+ var now = new Date().getTime();
+ var timeoutTime = now + timeoutValue;
+ return function() {
+ if (new Date().getTime() > timeoutTime) {
+ throw new SeleniumError("Timed out after " + timeoutValue + "ms");
+ }
+ return f();
+ };
+}
-Selenium.createForWindow = function(window) {
+Selenium.createForWindow = function(window, proxyInjectionMode) {
if (!window.location) {
throw "error: not a window!";
}
- return new Selenium(BrowserBot.createForWindow(window));
+ return new Selenium(BrowserBot.createForWindow(window, proxyInjectionMode));
};
Selenium.prototype.reset = function() {
@@ -176,7 +198,7 @@ Selenium.prototype.reset = function() {
};
Selenium.prototype.doClick = function(locator) {
- /**
+ /**
* Clicks on a link, button, checkbox or radio button. If the click action
* causes a new page to load (like a link usually does), call
* waitForPageToLoad.
@@ -184,43 +206,69 @@ Selenium.prototype.doClick = function(locator) {
* @param locator an element locator
*
*/
- var element = this.page().findElement(locator);
- this.page().clickElement(element);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.clickElement(element);
+};
+
+Selenium.prototype.doDoubleClick = function(locator) {
+ /**
+ * Double clicks on a link, button, checkbox or radio button. If the double click action
+ * causes a new page to load (like a link usually does), call
+ * waitForPageToLoad.
+ *
+ * @param locator an element locator
+ *
+ */
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.doubleClickElement(element);
};
Selenium.prototype.doClickAt = function(locator, coordString) {
- /**
+ /**
* Clicks on a link, button, checkbox or radio button. If the click action
* causes a new page to load (like a link usually does), call
* waitForPageToLoad.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
+ * @param locator an element locator
+ * @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
+ * event relative to the element returned by the locator.
+ *
+ */
+ var element = this.browserbot.findElement(locator);
+ var clientXY = getClientXY(element, coordString)
+ this.browserbot.clickElement(element, clientXY[0], clientXY[1]);
+};
+
+Selenium.prototype.doDoubleClickAt = function(locator, coordString) {
+ /**
+ * Doubleclicks on a link, button, checkbox or radio button. If the action
+ * causes a new page to load (like a link usually does), call
+ * waitForPageToLoad.
*
* @param locator an element locator
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- this.page().clickElement(element, clientXY[0], clientXY[1]);
+ this.browserbot.doubleClickElement(element, clientXY[0], clientXY[1]);
};
Selenium.prototype.doFireEvent = function(locator, eventName) {
- /**
+ /**
* Explicitly simulate an event, to trigger the corresponding &quot;on<em>event</em>&quot;
* handler.
*
* @param locator an <a href="#locators">element locator</a>
* @param eventName the event name, e.g. "focus" or "blur"
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
triggerEvent(element, eventName, false);
};
Selenium.prototype.doKeyPress = function(locator, keySequence) {
- /**
+ /**
* Simulates a user pressing and releasing a key.
*
* @param locator an <a href="#locators">element locator</a>
@@ -228,12 +276,80 @@ Selenium.prototype.doKeyPress = function(locator, keySequence) {
* of the key to be pressed, normally the ASCII value of that key), or a single
* character. For example: "w", "\119".
*/
- var element = this.page().findElement(locator);
- triggerKeyEvent(element, 'keypress', keySequence, true);
+ var element = this.browserbot.findElement(locator);
+ triggerKeyEvent(element, 'keypress', keySequence, true,
+ this.browserbot.controlKeyDown,
+ this.browserbot.altKeyDown,
+ this.browserbot.shiftKeyDown,
+ this.browserbot.metaKeyDown);
+};
+
+Selenium.prototype.doShiftKeyDown = function() {
+ /**
+ * Press the shift key and hold it down until doShiftUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.shiftKeyDown = true;
+};
+
+Selenium.prototype.doShiftKeyUp = function() {
+ /**
+ * Release the shift key.
+ *
+ */
+ this.browserbot.shiftKeyDown = false;
+};
+
+Selenium.prototype.doMetaKeyDown = function() {
+ /**
+ * Press the meta key and hold it down until doMetaUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.metaKeyDown = true;
+};
+
+Selenium.prototype.doMetaKeyUp = function() {
+ /**
+ * Release the meta key.
+ *
+ */
+ this.browserbot.metaKeyDown = false;
+};
+
+Selenium.prototype.doAltKeyDown = function() {
+ /**
+ * Press the alt key and hold it down until doAltUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.altKeyDown = true;
+};
+
+Selenium.prototype.doAltKeyUp = function() {
+ /**
+ * Release the alt key.
+ *
+ */
+ this.browserbot.altKeyDown = false;
+};
+
+Selenium.prototype.doControlKeyDown = function() {
+ /**
+ * Press the control key and hold it down until doControlUp() is called or a new page is loaded.
+ *
+ */
+ this.browserbot.controlKeyDown = true;
+};
+
+Selenium.prototype.doControlKeyUp = function() {
+ /**
+ * Release the control key.
+ *
+ */
+ this.browserbot.controlKeyDown = false;
};
Selenium.prototype.doKeyDown = function(locator, keySequence) {
- /**
+ /**
* Simulates a user pressing a key (without releasing it yet).
*
* @param locator an <a href="#locators">element locator</a>
@@ -241,12 +357,16 @@ Selenium.prototype.doKeyDown = function(locator, keySequence) {
* of the key to be pressed, normally the ASCII value of that key), or a single
* character. For example: "w", "\119".
*/
- var element = this.page().findElement(locator);
- triggerKeyEvent(element, 'keydown', keySequence, true);
+ var element = this.browserbot.findElement(locator);
+ triggerKeyEvent(element, 'keydown', keySequence, true,
+ this.browserbot.controlKeyDown,
+ this.browserbot.altKeyDown,
+ this.browserbot.shiftKeyDown,
+ this.browserbot.metaKeyDown);
};
Selenium.prototype.doKeyUp = function(locator, keySequence) {
- /**
+ /**
* Simulates a user releasing a key.
*
* @param locator an <a href="#locators">element locator</a>
@@ -254,8 +374,12 @@ Selenium.prototype.doKeyUp = function(locator, keySequence) {
* of the key to be pressed, normally the ASCII value of that key), or a single
* character. For example: "w", "\119".
*/
- var element = this.page().findElement(locator);
- triggerKeyEvent(element, 'keyup', keySequence, true);
+ var element = this.browserbot.findElement(locator);
+ triggerKeyEvent(element, 'keyup', keySequence, true,
+ this.browserbot.controlKeyDown,
+ this.browserbot.altKeyDown,
+ this.browserbot.shiftKeyDown,
+ this.browserbot.metaKeyDown);
};
function getClientXY(element, coordString) {
@@ -278,13 +402,13 @@ function getClientXY(element, coordString) {
}
Selenium.prototype.doMouseOver = function(locator) {
- /**
+ /**
* Simulates a user hovering a mouse over the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mouseover', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mouseover', true);
};
Selenium.prototype.doMouseOut = function(locator) {
@@ -293,100 +417,91 @@ Selenium.prototype.doMouseOut = function(locator) {
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mouseout', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mouseout', true);
};
Selenium.prototype.doMouseDown = function(locator) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mousedown', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mousedown', true);
};
Selenium.prototype.doMouseDownAt = function(locator, coordString) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param locator an <a href="#locators">element locator</a>
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- triggerMouseEvent(element, 'mousedown', true, clientXY[0], clientXY[1]);
+ this.browserbot.triggerMouseEvent(element, 'mousedown', true, clientXY[0], clientXY[1]);
};
Selenium.prototype.doMouseUp = function(locator) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mouseup', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mouseup', true);
};
Selenium.prototype.doMouseUpAt = function(locator, coordString) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param locator an <a href="#locators">element locator</a>
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- triggerMouseEvent(element, 'mouseup', true, clientXY[0], clientXY[1]);
+ this.browserbot.triggerMouseEvent(element, 'mouseup', true, clientXY[0], clientXY[1]);
};
Selenium.prototype.doMouseMove = function(locator) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
* @param locator an <a href="#locators">element locator</a>
*/
- var element = this.page().findElement(locator);
- triggerMouseEvent(element, 'mousemove', true);
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true);
};
Selenium.prototype.doMouseMoveAt = function(locator, coordString) {
- /**
+ /**
* Simulates a user pressing the mouse button (without releasing it yet) on
* the specified element.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param locator an <a href="#locators">element locator</a>
* @param coordString specifies the x,y position (i.e. - 10,20) of the mouse
* event relative to the element returned by the locator.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var clientXY = getClientXY(element, coordString)
- triggerMouseEvent(element, 'mousemove', true, clientXY[0], clientXY[1]);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientXY[0], clientXY[1]);
};
Selenium.prototype.doType = function(locator, value) {
- /**
+ /**
* Sets the value of an input field, as though you typed it in.
*
* <p>Can also be used to set the value of combo boxes, check boxes, etc. In these cases,
@@ -395,13 +510,65 @@ Selenium.prototype.doType = function(locator, value) {
* @param locator an <a href="#locators">element locator</a>
* @param value the value to type
*/
- // TODO fail if it can't be typed into.
- var element = this.page().findElement(locator);
- this.page().replaceText(element, value);
+ if (this.browserbot.controlKeyDown || this.browserbot.altKeyDown || this.browserbot.metaKeyDown) {
+ throw new SeleniumError("type not supported immediately after call to controlKeyDown() or altKeyDown() or metaKeyDown()");
+ }
+ // TODO fail if it can't be typed into.
+ var element = this.browserbot.findElement(locator);
+ if (this.browserbot.shiftKeyDown) {
+ value = new String(value).toUpperCase();
+ }
+ this.browserbot.replaceText(element, value);
+};
+
+Selenium.prototype.doTypeKeys = function(locator, value) {
+ /**
+ * Simulates keystroke events on the specified element, as though you typed the value key-by-key.
+ *
+ * <p>This is a convenience method for calling keyDown, keyUp, keyPress for every character in the specified string;
+ * this is useful for dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.</p>
+ *
+ * <p>Unlike the simple "type" command, which forces the specified value into the page directly, this command
+ * may or may not have any visible effect, even in cases where typing keys would normally have a visible effect.
+ * For example, if you use "typeKeys" on a form element, you may or may not see the results of what you typed in
+ * the field.</p>
+ * <p>In some cases, you may need to use the simple "type" command to set the value of the field and then the "typeKeys" command to
+ * send the keystroke events corresponding to what you just typed.</p>
+ *
+ * @param locator an <a href="#locators">element locator</a>
+ * @param value the value to type
+ */
+ var keys = new String(value).split("");
+ for (var i = 0; i < keys.length; i++) {
+ var c = keys[i];
+ this.doKeyDown(locator, c);
+ this.doKeyUp(locator, c);
+ this.doKeyPress(locator, c);
+ }
+};
+
+Selenium.prototype.doSetSpeed = function(value) {
+ /**
+ * Set execution speed (i.e., set the millisecond length of a delay which will follow each selenium operation). By default, there is no such delay, i.e.,
+ * the delay is 0 milliseconds.
+ *
+ * @param value the number of milliseconds to pause after operation
+ */
+ throw new SeleniumError("this operation is only implemented in selenium-rc, and should never result in a request making it across the wire");
+};
+
+Selenium.prototype.doGetSpeed = function() {
+ /**
+ * Get execution speed (i.e., get the millisecond length of the delay following each selenium operation). By default, there is no such delay, i.e.,
+ * the delay is 0 milliseconds.
+ *
+ * See also setSpeed.
+ */
+ throw new SeleniumError("this operation is only implemented in selenium-rc, and should never result in a request making it across the wire");
};
Selenium.prototype.findToggleButton = function(locator) {
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.checked == null) {
Assert.fail("Element " + locator + " is not a toggle-button.");
}
@@ -409,7 +576,7 @@ Selenium.prototype.findToggleButton = function(locator) {
}
Selenium.prototype.doCheck = function(locator) {
- /**
+ /**
* Check a toggle-button (checkbox/radio)
*
* @param locator an <a href="#locators">element locator</a>
@@ -418,7 +585,7 @@ Selenium.prototype.doCheck = function(locator) {
};
Selenium.prototype.doUncheck = function(locator) {
- /**
+ /**
* Uncheck a toggle-button (checkbox/radio)
*
* @param locator an <a href="#locators">element locator</a>
@@ -427,7 +594,7 @@ Selenium.prototype.doUncheck = function(locator) {
};
Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
- /**
+ /**
* Select an option from a drop-down using an option locator.
*
* <p>
@@ -436,37 +603,37 @@ Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
* that the selected option satisfies a specification). There are several
* forms of Select Option Locator.
* </p>
- * <dl>
- * <dt><strong>label</strong>=<em>labelPattern</em></dt>
- * <dd>matches options based on their labels, i.e. the visible text. (This
+ * <ul>
+ * <li><strong>label</strong>=<em>labelPattern</em>:
+ * matches options based on their labels, i.e. the visible text. (This
* is the default.)
* <ul class="first last simple">
* <li>label=regexp:^[Oo]ther</li>
* </ul>
- * </dd>
- * <dt><strong>value</strong>=<em>valuePattern</em></dt>
- * <dd>matches options based on their values.
+ * </li>
+ * <li><strong>value</strong>=<em>valuePattern</em>:
+ * matches options based on their values.
* <ul class="first last simple">
* <li>value=other</li>
* </ul>
*
*
- * </dd>
- * <dt><strong>id</strong>=<em>id</em></dt>
+ * </li>
+ * <li><strong>id</strong>=<em>id</em>:
*
- * <dd>matches options based on their ids.
+ * matches options based on their ids.
* <ul class="first last simple">
* <li>id=option1</li>
* </ul>
- * </dd>
- * <dt><strong>index</strong>=<em>index</em></dt>
- * <dd>matches an option based on its index (offset from zero).
+ * </li>
+ * <li><strong>index</strong>=<em>index</em>:
+ * matches an option based on its index (offset from zero).
* <ul class="first last simple">
*
* <li>index=2</li>
* </ul>
- * </dd>
- * </dl>
+ * </li>
+ * </ul>
* <p>
* If no option locator prefix is provided, the default behaviour is to match on <strong>label</strong>.
* </p>
@@ -475,15 +642,17 @@ Selenium.prototype.doSelect = function(selectLocator, optionLocator) {
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @param optionLocator an option locator (a label by default)
*/
- var element = this.page().findElement(selectLocator);
+ var element = this.browserbot.findElement(selectLocator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
var option = locator.findOption(element);
- this.page().selectOption(element, option);
+ this.browserbot.selectOption(element, option);
};
+
+
Selenium.prototype.doAddSelection = function(locator, optionLocator) {
/**
* Add a selection to the set of selected options in a multi-select element using an option locator.
@@ -493,13 +662,13 @@ Selenium.prototype.doAddSelection = function(locator, optionLocator) {
* @param locator an <a href="#locators">element locator</a> identifying a multi-select box
* @param optionLocator an option locator (a label by default)
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
var option = locator.findOption(element);
- this.page().addSelection(element, option);
+ this.browserbot.addSelection(element, option);
};
Selenium.prototype.doRemoveSelection = function(locator, optionLocator) {
@@ -512,45 +681,39 @@ Selenium.prototype.doRemoveSelection = function(locator, optionLocator) {
* @param optionLocator an option locator (a label by default)
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
var locator = this.optionLocatorFactory.fromLocatorString(optionLocator);
var option = locator.findOption(element);
- this.page().removeSelection(element, option);
+ this.browserbot.removeSelection(element, option);
};
+Selenium.prototype.doRemoveAllSelections = function(locator) {
+ /**
+ * Unselects all of the selected options in a multi-select element.
+ *
+ * @param locator an <a href="#locators">element locator</a> identifying a multi-select box
+ */
+ var element = this.browserbot.findElement(locator);
+ if (!("options" in element)) {
+ throw new SeleniumError("Specified element is not a Select (has no options)");
+ }
+ for (var i = 0; i < element.options.length; i++) {
+ this.browserbot.removeSelection(element, element.options[i]);
+ }
+}
+
Selenium.prototype.doSubmit = function(formLocator) {
- /**
+ /**
* Submit the specified form. This is particularly useful for forms without
* submit buttons, e.g. single-input "Search" forms.
*
* @param formLocator an <a href="#locators">element locator</a> for the form you want to submit
*/
- var form = this.page().findElement(formLocator);
- var actuallySubmit = true;
- if (form.onsubmit) {
- if (browserVersion.isHTA) {
- // run the code in the correct window so alerts are handled correctly even in HTA mode
- var win = this.browserbot.getCurrentWindow();
- var now = new Date().getTime();
- var marker = 'marker' + now;
- win[marker] = form;
- win.setTimeout("var actuallySubmit = "+marker+".onsubmit(); if (actuallySubmit) { "+marker+".submit(); };", 0);
- // pause for at least 20ms for this command to run
- testLoop.waitForCondition = function () {
- return new Date().getTime() > (now + 20);
- }
- } else {
- actuallySubmit = form.onsubmit();
- if (actuallySubmit) {
- form.submit();
- }
- }
- } else {
- form.submit();
- }
+ var form = this.browserbot.findElement(formLocator);
+ return this.browserbot.submit(form);
};
@@ -558,11 +721,11 @@ Selenium.prototype.makePageLoadCondition = function(timeout) {
if (timeout == null) {
timeout = this.defaultTimeout;
}
- return decorateFunctionWithTimeout(this._isNewPageLoaded.bind(this), timeout);
+ return Selenium.decorateFunctionWithTimeout(fnBind(this._isNewPageLoaded, this), timeout);
};
Selenium.prototype.doOpen = function(url) {
- /**
+ /**
* Opens an URL in the test frame. This accepts both relative and absolute
* URLs.
*
@@ -580,35 +743,70 @@ Selenium.prototype.doOpen = function(url) {
return this.makePageLoadCondition();
};
+Selenium.prototype.doOpenWindow = function(url, windowID) {
+ /**
+ * Opens a popup window (if a window with that ID isn't already open).
+ * After opening the window, you'll need to select it using the selectWindow
+ * command.
+ *
+ * <p>This command can also be a useful workaround for bug SEL-339. In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+ * In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+ * an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+ *
+ * @param url the URL to open, which can be blank
+ * @param windowID the JavaScript window ID of the window to select
+ */
+ this.browserbot.openWindow(url, windowID);
+};
+
Selenium.prototype.doSelectWindow = function(windowID) {
- /**
+ /**
* Selects a popup window; once a popup window has been selected, all
- * commands go to that window. To select the main window again, use "null"
+ * commands go to that window. To select the main window again, use null
* as the target.
*
+ * <p>Selenium has several strategies for finding the window object referred to by the "windowID" parameter.</p>
+ *
+ * <p>1.) if windowID is null, then it is assumed the user is referring to the original window instantiated by the browser).</p>
+ * <p>2.) if the value of the "windowID" parameter is a JavaScript variable name in the current application window, then it is assumed
+ * that this variable contains the return value from a call to the JavaScript window.open() method.</p>
+ * <p>3.) Otherwise, selenium looks in a hash it maintains that maps string names to window objects. Each of these string
+ * names matches the second parameter "windowName" past to the JavaScript method window.open(url, windowName, windowFeatures, replaceFlag)
+ * (which selenium intercepts).</p>
+ *
+ * <p>If you're having trouble figuring out what is the name of a window that you want to manipulate, look at the selenium log messages
+ * which identify the names of windows created via window.open (and therefore intercepted by selenium). You will see messages
+ * like the following for each window as it is opened:</p>
+ *
+ * <p><code>debug: window.open call intercepted; window ID (which you can use with selectWindow()) is "myNewWindow"</code></p>
+ *
+ * <p>In some cases, Selenium will be unable to intercept a call to window.open (if the call occurs during or before the "onLoad" event, for example).
+ * (This is bug SEL-339.) In those cases, you can force Selenium to notice the open window's name by using the Selenium openWindow command, using
+ * an empty (blank) url, like this: openWindow("", "myFunnyWindow").</p>
+ *
* @param windowID the JavaScript window ID of the window to select
*/
this.browserbot.selectWindow(windowID);
};
Selenium.prototype.doSelectFrame = function(locator) {
- /**
- * Selects a frame within the current window. (You may invoke this command
- * multiple times to select nested frames.) To select the parent frame, use
- * "relative=parent" as a locator; to select the top frame, use "relative=top".
- *
- * <p>You may also use a DOM expression to identify the frame you want directly,
- * like this: <code>dom=frames["main"].frames["subframe"]</code></p>
- *
- * @param locator an <a href="#locators">element locator</a> identifying a frame or iframe
- */
+ /**
+ * Selects a frame within the current window. (You may invoke this command
+ * multiple times to select nested frames.) To select the parent frame, use
+ * "relative=parent" as a locator; to select the top frame, use "relative=top".
+ *
+ * <p>You may also use a DOM expression to identify the frame you want directly,
+ * like this: <code>dom=frames["main"].frames["subframe"]</code></p>
+ *
+ * @param locator an <a href="#locators">element locator</a> identifying a frame or iframe
+ */
this.browserbot.selectFrame(locator);
};
Selenium.prototype.getLogMessages = function() {
- /**
+ /**
* Return the contents of the log.
- *
+ *
* <p>This is a placeholder intended to make the code generator make this API
* available to clients. The selenium server will intercept this call, however,
* and return its recordkeeping of log messages since the last call to this API.
@@ -617,9 +815,9 @@ Selenium.prototype.getLogMessages = function() {
* <p>The reason I opted for a servercentric solution is to be able to support
* multiple frames served from different domains, which would break a
* centralized JavaScript logging mechanism under some conditions.</p>
- *
+ *
* @return string all log messages seen since the last call to this API
- */
+ */
return "getLogMessages should be implemented in the selenium server";
};
@@ -678,52 +876,68 @@ Selenium.prototype.getWhetherThisFrameMatchFrameExpression = function(currentFra
return false;
};
-Selenium.prototype.doWaitForPopUp = function(windowID, timeout) {
- /**
- * Waits for a popup window to appear and load up.
- *
- * @param windowID the JavaScript window ID of the window that will appear
- * @param timeout a timeout in milliseconds, after which the action will return with an error
- */
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
+Selenium.prototype.getWhetherThisWindowMatchWindowExpression = function(currentWindowString, target) {
+ /**
+ * Determine whether currentWindowString plus target identify the window containing this running code.
+ *
+ * <p>This is useful in proxy injection mode, where this code runs in every
+ * browser frame and window, and sometimes the selenium server needs to identify
+ * the "current" window. In this case, when the test calls selectWindow, this
+ * routine is called for each window to figure out which one has been selected.
+ * The selected window will return true, while all others will return false.</p>
+ *
+ * @param currentWindowString starting window
+ * @param target new window (which might be relative to the current one, e.g., "_parent")
+ * @return boolean true if the new window is this code's window
+ */
+ if (window.opener!=null && window.opener[target]!=null && window.opener[target]==window) {
+ return true;
+ }
+ return false;
+};
+Selenium.prototype.doWaitForPopUp = function(windowID, timeout) {
+ /**
+ * Waits for a popup window to appear and load up.
+ *
+ * @param windowID the JavaScript window ID of the window that will appear
+ * @param timeout a timeout in milliseconds, after which the action will return with an error
+ */
var popupLoadedPredicate = function () {
var targetWindow = selenium.browserbot.getWindowByName(windowID, true);
if (!targetWindow) return false;
if (!targetWindow.location) return false;
if ("about:blank" == targetWindow.location) return false;
if (browserVersion.isKonqueror) {
- if ("/" == targetWindow.location.href) {
- // apparently Konqueror uses this as the temporary location, instead of about:blank
- return false;
- }
+ if ("/" == targetWindow.location.href) {
+ // apparently Konqueror uses this as the temporary location, instead of about:blank
+ return false;
+ }
}
if (browserVersion.isSafari) {
- if(targetWindow.location.href == selenium.browserbot.buttonWindow.location.href) {
- // Apparently Safari uses this as the temporary location, instead of about:blank
- // what a world!
- LOG.debug("DGF what a world!");
- return false;
- }
+ if(targetWindow.location.href == selenium.browserbot.buttonWindow.location.href) {
+ // Apparently Safari uses this as the temporary location, instead of about:blank
+ // what a world!
+ LOG.debug("DGF what a world!");
+ return false;
+ }
}
if (!targetWindow.document) return false;
if (!selenium.browserbot.getCurrentWindow().document.readyState) {
- // This is Firefox, with no readyState extension
- return true;
- }
+ // This is Firefox, with no readyState extension
+ return true;
+ }
if ('complete' != targetWindow.document.readyState) return false;
return true;
};
- return decorateFunctionWithTimeout(popupLoadedPredicate, timeout);
+ return Selenium.decorateFunctionWithTimeout(popupLoadedPredicate, timeout);
}
Selenium.prototype.doWaitForPopUp.dontCheckAlertsAndConfirms = true;
Selenium.prototype.doChooseCancelOnNextConfirmation = function() {
- /**
+ /**
* By default, Selenium's overridden window.confirm() function will
* return true, as if the user had manually clicked OK. After running
* this command, the next call to confirm() will return false, as if
@@ -735,7 +949,7 @@ Selenium.prototype.doChooseCancelOnNextConfirmation = function() {
Selenium.prototype.doAnswerOnNextPrompt = function(answer) {
- /**
+ /**
* Instructs Selenium to return the specified answer string in response to
* the next JavaScript prompt [window.prompt()].
*
@@ -750,7 +964,7 @@ Selenium.prototype.doGoBack = function() {
* Simulates the user clicking the "back" button on their browser.
*
*/
- this.page().goBack();
+ this.browserbot.goBack();
};
Selenium.prototype.doRefresh = function() {
@@ -758,7 +972,7 @@ Selenium.prototype.doRefresh = function() {
* Simulates the user clicking the "Refresh" button on their browser.
*
*/
- this.page().refresh();
+ this.browserbot.refresh();
};
Selenium.prototype.doClose = function() {
@@ -766,7 +980,7 @@ Selenium.prototype.doClose = function() {
* Simulates the user clicking the "close" button in the titlebar of a popup
* window or tab.
*/
- this.page().close();
+ this.browserbot.close();
};
Selenium.prototype.ensureNoUnhandledPopups = function() {
@@ -814,7 +1028,7 @@ Selenium.prototype.isConfirmationPresent = function() {
return this.browserbot.hasConfirmations();
};
Selenium.prototype.getAlert = function() {
- /**
+ /**
* Retrieves the message of a JavaScript alert generated during the previous action, or fail if there were no alerts.
*
* <p>Getting an alert has the same effect as manually clicking OK. If an
@@ -837,7 +1051,7 @@ Selenium.prototype.getAlert = function() {
Selenium.prototype.getAlert.dontCheckAlertsAndConfirms = true;
Selenium.prototype.getConfirmation = function() {
- /**
+ /**
* Retrieves the message of a JavaScript confirmation dialog generated during
* the previous action.
*
@@ -870,7 +1084,7 @@ Selenium.prototype.getConfirmation = function() {
Selenium.prototype.getConfirmation.dontCheckAlertsAndConfirms = true;
Selenium.prototype.getPrompt = function() {
- /**
+ /**
* Retrieves the message of a JavaScript question prompt dialog generated during
* the previous action.
*
@@ -893,28 +1107,28 @@ Selenium.prototype.getPrompt = function() {
};
Selenium.prototype.getLocation = function() {
- /** Gets the absolute URL of the current page.
+ /** Gets the absolute URL of the current page.
*
* @return string the absolute URL of the current page
*/
- return this.page().getCurrentWindow().location;
+ return this.browserbot.getCurrentWindow().location;
};
Selenium.prototype.getTitle = function() {
- /** Gets the title of the current page.
+ /** Gets the title of the current page.
*
* @return string the title of the current page
*/
- return this.page().getTitle();
+ return this.browserbot.getTitle();
};
Selenium.prototype.getBodyText = function() {
- /**
- * Gets the entire text of the page.
- * @return string the entire text of the page
- */
- return this.page().bodyText();
+ /**
+ * Gets the entire text of the page.
+ * @return string the entire text of the page
+ */
+ return this.browserbot.bodyText();
};
@@ -927,12 +1141,12 @@ Selenium.prototype.getValue = function(locator) {
* @param locator an <a href="#locators">element locator</a>
* @return string the element value, or "on/off" for checkbox/radio elements
*/
- var element = this.page().findElement(locator)
+ var element = this.browserbot.findElement(locator)
return getInputValue(element).trim();
}
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
@@ -941,12 +1155,22 @@ Selenium.prototype.getText = function(locator) {
* @param locator an <a href="#locators">element locator</a>
* @return string the text of the element
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
return getText(element).trim();
};
+Selenium.prototype.doHighlight = function(locator) {
+ /**
+ * Briefly changes the backgroundColor of the specified element yellow. Useful for debugging.
+ *
+ * @param locator an <a href="#locators">element locator</a>
+ */
+ var element = this.browserbot.findElement(locator);
+ this.browserbot.highlight(element, true);
+};
+
Selenium.prototype.getEval = function(script) {
- /** Gets the result of evaluating the specified JavaScript snippet. The snippet may
+ /** Gets the result of evaluating the specified JavaScript snippet. The snippet may
* have multiple lines, but only the result of the last line will be returned.
*
* <p>Note that, by default, the snippet will run in the context of the "selenium"
@@ -956,28 +1180,28 @@ Selenium.prototype.getEval = function(script) {
* <p>If you need a reference to the window of your application, you can refer
* to <code>this.browserbot.getCurrentWindow()</code> and if you need to use
* a locator to refer to a single element in your application page, you can
- * use <code>this.page().findElement("foo")</code> where "foo" is your locator.</p>
+ * use <code>this.browserbot.findElement("foo")</code> where "foo" is your locator.</p>
*
* @param script the JavaScript snippet to run
* @return string the results of evaluating the snippet
*/
try {
- var result = eval(script);
- // Selenium RC doesn't allow returning null
- if (null == result) return "null";
- return result;
+ var result = eval(script);
+ // Selenium RC doesn't allow returning null
+ if (null == result) return "null";
+ return result;
} catch (e) {
- throw new SeleniumError("Threw an exception: " + e.message);
+ throw new SeleniumError("Threw an exception: " + e.message);
}
};
Selenium.prototype.isChecked = function(locator) {
- /**
+ /**
* Gets whether a toggle-button (checkbox/radio) is checked. Fails if the specified element doesn't exist or isn't a toggle-button.
* @param locator an <a href="#locators">element locator</a> pointing to a checkbox or radio button
* @return boolean true if the checkbox is checked, false otherwise
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.checked == null) {
throw new SeleniumError("Element " + locator + " is not a toggle-button.");
}
@@ -985,7 +1209,7 @@ Selenium.prototype.isChecked = function(locator) {
};
Selenium.prototype.getTable = function(tableCellAddress) {
- /**
+ /**
* Gets the text from a cell of a table. The cellAddress syntax
* tableLocator.row.column, where row and column start at 0.
*
@@ -1006,7 +1230,7 @@ Selenium.prototype.getTable = function(tableCellAddress) {
row = pieces[2];
col = pieces[3];
- var table = this.page().findElement(tableName);
+ var table = this.browserbot.findElement(tableName);
if (row > table.rows.length) {
Assert.fail("Cannot access row " + row + " - table has " + table.rows.length + " rows");
}
@@ -1017,7 +1241,7 @@ Selenium.prototype.getTable = function(tableCellAddress) {
actualContent = getText(table.rows[row].cells[col]);
return actualContent.trim();
}
- return null;
+ return null;
};
Selenium.prototype.getSelectedLabels = function(selectLocator) {
@@ -1098,7 +1322,7 @@ Selenium.prototype.isSomethingSelected = function(selectLocator) {
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @return boolean true if some option has been selected, false otherwise
*/
- var element = this.page().findElement(selectLocator);
+ var element = this.browserbot.findElement(selectLocator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
@@ -1115,12 +1339,12 @@ Selenium.prototype.isSomethingSelected = function(selectLocator) {
}
Selenium.prototype.findSelectedOptionProperties = function(locator, property) {
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (!("options" in element)) {
throw new SeleniumError("Specified element is not a Select (has no options)");
}
- var selectedOptions = [];
+ var selectedOptions = [];
for (var i = 0; i < element.options.length; i++) {
if (element.options[i].selected)
@@ -1145,17 +1369,17 @@ Selenium.prototype.findSelectedOptionProperty = function(locator, property) {
}
Selenium.prototype.getSelectOptions = function(selectLocator) {
- /** Gets all option labels in the specified select drop-down.
+ /** Gets all option labels in the specified select drop-down.
*
* @param selectLocator an <a href="#locators">element locator</a> identifying a drop-down menu
* @return string[] an array of all option labels in the specified select drop-down
*/
- var element = this.page().findElement(selectLocator);
+ var element = this.browserbot.findElement(selectLocator);
var selectOptions = [];
for (var i = 0; i < element.options.length; i++) {
- var option = element.options[i].text.replace(/,/g, "\\,");
+ var option = element.options[i].text.replace(/,/g, "\\,");
selectOptions.push(option);
}
@@ -1164,49 +1388,49 @@ Selenium.prototype.getSelectOptions = function(selectLocator) {
Selenium.prototype.getAttribute = function(attributeLocator) {
- /**
+ /**
* Gets the value of an element attribute.
*
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
- *
* @param attributeLocator an element locator followed by an @ sign and then the name of the attribute, e.g. "foo@bar"
* @return string the value of the specified attribute
*/
- var result = this.page().findAttribute(attributeLocator);
+ var result = this.browserbot.findAttribute(attributeLocator);
if (result == null) {
- throw new SeleniumError("Could not find element attribute: " + attributeLocator);
- }
+ throw new SeleniumError("Could not find element attribute: " + attributeLocator);
+ }
return result;
};
Selenium.prototype.isTextPresent = function(pattern) {
- /**
+ /**
* Verifies that the specified text pattern appears somewhere on the rendered page shown to the user.
* @param pattern a <a href="#patterns">pattern</a> to match with the text of the page
* @return boolean true if the pattern matches the text, false otherwise
*/
- var allText = this.page().bodyText();
+ var allText = this.browserbot.bodyText();
var patternMatcher = new PatternMatcher(pattern);
if (patternMatcher.strategy == PatternMatcher.strategies.glob) {
- patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
+ if (pattern.indexOf("glob:")==0) {
+ pattern = pattern.substring("glob:".length); // strip off "glob:"
+ }
+ patternMatcher.matcher = new PatternMatcher.strategies.globContains(pattern);
}
else if (patternMatcher.strategy == PatternMatcher.strategies.exact) {
- pattern = pattern.substring("exact:".length); // strip off "exact:"
- return allText.indexOf(pattern) != -1;
+ pattern = pattern.substring("exact:".length); // strip off "exact:"
+ return allText.indexOf(pattern) != -1;
}
return patternMatcher.matches(allText);
};
Selenium.prototype.isElementPresent = function(locator) {
- /**
+ /**
* Verifies that the specified element is somewhere on the page.
* @param locator an <a href="#locators">element locator</a>
* @return boolean true if the element is present, false otherwise
*/
try {
- this.page().findElement(locator);
+ this.browserbot.findElement(locator);
} catch (e) {
return false;
}
@@ -1214,7 +1438,7 @@ Selenium.prototype.isElementPresent = function(locator) {
};
Selenium.prototype.isVisible = function(locator) {
- /**
+ /**
* Determines if the specified element is visible. An
* element can be rendered invisible by setting the CSS "visibility"
* property to "hidden", or the "display" property to "none", either for the
@@ -1225,7 +1449,7 @@ Selenium.prototype.isVisible = function(locator) {
* @return boolean true if the specified element is visible, false otherwise
*/
var element;
- element = this.page().findElement(locator);
+ element = this.browserbot.findElement(locator);
var visibility = this.findEffectiveStyleProperty(element, "visibility");
var _isDisplayed = this._isDisplayed(element);
return (visibility != "hidden" && _isDisplayed);
@@ -1269,14 +1493,14 @@ Selenium.prototype.findEffectiveStyle = function(element) {
};
Selenium.prototype.isEditable = function(locator) {
- /**
+ /**
* Determines whether the specified input element is editable, ie hasn't been disabled.
* This method will fail if the specified element isn't an input element.
*
* @param locator an <a href="#locators">element locator</a>
* @return boolean true if the input element is editable, false otherwise
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.value == undefined) {
Assert.fail("Element " + locator + " is not an input.");
}
@@ -1284,134 +1508,184 @@ Selenium.prototype.isEditable = function(locator) {
};
Selenium.prototype.getAllButtons = function() {
- /** Returns the IDs of all buttons on the page.
+ /** Returns the IDs of all buttons on the page.
*
* <p>If a given button has no ID, it will appear as "" in this array.</p>
*
* @return string[] the IDs of all buttons on the page
*/
- return this.page().getAllButtons();
+ return this.browserbot.getAllButtons();
};
Selenium.prototype.getAllLinks = function() {
- /** Returns the IDs of all links on the page.
+ /** Returns the IDs of all links on the page.
*
* <p>If a given link has no ID, it will appear as "" in this array.</p>
*
* @return string[] the IDs of all links on the page
*/
- return this.page().getAllLinks();
+ return this.browserbot.getAllLinks();
};
Selenium.prototype.getAllFields = function() {
- /** Returns the IDs of all input fields on the page.
+ /** Returns the IDs of all input fields on the page.
*
* <p>If a given field has no ID, it will appear as "" in this array.</p>
*
* @return string[] the IDs of all field on the page
*/
- return this.page().getAllFields();
-};
-
-Selenium.prototype._getTestAppParentOfAllWindows = function() {
- /** Returns the IDs of all input fields on the page.
- *
- * <p>If a given field has no ID, it will appear as "" in this array.</p>
- *
- * @return string[] the IDs of all field on the page
- */
- if (this.browserbot.getCurrentWindow().opener!=null) {
- return this.browserbot.getCurrentWindow().opener;
- }
- if (this.browserbot.buttonWindow!=null) {
- return this.browserbot.buttonWindow;
- }
- return top; // apparently we are in proxy injection mode
+ return this.browserbot.getAllFields();
};
Selenium.prototype.getAttributeFromAllWindows = function(attributeName) {
- /** Returns every instance of some attribute from all known windows.
- *
- * @param attributeName name of an attribute on the windows
- * @return string[] the set of values of this attribute from all known windows.
- */
- var attributes = new Array();
- var testAppParentOfAllWindows = this._getTestAppParentOfAllWindows();
- attributes.push(eval("testAppParentOfAllWindows." + attributeName));
- var selenium = testAppParentOfAllWindows.selenium==null ? testAppParentOfAllWindows.parent.selenium : testAppParentOfAllWindows.selenium;
- for (windowName in selenium.browserbot.openedWindows)
- {
- attributes.push(eval("selenium.browserbot.openedWindows[windowName]." + attributeName));
- }
- return attributes;
+ /** Returns every instance of some attribute from all known windows.
+ *
+ * @param attributeName name of an attribute on the windows
+ * @return string[] the set of values of this attribute from all known windows.
+ */
+ var attributes = new Array();
+
+ var win = selenium.browserbot.topWindow;
+
+ // DGF normally you should use []s instead of eval "win."+attributeName
+ // but in this case, attributeName may contain dots (e.g. document.title)
+ // in that case, we have no choice but to use eval...
+ attributes.push(eval("win."+attributeName));
+ for (var windowName in this.browserbot.openedWindows)
+ {
+ try {
+ win = selenium.browserbot.openedWindows[windowName];
+ attributes.push(eval("win."+attributeName));
+ } catch (e) {} // DGF If we miss one... meh. It's probably closed or inaccessible anyway.
+ }
+ return attributes;
};
Selenium.prototype.findWindow = function(soughtAfterWindowPropertyValue) {
- var testAppParentOfAllWindows = this._getTestAppParentOfAllWindows();
var targetPropertyName = "name";
if (soughtAfterWindowPropertyValue.match("^title=")) {
- targetPropertyName = "document.title";
+ targetPropertyName = "document.title";
soughtAfterWindowPropertyValue = soughtAfterWindowPropertyValue.replace(/^title=/, "");
}
else {
- // matching "name":
- // If we are not in proxy injection mode, then the top-level test window will be named myiframe.
+ // matching "name":
+ // If we are not in proxy injection mode, then the top-level test window will be named myiframe.
// But as far as the interface goes, we are expected to match a blank string to this window, if
// we are searching with respect to the widow name.
// So make a special case so that this logic will work:
if (PatternMatcher.matches(soughtAfterWindowPropertyValue, "")) {
- return this.browserbot.getCurrentWindow();
+ return this.browserbot.getCurrentWindow();
}
}
- if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("testAppParentOfAllWindows." + targetPropertyName))) {
- return testAppParentOfAllWindows;
+ // DGF normally you should use []s instead of eval "win."+attributeName
+ // but in this case, attributeName may contain dots (e.g. document.title)
+ // in that case, we have no choice but to use eval...
+ if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("this.browserbot.topWindow." + targetPropertyName))) {
+ return this.browserbot.topWindow;
}
for (windowName in selenium.browserbot.openedWindows) {
- var openedWindow = selenium.browserbot.openedWindows[windowName];
- if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("openedWindow." + targetPropertyName))) {
- return openedWindow;
+ var openedWindow = selenium.browserbot.openedWindows[windowName];
+ if (PatternMatcher.matches(soughtAfterWindowPropertyValue, eval("openedWindow." + targetPropertyName))) {
+ return openedWindow;
}
}
throw new SeleniumError("could not find window with property " + targetPropertyName + " matching " + soughtAfterWindowPropertyValue);
};
Selenium.prototype.doDragdrop = function(locator, movementsString) {
- /** Drags an element a certain distance and then drops it
- * Beware of http://jira.openqa.org/browse/SEL-280, which will lead some event handlers to
- * get null event arguments. Read the bug for more details, including a workaround.
+/** deprecated - use dragAndDrop instead
*
- * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
* @param locator an element locator
+ * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
*/
- var element = this.page().findElement(locator);
- var clientStartXY = getClientXY(element)
- var clientStartX = clientStartXY[0];
- var clientStartY = clientStartXY[1];
-
- var movements = movementsString.split(/,/);
- var movementX = Number(movements[0]);
- var movementY = Number(movements[1]);
+ this.doDragAndDrop(locator, movementsString);
+};
- var clientFinishX = ((clientStartX + movementX) < 0) ? 0 : (clientStartX + movementX);
- var clientFinishY = ((clientStartY + movementY) < 0) ? 0 : (clientStartY + movementY);
+Selenium.prototype.doSetMouseSpeed = function(pixels) {
+ /** Configure the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+ * <p>Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
+ * in between the start location and the end location; that can be very slow, and may
+ * cause some browsers to force the JavaScript to timeout.</p>
+ *
+ * <p>If the mouse speed is greater than the distance between the two dragged objects, we'll
+ * just send one "mousemove" at the start location and then one final one at the end location.</p>
+ * @param pixels the number of pixels between "mousemove" events
+ */
+ this.mouseSpeed = pixels;
+}
+
+Selenium.prototype.getMouseSpeed = function() {
+ /** Returns the number of pixels between "mousemove" events during dragAndDrop commands (default=10).
+ *
+ * @return number the number of pixels between "mousemove" events during dragAndDrop commands (default=10)
+ */
+ this.mouseSpeed = pixels;
+}
- var movementXincrement = (movementX > 0) ? 1 : -1;
- var movementYincrement = (movementY > 0) ? 1 : -1;
- triggerMouseEvent(element, 'mousedown', true, clientStartX, clientStartY);
- var clientX = clientStartX;
- var clientY = clientStartY;
- while ((clientX != clientFinishX) || (clientY != clientFinishY)) {
- if (clientX != clientFinishX) {
- clientX += movementXincrement;
- }
- if (clientY != clientFinishY) {
- clientY += movementYincrement;
- }
- triggerMouseEvent(element, 'mousemove', true, clientX, clientY);
+Selenium.prototype.doDragAndDrop = function(locator, movementsString) {
+ /** Drags an element a certain distance and then drops it
+ * @param locator an element locator
+ * @param movementsString offset in pixels from the current location to which the element should be moved, e.g., "+70,-300"
+ */
+ var element = this.browserbot.findElement(locator);
+ var clientStartXY = getClientXY(element)
+ var clientStartX = clientStartXY[0];
+ var clientStartY = clientStartXY[1];
+
+ var movements = movementsString.split(/,/);
+ var movementX = Number(movements[0]);
+ var movementY = Number(movements[1]);
+
+ var clientFinishX = ((clientStartX + movementX) < 0) ? 0 : (clientStartX + movementX);
+ var clientFinishY = ((clientStartY + movementY) < 0) ? 0 : (clientStartY + movementY);
+
+ var mouseSpeed = this.mouseSpeed;
+ var move = function(current, dest) {
+ if (current == dest) return current;
+ if (Math.abs(current - dest) < mouseSpeed) return dest;
+ return (current < dest) ? current + mouseSpeed : current - mouseSpeed;
+ }
+
+ this.browserbot.triggerMouseEvent(element, 'mousedown', true, clientStartX, clientStartY);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientStartX, clientStartY);
+ var clientX = clientStartX;
+ var clientY = clientStartY;
+
+ while ((clientX != clientFinishX) || (clientY != clientFinishY)) {
+ clientX = move(clientX, clientFinishX);
+ clientY = move(clientY, clientFinishY);
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientX, clientY);
}
- triggerMouseEvent(element, 'mouseup', true, clientFinishX, clientFinishY);
+
+ this.browserbot.triggerMouseEvent(element, 'mousemove', true, clientFinishX, clientFinishY);
+ this.browserbot.triggerMouseEvent(element, 'mouseup', true, clientFinishX, clientFinishY);
+};
+
+Selenium.prototype.doDragAndDropToObject = function(locatorOfObjectToBeDragged, locatorOfDragDestinationObject) {
+/** Drags an element and drops it on another element
+ *
+ * @param locatorOfObjectToBeDragged an element to be dragged
+ * @param locatorOfDragDestinationObject an element whose location (i.e., whose center-most pixel) will be the point where locatorOfObjectToBeDragged is dropped
+ */
+ var startX = this.getElementPositionLeft(locatorOfObjectToBeDragged);
+ var startY = this.getElementPositionTop(locatorOfObjectToBeDragged);
+
+ var destinationLeftX = this.getElementPositionLeft(locatorOfDragDestinationObject);
+ var destinationTopY = this.getElementPositionTop(locatorOfDragDestinationObject);
+ var destinationWidth = this.getElementWidth(locatorOfDragDestinationObject);
+ var destinationHeight = this.getElementHeight(locatorOfDragDestinationObject);
+
+ var endX = Math.round(destinationLeftX + (destinationWidth / 2));
+ var endY = Math.round(destinationTopY + (destinationHeight / 2));
+
+ var deltaX = endX - startX;
+ var deltaY = endY - startY;
+
+ var movementsString = "" + deltaX + "," + deltaY;
+
+ this.doDragAndDrop(locatorOfObjectToBeDragged, movementsString);
};
Selenium.prototype.doWindowFocus = function(windowName) {
@@ -1430,7 +1704,7 @@ Selenium.prototype.doWindowMaximize = function(windowName) {
*/
var window = this.findWindow(windowName);
if (window!=null && window.screen) {
- window.moveTo(0,0);
+ window.moveTo(0,0);
window.outerHeight = screen.availHeight;
window.outerWidth = screen.availWidth;
}
@@ -1461,32 +1735,32 @@ Selenium.prototype.getAllWindowTitles = function() {
};
Selenium.prototype.getHtmlSource = function() {
- /** Returns the entire HTML source between the opening and
+ /** Returns the entire HTML source between the opening and
* closing "html" tags.
*
* @return string the entire HTML source
*/
- return this.page().document().getElementsByTagName("html")[0].innerHTML;
+ return this.browserbot.getDocument().getElementsByTagName("html")[0].innerHTML;
};
Selenium.prototype.doSetCursorPosition = function(locator, position) {
- /**
+ /**
* Moves the text cursor to the specified position in the given input element or textarea.
* This method will fail if the specified element isn't an input element or textarea.
*
* @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea
* @param position the numerical position of the cursor in the field; position should be 0 to move the position to the beginning of the field. You can also set the cursor to -1 to move it to the end of the field.
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
if (element.value == undefined) {
Assert.fail("Element " + locator + " is not an input.");
}
if (position == -1) {
- position = element.value.length;
+ position = element.value.length;
}
if( element.setSelectionRange && !browserVersion.isOpera) {
- element.focus();
+ element.focus();
element.setSelectionRange(/*start*/position,/*end*/position);
}
else if( element.createTextRange ) {
@@ -1507,7 +1781,7 @@ Selenium.prototype.getElementIndex = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element
* @return number of relative index of the element to its parent (starting from 0)
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
var previousSibling;
var index = 0;
while ((previousSibling = element.previousSibling) != null) {
@@ -1528,8 +1802,8 @@ Selenium.prototype.isOrdered = function(locator1, locator2) {
* @param locator2 an <a href="#locators">element locator</a> pointing to the second element
* @return boolean true if two elements are ordered and have same parent, false otherwise
*/
- var element1 = this.page().findElement(locator1);
- var element2 = this.page().findElement(locator2);
+ var element1 = this.browserbot.findElement(locator1);
+ var element2 = this.browserbot.findElement(locator2);
if (element1 === element2) return false;
var previousSibling;
@@ -1553,48 +1827,48 @@ Selenium.prototype.getElementPositionLeft = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element OR an element itself
* @return number of pixels from the edge of the frame.
*/
- var element;
+ var element;
if ("string"==typeof locator) {
- element = this.page().findElement(locator);
+ element = this.browserbot.findElement(locator);
}
else {
- element = locator;
+ element = locator;
+ }
+ var x = element.offsetLeft;
+ var elementParent = element.offsetParent;
+
+ while (elementParent != null)
+ {
+ if(document.all)
+ {
+ if( (elementParent.tagName != "TABLE") && (elementParent.tagName != "BODY") )
+ {
+ x += elementParent.clientLeft;
+ }
+ }
+ else // Netscape/DOM
+ {
+ if(elementParent.tagName == "TABLE")
+ {
+ var parentBorder = parseInt(elementParent.border);
+ if(isNaN(parentBorder))
+ {
+ var parentFrame = elementParent.getAttribute('frame');
+ if(parentFrame != null)
+ {
+ x += 1;
+ }
+ }
+ else if(parentBorder > 0)
+ {
+ x += parentBorder;
+ }
+ }
}
- var x = element.offsetLeft;
- var elementParent = element.offsetParent;
-
- while (elementParent != null)
- {
- if(document.all)
- {
- if( (elementParent.tagName != "TABLE") && (elementParent.tagName != "BODY") )
- {
- x += elementParent.clientLeft;
- }
- }
- else // Netscape/DOM
- {
- if(elementParent.tagName == "TABLE")
- {
- var parentBorder = parseInt(elementParent.border);
- if(isNaN(parentBorder))
- {
- var parentFrame = elementParent.getAttribute('frame');
- if(parentFrame != null)
- {
- x += 1;
- }
- }
- else if(parentBorder > 0)
- {
- x += parentBorder;
- }
- }
- }
- x += elementParent.offsetLeft;
- elementParent = elementParent.offsetParent;
- }
- return x;
+ x += elementParent.offsetLeft;
+ elementParent = elementParent.offsetParent;
+ }
+ return x;
};
Selenium.prototype.getElementPositionTop = function(locator) {
@@ -1604,61 +1878,61 @@ Selenium.prototype.getElementPositionTop = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element OR an element itself
* @return number of pixels from the edge of the frame.
*/
- var element;
+ var element;
if ("string"==typeof locator) {
- element = this.page().findElement(locator);
+ element = this.browserbot.findElement(locator);
}
else {
- element = locator;
+ element = locator;
}
- var y = 0;
-
- while (element != null)
- {
- if(document.all)
- {
- if( (element.tagName != "TABLE") && (element.tagName != "BODY") )
- {
- y += element.clientTop;
- }
- }
- else // Netscape/DOM
- {
- if(element.tagName == "TABLE")
- {
- var parentBorder = parseInt(element.border);
- if(isNaN(parentBorder))
- {
- var parentFrame = element.getAttribute('frame');
- if(parentFrame != null)
- {
- y += 1;
- }
- }
- else if(parentBorder > 0)
- {
- y += parentBorder;
- }
- }
- }
- y += element.offsetTop;
-
- // Netscape can get confused in some cases, such that the height of the parent is smaller
- // than that of the element (which it shouldn't really be). If this is the case, we need to
- // exclude this element, since it will result in too large a 'top' return value.
- if (element.offsetParent && element.offsetParent.offsetHeight && element.offsetParent.offsetHeight < element.offsetHeight)
- {
- // skip the parent that's too small
- element = element.offsetParent.offsetParent;
- }
- else
- {
- // Next up...
- element = element.offsetParent;
- }
- }
- return y;
+ var y = 0;
+
+ while (element != null)
+ {
+ if(document.all)
+ {
+ if( (element.tagName != "TABLE") && (element.tagName != "BODY") )
+ {
+ y += element.clientTop;
+ }
+ }
+ else // Netscape/DOM
+ {
+ if(element.tagName == "TABLE")
+ {
+ var parentBorder = parseInt(element.border);
+ if(isNaN(parentBorder))
+ {
+ var parentFrame = element.getAttribute('frame');
+ if(parentFrame != null)
+ {
+ y += 1;
+ }
+ }
+ else if(parentBorder > 0)
+ {
+ y += parentBorder;
+ }
+ }
+ }
+ y += element.offsetTop;
+
+ // Netscape can get confused in some cases, such that the height of the parent is smaller
+ // than that of the element (which it shouldn't really be). If this is the case, we need to
+ // exclude this element, since it will result in too large a 'top' return value.
+ if (element.offsetParent && element.offsetParent.offsetHeight && element.offsetParent.offsetHeight < element.offsetHeight)
+ {
+ // skip the parent that's too small
+ element = element.offsetParent.offsetParent;
+ }
+ else
+ {
+ // Next up...
+ element = element.offsetParent;
+ }
+ }
+ return y;
};
Selenium.prototype.getElementWidth = function(locator) {
@@ -1668,7 +1942,7 @@ Selenium.prototype.getElementWidth = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element
* @return number width of an element in pixels
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
return element.offsetWidth;
};
@@ -1679,12 +1953,12 @@ Selenium.prototype.getElementHeight = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an element
* @return number height of an element in pixels
*/
- var element = this.page().findElement(locator);
+ var element = this.browserbot.findElement(locator);
return element.offsetHeight;
};
Selenium.prototype.getCursorPosition = function(locator) {
- /**
+ /**
* Retrieves the text cursor position in the given input element or textarea; beware, this may not work perfectly on all browsers.
*
* <p>Specifically, if the cursor/selection has been cleared by JavaScript, this command will tend to
@@ -1694,37 +1968,37 @@ Selenium.prototype.getCursorPosition = function(locator) {
* @param locator an <a href="#locators">element locator</a> pointing to an input element or textarea
* @return number the numerical position of the cursor in the field
*/
- var element = this.page().findElement(locator);
- var doc = this.page().getDocument();
- var win = this.browserbot.getCurrentWindow();
- if( doc.selection && !browserVersion.isOpera){
- var selectRange = doc.selection.createRange().duplicate();
- var elementRange = element.createTextRange();
- selectRange.move("character",0);
- elementRange.move("character",0);
- var inRange1 = selectRange.inRange(elementRange);
- var inRange2 = elementRange.inRange(selectRange);
- try {
- elementRange.setEndPoint("EndToEnd", selectRange);
- } catch (e) {
- Assert.fail("There is no cursor on this page!");
- }
- var answer = String(elementRange.text).replace(/\r/g,"").length;
- return answer;
- } else {
- if (typeof(element.selectionStart) != "undefined") {
- if (win.getSelection && typeof(win.getSelection().rangeCount) != undefined && win.getSelection().rangeCount == 0) {
- Assert.fail("There is no cursor on this page!");
- }
- return element.selectionStart;
- }
- }
- throw new Error("Couldn't detect cursor position on this browser!");
+ var element = this.browserbot.findElement(locator);
+ var doc = this.browserbot.getDocument();
+ var win = this.browserbot.getCurrentWindow();
+ if( doc.selection && !browserVersion.isOpera){
+ try {
+ var selectRange = doc.selection.createRange().duplicate();
+ var elementRange = element.createTextRange();
+ selectRange.move("character",0);
+ elementRange.move("character",0);
+ var inRange1 = selectRange.inRange(elementRange);
+ var inRange2 = elementRange.inRange(selectRange);
+ elementRange.setEndPoint("EndToEnd", selectRange);
+ } catch (e) {
+ Assert.fail("There is no cursor on this page!");
+ }
+ var answer = String(elementRange.text).replace(/\r/g,"").length;
+ return answer;
+ } else {
+ if (typeof(element.selectionStart) != "undefined") {
+ if (win.getSelection && typeof(win.getSelection().rangeCount) != undefined && win.getSelection().rangeCount == 0) {
+ Assert.fail("There is no cursor on this page!");
+ }
+ return element.selectionStart;
+ }
+ }
+ throw new Error("Couldn't detect cursor position on this browser!");
}
Selenium.prototype.doSetContext = function(context, logLevelThreshold) {
- /**
+ /**
* Writes a message to the status bar and adds a note to the browser-side
* log.
*
@@ -1739,26 +2013,26 @@ Selenium.prototype.doSetContext = function(context, logLevelThreshold) {
* @param logLevelThreshold one of "debug", "info", "warn", "error", sets the threshold for browser-side logging
*/
if (logLevelThreshold==null || logLevelThreshold=="") {
- return this.page().setContext(context);
+ return this.browserbot.setContext(context);
}
- return this.page().setContext(context, logLevelThreshold);
+ return this.browserbot.setContext(context, logLevelThreshold);
};
Selenium.prototype.getExpression = function(expression) {
- /**
- * Returns the specified expression.
- *
- * <p>This is useful because of JavaScript preprocessing.
- * It is used to generate commands like assertExpression and waitForExpression.</p>
- *
- * @param expression the value to return
- * @return string the value passed in
- */
- return expression;
+ /**
+ * Returns the specified expression.
+ *
+ * <p>This is useful because of JavaScript preprocessing.
+ * It is used to generate commands like assertExpression and waitForExpression.</p>
+ *
+ * @param expression the value to return
+ * @return string the value passed in
+ */
+ return expression;
}
Selenium.prototype.doWaitForCondition = function(script, timeout) {
- /**
+ /**
* Runs the specified JavaScript snippet repeatedly until it evaluates to "true".
* The snippet may have multiple lines, but only the result of the last line
* will be considered.
@@ -1770,10 +2044,7 @@ Selenium.prototype.doWaitForCondition = function(script, timeout) {
* @param script the JavaScript snippet to run
* @param timeout a timeout in milliseconds, after which this command will return with an error
*/
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
- return decorateFunctionWithTimeout(function () {
+ return Selenium.decorateFunctionWithTimeout(function () {
return eval(script);
}, timeout);
};
@@ -1781,32 +2052,32 @@ Selenium.prototype.doWaitForCondition = function(script, timeout) {
Selenium.prototype.doWaitForCondition.dontCheckAlertsAndConfirms = true;
Selenium.prototype.doSetTimeout = function(timeout) {
- /**
- * Specifies the amount of time that Selenium will wait for actions to complete.
- *
- * <p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
- * The default timeout is 30 seconds.
- * @param timeout a timeout in milliseconds, after which the action will return with an error
- */
- this.defaultTimeout = parseInt(timeout);
+ /**
+ * Specifies the amount of time that Selenium will wait for actions to complete.
+ *
+ * <p>Actions that require waiting include "open" and the "waitFor*" actions.</p>
+ * The default timeout is 30 seconds.
+ * @param timeout a timeout in milliseconds, after which the action will return with an error
+ */
+ if (!timeout) {
+ timeout = Selenium.DEFAULT_TIMEOUT;
+ }
+ this.defaultTimeout = timeout;
}
Selenium.prototype.doWaitForPageToLoad = function(timeout) {
- /**
- * Waits for a new page to load.
- *
- * <p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
- * (which are only available in the JS API).</p>
- *
- * <p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
- * flag when it first notices a page load. Running any other Selenium command after
- * turns the flag to false. Hence, if you want to wait for a page to load, you must
- * wait immediately after a Selenium command that caused a page-load.</p>
- * @param timeout a timeout in milliseconds, after which this command will return with an error
- */
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
+ /**
+ * Waits for a new page to load.
+ *
+ * <p>You can use this command instead of the "AndWait" suffixes, "clickAndWait", "selectAndWait", "typeAndWait" etc.
+ * (which are only available in the JS API).</p>
+ *
+ * <p>Selenium constantly keeps track of new pages loading, and sets a "newPageLoaded"
+ * flag when it first notices a page load. Running any other Selenium command after
+ * turns the flag to false. Hence, if you want to wait for a page to load, you must
+ * wait immediately after a Selenium command that caused a page-load.</p>
+ * @param timeout a timeout in milliseconds, after which this command will return with an error
+ */
// in pi-mode, the test and the harness share the window; thus if we are executing this code, then we have loaded
if (window["proxyInjectionMode"] == null || !window["proxyInjectionMode"]) {
return this.makePageLoadCondition(timeout);
@@ -1862,7 +2133,7 @@ Selenium.prototype.getCookie = function() {
*
* @return string all cookies of the current page under test
*/
- var doc = this.page().document();
+ var doc = this.browserbot.getDocument();
return doc.cookie;
};
@@ -1888,9 +2159,17 @@ Selenium.prototype.doCreateCookie = function(nameValuePair, optionsString) {
}
results = /path=([^\s,]+)[,]?/.exec(optionsString);
if (results) {
- cookie += "; path=" + results[1];
+ var path = results[1];
+ if (browserVersion.khtml) {
+ // Safari and conquerer don't like paths with / at the end
+ if ("/" != path) {
+ path = path.replace(/\/$/, "");
+ }
+ }
+ cookie += "; path=" + path;
}
- this.page().document().cookie = cookie;
+ LOG.debug("Setting cookie to: " + cookie);
+ this.browserbot.getDocument().cookie = cookie;
}
Selenium.prototype.doDeleteCookie = function(name,path) {
@@ -1901,8 +2180,17 @@ Selenium.prototype.doDeleteCookie = function(name,path) {
* @param path the path property of the cookie to be deleted
*/
// set the expire time of the cookie to be deleted to one minute before now.
+ path = path.trim();
+ if (browserVersion.khtml) {
+ // Safari and conquerer don't like paths with / at the end
+ if ("/" != path) {
+ path = path.replace(/\/$/, "");
+ }
+ }
var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
- this.page().document().cookie = name.trim() + "=deleted; path=" + path.trim() + "; expires=" + new Date(expireDateInMilliseconds).toGMTString();
+ var cookie = name.trim() + "=deleted; path=" + path + "; expires=" + new Date(expireDateInMilliseconds).toGMTString();
+ LOG.debug("Setting cookie to: " + cookie);
+ this.browserbot.getDocument().cookie = cookie;
}
@@ -2015,7 +2303,7 @@ OptionLocatorFactory.prototype.OptionLocatorByIndex = function(index) {
};
this.assertSelected = function(element) {
- Assert.equals(this.index, element.selectedIndex);
+ Assert.equals(this.index, element.selectedIndex);
};
};
diff --git a/tests/test_tools/selenium/core/scripts/selenium-browserbot.js b/tests/test_tools/selenium/core/scripts/selenium-browserbot.js
index 22df0fdb..633289e2 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-browserbot.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-browserbot.js
@@ -26,19 +26,24 @@
// The window to which the commands will be sent. For example, to click on a
// popup window, first select that window, and then do a normal click command.
-
var BrowserBot = function(topLevelApplicationWindow) {
this.topWindow = topLevelApplicationWindow;
+ this.topFrame = this.topWindow;
+ this.baseUrl=window.location.href;
// the buttonWindow is the Selenium window
// it contains the Run/Pause buttons... this should *not* be the AUT window
- // todo: Here the buttonWindow is not Selenium window. It will be set to Selenium window in pollForLoad.
- // Change this!!!
- this.buttonWindow = this.topWindow;
- // not sure what this is used for
- this.currentPage = null;
+ this.buttonWindow = window;
this.currentWindow = this.topWindow;
this.currentWindowName = null;
+
+ // We need to know this in advance, in case the frame closes unexpectedly
+ this.isSubFrameSelected = false;
+
+ this.altKeyDown = false;
+ this.controlKeyDown = false;
+ this.shiftKeyDown = false;
+ this.metaKeyDown = false;
this.modalDialogTest = null;
this.recordedAlerts = new Array();
@@ -49,35 +54,56 @@ var BrowserBot = function(topLevelApplicationWindow) {
this.nextPromptResult = '';
this.newPageLoaded = false;
this.pageLoadError = null;
+
+ this.shouldHighlightLocatedElement = false;
this.uniqueId = new Date().getTime();
this.pollingForLoad = new Object();
+ this.permDeniedCount = new Object();
this.windowPollers = new Array();
+ // DGF for backwards compatibility
+ this.browserbot = this;
var self = this;
- this.recordPageLoad = function() {
+
+ objectExtend(this, PageBot.prototype);
+ this._registerAllLocatorFunctions();
+
+ this.recordPageLoad = function(elementOrWindow) {
LOG.debug("Page load detected");
try {
- LOG.debug("Page load location=" + self.getCurrentWindow(true).location);
+ if (elementOrWindow.location && elementOrWindow.location.href) {
+ LOG.debug("Page load location=" + elementOrWindow.location.href);
+ } else if (elementOrWindow.contentWindow && elementOrWindow.contentWindow.location && elementOrWindow.contentWindow.location.href) {
+ LOG.debug("Page load location=" + elementOrWindow.contentWindow.location.href);
+ } else {
+ LOG.debug("Page load location unknown, current window location=" + this.getCurrentWindow(true).location);
+ }
} catch (e) {
+ LOG.error("Caught an exception attempting to log location; this should get noticed soon!");
+ LOG.exception(e);
self.pageLoadError = e;
return;
}
- self.currentPage = null;
self.newPageLoaded = true;
};
this.isNewPageLoaded = function() {
if (this.pageLoadError) {
+ LOG.error("isNewPageLoaded found an old pageLoadError");
var e = this.pageLoadError;
this.pageLoadError = null;
throw e;
}
return self.newPageLoaded;
};
+
};
-BrowserBot.createForWindow = function(window) {
+// DGF PageBot exists for backwards compatibility with old user-extensions
+var PageBot = function(){};
+
+BrowserBot.createForWindow = function(window, proxyInjectionMode) {
var browserbot;
LOG.debug('createForWindow');
LOG.debug("browserName: " + browserVersion.name);
@@ -98,8 +124,9 @@ BrowserBot.createForWindow = function(window) {
// Use mozilla by default
browserbot = new MozillaBrowserBot(window);
}
- browserbot.getCurrentWindow();
- // todo: why?
+ // getCurrentWindow has the side effect of modifying it to handle page loads etc
+ browserbot.proxyInjectionMode = proxyInjectionMode;
+ browserbot.getCurrentWindow(); // for modifyWindow side effect. This is not a transparent style
return browserbot;
};
@@ -156,6 +183,74 @@ BrowserBot.prototype.getNextPrompt = function() {
return t;
};
+/* Fire a mouse event in a browser-compatible manner */
+
+BrowserBot.prototype.triggerMouseEvent = function(element, eventType, canBubble, clientX, clientY) {
+ clientX = clientX ? clientX : 0;
+ clientY = clientY ? clientY : 0;
+
+ LOG.warn("triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
+ var screenX = 0;
+ var screenY = 0;
+
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+ if (element.fireEvent) {
+ LOG.info("element has fireEvent");
+ var evt = createEventObject(element, this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown);
+ evt.detail = 0;
+ evt.button = 1;
+ evt.relatedTarget = null;
+ if (!screenX && !screenY && !clientX && !clientY && !this.controlKeyDown && !this.altKeyDown && !this.shiftKeyDown && !this.metaKeyDown) {
+ element.fireEvent('on' + eventType);
+ }
+ else {
+ evt.screenX = screenX;
+ evt.screenY = screenY;
+ evt.clientX = clientX;
+ evt.clientY = clientY;
+
+ // when we go this route, window.event is never set to contain the event we have just created.
+ // ideally we could just slide it in as follows in the try-block below, but this normally
+ // doesn't work. This is why I try to avoid this code path, which is only required if we need to
+ // set attributes on the event (e.g., clientX).
+ try {
+ window.event = evt;
+ }
+ catch(e) {
+ // getting an "Object does not support this action or property" error. Save the event away
+ // for future reference.
+ // TODO: is there a way to update window.event?
+
+ // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
+ selenium.browserbot.getCurrentWindow().selenium_event = evt;
+ }
+ element.fireEvent('on' + eventType, evt);
+ }
+ }
+ else {
+ LOG.info("element doesn't have fireEvent");
+ var evt = document.createEvent('MouseEvents');
+ if (evt.initMouseEvent)
+ {
+ LOG.info("element has initMouseEvent");
+ //Safari
+ evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY,
+ this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown, 0, null);
+ }
+ else {
+ LOG.warn("element doesn't have initMouseEvent; firing an event which should -- but doesn't -- have other mouse-event related attributes here, as well as controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown");
+ evt.initEvent(eventType, canBubble, true);
+
+ evt.shiftKey = this.shiftKeyDown;
+ evt.metaKey = this.metaKeyDown;
+ evt.altKey = this.altKeyDown;
+ evt.ctrlKey = this.controlKeyDown;
+
+ }
+ element.dispatchEvent(evt);
+ }
+}
+
BrowserBot.prototype._windowClosed = function(win) {
var c = win.closed;
if (c == null) return true;
@@ -163,25 +258,35 @@ BrowserBot.prototype._windowClosed = function(win) {
};
BrowserBot.prototype._modifyWindow = function(win) {
+ // In proxyInjectionMode, have to suppress LOG calls in _modifyWindow to avoid an infinite loop
if (this._windowClosed(win)) {
- LOG.error("modifyWindow: Window was closed!");
+ if (!this.proxyInjectionMode) {
+ LOG.error("modifyWindow: Window was closed!");
+ }
return null;
}
- LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
+ if (!this.proxyInjectionMode) {
+ LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
+ }
if (!win[this.uniqueId]) {
win[this.uniqueId] = true;
this.modifyWindowToRecordPopUpDialogs(win, this);
- this.currentPage = PageBot.createForWindow(this);
- this.newPageLoaded = false;
}
- this.modifySeparateTestWindowToDetectPageLoads(win);
+ // In proxyInjection mode, we have our own mechanism for detecting page loads
+ if (!this.proxyInjectionMode) {
+ this.modifySeparateTestWindowToDetectPageLoads(win);
+ }
+ if (win.frames && win.frames.length && win.frames.length > 0) {
+ for (var i = 0; i < win.frames.length; i++) {
+ try {
+ this._modifyWindow(win.frames[i]);
+ } catch (e) {} // we're just trying to be opportunistic; don't worry if this doesn't work out
+ }
+ }
return win;
};
BrowserBot.prototype.selectWindow = function(target) {
- // we've moved to a new page - clear the current one
- this.currentPage = null;
-
if (target && target != "null") {
this._selectWindowByName(target);
} else {
@@ -192,67 +297,117 @@ BrowserBot.prototype.selectWindow = function(target) {
BrowserBot.prototype._selectTopWindow = function() {
this.currentWindowName = null;
this.currentWindow = this.topWindow;
+ this.topFrame = this.topWindow;
+ this.isSubFrameSelected = false;
}
BrowserBot.prototype._selectWindowByName = function(target) {
this.currentWindow = this.getWindowByName(target, false);
+ this.topFrame = this.currentWindow;
this.currentWindowName = target;
+ this.isSubFrameSelected = false;
}
BrowserBot.prototype.selectFrame = function(target) {
if (target == "relative=up") {
this.currentWindow = this.getCurrentWindow().parent;
+ this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
} else if (target == "relative=top") {
- this.currentWindow = this.topWindow;
+ this.currentWindow = this.topFrame;
+ this.isSubFrameSelected = false;
} else {
- var frame = this.getCurrentPage().findElement(target);
+ var frame = this.findElement(target);
if (frame == null) {
throw new SeleniumError("Not found: " + target);
}
// now, did they give us a frame or a frame ELEMENT?
+ var match = false;
if (frame.contentWindow) {
// this must be a frame element
- this.currentWindow = frame.contentWindow;
- } else if (frame.document) {
+ if (browserVersion.isHTA) {
+ // stupid HTA bug; can't get in the front door
+ target = frame.contentWindow.name;
+ } else {
+ this.currentWindow = frame.contentWindow;
+ this.isSubFrameSelected = true;
+ match = true;
+ }
+ } else if (frame.document && frame.location) {
// must be an actual window frame
this.currentWindow = frame;
- } else {
- // neither
- throw new SeleniumError("Not a frame: " + target);
+ this.isSubFrameSelected = true;
+ match = true;
+ }
+
+ if (!match) {
+ // neither, let's loop through the frame names
+ var win = this.getCurrentWindow();
+
+ if (win && win.frames && win.frames.length) {
+ for (var i = 0; i < win.frames.length; i++) {
+ if (win.frames[i].name == target) {
+ this.currentWindow = win.frames[i];
+ this.isSubFrameSelected = true;
+ match = true;
+ break;
+ }
+ }
+ }
+ if (!match) {
+ throw new SeleniumError("Not a frame: " + target);
+ }
}
}
- this.currentPage = null;
+ // modifies the window
+ this.getCurrentWindow();
};
BrowserBot.prototype.openLocation = function(target) {
// We're moving to a new page - clear the current one
var win = this.getCurrentWindow();
LOG.debug("openLocation newPageLoaded = false");
- this.currentPage = null;
this.newPageLoaded = false;
this.setOpenLocation(win, target);
};
+BrowserBot.prototype.openWindow = function(url, windowID) {
+ if (url != "") {
+ url = absolutify(url, this.baseUrl);
+ }
+ if (browserVersion.isHTA) {
+ // in HTA mode, calling .open on the window interprets the url relative to that window
+ // we need to absolute-ize the URL to make it consistent
+ var child = this.getCurrentWindow().open(url, windowID);
+ selenium.browserbot.openedWindows[windowID] = child;
+ } else {
+ this.getCurrentWindow().open(url, windowID);
+ }
+};
+
BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
iframe.src = location;
};
BrowserBot.prototype.setOpenLocation = function(win, loc) {
-
- // is there a Permission Denied risk here? setting a timeout breaks Firefox
- //win.setTimeout(function() { win.location.href = loc; }, 0);
- win.location.href = loc;
+ loc = absolutify(loc, this.baseUrl);
+ if (browserVersion.isHTA) {
+ var oldHref = win.location.href;
+ win.location.href = loc;
+ var marker = null;
+ try {
+ marker = this.isPollingForLoad(win);
+ if (marker && win.location[marker]) {
+ win.location[marker] = false;
+ }
+ } catch (e) {} // DGF don't know why, but this often fails
+ } else {
+ win.location.href = loc;
+ }
};
BrowserBot.prototype.getCurrentPage = function() {
- if (this.currentPage == null) {
- var testWindow = this.getCurrentWindow();
- this.currentPage = PageBot.createForWindow(this);
- this.newPageLoaded = false;
- }
-
- return this.currentPage;
+ return this;
};
BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
@@ -283,11 +438,45 @@ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify,
// Keep a reference to all popup windows by name
// note that in IE the "windowName" argument must be a valid javascript identifier, it seems.
var originalOpen = windowToModify.open;
- windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
- var openedWindow = originalOpen(url, windowName, windowFeatures, replaceFlag);
+ var originalOpenReference;
+ if (browserVersion.isHTA) {
+ originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
+ windowToModify[originalOpenReference] = windowToModify.open;
+ }
+
+ var isHTA = browserVersion.isHTA;
+
+ var newOpen = function(url, windowName, windowFeatures, replaceFlag) {
+ var myOriginalOpen = originalOpen;
+ if (isHTA) {
+ myOriginalOpen = this[originalOpenReference];
+ }
+ var openedWindow = myOriginalOpen(url, windowName, windowFeatures, replaceFlag);
+ LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" + windowName + "\"");
+ if (windowName!=null) {
+ openedWindow["seleniumWindowName"] = windowName;
+ }
selenium.browserbot.openedWindows[windowName] = openedWindow;
return openedWindow;
};
+
+ if (browserVersion.isHTA) {
+ originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
+ newOpenReference = 'selenium_newOpen' + new Date().getTime();
+ var setOriginalRef = "this['" + originalOpenReference + "'] = this.open;";
+
+ if (windowToModify.eval) {
+ windowToModify.eval(setOriginalRef);
+ windowToModify.open = newOpen;
+ } else {
+ // DGF why can't I eval here? Seems like I'm querying the window at a bad time, maybe?
+ setOriginalRef += "this.open = this['" + newOpenReference + "'];";
+ windowToModify[newOpenReference] = newOpen;
+ windowToModify.setTimeout(setOriginalRef, 0);
+ }
+ } else {
+ windowToModify.open = newOpen;
+ }
};
/**
@@ -311,27 +500,51 @@ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(window
}
var marker = 'selenium' + new Date().getTime();
- LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.document.location);
+ LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.location);
this.pollingForLoad[marker] = true;
// if this is a frame, add a load listener, otherwise, attach a poller
- if (this._getFrameElement(windowObject)) {
+ var frameElement = this._getFrameElement(windowObject);
+ // DGF HTA mode can't attach load listeners to subframes (yuk!)
+ var htaSubFrame = this._isHTASubFrame(windowObject);
+ if (frameElement && !htaSubFrame) {
LOG.debug("modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener");
- addLoadListener(windowObject.frameElement, this.recordPageLoad);
- windowObject.frameElement[marker] = true;
- windowObject.frameElement[this.uniqueId] = marker;
+ addLoadListener(frameElement, this.recordPageLoad);
+ frameElement[marker] = true;
+ frameElement[this.uniqueId] = marker;
} else {
- windowObject.document.location[marker] = true;
+ windowObject.location[marker] = true;
windowObject[this.uniqueId] = marker;
this.pollForLoad(this.recordPageLoad, windowObject, windowObject.document, windowObject.location, windowObject.location.href, marker);
}
};
+BrowserBot.prototype._isHTASubFrame = function(win) {
+ if (!browserVersion.isHTA) return false;
+ // DGF this is wrong! what if "win" isn't the selected window?
+ return this.isSubFrameSelected;
+}
+
BrowserBot.prototype._getFrameElement = function(win) {
var frameElement = null;
+ var caught;
try {
frameElement = win.frameElement;
} catch (e) {
- } // on IE, checking frameElement on a pop-up results in a "No such interface supported" exception
+ caught = true;
+ }
+ if (caught) {
+ // on IE, checking frameElement in a pop-up results in a "No such interface supported" exception
+ // but it might have a frame element anyway!
+ var parentContainsIdenticallyNamedFrame = false;
+ try {
+ parentContainsIdenticallyNamedFrame = win.parent.frames[win.name];
+ } catch (e) {} // this may fail if access is denied to the parent; in that case, assume it's not a pop-up
+
+ if (parentContainsIdenticallyNamedFrame) {
+ // it can't be a coincidence that the parent has a frame with the same name as myself!
+ return BrowserBot.prototype.locateElementByName(win.name, win.parent.document, win.parent);
+ }
+ }
return frameElement;
}
@@ -342,18 +555,12 @@ BrowserBot.prototype._getFrameElement = function(win) {
*/
BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
LOG.debug("pollForLoad original (" + marker + "): " + originalHref);
-
try {
if (this._windowClosed(windowObject)) {
LOG.debug("pollForLoad WINDOW CLOSED (" + marker + ")");
delete this.pollingForLoad[marker];
return;
}
- // todo: Change this!!!
- // under multi-window layout, buttonWindow should be TestRunner window
- // but only after the _windowClosed checking, we can ensure that this.topWindow exists
- // then we can assign the TestRunner window to buttonWindow
- this.buttonWindow = windowObject.opener;
var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
var rs = this.getReadyState(windowObject, windowObject.document);
@@ -369,13 +576,18 @@ BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, original
this.modifySeparateTestWindowToDetectPageLoads(windowObject);
}
newMarker = this.isPollingForLoad(windowObject);
+ var currentlySelectedWindow;
+ var currentlySelectedWindowMarker;
+ currentlySelectedWindow =this.getCurrentWindow(true);
+ currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
+
LOG.debug("pollForLoad (" + marker + ") restarting " + newMarker);
if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
LOG.debug("pollForLoad Oh, it's just the starting page. Never mind!");
- } else if (this.currentWindow[this.uniqueId] == newMarker) {
- loadFunction();
+ } else if (currentlySelectedWindowMarker == newMarker) {
+ loadFunction(currentlySelectedWindow);
} else {
- LOG.debug("pollForLoad page load detected in non-current window; ignoring");
+ LOG.debug("pollForLoad page load detected in non-current window; ignoring (currentlySelected="+currentlySelectedWindowMarker+", detection in "+newMarker+")");
}
return;
}
@@ -396,6 +608,19 @@ BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, orig
var sameDoc = this._isSameDocument(originalDocument, currentDocument);
var sameLoc = (originalLocation === currentLocation);
+
+ // hash marks don't meant the page has loaded, so we need to strip them off if they exist...
+ var currentHash = currentHref.indexOf('#');
+ if (currentHash > 0) {
+ currentHref = currentHref.substring(0, currentHash);
+ }
+ var originalHash = originalHref.indexOf('#');
+ if (originalHash > 0) {
+ originalHref = originalHref.substring(0, originalHash);
+ }
+ LOG.debug("_isSamePage: currentHref: " + currentHref);
+ LOG.debug("_isSamePage: originalHref: " + originalHref);
+
var sameHref = (originalHref === currentHref);
var markedLoc = currentLocation[marker];
@@ -403,6 +628,13 @@ BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, orig
// the mark disappears too early on these browsers
markedLoc = true;
}
+
+ // since this is some _very_ important logic, especially for PI and multiWindow mode, we should log all these out
+ LOG.debug("_isSamePage: sameDoc: " + sameDoc);
+ LOG.debug("_isSamePage: sameLoc: " + sameLoc);
+ LOG.debug("_isSamePage: sameHref: " + sameHref);
+ LOG.debug("_isSamePage: markedLoc: " + markedLoc);
+
return sameDoc && sameLoc && sameHref && markedLoc
};
@@ -489,17 +721,21 @@ BrowserBot.prototype.reschedulePoller = function(loadFunction, windowObject, ori
};
BrowserBot.prototype.runScheduledPollers = function() {
+ LOG.debug("runScheduledPollers");
var oldPollers = this.windowPollers;
this.windowPollers = new Array();
for (var i = 0; i < oldPollers.length; i++) {
oldPollers[i].call();
}
+ LOG.debug("runScheduledPollers DONE");
};
BrowserBot.prototype.isPollingForLoad = function(win) {
var marker;
- if (this._getFrameElement(win)) {
- marker = win.frameElement[this.uniqueId];
+ var frameElement = this._getFrameElement(win);
+ var htaSubFrame = this._isHTASubFrame(win);
+ if (frameElement && !htaSubFrame) {
+ marker = frameElement[this.uniqueId];
} else {
marker = win[this.uniqueId];
}
@@ -521,9 +757,32 @@ BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
if (!targetWindow) {
targetWindow = this.topWindow[windowName];
}
+ if (!targetWindow && windowName == "_blank") {
+ for (var winName in this.openedWindows) {
+ // _blank can match selenium_blank*, if it looks like it's OK (valid href, not closed)
+ if (/^selenium_blank/.test(winName)) {
+ targetWindow = this.openedWindows[winName];
+ var ok;
+ try {
+ if (!this._windowClosed(targetWindow)) {
+ ok = targetWindow.location.href;
+ }
+ } catch (e) {}
+ if (ok) break;
+ }
+ }
+ }
if (!targetWindow) {
throw new SeleniumError("Window does not exist");
}
+ if (browserVersion.isHTA) {
+ try {
+ targetWindow.location.href;
+ } catch (e) {
+ targetWindow = window.open("", targetWindow.name);
+ this.openedWindows[targetWindow.name] = targetWindow;
+ }
+ }
if (!doNotModify) {
this._modifyWindow(targetWindow);
}
@@ -534,210 +793,62 @@ BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
var testWindow = this.currentWindow;
if (!doNotModify) {
this._modifyWindow(testWindow);
+ if (!this.proxyInjectionMode) {
+ // In proxy injection mode, have to avoid logging during getCurrentWindow to avoid an infinite loop
+ LOG.debug("getCurrentWindow newPageLoaded = false");
+ }
+ this.newPageLoaded = false;
}
+ testWindow = this._handleClosedSubFrame(testWindow, doNotModify);
return testWindow;
};
-function MozillaBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-MozillaBrowserBot.prototype = new BrowserBot;
-
-function KonquerorBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-KonquerorBrowserBot.prototype = new BrowserBot;
-
-KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
- // Window doesn't fire onload event when setting src to the current value,
- // so we set it to blank first.
- iframe.src = "about:blank";
- iframe.src = location;
-};
-
-KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
- // Window doesn't fire onload event when setting src to the current value,
- // so we set it to blank first.
- win.location.href = "about:blank";
- win.location.href = loc;
- // force the current polling thread to detect a page load
- var marker = this.isPollingForLoad(win);
- if (marker) {
- delete win.location[marker];
- }
-};
-
-KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
- // under Konqueror, there may be this case:
- // originalDocument and currentDocument are different objects
- // while their location are same.
- if (originalDocument) {
- return originalDocument.location == currentDocument.location
- } else {
- return originalDocument === currentDocument;
- }
-};
-
-function SafariBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-SafariBrowserBot.prototype = new BrowserBot;
-
-SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
-SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
-
-
-function OperaBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-OperaBrowserBot.prototype = new BrowserBot;
-OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
- if (iframe.src == location) {
- iframe.src = location + '?reload';
- } else {
- iframe.src = location;
+BrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
+ if (this.proxyInjectionMode) {
+ return testWindow;
}
-}
-
-function IEBrowserBot(frame) {
- BrowserBot.call(this, frame);
-}
-IEBrowserBot.prototype = new BrowserBot;
-
-IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
- BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
-
- // we will call the previous version of this method from within our own interception
- oldShowModalDialog = windowToModify.showModalDialog;
-
- windowToModify.showModalDialog = function(url, args, features) {
- // Get relative directory to where TestRunner.html lives
- // A risky assumption is that the user's TestRunner is named TestRunner.html
- var doc_location = document.location.toString();
- var end_of_base_ref = doc_location.indexOf('TestRunner.html');
- var base_ref = doc_location.substring(0, end_of_base_ref);
-
- var fullURL = base_ref + "TestRunner.html?singletest=" + escape(browserBot.modalDialogTest) + "&autoURL=" + escape(url) + "&runInterval=" + runOptions.runInterval;
- browserBot.modalDialogTest = null;
-
- var returnValue = oldShowModalDialog(fullURL, args, features);
- return returnValue;
- };
-};
-
-IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
- this.pageUnloading = false;
- this.permDeniedCount = 0;
- var self = this;
- var pageUnloadDetector = function() {
- self.pageUnloading = true;
- };
- windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
- BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
-};
-
-IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
- BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
- if (this.pageLoadError) {
- if (this.pageUnloading) {
- var self = this;
- LOG.warn("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
- this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
- this.pageLoadError = null;
- return;
- } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
- && this.permDeniedCount++ < 4) {
- var self = this;
- LOG.warn("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount + "), waiting to see if it goes away");
- this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
- this.pageLoadError = null;
- return;
- }
- //handy for debugging!
- //throw this.pageLoadError;
- }
-};
-
-IEBrowserBot.prototype._windowClosed = function(win) {
- try {
- var c = win.closed;
- // frame windows claim to be non-closed when their parents are closed
- // but you can't access their document objects in that case
- if (!c) {
- try {
- win.document;
- } catch (de) {
- if (de.message == "Permission denied") {
- // the window is probably unloading, which means it's probably not closed yet
- return false;
- }
- else if (/^Access is denied/.test(de.message)) {
- // rare variation on "Permission denied"?
- LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
- return false;
- } else {
- // this is probably one of those frame window situations
- LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
- return true;
+
+ if (this.isSubFrameSelected) {
+ var missing = true;
+ if (testWindow.parent && testWindow.parent.frames && testWindow.parent.frames.length) {
+ for (var i = 0; i < testWindow.parent.frames.length; i++) {
+ if (testWindow.parent.frames[i] == testWindow) {
+ missing = false;
+ break;
}
}
}
- if (c == null) {
- LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
- return true;
- }
- return c;
- } catch (e) {
- // Got an exception trying to read win.closed; we'll have to take a guess!
- if (browserVersion.isHTA) {
- if (e.message == "Permission denied") {
- // the window is probably unloading, which means it's probably not closed yet
- return false;
- } else {
- // there's a good chance that we've lost contact with the window object if it is closed
- return true;
- }
- } else {
- // the window is probably unloading, which means it's probably not closed yet
- return false;
+ if (missing) {
+ LOG.warn("Current subframe appears to have closed; selecting top frame");
+ this.selectFrame("relative=top");
+ return this.getCurrentWindow(doNotModify);
}
+ } else if (this._windowClosed(testWindow)) {
+ var closedError = new SeleniumError("Current window or frame is closed!");
+ closedError.windowClosed = true;
+ throw closedError;
}
+ return testWindow;
};
-SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
- BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
-
- var originalOpen = windowToModify.open;
- /*
- * Safari seems to be broken, so that when we manually trigger the onclick method
- * of a button/href, any window.open calls aren't resolved relative to the app location.
- * So here we replace the open() method with one that does resolve the url correctly.
- */
- windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
-
- if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
- return originalOpen(url, windowName, windowFeatures, replaceFlag);
- }
-
- // Reduce the current path to the directory
- var currentPath = windowToModify.location.pathname || "/";
- currentPath = currentPath.replace(/\/[^\/]*$/, "/");
-
- // Remove any leading "./" from the new url.
- url = url.replace(/^\.\//, "");
+BrowserBot.prototype.highlight = function (element, force) {
+ if (force || this.shouldHighlightLocatedElement) {
+ try {
+ highlight(element);
+ } catch (e) {} // DGF element highlighting is low-priority and possibly dangerous
+ }
+ return element;
+}
- newUrl = currentPath + url;
+BrowserBot.prototype.setShouldHighlightElement = function (shouldHighlight) {
+ this.shouldHighlightLocatedElement = shouldHighlight;
+}
- return originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
- };
-};
+/*****************************************************************/
+/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
-var PageBot = function(browserbot) {
- this.browserbot = browserbot;
- this._registerAllLocatorFunctions();
-};
-PageBot.prototype._registerAllLocatorFunctions = function() {
+BrowserBot.prototype._registerAllLocatorFunctions = function() {
// TODO - don't do this in the constructor - only needed once ever
this.locationStrategies = {};
for (var functionName in this) {
@@ -779,15 +890,11 @@ PageBot.prototype._registerAllLocatorFunctions = function() {
};
}
-PageBot.prototype.getDocument = function() {
+BrowserBot.prototype.getDocument = function() {
return this.getCurrentWindow().document;
}
-PageBot.prototype.getCurrentWindow = function() {
- return this.browserbot.getCurrentWindow();
-}
-
-PageBot.prototype.getTitle = function() {
+BrowserBot.prototype.getTitle = function() {
var t = this.getDocument().title;
if (typeof(t) == "string") {
t = t.trim();
@@ -795,55 +902,30 @@ PageBot.prototype.getTitle = function() {
return t;
}
-// todo: this is a bad name ... we're not passing a window in
-PageBot.createForWindow = function(browserbot) {
- if (browserVersion.isIE) {
- return new IEPageBot(browserbot);
- }
- else if (browserVersion.isKonqueror) {
- return new KonquerorPageBot(browserbot);
- }
- else if (browserVersion.isSafari) {
- return new SafariPageBot(browserbot);
- }
- else if (browserVersion.isOpera) {
- return new OperaPageBot(browserbot);
+/*
+ * Finds an element recursively in frames and nested frames
+ * in the specified document, using various lookup protocols
+ */
+BrowserBot.prototype.findElementRecursive = function(locatorType, locatorString, inDocument, inWindow) {
+
+ var element = this.findElementBy(locatorType, locatorString, inDocument, inWindow);
+ if (element != null) {
+ return element;
}
- else {
- // Use mozilla by default
- return new MozillaPageBot(browserbot);
+
+ for (var i = 0; i < inWindow.frames.length; i++) {
+ element = this.findElementRecursive(locatorType, locatorString, inWindow.frames[i].document, inWindow.frames[i]);
+
+ if (element != null) {
+ return element;
+ }
}
};
-var MozillaPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-MozillaPageBot.prototype = new PageBot();
-
-var KonquerorPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-KonquerorPageBot.prototype = new PageBot();
-
-var SafariPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-SafariPageBot.prototype = new PageBot();
-
-var IEPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-IEPageBot.prototype = new PageBot();
-
-var OperaPageBot = function(browserbot) {
- PageBot.call(this, browserbot);
-};
-OperaPageBot.prototype = new PageBot();
-
/*
* Finds an element on the current page, using various lookup protocols
*/
-PageBot.prototype.findElement = function(locator) {
+BrowserBot.prototype.findElement = function(locator) {
var locatorType = 'implicit';
var locatorString = locator;
@@ -853,57 +935,31 @@ PageBot.prototype.findElement = function(locator) {
locatorType = result[1].toLowerCase();
locatorString = result[2];
}
-
- var element = this.findElementBy(locatorType, locatorString, this.getDocument(), this.getCurrentWindow());
+
+ var element = this.findElementRecursive(locatorType, locatorString, this.getDocument(), this.getCurrentWindow())
+
if (element != null) {
- return this.highlight(element);
- }
- for (var i = 0; i < this.getCurrentWindow().frames.length; i++) {
- element = this.findElementBy(locatorType, locatorString, this.getCurrentWindow().frames[i].document, this.getCurrentWindow().frames[i]);
- if (element != null) {
- return this.highlight(element);
- }
+ return this.browserbot.highlight(element);
}
// Element was not found by any locator function.
throw new SeleniumError("Element " + locator + " not found");
};
-PageBot.prototype.highlight = function (element) {
- if (shouldHighlightLocatedElement) {
- Effect.highlight(element);
- }
- return element;
-}
-
-// as a static variable.
-var shouldHighlightLocatedElement = false;
-
-PageBot.prototype.setHighlightElement = function (shouldHighlight) {
- shouldHighlightLocatedElement = shouldHighlight;
-}
-
/**
* In non-IE browsers, getElementById() does not search by name. Instead, we
* we search separately by id and name.
*/
-PageBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
- return PageBot.prototype.locateElementById(identifier, inDocument, inWindow)
- || PageBot.prototype.locateElementByName(identifier, inDocument, inWindow)
+BrowserBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
+ return BrowserBot.prototype.locateElementById(identifier, inDocument, inWindow)
+ || BrowserBot.prototype.locateElementByName(identifier, inDocument, inWindow)
|| null;
};
/**
- * In IE, getElementById() also searches by name - this is an optimisation for IE.
- */
-IEPageBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
- return inDocument.getElementById(identifier);
-};
-
-/**
* Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
*/
-PageBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
+BrowserBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
var element = inDocument.getElementById(identifier);
if (element && element.id === identifier) {
return element;
@@ -917,7 +973,7 @@ PageBot.prototype.locateElementById = function(identifier, inDocument, inWindow)
* Find an element by name, refined by (optional) element-filter
* expressions.
*/
-PageBot.prototype.locateElementByName = function(locator, document, inWindow) {
+BrowserBot.prototype.locateElementByName = function(locator, document, inWindow) {
var elements = document.getElementsByTagName("*");
var filters = locator.split(' ');
@@ -937,15 +993,12 @@ PageBot.prototype.locateElementByName = function(locator, document, inWindow) {
/**
* Finds an element using by evaluating the specfied string.
*/
-PageBot.prototype.locateElementByDomTraversal = function(domTraversal, inDocument, inWindow) {
+BrowserBot.prototype.locateElementByDomTraversal = function(domTraversal, document, window) {
+ var browserbot = this.browserbot;
var element = null;
try {
- if (browserVersion.isOpera) {
- element = inWindow.eval(domTraversal);
- } else {
- element = eval("inWindow." + domTraversal);
- }
+ element = eval(domTraversal);
} catch (e) {
e.isSeleniumError = true;
throw e;
@@ -957,13 +1010,13 @@ PageBot.prototype.locateElementByDomTraversal = function(domTraversal, inDocumen
return element;
};
-PageBot.prototype.locateElementByDomTraversal.prefix = "dom";
+BrowserBot.prototype.locateElementByDomTraversal.prefix = "dom";
/**
* Finds an element identified by the xpath expression. Expressions _must_
* begin with "//".
*/
-PageBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
+BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
// Trim any trailing "/": not valid xpath, and remains from attribute
// locator.
@@ -982,12 +1035,21 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
// Handle //tag[@attr='value']
var match = xpath.match(/^\/\/(\w+|\*)\[@(\w+)=('([^\']+)'|"([^\"]+)")\]$/);
if (match) {
- return this._findElementByTagNameAndAttributeValue(
+ // We don't return the value without checking if it is null first.
+ // This is beacuse in some rare cases, this shortcut actually WONT work
+ // but that the full XPath WILL. A known case, for example, is in IE
+ // when the attribute is onclick/onblur/onsubmit/etc. Due to a bug in IE
+ // this shortcut won't work because the actual function is returned
+ // by getAttribute() rather than the text of the attribute.
+ var val = this._findElementByTagNameAndAttributeValue(
inDocument,
match[1].toUpperCase(),
match[2].toLowerCase(),
match[3].slice(1, -1)
);
+ if (val) {
+ return val;
+ }
}
// Handle //tag[text()='value']
@@ -1003,7 +1065,7 @@ PageBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
return this._findElementUsingFullXPath(xpath, inDocument);
};
-PageBot.prototype._findElementByTagNameAndAttributeValue = function(
+BrowserBot.prototype._findElementByTagNameAndAttributeValue = function(
inDocument, tagName, attributeName, attributeValue
) {
if (browserVersion.isIE && attributeName == "class") {
@@ -1019,7 +1081,7 @@ PageBot.prototype._findElementByTagNameAndAttributeValue = function(
return null;
};
-PageBot.prototype._findElementByTagNameAndText = function(
+BrowserBot.prototype._findElementByTagNameAndText = function(
inDocument, tagName, text
) {
var elements = inDocument.getElementsByTagName(tagName);
@@ -1031,7 +1093,7 @@ PageBot.prototype._findElementByTagNameAndText = function(
return null;
};
-PageBot.prototype._namespaceResolver = function(prefix) {
+BrowserBot.prototype._namespaceResolver = function(prefix) {
if (prefix == 'html' || prefix == 'xhtml' || prefix == 'x') {
return 'http://www.w3.org/1999/xhtml';
} else if (prefix == 'mathml') {
@@ -1041,7 +1103,7 @@ PageBot.prototype._namespaceResolver = function(prefix) {
}
}
-PageBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
+BrowserBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
// HUGE hack - remove namespace from xpath for IE
if (browserVersion.isIE) {
xpath = xpath.replace(/x:/g, '')
@@ -1066,7 +1128,7 @@ PageBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWin
* Finds a link element with text matching the expression supplied. Expressions must
* begin with "link:".
*/
-PageBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
+BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
var links = inDocument.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
var element = links[i];
@@ -1076,13 +1138,13 @@ PageBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWin
}
return null;
};
-PageBot.prototype.locateElementByLinkText.prefix = "link";
+BrowserBot.prototype.locateElementByLinkText.prefix = "link";
/**
* Returns an attribute based on an attribute locator. This is made up of an element locator
* suffixed with @attribute-name.
*/
-PageBot.prototype.findAttribute = function(locator) {
+BrowserBot.prototype.findAttribute = function(locator) {
// Split into locator + attributeName
var attributePos = locator.lastIndexOf("@");
var elementLocator = locator.slice(0, attributePos);
@@ -1105,7 +1167,7 @@ PageBot.prototype.findAttribute = function(locator) {
/*
* Select the specified option and trigger the relevant events of the element.
*/
-PageBot.prototype.selectOption = function(element, optionToSelect) {
+BrowserBot.prototype.selectOption = function(element, optionToSelect) {
triggerEvent(element, 'focus', false);
var changed = false;
for (var i = 0; i < element.options.length; i++) {
@@ -1128,7 +1190,7 @@ PageBot.prototype.selectOption = function(element, optionToSelect) {
/*
* Select the specified option and trigger the relevant events of the element.
*/
-PageBot.prototype.addSelection = function(element, option) {
+BrowserBot.prototype.addSelection = function(element, option) {
this.checkMultiselect(element);
triggerEvent(element, 'focus', false);
if (!option.selected) {
@@ -1140,7 +1202,7 @@ PageBot.prototype.addSelection = function(element, option) {
/*
* Select the specified option and trigger the relevant events of the element.
*/
-PageBot.prototype.removeSelection = function(element, option) {
+BrowserBot.prototype.removeSelection = function(element, option) {
this.checkMultiselect(element);
triggerEvent(element, 'focus', false);
if (option.selected) {
@@ -1149,7 +1211,7 @@ PageBot.prototype.removeSelection = function(element, option) {
}
};
-PageBot.prototype.checkMultiselect = function(element) {
+BrowserBot.prototype.checkMultiselect = function(element) {
if (!element.multiple)
{
throw new SeleniumError("Not a multi-select");
@@ -1157,7 +1219,7 @@ PageBot.prototype.checkMultiselect = function(element) {
};
-PageBot.prototype.replaceText = function(element, stringValue) {
+BrowserBot.prototype.replaceText = function(element, stringValue) {
triggerEvent(element, 'focus', false);
triggerEvent(element, 'select', true);
var maxLengthAttr = element.getAttribute("maxLength");
@@ -1170,42 +1232,83 @@ PageBot.prototype.replaceText = function(element, stringValue) {
LOG.warn("AFTER")
}
}
- element.value = actualValue;
+
+ if (getTagName(element) == "body") {
+ if (element.ownerDocument && element.ownerDocument.designMode) {
+ var designMode = new String(element.ownerDocument.designMode).toLowerCase();
+ if (designMode = "on") {
+ // this must be a rich text control!
+ element.innerHTML = actualValue;
+ }
+ }
+ } else {
+ element.value = actualValue;
+ }
// DGF this used to be skipped in chrome URLs, but no longer. Is xpcnativewrappers to blame?
- triggerEvent(element, 'change', true);
+ try {
+ triggerEvent(element, 'change', true);
+ } catch (e) {}
};
-MozillaPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- // Add an event listener that detects if the default action has been prevented.
- // (This is caused by a javascript onclick handler returning false)
- var preventDefault = false;
-
- element.addEventListener("click", function(evt) {
- preventDefault = evt.getPreventDefault();
- }, false);
-
- // Trigger the click event.
- triggerMouseEvent(element, 'click', true, clientX, clientY);
-
- // Perform the link action if preventDefault was set.
- // In chrome URL, the link action is already executed by triggerMouseEvent.
- if (!browserVersion.isChrome && !preventDefault) {
- var targetWindow = this.browserbot._getTargetWindow(element);
- if (element.href) {
- targetWindow.location.href = element.href;
+BrowserBot.prototype.submit = function(formElement) {
+ var actuallySubmit = true;
+ this._modifyElementTarget(formElement);
+ if (formElement.onsubmit) {
+ if (browserVersion.isHTA) {
+ // run the code in the correct window so alerts are handled correctly even in HTA mode
+ var win = this.browserbot.getCurrentWindow();
+ var now = new Date().getTime();
+ var marker = 'marker' + now;
+ win[marker] = formElement;
+ win.setTimeout("var actuallySubmit = "+marker+".onsubmit();" +
+ "if (actuallySubmit) { " +
+ marker+".submit(); " +
+ "if ("+marker+".target && !/^_/.test("+marker+".target)) {"+
+ "window.open('', "+marker+".target);"+
+ "}"+
+ "};"+
+ marker+"=null", 0);
+ // pause for up to 2s while this command runs
+ var terminationCondition = function () {
+ return !win[marker];
+ }
+ return Selenium.decorateFunctionWithTimeout(terminationCondition, 2000);
} else {
- this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
+ actuallySubmit = formElement.onsubmit();
+ if (actuallySubmit) {
+ formElement.submit();
+ if (formElement.target && !/^_/.test(formElement.target)) {
+ this.browserbot.openWindow('', formElement.target);
+ }
+ }
}
+ } else {
+ formElement.submit();
}
+}
- if (this._windowClosed()) {
- return;
+BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
+ this._fireEventOnElement("click", element, clientX, clientY);
+};
+
+BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
+ this._fireEventOnElement("dblclick", element, clientX, clientY);
+};
+
+BrowserBot.prototype._modifyElementTarget = function(element) {
+ if (element.target) {
+ if (element.target == "_blank" || /^selenium_blank/.test(element.target) ) {
+ var tagName = getTagName(element);
+ if (tagName == "a" || tagName == "form") {
+ var newTarget = "selenium_blank" + Math.round(100000 * Math.random());
+ LOG.warn("Link has target '_blank', which is not supported in Selenium! Randomizing target to be: " + newTarget);
+ this.browserbot.openWindow('', newTarget);
+ element.target = newTarget;
+ }
+ }
}
+}
-};
BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, element) {
if (element.parentNode && element.parentNode.href) {
@@ -1214,126 +1317,38 @@ BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, e
}
BrowserBot.prototype._getTargetWindow = function(element) {
- var targetWindow = this.getCurrentWindow();
+ var targetWindow = element.ownerDocument.defaultView;
if (element.target) {
- var frame = this._getFrameFromGlobal(element.target);
- targetWindow = frame.contentWindow;
+ targetWindow = this._getFrameFromGlobal(element.target);
}
return targetWindow;
}
BrowserBot.prototype._getFrameFromGlobal = function(target) {
- pagebot = PageBot.createForWindow(this);
- return pagebot.findElementBy("implicit", target, this.topWindow.document, this.topWindow);
+
+ if (target == "_top") {
+ return this.topFrame;
+ } else if (target == "_parent") {
+ return this.getCurrentWindow().parent;
+ } else if (target == "_blank") {
+ // TODO should this set cleverer window defaults?
+ return this.getCurrentWindow().open('', '_blank');
+ }
+ var frameElement = this.findElementBy("implicit", target, this.topFrame.document, this.topFrame);
+ if (frameElement) {
+ return frameElement.contentWindow;
+ }
+ var win = this.getWindowByName(target);
+ if (win) return win;
+ return this.getCurrentWindow().open('', target);
}
-OperaPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- // Trigger the click event.
- triggerMouseEvent(element, 'click', true, clientX, clientY);
-
- if (this._windowClosed()) {
- return;
- }
-
-};
-
-
-KonquerorPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- if (element.click) {
- element.click();
- }
- else {
- triggerMouseEvent(element, 'click', true, clientX, clientY);
- }
-
- if (this._windowClosed()) {
- return;
- }
-
-};
-
-SafariPageBot.prototype.clickElement = function(element, clientX, clientY) {
- triggerEvent(element, 'focus', false);
- var wasChecked = element.checked;
- // For form element it is simple.
- if (element.click) {
- element.click();
- }
- // For links and other elements, event emulation is required.
- else {
- var targetWindow = this.browserbot._getTargetWindow(element);
- // todo: what if the target anchor is on another page?
- if (element.href && element.href.indexOf("#") != -1) {
- var b = targetWindow.document.getElementById(element.href.split("#")[1]);
- targetWindow.document.body.scrollTop = b.offsetTop;
- } else {
- triggerMouseEvent(element, 'click', true, clientX, clientY);
- }
-
- }
-
-};
-
-IEPageBot.prototype.clickElement = function(element, clientX, clientY) {
-
- triggerEvent(element, 'focus', false);
-
- var wasChecked = element.checked;
-
- // Set a flag that records if the page will unload - this isn't always accurate, because
- // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
- var pageUnloading = false;
- var pageUnloadDetector = function() {
- pageUnloading = true;
- };
- this.getCurrentWindow().attachEvent("onbeforeunload", pageUnloadDetector);
- element.click();
-
-
- // If the page is going to unload - still attempt to fire any subsequent events.
- // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
- try {
- this.getCurrentWindow().detachEvent("onbeforeunload", pageUnloadDetector);
-
- if (this._windowClosed()) {
- return;
- }
-
- // Onchange event is not triggered automatically in IE.
- if (isDefined(element.checked) && wasChecked != element.checked) {
- triggerEvent(element, 'change', true);
- }
-
- }
- catch (e) {
- // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
- // Just ignore it, because the document may have unloaded.
- if (pageUnloading) {
- LOG.logHook = function() {
- };
- LOG.warn("Caught exception when firing events on unloading page: " + e.message);
- return;
- }
- throw e;
- }
-};
-
-PageBot.prototype._windowClosed = function(element) {
- return selenium.browserbot._windowClosed(this.getCurrentWindow());
-};
-
-PageBot.prototype.bodyText = function() {
+BrowserBot.prototype.bodyText = function() {
return getText(this.getDocument().body);
};
-PageBot.prototype.getAllButtons = function() {
+BrowserBot.prototype.getAllButtons = function() {
var elements = this.getDocument().getElementsByTagName('input');
var result = '';
@@ -1349,7 +1364,7 @@ PageBot.prototype.getAllButtons = function() {
};
-PageBot.prototype.getAllFields = function() {
+BrowserBot.prototype.getAllFields = function() {
var elements = this.getDocument().getElementsByTagName('input');
var result = '';
@@ -1364,7 +1379,7 @@ PageBot.prototype.getAllFields = function() {
return result;
};
-PageBot.prototype.getAllLinks = function() {
+BrowserBot.prototype.getAllLinks = function() {
var elements = this.getDocument().getElementsByTagName('a');
var result = '';
@@ -1377,7 +1392,8 @@ PageBot.prototype.getAllLinks = function() {
return result;
};
-PageBot.prototype.setContext = function(strContext, logLevel) {
+BrowserBot.prototype.setContext = function(strContext, logLevel) {
+
//set the current test title
var ctx = document.getElementById("context");
if (ctx != null) {
@@ -1392,31 +1408,31 @@ function isDefined(value) {
return typeof(value) != undefined;
}
-PageBot.prototype.goBack = function() {
+BrowserBot.prototype.goBack = function() {
this.getCurrentWindow().history.back();
};
-PageBot.prototype.goForward = function() {
+BrowserBot.prototype.goForward = function() {
this.getCurrentWindow().history.forward();
};
-PageBot.prototype.close = function() {
- if (browserVersion.isChrome || browserVersion.isSafari) {
+BrowserBot.prototype.close = function() {
+ if (browserVersion.isChrome || browserVersion.isSafari || browserVersion.isOpera) {
this.getCurrentWindow().close();
} else {
this.getCurrentWindow().eval("window.close();");
}
};
-PageBot.prototype.refresh = function() {
+BrowserBot.prototype.refresh = function() {
this.getCurrentWindow().location.reload(true);
};
/**
* Refine a list of elements using a filter.
*/
-PageBot.prototype.selectElementsBy = function(filterType, filter, elements) {
- var filterFunction = PageBot.filterFunctions[filterType];
+BrowserBot.prototype.selectElementsBy = function(filterType, filter, elements) {
+ var filterFunction = BrowserBot.filterFunctions[filterType];
if (! filterFunction) {
throw new SeleniumError("Unrecognised element-filter type: '" + filterType + "'");
}
@@ -1424,9 +1440,9 @@ PageBot.prototype.selectElementsBy = function(filterType, filter, elements) {
return filterFunction(filter, elements);
};
-PageBot.filterFunctions = {};
+BrowserBot.filterFunctions = {};
-PageBot.filterFunctions.name = function(name, elements) {
+BrowserBot.filterFunctions.name = function(name, elements) {
var selectedElements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].name === name) {
@@ -1436,7 +1452,7 @@ PageBot.filterFunctions.name = function(name, elements) {
return selectedElements;
};
-PageBot.filterFunctions.value = function(value, elements) {
+BrowserBot.filterFunctions.value = function(value, elements) {
var selectedElements = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i].value === value) {
@@ -1446,7 +1462,7 @@ PageBot.filterFunctions.value = function(value, elements) {
return selectedElements;
};
-PageBot.filterFunctions.index = function(index, elements) {
+BrowserBot.filterFunctions.index = function(index, elements) {
index = Number(index);
if (isNaN(index) || index < 0) {
throw new SeleniumError("Illegal Index: " + index);
@@ -1457,7 +1473,7 @@ PageBot.filterFunctions.index = function(index, elements) {
return [elements[index]];
};
-PageBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
+BrowserBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
var filterType = (defaultFilterType || 'value');
@@ -1474,8 +1490,8 @@ PageBot.prototype.selectElements = function(filterExpr, elements, defaultFilterT
/**
* Find an element by class
*/
-PageBot.prototype.locateElementByClass = function(locator, document) {
- return Element.findFirstMatchingChild(document,
+BrowserBot.prototype.locateElementByClass = function(locator, document) {
+ return elementFindFirstMatchingChild(document,
function(element) {
return element.className == locator
}
@@ -1485,8 +1501,8 @@ PageBot.prototype.locateElementByClass = function(locator, document) {
/**
* Find an element by alt
*/
-PageBot.prototype.locateElementByAlt = function(locator, document) {
- return Element.findFirstMatchingChild(document,
+BrowserBot.prototype.locateElementByAlt = function(locator, document) {
+ return elementFindFirstMatchingChild(document,
function(element) {
return element.alt == locator
}
@@ -1496,9 +1512,435 @@ PageBot.prototype.locateElementByAlt = function(locator, document) {
/**
* Find an element by css selector
*/
-PageBot.prototype.locateElementByCss = function(locator, document) {
+BrowserBot.prototype.locateElementByCss = function(locator, document) {
var elements = cssQuery(locator, document);
if (elements.length != 0)
return elements[0];
return null;
}
+
+
+/*****************************************************************/
+/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
+
+function MozillaBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(MozillaBrowserBot.prototype, BrowserBot.prototype);
+
+function KonquerorBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(KonquerorBrowserBot.prototype, BrowserBot.prototype);
+
+KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+ // Window doesn't fire onload event when setting src to the current value,
+ // so we set it to blank first.
+ iframe.src = "about:blank";
+ iframe.src = location;
+};
+
+KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
+ // Window doesn't fire onload event when setting src to the current value,
+ // so we just refresh in that case instead.
+ loc = absolutify(loc, this.baseUrl);
+ loc = canonicalize(loc);
+ var startLoc = parseUrl(win.location.href);
+ startLoc.hash = null;
+ var startUrl = reassembleLocation(startLoc);
+ LOG.debug("startUrl="+startUrl);
+ LOG.debug("win.location.href="+win.location.href);
+ LOG.debug("loc="+loc);
+ if (startUrl == loc) {
+ LOG.debug("opening exact same location");
+ this.refresh();
+ } else {
+ LOG.debug("locations differ");
+ win.location.href = loc;
+ }
+ // force the current polling thread to detect a page load
+ var marker = this.isPollingForLoad(win);
+ if (marker) {
+ delete win.location[marker];
+ }
+};
+
+KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
+ // under Konqueror, there may be this case:
+ // originalDocument and currentDocument are different objects
+ // while their location are same.
+ if (originalDocument) {
+ return originalDocument.location == currentDocument.location
+ } else {
+ return originalDocument === currentDocument;
+ }
+};
+
+function SafariBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(SafariBrowserBot.prototype, BrowserBot.prototype);
+
+SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
+SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
+
+
+function OperaBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(OperaBrowserBot.prototype, BrowserBot.prototype);
+OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
+ if (iframe.src == location) {
+ iframe.src = location + '?reload';
+ } else {
+ iframe.src = location;
+ }
+}
+
+function IEBrowserBot(frame) {
+ BrowserBot.call(this, frame);
+}
+objectExtend(IEBrowserBot.prototype, BrowserBot.prototype);
+
+IEBrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
+ if (this.proxyInjectionMode) {
+ return testWindow;
+ }
+
+ try {
+ testWindow.location.href;
+ this.permDenied = 0;
+ } catch (e) {
+ this.permDenied++;
+ }
+ if (this._windowClosed(testWindow) || this.permDenied > 4) {
+ if (this.isSubFrameSelected) {
+ LOG.warn("Current subframe appears to have closed; selecting top frame");
+ this.selectFrame("relative=top");
+ return this.getCurrentWindow(doNotModify);
+ } else {
+ var closedError = new SeleniumError("Current window or frame is closed!");
+ closedError.windowClosed = true;
+ throw closedError;
+ }
+ }
+ return testWindow;
+};
+
+IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
+
+ // we will call the previous version of this method from within our own interception
+ oldShowModalDialog = windowToModify.showModalDialog;
+
+ windowToModify.showModalDialog = function(url, args, features) {
+ // Get relative directory to where TestRunner.html lives
+ // A risky assumption is that the user's TestRunner is named TestRunner.html
+ var doc_location = document.location.toString();
+ var end_of_base_ref = doc_location.indexOf('TestRunner.html');
+ var base_ref = doc_location.substring(0, end_of_base_ref);
+
+ var fullURL = base_ref + "TestRunner.html?singletest=" + escape(browserBot.modalDialogTest) + "&autoURL=" + escape(url) + "&runInterval=" + runOptions.runInterval;
+ browserBot.modalDialogTest = null;
+
+ var returnValue = oldShowModalDialog(fullURL, args, features);
+ return returnValue;
+ };
+};
+
+IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
+ this.pageUnloading = false;
+ var self = this;
+ var pageUnloadDetector = function() {
+ self.pageUnloading = true;
+ };
+ windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
+ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
+};
+
+IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
+ LOG.debug("IEBrowserBot.pollForLoad: " + marker);
+ if (!this.permDeniedCount[marker]) this.permDeniedCount[marker] = 0;
+ BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+ if (this.pageLoadError) {
+ if (this.pageUnloading) {
+ var self = this;
+ LOG.warn("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+ this.pageLoadError = null;
+ return;
+ } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
+ && this.permDeniedCount[marker]++ < 8) {
+ if (this.permDeniedCount[marker] > 4) {
+ var canAccessThisWindow;
+ var canAccessCurrentlySelectedWindow;
+ try {
+ windowObject.location.href;
+ canAccessThisWindow = true;
+ } catch (e) {}
+ try {
+ this.getCurrentWindow(true).location.href;
+ canAccessCurrentlySelectedWindow = true;
+ } catch (e) {}
+ if (canAccessCurrentlySelectedWindow & !canAccessThisWindow) {
+ LOG.warn("pollForLoad (" + marker + ") ABORTING: " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), but the currently selected window is fine");
+ // returning without rescheduling
+ this.pageLoadError = null;
+ return;
+ }
+ }
+
+ var self = this;
+ LOG.warn("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), waiting to see if it goes away");
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
+ this.pageLoadError = null;
+ return;
+ }
+ //handy for debugging!
+ //throw this.pageLoadError;
+ }
+};
+
+IEBrowserBot.prototype._windowClosed = function(win) {
+ try {
+ var c = win.closed;
+ // frame windows claim to be non-closed when their parents are closed
+ // but you can't access their document objects in that case
+ if (!c) {
+ try {
+ win.document;
+ } catch (de) {
+ if (de.message == "Permission denied") {
+ // the window is probably unloading, which means it's probably not closed yet
+ return false;
+ }
+ else if (/^Access is denied/.test(de.message)) {
+ // rare variation on "Permission denied"?
+ LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
+ return false;
+ } else {
+ // this is probably one of those frame window situations
+ LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
+ return true;
+ }
+ }
+ }
+ if (c == null) {
+ LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
+ return true;
+ }
+ return c;
+ } catch (e) {
+ LOG.debug("IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!");
+
+ if (browserVersion.isHTA) {
+ if (e.message == "Permission denied") {
+ // the window is probably unloading, which means it's not closed yet
+ return false;
+ } else {
+ // there's a good chance that we've lost contact with the window object if it is closed
+ return true;
+ }
+ } else {
+ // the window is probably unloading, which means it's not closed yet
+ return false;
+ }
+ }
+};
+
+/**
+ * In IE, getElementById() also searches by name - this is an optimisation for IE.
+ */
+IEBrowserBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
+ return inDocument.getElementById(identifier);
+};
+
+SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
+
+ var originalOpen = windowToModify.open;
+ /*
+ * Safari seems to be broken, so that when we manually trigger the onclick method
+ * of a button/href, any window.open calls aren't resolved relative to the app location.
+ * So here we replace the open() method with one that does resolve the url correctly.
+ */
+ windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
+
+ if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
+ return originalOpen(url, windowName, windowFeatures, replaceFlag);
+ }
+
+ // Reduce the current path to the directory
+ var currentPath = windowToModify.location.pathname || "/";
+ currentPath = currentPath.replace(/\/[^\/]*$/, "/");
+
+ // Remove any leading "./" from the new url.
+ url = url.replace(/^\.\//, "");
+
+ newUrl = currentPath + url;
+
+ var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
+ LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" + windowName + "\"");
+ if (windowName!=null) {
+ openedWindow["seleniumWindowName"] = windowName;
+ }
+ return openedWindow;
+ };
+};
+
+MozillaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ // Add an event listener that detects if the default action has been prevented.
+ // (This is caused by a javascript onclick handler returning false)
+ // we capture the whole event, rather than the getPreventDefault() state at the time,
+ // because we need to let the entire event bubbling and capturing to go through
+ // before making a decision on whether we should force the href
+ var savedEvent = null;
+
+ element.addEventListener(eventType, function(evt) {
+ savedEvent = evt;
+ }, false);
+
+ this._modifyElementTarget(element);
+
+ // Trigger the event.
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+ // Perform the link action if preventDefault was set.
+ // In chrome URL, the link action is already executed by triggerMouseEvent.
+ if (!browserVersion.isChrome && savedEvent != null && !savedEvent.getPreventDefault()) {
+ var targetWindow = this.browserbot._getTargetWindow(element);
+ if (element.href) {
+ targetWindow.location.href = element.href;
+ } else {
+ this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
+ }
+ }
+
+};
+
+
+OperaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ this._modifyElementTarget(element);
+
+ // Trigger the click event.
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+};
+
+
+KonquerorBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ this._modifyElementTarget(element);
+
+ if (element[eventType]) {
+ element[eventType]();
+ }
+ else {
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+ }
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+};
+
+SafariBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ triggerEvent(element, 'focus', false);
+ var wasChecked = element.checked;
+
+ this._modifyElementTarget(element);
+
+ // For form element it is simple.
+ if (element[eventType]) {
+ element[eventType]();
+ }
+ // For links and other elements, event emulation is required.
+ else {
+ var targetWindow = this.browserbot._getTargetWindow(element);
+ // todo: deal with anchors?
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+
+ }
+
+};
+
+SafariBrowserBot.prototype.refresh = function() {
+ var win = this.getCurrentWindow();
+ if (win.location.hash) {
+ // DGF Safari refuses to refresh when there's a hash symbol in the URL
+ win.location.hash = "";
+ var actuallyReload = function() {
+ win.location.reload(true);
+ }
+ window.setTimeout(actuallyReload, 1);
+ } else {
+ win.location.reload(true);
+ }
+};
+
+IEBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
+ var win = this.getCurrentWindow();
+ triggerEvent(element, 'focus', false);
+
+ var wasChecked = element.checked;
+
+ // Set a flag that records if the page will unload - this isn't always accurate, because
+ // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
+ var pageUnloading = false;
+ var pageUnloadDetector = function() {
+ pageUnloading = true;
+ };
+ win.attachEvent("onbeforeunload", pageUnloadDetector);
+ this._modifyElementTarget(element);
+ if (element[eventType]) {
+ element[eventType]();
+ }
+ else {
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
+ }
+
+
+ // If the page is going to unload - still attempt to fire any subsequent events.
+ // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
+ try {
+ win.detachEvent("onbeforeunload", pageUnloadDetector);
+
+ if (this._windowClosed(win)) {
+ return;
+ }
+
+ // Onchange event is not triggered automatically in IE.
+ if (isDefined(element.checked) && wasChecked != element.checked) {
+ triggerEvent(element, 'change', true);
+ }
+
+ }
+ catch (e) {
+ // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
+ // Just ignore it, because the document may have unloaded.
+ if (pageUnloading) {
+ LOG.logHook = function() {
+ };
+ LOG.warn("Caught exception when firing events on unloading page: " + e.message);
+ return;
+ }
+ throw e;
+ }
+};
diff --git a/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js b/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js
index d97e5a58..a9607371 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-browserdetect.js
@@ -30,32 +30,66 @@ var BrowserVersion = function() {
return;
}
+ var _getQueryParameter = function(searchKey) {
+ var str = location.search.substr(1);
+ if (str == null) return null;
+ var clauses = str.split('&');
+ for (var i = 0; i < clauses.length; i++) {
+ var keyValuePair = clauses[i].split('=', 2);
+ var key = unescape(keyValuePair[0]);
+ if (key == searchKey) {
+ return unescape(keyValuePair[1]);
+ }
+ }
+ return null;
+ };
+
var self = this;
var checkChrome = function() {
var loc = window.document.location.href;
try {
loc = window.top.document.location.href;
+ if (/^chrome:\/\//.test(loc)) {
+ self.isChrome = true;
+ } else {
+ self.isChrome = false;
+ }
} catch (e) {
// can't see the top (that means we might be chrome, but it's impossible to be sure)
self.isChromeDetectable = "no, top location couldn't be read in this window";
+ if (_getQueryParameter('thisIsChrome')) {
+ self.isChrome = true;
+ } else {
+ self.isChrome = false;
+ }
}
- if (/^chrome:\/\//.test(loc)) {
- self.isChrome = true;
- } else {
- self.isChrome = false;
- }
+
}
+
+
if (this.name == "Microsoft Internet Explorer") {
this.browser = BrowserVersion.IE;
this.isIE = true;
- if (window.top.SeleniumHTARunner && window.top.document.location.pathname.match(/.hta$/i)) {
- this.isHTA = true;
+ try {
+ if (window.top.SeleniumHTARunner && window.top.document.location.pathname.match(/.hta$/i)) {
+ this.isHTA = true;
+ }
+ } catch (e) {
+ this.isHTADetectable = "no, top location couldn't be read in this window";
+ if (_getQueryParameter('thisIsHTA')) {
+ self.isHTA = true;
+ } else {
+ self.isHTA = false;
+ }
}
if ("0" == navigator.appMinorVersion) {
this.preSV1 = true;
+ if (navigator.appVersion.match(/MSIE 6.0/)) {
+ this.appearsToBeBrokenInitialIE6 = true;
+ }
}
return;
}
diff --git a/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js b/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js
index c11a80ad..a23e9335 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-commandhandlers.js
@@ -12,141 +12,165 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*
*/
-function CommandHandlerFactory() {
-
- var self = this;
-
- this.handlers = {};
-
- this.registerAction = function(name, action, wait, dontCheckAlertsAndConfirms) {
- var handler = new ActionHandler(action, wait, dontCheckAlertsAndConfirms);
- this.handlers[name] = handler;
- };
-
- this.registerAccessor = function(name, accessor) {
- var handler = new AccessorHandler(accessor);
- this.handlers[name] = handler;
- };
-
- this.registerAssert = function(name, assertion, haltOnFailure) {
- var handler = new AssertHandler(assertion, haltOnFailure);
- this.handlers[name] = handler;
- };
-
- this.getCommandHandler = function(name) {
- return this.handlers[name] || null; // todo: why null, and not undefined?
- };
-
- // Methods of the form getFoo(target) result in commands:
- // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
- // storeFoo, waitForFoo, and waitForNotFoo.
- var _registerAllAccessors = function(commandObject) {
- for (var functionName in commandObject) {
- var matchForGetter = /^get([A-Z].+)$/.exec(functionName);
- if (matchForGetter != null) {
- var accessor = commandObject[functionName];
- var baseName = matchForGetter[1];
- self.registerAccessor(functionName, accessor);
- self.registerAssertionsBasedOnAccessor(accessor, baseName);
- self.registerStoreCommandBasedOnAccessor(accessor, baseName);
- self.registerWaitForCommandsBasedOnAccessor(accessor, baseName);
- }
- var matchForIs = /^is([A-Z].+)$/.exec(functionName);
- if (matchForIs != null) {
- var accessor = commandObject[functionName];
- var baseName = matchForIs[1];
- var predicate = self.createPredicateFromBooleanAccessor(accessor);
- self.registerAccessor(functionName, accessor);
- self.registerAssertionsBasedOnAccessor(accessor, baseName, predicate);
- self.registerStoreCommandBasedOnAccessor(accessor, baseName);
- self.registerWaitForCommandsBasedOnAccessor(accessor, baseName, predicate);
+
+// A naming convention used in this file:
+//
+//
+// - a "seleniumApi" is an instance of the Selenium object, defined in selenium-api.js.
+//
+// - a "Method" is an unbound function whose target must be supplied when it's called, ie.
+// it should be invoked using Function.call() or Function.apply()
+//
+// - a "Block" is a function that has been bound to a target object, so can be called invoked directly
+// (or with a null target)
+//
+// - "CommandHandler" is effectively an abstract base for
+// various handlers including ActionHandler, AccessorHandler and AssertHandler.
+// Subclasses need to implement an execute(seleniumApi, command) function,
+// where seleniumApi is the Selenium object, and command a SeleniumCommand object.
+//
+// - Handlers will return a "result" object (ActionResult, AccessorResult, AssertResult).
+// ActionResults may contain a .terminationCondition function which is run by
+// -executionloop.js after the command is run; we'll run it over and over again
+// until it returns true or the .terminationCondition throws an exception.
+// AccessorResults will contain the results of running getter (e.g. getTitle returns
+// the title as a string).
+
+var CommandHandlerFactory = classCreate();
+objectExtend(CommandHandlerFactory.prototype, {
+
+ initialize: function() {
+ this.handlers = {};
+ },
+
+ registerAction: function(name, actionBlock, wait, dontCheckAlertsAndConfirms) {
+ this.handlers[name] = new ActionHandler(actionBlock, wait, dontCheckAlertsAndConfirms);
+ },
+
+ registerAccessor: function(name, accessBlock) {
+ this.handlers[name] = new AccessorHandler(accessBlock);
+ },
+
+ registerAssert: function(name, assertBlock, haltOnFailure) {
+ this.handlers[name] = new AssertHandler(assertBlock, haltOnFailure);
+ },
+
+ getCommandHandler: function(name) {
+ return this.handlers[name];
+ },
+
+ _registerAllAccessors: function(seleniumApi) {
+ // Methods of the form getFoo(target) result in commands:
+ // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
+ // storeFoo, waitForFoo, and waitForNotFoo.
+ for (var functionName in seleniumApi) {
+ var match = /^(get|is)([A-Z].+)$/.exec(functionName);
+ if (match) {
+ var accessMethod = seleniumApi[functionName];
+ var accessBlock = fnBind(accessMethod, seleniumApi);
+ var baseName = match[2];
+ var isBoolean = (match[1] == "is");
+ var requiresTarget = (accessMethod.length == 1);
+
+ this.registerAccessor(functionName, accessBlock);
+ this._registerStoreCommandForAccessor(baseName, accessBlock, requiresTarget);
+
+ var predicateBlock = this._predicateForAccessor(accessBlock, requiresTarget, isBoolean);
+ this._registerAssertionsForPredicate(baseName, predicateBlock);
+ this._registerWaitForCommandsForPredicate(seleniumApi, baseName, predicateBlock);
}
}
- };
-
- var _registerAllActions = function(commandObject) {
- for (var functionName in commandObject) {
- var result = /^do([A-Z].+)$/.exec(functionName);
- if (result != null) {
- var actionName = result[1].lcfirst();
-
- // Register the action without the wait flag.
- var action = commandObject[functionName];
- self.registerAction(actionName, action, false, action.dontCheckAlertsAndConfirms);
-
- // Register actionName + "AndWait" with the wait flag;
- var waitActionName = actionName + "AndWait";
- self.registerAction(waitActionName, action, true, action.dontCheckAlertsAndConfirms);
+ },
+
+ _registerAllActions: function(seleniumApi) {
+ for (var functionName in seleniumApi) {
+ var match = /^do([A-Z].+)$/.exec(functionName);
+ if (match) {
+ var actionName = match[1].lcfirst();
+ var actionMethod = seleniumApi[functionName];
+ var dontCheckPopups = actionMethod.dontCheckAlertsAndConfirms;
+ var actionBlock = fnBind(actionMethod, seleniumApi);
+ this.registerAction(actionName, actionBlock, false, dontCheckPopups);
+ this.registerAction(actionName + "AndWait", actionBlock, true, dontCheckPopups);
}
}
- };
+ },
- var _registerAllAsserts = function(commandObject) {
- for (var functionName in commandObject) {
- var result = /^assert([A-Z].+)$/.exec(functionName);
- if (result != null) {
- var assert = commandObject[functionName];
+ _registerAllAsserts: function(seleniumApi) {
+ for (var functionName in seleniumApi) {
+ var match = /^assert([A-Z].+)$/.exec(functionName);
+ if (match) {
+ var assertBlock = fnBind(seleniumApi[functionName], seleniumApi);
// Register the assert with the "assert" prefix, and halt on failure.
var assertName = functionName;
- self.registerAssert(assertName, assert, true);
+ this.registerAssert(assertName, assertBlock, true);
// Register the assert with the "verify" prefix, and do not halt on failure.
- var verifyName = "verify" + result[1];
- self.registerAssert(verifyName, assert, false);
+ var verifyName = "verify" + match[1];
+ this.registerAssert(verifyName, assertBlock, false);
}
}
- };
-
- this.registerAll = function(commandObject) {
- _registerAllAccessors(commandObject);
- _registerAllActions(commandObject);
- _registerAllAsserts(commandObject);
- };
-
- // Given an accessor function getBlah(target),
- // return a "predicate" equivalient to isBlah(target, value) that
- // is true when the value returned by the accessor matches the specified value.
- this.createPredicateFromSingleArgAccessor = function(accessor) {
+ },
+
+ registerAll: function(seleniumApi) {
+ this._registerAllAccessors(seleniumApi);
+ this._registerAllActions(seleniumApi);
+ this._registerAllAsserts(seleniumApi);
+ },
+
+ _predicateForAccessor: function(accessBlock, requiresTarget, isBoolean) {
+ if (isBoolean) {
+ return this._predicateForBooleanAccessor(accessBlock);
+ }
+ if (requiresTarget) {
+ return this._predicateForSingleArgAccessor(accessBlock);
+ }
+ return this._predicateForNoArgAccessor(accessBlock);
+ },
+
+ _predicateForSingleArgAccessor: function(accessBlock) {
+ // Given an accessor function getBlah(target),
+ // return a "predicate" equivalient to isBlah(target, value) that
+ // is true when the value returned by the accessor matches the specified value.
return function(target, value) {
- var accessorResult = accessor.call(this, target);
+ var accessorResult = accessBlock(target);
if (PatternMatcher.matches(value, accessorResult)) {
return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
} else {
return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
}
};
- };
+ },
- // Given a (no-arg) accessor function getBlah(),
- // return a "predicate" equivalient to isBlah(value) that
- // is true when the value returned by the accessor matches the specified value.
- this.createPredicateFromNoArgAccessor = function(accessor) {
+ _predicateForNoArgAccessor: function(accessBlock) {
+ // Given a (no-arg) accessor function getBlah(),
+ // return a "predicate" equivalient to isBlah(value) that
+ // is true when the value returned by the accessor matches the specified value.
return function(value) {
- var accessorResult = accessor.call(this);
+ var accessorResult = accessBlock();
if (PatternMatcher.matches(value, accessorResult)) {
return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
} else {
return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
}
};
- };
+ },
- // Given a boolean accessor function isBlah(),
- // return a "predicate" equivalient to isBlah() that
- // returns an appropriate PredicateResult value.
- this.createPredicateFromBooleanAccessor = function(accessor) {
+ _predicateForBooleanAccessor: function(accessBlock) {
+ // Given a boolean accessor function isBlah(),
+ // return a "predicate" equivalient to isBlah() that
+ // returns an appropriate PredicateResult value.
return function() {
var accessorResult;
if (arguments.length > 2) throw new SeleniumError("Too many arguments! " + arguments.length);
if (arguments.length == 2) {
- accessorResult = accessor.call(this, arguments[0], arguments[1]);
+ accessorResult = accessBlock(arguments[0], arguments[1]);
} else if (arguments.length == 1) {
- accessorResult = accessor.call(this, arguments[0]);
+ accessorResult = accessBlock(arguments[0]);
} else {
- accessorResult = accessor.call(this);
+ accessorResult = accessBlock();
}
if (accessorResult) {
return new PredicateResult(true, "true");
@@ -154,71 +178,56 @@ function CommandHandlerFactory() {
return new PredicateResult(false, "false");
}
};
- };
-
- // Given an accessor fuction getBlah([target]) (target is optional)
- // return a predicate equivalent to isBlah([target,] value) that
- // is true when the value returned by the accessor matches the specified value.
- this.createPredicateFromAccessor = function(accessor) {
- if (accessor.length == 0) {
- return self.createPredicateFromNoArgAccessor(accessor);
- }
- return self.createPredicateFromSingleArgAccessor(accessor);
- };
+ },
- // Given a predicate, return the negation of that predicate.
- // Leaves the message unchanged.
- // Used to create assertNot, verifyNot, and waitForNot commands.
- this.invertPredicate = function(predicate) {
+ _invertPredicate: function(predicateBlock) {
+ // Given a predicate, return the negation of that predicate.
+ // Leaves the message unchanged.
+ // Used to create assertNot, verifyNot, and waitForNot commands.
return function(target, value) {
- var result = predicate.call(this, target, value);
- result.isTrue = ! result.isTrue;
+ var result = predicateBlock(target, value);
+ result.isTrue = !result.isTrue;
return result;
};
- };
+ },
- // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
- this.createAssertionFromPredicate = function(predicate) {
+ createAssertionFromPredicate: function(predicateBlock) {
+ // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
return function(target, value) {
- var result = predicate.call(this, target, value);
+ var result = predicateBlock(target, value);
if (!result.isTrue) {
Assert.fail(result.message);
}
};
- };
-
+ },
- var _negtiveName = function(baseName) {
+ _invertPredicateName: function(baseName) {
var matchResult = /^(.*)Present$/.exec(baseName);
if (matchResult != null) {
return matchResult[1] + "NotPresent";
}
return "Not" + baseName;
- };
-
- // Register an assertion, a verification, a negative assertion,
- // and a negative verification based on the specified accessor.
- this.registerAssertionsBasedOnAccessor = function(accessor, baseName, predicate) {
- if (predicate == null) {
- predicate = self.createPredicateFromAccessor(accessor);
- }
- var assertion = self.createAssertionFromPredicate(predicate);
- self.registerAssert("assert" + baseName, assertion, true);
- self.registerAssert("verify" + baseName, assertion, false);
-
- var invertedPredicate = self.invertPredicate(predicate);
- var negativeAssertion = self.createAssertionFromPredicate(invertedPredicate);
- self.registerAssert("assert" + _negtiveName(baseName), negativeAssertion, true);
- self.registerAssert("verify" + _negtiveName(baseName), negativeAssertion, false);
- };
-
- // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.
- this.createWaitForActionFromPredicate = function(predicate) {
+ },
+
+ _registerAssertionsForPredicate: function(baseName, predicateBlock) {
+ // Register an assertion, a verification, a negative assertion,
+ // and a negative verification based on the specified accessor.
+ var assertBlock = this.createAssertionFromPredicate(predicateBlock);
+ this.registerAssert("assert" + baseName, assertBlock, true);
+ this.registerAssert("verify" + baseName, assertBlock, false);
+
+ var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+ var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);
+ this.registerAssert("assert" + this._invertPredicateName(baseName), negativeassertBlock, true);
+ this.registerAssert("verify" + this._invertPredicateName(baseName), negativeassertBlock, false);
+ },
+
+ _waitForActionForPredicate: function(predicateBlock) {
+ // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.
return function(target, value) {
- var seleniumApi = this;
- return function () {
+ var terminationCondition = function () {
try {
- return predicate.call(seleniumApi, target, value).isTrue;
+ return predicateBlock(target, value).isTrue;
} catch (e) {
// Treat exceptions as meaning the condition is not yet met.
// Useful, for example, for waitForValue when the element has
@@ -227,40 +236,41 @@ function CommandHandlerFactory() {
return false;
}
};
+ return Selenium.decorateFunctionWithTimeout(terminationCondition, this.defaultTimeout);
};
- };
-
- // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.
- this.registerWaitForCommandsBasedOnAccessor = function(accessor, baseName, predicate) {
- if (predicate==null) {
- predicate = self.createPredicateFromAccessor(accessor);
- }
- var waitForAction = self.createWaitForActionFromPredicate(predicate);
- self.registerAction("waitFor"+baseName, waitForAction, false, true);
- var invertedPredicate = self.invertPredicate(predicate);
- var waitForNotAction = self.createWaitForActionFromPredicate(invertedPredicate);
- self.registerAction("waitFor"+_negtiveName(baseName), waitForNotAction, false, true);
+ },
+
+ _registerWaitForCommandsForPredicate: function(seleniumApi, baseName, predicateBlock) {
+ // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.
+ var waitForActionMethod = this._waitForActionForPredicate(predicateBlock);
+ var waitForActionBlock = fnBind(waitForActionMethod, seleniumApi);
+
+ var invertedPredicateBlock = this._invertPredicate(predicateBlock);
+ var waitForNotActionMethod = this._waitForActionForPredicate(invertedPredicateBlock);
+ var waitForNotActionBlock = fnBind(waitForNotActionMethod, seleniumApi);
+
+ this.registerAction("waitFor" + baseName, waitForActionBlock, false, true);
+ this.registerAction("waitFor" + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);
//TODO decide remove "waitForNot.*Present" action name or not
//for the back compatiblity issues we still make waitForNot.*Present availble
- self.registerAction("waitForNot"+baseName, waitForNotAction, false, true);
- }
+ this.registerAction("waitForNot" + baseName, waitForNotActionBlock, false, true);
+ },
- // Register a storeBlahBlah based on the specified accessor.
- this.registerStoreCommandBasedOnAccessor = function(accessor, baseName) {
+ _registerStoreCommandForAccessor: function(baseName, accessBlock, requiresTarget) {
var action;
- if (accessor.length == 1) {
+ if (requiresTarget) {
action = function(target, varName) {
- storedVars[varName] = accessor.call(this, target);
+ storedVars[varName] = accessBlock(target);
};
} else {
action = function(varName) {
- storedVars[varName] = accessor.call(this);
+ storedVars[varName] = accessBlock();
};
}
- self.registerAction("store"+baseName, action, false, accessor.dontCheckAlertsAndConfirms);
- };
+ this.registerAction("store" + baseName, action, false, true);
+ }
-}
+});
function PredicateResult(isTrue, message) {
this.isTrue = isTrue;
@@ -271,17 +281,17 @@ function PredicateResult(isTrue, message) {
// various handlers including ActionHandler, AccessorHandler and AssertHandler.
// Subclasses need to implement an execute(seleniumApi, command) function,
// where seleniumApi is the Selenium object, and command a SeleniumCommand object.
-function CommandHandler(type, haltOnFailure, executor) {
+function CommandHandler(type, haltOnFailure) {
this.type = type;
this.haltOnFailure = haltOnFailure;
- this.executor = executor;
}
// An ActionHandler is a command handler that executes the sepcified action,
// possibly checking for alerts and confirmations (if checkAlerts is set), and
// possibly waiting for a page load if wait is set.
-function ActionHandler(action, wait, dontCheckAlerts) {
- CommandHandler.call(this, "action", true, action);
+function ActionHandler(actionBlock, wait, dontCheckAlerts) {
+ this.actionBlock = actionBlock;
+ CommandHandler.call(this, "action", true);
if (wait) {
this.wait = true;
}
@@ -290,10 +300,11 @@ function ActionHandler(action, wait, dontCheckAlerts) {
}
ActionHandler.prototype = new CommandHandler;
ActionHandler.prototype.execute = function(seleniumApi, command) {
- if (this.checkAlerts && (null==/(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
+ if (this.checkAlerts && (null == /(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
+ // todo: this conditional logic is ugly
seleniumApi.ensureNoUnhandledPopups();
}
- var terminationCondition = this.executor.call(seleniumApi, command.target, command.value);
+ var terminationCondition = this.actionBlock(command.target, command.value);
// If the handler didn't return a wait flag, check to see if the
// handler was registered with the wait flag.
if (terminationCondition == undefined && this.wait) {
@@ -306,12 +317,13 @@ function ActionResult(terminationCondition) {
this.terminationCondition = terminationCondition;
}
-function AccessorHandler(accessor) {
- CommandHandler.call(this, "accessor", true, accessor);
+function AccessorHandler(accessBlock) {
+ this.accessBlock = accessBlock;
+ CommandHandler.call(this, "accessor", true);
}
AccessorHandler.prototype = new CommandHandler;
AccessorHandler.prototype.execute = function(seleniumApi, command) {
- var returnValue = this.executor.call(seleniumApi, command.target, command.value);
+ var returnValue = this.accessBlock(command.target, command.value);
return new AccessorResult(returnValue);
};
@@ -322,14 +334,15 @@ function AccessorResult(result) {
/**
* Handler for assertions and verifications.
*/
-function AssertHandler(assertion, haltOnFailure) {
- CommandHandler.call(this, "assert", haltOnFailure || false, assertion);
+function AssertHandler(assertBlock, haltOnFailure) {
+ this.assertBlock = assertBlock;
+ CommandHandler.call(this, "assert", haltOnFailure || false);
}
AssertHandler.prototype = new CommandHandler;
AssertHandler.prototype.execute = function(seleniumApi, command) {
var result = new AssertResult();
try {
- this.executor.call(seleniumApi, command.target, command.value);
+ this.assertBlock(command.target, command.value);
} catch (e) {
// If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.
if (!e.isAssertionFailedError) {
diff --git a/tests/test_tools/selenium/core/scripts/selenium-executionloop.js b/tests/test_tools/selenium/core/scripts/selenium-executionloop.js
index d59fc148..be54115e 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-executionloop.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-executionloop.js
@@ -64,7 +64,7 @@ TestLoop.prototype = {
// Pause: enable the "next/continue" button
this.pause();
} else {
- window.setTimeout(this.resume.bind(this), delay);
+ window.setTimeout(fnBind(this.resume, this), delay);
}
},
@@ -78,9 +78,11 @@ TestLoop.prototype = {
this._executeCurrentCommand();
this.continueTestWhenConditionIsTrue();
} catch (e) {
- this._handleCommandError(e);
- this._testComplete();
- return;
+ if (!this._handleCommandError(e)) {
+ this._testComplete();
+ } else {
+ this.continueTest();
+ }
}
},
@@ -107,11 +109,10 @@ TestLoop.prototype = {
command.target = selenium.preprocessParameter(command.target);
command.value = selenium.preprocessParameter(command.value);
LOG.debug("Command found, going to execute " + command.command);
- var result = handler.execute(selenium, command);
- LOG.debug("Command complete");
- this.commandComplete(result);
+ this.result = handler.execute(selenium, command);
+
- this.waitForCondition = result.terminationCondition;
+ this.waitForCondition = this.result.terminationCondition;
},
@@ -122,10 +123,10 @@ TestLoop.prototype = {
if (e.message) {
msg += " The error message is: " + e.message;
}
- this.commandError(msg);
+ return this.commandError(msg);
} else {
LOG.error(e.message);
- this.commandError(e.message);
+ return this.commandError(e.message);
}
},
@@ -135,23 +136,30 @@ TestLoop.prototype = {
* on with test. Fail the current test if there's a timeout or an
* exception.
*/
- LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
+ //LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
selenium.browserbot.runScheduledPollers();
try {
- if (this.waitForCondition == null || this.waitForCondition()) {
+ 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(this.continueTestWhenConditionIsTrue.bind(this), 100);
+ //LOG.debug("waitForCondition was false; keep waiting!");
+ window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
}
} catch (e) {
- var lastResult = {};
- lastResult.failed = true;
- lastResult.failureMessage = e.message;
- this.commandComplete(lastResult);
- this.testComplete();
+ this.result = {};
+ this.result.failed = true;
+ this.result.failureMessage = extractExceptionMessage(e);
+ this.commandComplete(this.result);
+ this.continueTest();
}
},
@@ -167,20 +175,3 @@ TestLoop.prototype = {
}
}
-
-function decorateFunctionWithTimeout(f, timeout) {
- if (f == null) {
- return null;
- }
- if (isNaN(timeout)) {
- throw new SeleniumError("Timeout is not a number: " + timeout);
- }
- var now = new Date().getTime();
- var timeoutTime = now + timeout;
- return function() {
- if (new Date().getTime() > timeoutTime) {
- throw new SeleniumError("Timed out after " + timeout + "ms");
- }
- return f();
- };
-}
diff --git a/tests/test_tools/selenium/core/scripts/selenium-logging.js b/tests/test_tools/selenium/core/scripts/selenium-logging.js
index 25e11463..6dac9518 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-logging.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-logging.js
@@ -52,6 +52,13 @@ Logger.prototype = {
"width=600,height=1000,bottom=0,right=0,status,scrollbars,resizable"
);
this.logWindow.moveTo(window.screenX + 1210, window.screenY + window.outerHeight - 1400);
+ if (browserVersion.appearsToBeBrokenInitialIE6) {
+ // I would really prefer for the message to immediately appear in the log window, the instant the user requests that the log window be
+ // visible. But when I initially coded it this way, thou message simply didn't appear unless I stepped through the code with a debugger.
+ // So obviously there is some timing issue here which I don't have the patience to figure out.
+ var pendingMessage = new LogMessage("warn", "You appear to be running an unpatched IE 6, which is not stable and can crash due to memory problems. We recommend you run Windows update to install a more stable version of IE.");
+ this.pendingMessages.push(pendingMessage);
+ }
return this.logWindow;
},
@@ -59,14 +66,15 @@ Logger.prototype = {
if (! this.getLogWindow()) {
this.openLogWindow();
}
+ setTimeout(function(){LOG.info("Log window displayed");}, 500);
},
- logHook: function(message, className) {
+ logHook: function(className, message) {
},
- log: function(message, className) {
+ log: function(className, message) {
var logWindow = this.getLogWindow();
- this.logHook(message, className);
+ this.logHook(className, message);
if (logWindow) {
if (logWindow.append) {
if (this.pendingMessages.length > 0) {
@@ -84,7 +92,7 @@ Logger.prototype = {
/* these logging messages are never flushed, which creates
an enormous array of strings that never stops growing. Only
turn this on if you need it for debugging! */
- //this.pendingMessages.push(new LogMessage(message, className));
+ //this.pendingMessages.push(new LogMessage(className, message));
}
},
@@ -101,31 +109,31 @@ Logger.prototype = {
},
debug: function(message) {
- this.log(message, "debug");
+ this.log("debug", message);
},
info: function(message) {
- this.log(message, "info");
+ this.log("info", message);
},
warn: function(message) {
- this.log(message, "warn");
+ this.log("warn", message);
},
error: function(message) {
- this.log(message, "error");
+ this.log("error", message);
},
exception: function(exception) {
- var msg = "Unexpected Exception: " + describe(exception, ', ');
- this.error(msg);
+ this.error("Unexpected Exception: " + extractExceptionMessage(exception));
+ this.error("Exception details: " + describe(exception, ', '));
}
};
var LOG = new Logger();
-var LogMessage = function(msg, type) {
+var LogMessage = function(type, msg) {
this.type = type;
this.msg = msg;
}
diff --git a/tests/test_tools/selenium/core/scripts/selenium-testrunner.js b/tests/test_tools/selenium/core/scripts/selenium-testrunner.js
index b5104d39..fd7f2076 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-testrunner.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-testrunner.js
@@ -20,36 +20,55 @@ var currentTest = null; // TODO: get rid of this global, which mirrors the htmlT
var selenium = null;
var htmlTestRunner;
-var HtmlTestRunner = Class.create();
-Object.extend(HtmlTestRunner.prototype, {
+var HtmlTestRunner = classCreate();
+objectExtend(HtmlTestRunner.prototype, {
initialize: function() {
this.metrics = new Metrics();
this.controlPanel = new HtmlTestRunnerControlPanel();
- this.htmlTestSuite = null;
this.testFailed = false;
this.currentTest = null;
this.runAllTests = false;
this.appWindow = null;
// we use a timeout here to make sure the LOG has loaded first, so we can see _every_ error
- setTimeout(function() {
+ setTimeout(fnBind(function() {
this.loadSuiteFrame();
- }.bind(this), 500);
+ }, this), 500);
+ },
+
+ getTestSuite: function() {
+ return suiteFrame.getCurrentTestSuite();
},
markFailed: function() {
this.testFailed = true;
- this.htmlTestSuite.markFailed();
+ this.getTestSuite().markFailed();
},
loadSuiteFrame: function() {
if (selenium == null) {
- selenium = Selenium.createForWindow(this._getApplicationWindow());
+ var appWindow = this._getApplicationWindow();
+ try { appWindow.location; }
+ catch (e) {
+ // when reloading, we may be pointing at an old window (Perm Denied)
+ setTimeout(fnBind(function() {
+ this.loadSuiteFrame();
+ }, this), 50);
+ return;
+ }
+ selenium = Selenium.createForWindow(appWindow);
this._registerCommandHandlers();
}
this.controlPanel.setHighlightOption();
var testSuiteName = this.controlPanel.getTestSuiteName();
+ var self = this;
if (testSuiteName) {
- suiteFrame.load(testSuiteName, this._onloadTestSuite.bind(this));
+ suiteFrame.load(testSuiteName, function() {setTimeout(fnBind(self._onloadTestSuite, self), 50)} );
+ selenium.browserbot.baseUrl = absolutify(testSuiteName, window.location.href);
+ }
+ // DGF or should we use the old default?
+ // selenium.browserbot.baseUrl = window.location.href;
+ if (this.controlPanel.getBaseUrl()) {
+ selenium.browserbot.baseUrl = this.controlPanel.getBaseUrl();
}
},
@@ -62,31 +81,30 @@ Object.extend(HtmlTestRunner.prototype, {
_getSeparateApplicationWindow: function () {
if (this.appWindow == null) {
- this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html');
+ this.appWindow = openSeparateApplicationWindow('TestRunner-splash.html', this.controlPanel.isAutomatedRun());
}
return this.appWindow;
},
_onloadTestSuite:function () {
- this.htmlTestSuite = new HtmlTestSuite(suiteFrame.getDocument());
- if (! this.htmlTestSuite.isAvailable()) {
+ if (! this.getTestSuite().isAvailable()) {
return;
}
if (this.controlPanel.isAutomatedRun()) {
- htmlTestRunner.startTestSuite();
+ this.startTestSuite();
} else if (this.controlPanel.getAutoUrl()) {
//todo what is the autourl doing, left to check it out
- addLoadListener(this._getApplicationWindow(), this._startSingleTest.bind(this));
+ addLoadListener(this._getApplicationWindow(), fnBind(this._startSingleTest, this));
this._getApplicationWindow().src = this.controlPanel.getAutoUrl();
} else {
- this.htmlTestSuite.getSuiteRows()[0].loadTestCase();
+ this.getTestSuite().getSuiteRows()[0].loadTestCase();
}
},
_startSingleTest:function () {
- removeLoadListener(getApplicationWindow(), this._startSingleTest.bind(this));
+ removeLoadListener(getApplicationWindow(), fnBind(this._startSingleTest, this));
var singleTestName = this.controlPanel.getSingleTestName();
- testFrame.load(singleTestName, this.startTest.bind(this));
+ testFrame.load(singleTestName, fnBind(this.startTest, this));
},
_registerCommandHandlers: function () {
@@ -97,16 +115,17 @@ Object.extend(HtmlTestRunner.prototype, {
startTestSuite: function() {
this.controlPanel.reset();
this.metrics.resetMetrics();
- this.htmlTestSuite.reset();
+ this.getTestSuite().reset();
this.runAllTests = true;
this.runNextTest();
},
runNextTest: function () {
+ this.getTestSuite().updateSuiteWithResultOfPreviousTest();
if (!this.runAllTests) {
return;
}
- this.htmlTestSuite.runNextTestInSuite();
+ this.getTestSuite().runNextTestInSuite();
},
startTest: function () {
@@ -127,26 +146,15 @@ Object.extend(HtmlTestRunner.prototype, {
}
});
-var FeedbackColors = Class.create();
-Object.extend(FeedbackColors, {
- passColor : "#ccffcc",
- doneColor : "#eeffee",
- failColor : "#ffcccc",
- workingColor : "#ffffcc",
- breakpointColor : "#cccccc"
-});
-
-
var runInterval = 0;
-
/** SeleniumFrame encapsulates an iframe element */
-var SeleniumFrame = Class.create();
-Object.extend(SeleniumFrame.prototype, {
+var SeleniumFrame = classCreate();
+objectExtend(SeleniumFrame.prototype, {
initialize : function(frame) {
this.frame = frame;
- addLoadListener(this.frame, this._handleLoad.bind(this));
+ addLoadListener(this.frame, fnBind(this._handleLoad, this));
},
getDocument : function() {
@@ -154,13 +162,38 @@ Object.extend(SeleniumFrame.prototype, {
},
_handleLoad: function() {
- this._onLoad();
+ this._attachStylesheet();
+ this._onLoad();
if (this.loadCallback) {
this.loadCallback();
this.loadCallback = null;
}
},
+ _attachStylesheet: function() {
+ var d = this.getDocument();
+ var head = d.getElementsByTagName('head').item(0);
+ var styleLink = d.createElement("link");
+ styleLink.rel = "stylesheet";
+ styleLink.type = "text/css";
+ if (browserVersion && browserVersion.isChrome) {
+ // DGF We have to play a clever trick to get the right absolute path.
+ // This trick works on most browsers, (not IE), but is only needed in
+ // chrome
+ var tempLink = window.document.createElement("link");
+ tempLink.href = "selenium-test.css"; // this will become an absolute href
+ styleLink.href = tempLink.href;
+ } else {
+ // this works in every browser (except Firefox in chrome mode)
+ var styleSheetPath = window.location.pathname.replace(/[^\/\\]+$/, "selenium-test.css");
+ if (browserVersion.isIE && window.location.protocol == "file:") {
+ styleSheetPath = "file://" + styleSheetPath;
+ }
+ styleLink.href = styleSheetPath;
+ }
+ head.appendChild(styleLink);
+ },
+
_onLoad: function() {
},
@@ -169,10 +202,14 @@ Object.extend(SeleniumFrame.prototype, {
},
_setLocation: function(location) {
+ var isChrome = browserVersion.isChrome || false;
+ var isHTA = browserVersion.isHTA || false;
+ // DGF TODO multiWindow
+ location += "?thisIsChrome=" + isChrome + "&thisIsHTA=" + isHTA;
if (browserVersion.isSafari) {
- // safari doesn't reload the page when the location equals to current location.
- // hence, set the location to blank so that the page will reload automatically.
- this.frame.src = "about:blank";
+ // safari doesn't reload the page when the location equals to current location.
+ // hence, set the location to blank so that the page will reload automatically.
+ this.frame.src = "about:blank";
this.frame.src = location;
} else {
this.frame.contentWindow.location.replace(location);
@@ -189,18 +226,27 @@ Object.extend(SeleniumFrame.prototype, {
});
+/** HtmlTestSuiteFrame - encapsulates the suite iframe element */
+var HtmlTestSuiteFrame = classCreate();
+objectExtend(HtmlTestSuiteFrame.prototype, SeleniumFrame.prototype);
+objectExtend(HtmlTestSuiteFrame.prototype, {
+
+ getCurrentTestSuite: function() {
+ if (!this.currentTestSuite) {
+ this.currentTestSuite = new HtmlTestSuite(this.getDocument());
+ }
+ return this.currentTestSuite;
+ }
+
+});
+
/** HtmlTestFrame - encapsulates the test-case iframe element */
-var HtmlTestFrame = Class.create();
-Object.extend(HtmlTestFrame.prototype, SeleniumFrame.prototype);
-Object.extend(HtmlTestFrame.prototype, {
+var HtmlTestFrame = classCreate();
+objectExtend(HtmlTestFrame.prototype, SeleniumFrame.prototype);
+objectExtend(HtmlTestFrame.prototype, {
_onLoad: function() {
- this.setCurrentTestCase();
- },
-
- setCurrentTestCase: function() {
- //todo: this is not good looking
- this.currentTestCase = new HtmlTestCase(this.getDocument(), htmlTestRunner.htmlTestSuite.getCurrentRow());
+ this.currentTestCase = new HtmlTestCase(this.getDocument(), htmlTestRunner.getTestSuite().getCurrentRow());
},
getCurrentTestCase: function() {
@@ -210,14 +256,14 @@ Object.extend(HtmlTestFrame.prototype, {
});
function onSeleniumLoad() {
- suiteFrame = new SeleniumFrame(getSuiteFrame());
+ suiteFrame = new HtmlTestSuiteFrame(getSuiteFrame());
testFrame = new HtmlTestFrame(getTestFrame());
htmlTestRunner = new HtmlTestRunner();
}
-
var suiteFrame;
var testFrame;
+
function getSuiteFrame() {
var f = $('testSuiteFrame');
if (f == null) {
@@ -236,9 +282,9 @@ function getTestFrame() {
return f;
}
-var HtmlTestRunnerControlPanel = Class.create();
-Object.extend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype);
-Object.extend(HtmlTestRunnerControlPanel.prototype, {
+var HtmlTestRunnerControlPanel = classCreate();
+objectExtend(HtmlTestRunnerControlPanel.prototype, URLConfiguration.prototype);
+objectExtend(HtmlTestRunnerControlPanel.prototype, {
initialize: function() {
this._acquireQueryString();
@@ -248,16 +294,17 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
this.pauseButton = $('pauseTest');
this.stepButton = $('stepTest');
- this.highlightOption.onclick = (function() {
+ this.highlightOption.onclick = fnBindAsEventListener((function() {
this.setHighlightOption();
- }).bindAsEventListener(this);
- this.pauseButton.onclick = this.pauseCurrentTest.bindAsEventListener(this);
- this.stepButton.onclick = this.stepCurrentTest.bindAsEventListener(this);
+ }), this);
+ this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
+ this.stepButton.onclick = fnBindAsEventListener(this.stepCurrentTest, this);
+
this.speedController = new Control.Slider('speedHandle', 'speedTrack', {
range: $R(0, 1000),
- onSlide: this.setRunInterval.bindAsEventListener(this),
- onChange: this.setRunInterval.bindAsEventListener(this)
+ onSlide: fnBindAsEventListener(this.setRunInterval, this),
+ onChange: fnBindAsEventListener(this.setRunInterval, this)
});
this._parseQueryParameter();
@@ -265,7 +312,7 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
setHighlightOption: function () {
var isHighlight = this.highlightOption.checked;
- selenium.browserbot.getCurrentPage().setHighlightElement(isHighlight);
+ selenium.browserbot.setShouldHighlightElement(isHighlight);
},
_parseQueryParameter: function() {
@@ -295,19 +342,19 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
},
reset: function() {
- this.runInterval = this.speedController.value;
+ // this.runInterval = this.speedController.value;
this._switchContinueButtonToPause();
},
_switchContinueButtonToPause: function() {
- this.pauseButton.innerHTML = "Pause";
- this.pauseButton.onclick = this.pauseCurrentTest.bindAsEventListener(this);
+ this.pauseButton.className = "cssPauseTest";
+ this.pauseButton.onclick = fnBindAsEventListener(this.pauseCurrentTest, this);
},
_switchPauseButtonToContinue: function() {
$('stepTest').disabled = false;
- this.pauseButton.innerHTML = "Continue";
- this.pauseButton.onclick = this.continueCurrentTest.bindAsEventListener(this);
+ this.pauseButton.className = "cssContinueTest";
+ this.pauseButton.onclick = fnBindAsEventListener(this.continueCurrentTest, this);
},
stepCurrentTest: function () {
@@ -356,36 +403,58 @@ Object.extend(HtmlTestRunnerControlPanel.prototype, {
});
-
-var AbstractResultAwareRow = Class.create();
-Object.extend(AbstractResultAwareRow.prototype, {
+var AbstractResultAwareRow = classCreate();
+objectExtend(AbstractResultAwareRow.prototype, {
initialize: function(trElement) {
this.trElement = trElement;
},
- markWorking: function() {
- this.trElement.bgColor = FeedbackColors.workingColor;
+ setStatus: function(status) {
+ this.unselect();
+ this.trElement.className = this.trElement.className.replace(/status_[a-z]+/, "");
+ if (status) {
+ addClassName(this.trElement, "status_" + status);
+ }
+ },
+
+ select: function() {
+ addClassName(this.trElement, "selected");
safeScrollIntoView(this.trElement);
},
+ unselect: function() {
+ removeClassName(this.trElement, "selected");
+ },
+
markPassed: function() {
- this.trElement.bgColor = FeedbackColors.passColor;
+ this.setStatus("passed");
},
markDone: function() {
- this.trElement.bgColor = FeedbackColors.doneColor;
+ this.setStatus("done");
},
markFailed: function() {
- this.trElement.bgColor = FeedbackColors.failColor;
+ this.setStatus("failed");
+ }
+
+});
+
+var TitleRow = classCreate();
+objectExtend(TitleRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(TitleRow.prototype, {
+
+ initialize: function(trElement) {
+ this.trElement = trElement;
+ trElement.className = "title";
}
});
-var HtmlTestCaseRow = Class.create();
-Object.extend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype);
-Object.extend(HtmlTestCaseRow.prototype, {
+var HtmlTestCaseRow = classCreate();
+objectExtend(HtmlTestCaseRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(HtmlTestCaseRow.prototype, {
getCommand: function () {
return new SeleniumCommand(getText(this.trElement.cells[0]),
@@ -395,16 +464,16 @@ Object.extend(HtmlTestCaseRow.prototype, {
},
markFailed: function(errorMsg) {
- this.trElement.bgColor = FeedbackColors.failColor;
+ AbstractResultAwareRow.prototype.markFailed.call(this, errorMsg);
this.setMessage(errorMsg);
},
setMessage: function(message) {
- this.trElement.cells[2].innerHTML = message;
+ setText(this.trElement.cells[2], message);
},
reset: function() {
- this.trElement.bgColor = '';
+ this.setStatus(null);
var thirdCell = this.trElement.cells[2];
if (thirdCell) {
if (thirdCell.originalHTML) {
@@ -418,19 +487,18 @@ Object.extend(HtmlTestCaseRow.prototype, {
onClick: function() {
if (this.trElement.isBreakpoint == undefined) {
this.trElement.isBreakpoint = true;
- this.trElement.beforeBackgroundColor = Element.getStyle(this.trElement, "backgroundColor");
- Element.setStyle(this.trElement, {"background-color" : FeedbackColors.breakpointColor});
+ addClassName(this.trElement, "breakpoint");
} else {
this.trElement.isBreakpoint = undefined;
- Element.setStyle(this.trElement, {"background-color" : this.trElement.beforeBackgroundColor});
+ removeClassName(this.trElement, "breakpoint");
}
},
addBreakpointSupport: function() {
- Element.setStyle(this.trElement, {"cursor" : "pointer"});
- this.trElement.onclick = function() {
+ elementSetStyle(this.trElement, {"cursor" : "pointer"});
+ this.trElement.onclick = fnBindAsEventListener(function() {
this.onClick();
- }.bindAsEventListener(this);
+ }, this);
},
isBreakpoint: function() {
@@ -441,42 +509,44 @@ Object.extend(HtmlTestCaseRow.prototype, {
}
});
-var HtmlTestSuiteRow = Class.create();
-Object.extend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype);
-Object.extend(HtmlTestSuiteRow.prototype, {
+var HtmlTestSuiteRow = classCreate();
+objectExtend(HtmlTestSuiteRow.prototype, AbstractResultAwareRow.prototype);
+objectExtend(HtmlTestSuiteRow.prototype, {
initialize: function(trElement, testFrame, htmlTestSuite) {
this.trElement = trElement;
this.testFrame = testFrame;
this.htmlTestSuite = htmlTestSuite;
this.link = trElement.getElementsByTagName("a")[0];
- this.link.onclick = this._onClick.bindAsEventListener(this);
+ this.link.onclick = fnBindAsEventListener(this._onClick, this);
},
reset: function() {
- this.trElement.bgColor = '';
+ this.setStatus(null);
},
_onClick: function() {
- // todo: just send a message to the testSuite
this.loadTestCase(null);
return false;
},
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) {
- // this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
+ // todo: delegate to TestFrame, e.g.
+ // this.testFrame.restoreTestCase(resultsFromPreviousRun.innerHTML);
var testBody = this.testFrame.getDocument().body;
testBody.innerHTML = resultsFromPreviousRun.innerHTML;
- testFrame.setCurrentTestCase();
+ this.testFrame._onLoad();
if (onloadFunction) {
onloadFunction();
}
} else {
- this.testFrame.load(this.link.href, onloadFunction);
+ this.testFrame.load(this.link.href, onloadFunction);
}
},
@@ -498,28 +568,24 @@ Object.extend(HtmlTestSuiteRow.prototype, {
});
-var HtmlTestSuite = Class.create();
-Object.extend(HtmlTestSuite.prototype, {
+var HtmlTestSuite = classCreate();
+objectExtend(HtmlTestSuite.prototype, {
initialize: function(suiteDocument) {
this.suiteDocument = suiteDocument;
this.suiteRows = this._collectSuiteRows();
- this.titleRow = this.getTestTable().rows[0];
- this.title = this.titleRow.cells[0].innerHTML;
+ this.titleRow = new TitleRow(this.getTestTable().rows[0]);
this.reset();
},
reset: function() {
this.failed = false;
this.currentRowInSuite = -1;
- this.titleRow.bgColor = '';
- this.suiteRows.each(function(row) {
+ this.titleRow.setStatus(null);
+ for (var i = 0; i < this.suiteRows.length; i++) {
+ var row = this.suiteRows[i];
row.reset();
- });
- },
-
- getTitle: function() {
- return this.title;
+ }
},
getSuiteRows: function() {
@@ -537,31 +603,51 @@ Object.extend(HtmlTestSuite.prototype, {
_collectSuiteRows: function () {
var result = [];
- for (rowNum = 1; rowNum < this.getTestTable().rows.length; rowNum++) {
- var rowElement = this.getTestTable().rows[rowNum];
+ var tables = $A(this.suiteDocument.getElementsByTagName("table"));
+ var testTable = tables[0];
+ for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
+ var rowElement = testTable.rows[rowNum];
result.push(new HtmlTestSuiteRow(rowElement, testFrame, this));
}
+
+ // process the unsuited rows as well
+ for (var tableNum = 1; tableNum < $A(this.suiteDocument.getElementsByTagName("table")).length; tableNum++) {
+ testTable = tables[tableNum];
+ for (rowNum = 1; rowNum < testTable.rows.length; rowNum++) {
+ var rowElement = testTable.rows[rowNum];
+ new HtmlTestSuiteRow(rowElement, testFrame, this);
+ }
+ }
return result;
},
getCurrentRow: function() {
+ if (this.currentRowInSuite == -1) {
+ return null;
+ }
return this.suiteRows[this.currentRowInSuite];
},
+ unselectCurrentRow: function() {
+ var currentRow = this.getCurrentRow()
+ if (currentRow) {
+ currentRow.unselect();
+ }
+ },
+
markFailed: function() {
this.failed = true;
- this.titleRow.bgColor = FeedbackColors.failColor;
+ this.titleRow.markFailed();
},
markDone: function() {
if (!this.failed) {
- this.titleRow.bgColor = FeedbackColors.passColor;
+ this.titleRow.markPassed();
}
},
_startCurrentTestCase: function() {
- this.getCurrentRow().markWorking();
- this.getCurrentRow().loadTestCase(htmlTestRunner.startTest.bind(htmlTestRunner));
+ this.getCurrentRow().loadTestCase(fnBind(htmlTestRunner.startTest, htmlTestRunner));
},
_onTestSuiteComplete: function() {
@@ -569,14 +655,13 @@ Object.extend(HtmlTestSuite.prototype, {
new TestResult(this.failed, this.getTestTable()).post();
},
- _updateSuiteWithResultOfPreviousTest: function() {
+ updateSuiteWithResultOfPreviousTest: function() {
if (this.currentRowInSuite >= 0) {
this.getCurrentRow().saveTestResults();
}
},
runNextTestInSuite: function() {
- this._updateSuiteWithResultOfPreviousTest();
this.currentRowInSuite++;
// If we are done with all of the tests, set the title bar as pass or fail
@@ -591,8 +676,8 @@ Object.extend(HtmlTestSuite.prototype, {
});
-var TestResult = Class.create();
-Object.extend(TestResult.prototype, {
+var TestResult = classCreate();
+objectExtend(TestResult.prototype, {
// Post the results to a servlet, CGI-script, etc. The URL of the
// results-handler defaults to "/postResults", but an alternative location
@@ -724,8 +809,8 @@ Object.extend(TestResult.prototype, {
});
/** HtmlTestCase encapsulates an HTML test document */
-var HtmlTestCase = Class.create();
-Object.extend(HtmlTestCase.prototype, {
+var HtmlTestCase = classCreate();
+objectExtend(HtmlTestCase.prototype, {
initialize: function(testDocument, htmlTestSuiteRow) {
if (testDocument == null) {
@@ -736,6 +821,7 @@ Object.extend(HtmlTestCase.prototype, {
}
this.testDocument = testDocument;
this.htmlTestSuiteRow = htmlTestSuiteRow;
+ this.headerRow = new TitleRow(this.testDocument.getElementsByTagName("tr")[0]);
this.commandRows = this._collectCommandRows();
this.nextCommandRowIndex = 0;
this._addBreakpointSupport();
@@ -745,13 +831,16 @@ Object.extend(HtmlTestCase.prototype, {
var commandRows = [];
var tables = $A(this.testDocument.getElementsByTagName("table"));
var self = this;
- tables.each(function (table) {
- $A(table.rows).each(function(candidateRow) {
+ for (var i = 0; i < tables.length; i++) {
+ var table = tables[i];
+ var tableRows = $A(table.rows);
+ for (var j = 0; j < tableRows.length; j++) {
+ var candidateRow = tableRows[j];
if (self.isCommandRow(candidateRow)) {
commandRows.push(new HtmlTestCaseRow(candidateRow));
}
- }.bind(this));
- });
+ }
+ }
return commandRows;
},
@@ -765,15 +854,16 @@ Object.extend(HtmlTestCase.prototype, {
*/
this.nextCommandRowIndex = 0;
- this._setTitleColor('');
- this.commandRows.each(function(row) {
+ this.setStatus('');
+ for (var i = 0; i < this.commandRows.length; i++) {
+ var row = this.commandRows[i];
row.reset();
- });
+ }
// remove any additional fake "error" row added to the end of the document
var errorElement = this.testDocument.getElementById('error');
if (errorElement) {
- Element.remove(errorElement);
+ errorElement.parentNode.removeChild(errorElement);
}
},
@@ -781,39 +871,38 @@ Object.extend(HtmlTestCase.prototype, {
return this.commandRows;
},
- _setTitleColor: function(color) {
- var headerRow = this.testDocument.getElementsByTagName("tr")[0];
- if (headerRow) {
- headerRow.bgColor = color;
- }
+ setStatus: function(status) {
+ this.headerRow.setStatus(status);
},
markFailed: function() {
- this._setTitleColor(FeedbackColors.failColor);
+ this.setStatus("failed");
this.htmlTestSuiteRow.markFailed();
},
markPassed: function() {
- this._setTitleColor(FeedbackColors.passColor);
+ this.setStatus("passed");
this.htmlTestSuiteRow.markPassed();
},
addErrorMessage: function(errorMsg, currentRow) {
+ errorMsg = errorMsg.replace(/ /g, String.fromCharCode(160)).replace("\n", '\\n');
if (currentRow) {
currentRow.markFailed(errorMsg);
} else {
var errorElement = this.testDocument.createElement("p");
errorElement.id = "error";
- errorElement.innerHTML = errorMsg;
+ setText(errorElement, errorMsg);
this.testDocument.body.appendChild(errorElement);
- Element.setStyle(errorElement, {'backgroundColor': FeedbackColors.failColor});
+ errorElement.className = "status_failed";
}
},
_addBreakpointSupport: function() {
- this.commandRows.each(function(row) {
+ for (var i = 0; i < this.commandRows.length; i++) {
+ var row = this.commandRows[i];
row.addBreakpointSupport();
- });
+ }
},
hasMoreCommandRows: function() {
@@ -850,8 +939,8 @@ var get_new_rows = function() {
};
-var Metrics = Class.create();
-Object.extend(Metrics.prototype, {
+var Metrics = classCreate();
+objectExtend(Metrics.prototype, {
initialize: function() {
// The number of tests run
this.numTestPasses = 0;
@@ -903,20 +992,13 @@ Object.extend(Metrics.prototype, {
});
-var HtmlRunnerCommandFactory = Class.create();
-Object.extend(HtmlRunnerCommandFactory.prototype, {
+var HtmlRunnerCommandFactory = classCreate();
+objectExtend(HtmlRunnerCommandFactory.prototype, {
initialize: function(seleniumCommandFactory, testLoop) {
this.seleniumCommandFactory = seleniumCommandFactory;
this.testLoop = testLoop;
- this.handlers = {
- pause: {
- execute: function(selenium, command) {
- testLoop.pauseInterval = command.target;
- return {};
- }
- }
- };
+ this.handlers = {};
//todo: register commands
},
@@ -929,9 +1011,9 @@ Object.extend(HtmlRunnerCommandFactory.prototype, {
});
-var HtmlRunnerTestLoop = Class.create();
-Object.extend(HtmlRunnerTestLoop.prototype, new TestLoop());
-Object.extend(HtmlRunnerTestLoop.prototype, {
+var HtmlRunnerTestLoop = classCreate();
+objectExtend(HtmlRunnerTestLoop.prototype, new TestLoop());
+objectExtend(HtmlRunnerTestLoop.prototype, {
initialize: function(htmlTestCase, metrics, seleniumCommandFactory) {
this.commandFactory = new HtmlRunnerCommandFactory(seleniumCommandFactory, this);
@@ -948,6 +1030,8 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
// used for selenium tests in javascript
this.currentItem = null;
this.commandAgenda = new Array();
+ this.expectedFailure = null;
+ this.expectedFailureType = null;
this.htmlTestCase.reset();
@@ -986,11 +1070,12 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
commandStarted : function() {
$('pauseTest').disabled = false;
- this.currentRow.markWorking();
+ this.currentRow.select();
this.metrics.printMetrics();
},
commandComplete : function(result) {
+ this._checkExpectedFailure(result);
if (result.failed) {
this.metrics.numCommandFailures += 1;
this._recordFailure(result.failureMessage);
@@ -1002,7 +1087,49 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
}
},
+ _checkExpectedFailure : function(result) {
+ if (this.expectedFailure != null) {
+ if (this.expectedFailureJustSet) {
+ this.expectedFailureJustSet = false;
+ return;
+ }
+ if (!result.failed) {
+ result.passed = false;
+ result.failed = true;
+ result.failureMessage = "Expected " + this.expectedFailureType + " did not occur.";
+ } else {
+ if (PatternMatcher.matches(this.expectedFailure, result.failureMessage)) {
+ var failureType = result.error ? "error" : "failure";
+ if (failureType == this.expectedFailureType) {
+ result.failed = false;
+ result.passed = true;
+ } else {
+ result.failed = true;
+ result.failureMessage = "Expected "+this.expectedFailureType+", but "+failureType+" occurred instead";
+ }
+ } else {
+ result.failed = true;
+ result.failureMessage = "Expected " + this.expectedFailureType + " message '" + this.expectedFailure
+ + "' but was '" + result.failureMessage + "'";
+ }
+ }
+ this.expectedFailure = null;
+ this.expectedFailureType = null;
+ }
+ },
+
commandError : function(errorMessage) {
+ var tempResult = {};
+ tempResult.passed = false;
+ tempResult.failed = true;
+ tempResult.error = true;
+ tempResult.failureMessage = errorMessage;
+ this._checkExpectedFailure(tempResult);
+ if (tempResult.passed) {
+ this.currentRow.markDone();
+ return true;
+ }
+ errorMessage = tempResult.failureMessage;
this.metrics.numCommandErrors += 1;
this._recordFailure(errorMessage);
},
@@ -1062,6 +1189,13 @@ Object.extend(HtmlRunnerTestLoop.prototype, {
});
+Selenium.prototype.doPause = function(waitTime) {
+ /** Wait for the specified amount of time (in milliseconds)
+ * @param waitTime the amount of time to sleep (in milliseconds)
+ */
+ // todo: should not refer to currentTest directly
+ currentTest.pauseInterval = waitTime;
+};
Selenium.prototype.doBreak = function() {
/** Halt the currently running test, and wait for the user to press the Continue button.
@@ -1115,81 +1249,33 @@ Selenium.prototype.assertSelected = function(selectLocator, optionLocator) {
locator.assertSelected(element);
};
-/**
- * Tell Selenium to expect a failure on the next command execution. This
- * command temporarily installs a CommandFactory that generates
- * CommandHandlers that expect a failure.
- */
Selenium.prototype.assertFailureOnNext = function(message) {
+ /**
+ * Tell Selenium to expect a failure on the next command execution.
+ * @param message The failure message we should expect. This command will fail if the wrong failure message appears.
+ */
if (!message) {
- throw new Error("Message must be provided");
+ throw new SeleniumError("Message must be provided");
}
- var expectFailureCommandFactory =
- new ExpectFailureCommandFactory(currentTest.commandFactory, message, "failure", executeCommandAndReturnFailureMessage);
- currentTest.commandFactory = expectFailureCommandFactory;
+ currentTest.expectedFailure = message;
+ currentTest.expectedFailureType = "failure";
+ currentTest.expectedFailureJustSet = true;
};
-/**
- * Tell Selenium to expect an error on the next command execution. This
- * command temporarily installs a CommandFactory that generates
- * CommandHandlers that expect a failure.
- */
Selenium.prototype.assertErrorOnNext = function(message) {
+ /**
+ * Tell Selenium to expect an error on the next command execution.
+ * @param message The error message we should expect. This command will fail if the wrong error message appears.
+ */
+ // This command temporarily installs a CommandFactory that generates
+ // CommandHandlers that expect an error.
if (!message) {
- throw new Error("Message must be provided");
+ throw new SeleniumError("Message must be provided");
}
- var expectFailureCommandFactory =
- new ExpectFailureCommandFactory(currentTest.commandFactory, message, "error", executeCommandAndReturnErrorMessage);
- currentTest.commandFactory = expectFailureCommandFactory;
-};
-
-function executeCommandAndReturnFailureMessage(baseHandler, originalArguments) {
- var baseResult = baseHandler.execute.apply(baseHandler, originalArguments);
- if (baseResult.passed) {
- return null;
- }
- return baseResult.failureMessage;
-};
-
-function executeCommandAndReturnErrorMessage(baseHandler, originalArguments) {
- try {
- baseHandler.execute.apply(baseHandler, originalArguments);
- return null;
- }
- catch (expected) {
- return expected.message;
- }
+ currentTest.expectedFailure = message;
+ currentTest.expectedFailureType = "error";
+ currentTest.expectedFailureJustSet = true;
};
-function ExpectFailureCommandHandler(baseHandler, originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor) {
- this.execute = function() {
- var baseFailureMessage = decoratedExecutor(baseHandler, arguments);
- var result = {};
- if (!baseFailureMessage) {
- result.failed = true;
- result.failureMessage = "Expected " + errorType + " did not occur.";
- }
- else {
- if (! PatternMatcher.matches(expectedErrorMessage, baseFailureMessage)) {
- result.failed = true;
- result.failureMessage = "Expected " + errorType + " message '" + expectedErrorMessage
- + "' but was '" + baseFailureMessage + "'";
- }
- else {
- result.passed = true;
- result.result = baseFailureMessage;
- }
- }
- currentTest.commandFactory = originalCommandFactory;
- return result;
- };
-}
-
-function ExpectFailureCommandFactory(originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor) {
- this.getCommandHandler = function(name) {
- var baseHandler = originalCommandFactory.getCommandHandler(name);
- return new ExpectFailureCommandHandler(baseHandler, originalCommandFactory, expectedErrorMessage, errorType, decoratedExecutor);
- };
-};
diff --git a/tests/test_tools/selenium/core/scripts/selenium-version.js b/tests/test_tools/selenium/core/scripts/selenium-version.js
index c4a5508c..4156723d 100644
--- a/tests/test_tools/selenium/core/scripts/selenium-version.js
+++ b/tests/test_tools/selenium/core/scripts/selenium-version.js
@@ -1,5 +1,5 @@
-Selenium.version = "0.8.0";
-Selenium.revision = "1472:1473";
+Selenium.version = "0.8.2";
+Selenium.revision = "1727";
window.top.document.title += " v" + Selenium.version + " [" + Selenium.revision + "]";
diff --git a/tests/test_tools/selenium/core/selenium.css b/tests/test_tools/selenium/core/selenium.css
index 963c63ad..f5240822 100644
--- a/tests/test_tools/selenium/core/selenium.css
+++ b/tests/test_tools/selenium/core/selenium.css
@@ -17,12 +17,12 @@
/*---( Layout )---*/
* {
- margin: 0px;
- padding: 0px;
}
body {
overflow: auto;
+ margin: 0px;
+ padding: 0px;
}
td {
@@ -84,9 +84,6 @@ body, html {
color: black;
}
-#controlPanel button {
- margin: 0.5ex;
-}
#controlPanel table {
font-size: 100%;
@@ -206,33 +203,6 @@ button, label {
color: green;
}
-table.selenium {
- font-family: Verdana, Arial, sans-serif;
- font-size: 12;
- border-width: 1px 1px 1px 1px;
- border-spacing: 2px;
- border-style: solid none solid none;
- border-color: gray gray gray gray;
- border-collapse: separate;
- background-color: white;
-}
-
-table.selenium th {
- border-width: 1px 1px 1px 1px;
- padding: 1px 1px 1px 1px;
- border-style: none none none none;
- border-color: gray gray gray gray;
- -moz-border-radius: 0px 0px 0px 0px;
-}
-
-table.selenium td {
- border-width: 1px 1px 1px 1px;
- padding: 1px 1px 1px 1px;
- border-style: none none none none;
- border-color: gray gray gray gray;
- -moz-border-radius: 0px 0px 0px 0px;
-}
-
div.executionOptions {
padding-left: 5em;
}
@@ -259,10 +229,7 @@ div.executionOptions br {
background-color: #333;
width: 260px;
height: 2px;
- <!--[if IE]>
- height: 2px;
- line-height: 2px;
- <![endif]-->
+ line-height: 2px;
z-index: 1;
border: 1px solid;
border-color: #999 #ddd #ddd #999;
@@ -276,10 +243,7 @@ div.executionOptions br {
position: relative;
margin: 0px;
height: 8px;
- <!--[if IE]>
- height: 8px;
- line-height: 8px;
- <![endif]-->
+ line-height: 8px;
z-index: 1;
border: 1px solid;
border-color: #999 #333 #333 #999;