Update selenium on rails using 'official' git repo

git://github.com/paytonrules/selenium-on-rails.git
This commit is contained in:
Reinier Balt 2008-12-02 10:05:41 +01:00
parent 198f3240b8
commit 9b504b3e47
159 changed files with 16409 additions and 11794 deletions

View file

@ -0,0 +1,69 @@
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*
*/
elementFindMatchingChildren = function(element, selector) {
var matches = [];
var childCount = element.childNodes.length;
for (var i=0; i<childCount; i++) {
var child = element.childNodes[i];
if (selector(child)) {
matches.push(child);
} else {
childMatches = elementFindMatchingChildren(child, selector);
matches.push(childMatches);
}
}
return matches.flatten();
}
ELEMENT_NODE_TYPE = 1;
elementFindFirstMatchingChild = function(element, selector) {
var childCount = element.childNodes.length;
for (var i=0; i<childCount; i++) {
var child = element.childNodes[i];
if (child.nodeType == ELEMENT_NODE_TYPE) {
if (selector(child)) {
return child;
}
result = elementFindFirstMatchingChild(child, selector);
if (result) {
return result;
}
}
}
return null;
}
elementFindFirstMatchingParent = function(element, selector) {
var current = element.parentNode;
while (current != null) {
if (selector(current)) {
break;
}
current = current.parentNode;
}
return current;
}
elementFindMatchingChildById = function(element, id) {
return elementFindFirstMatchingChild(element, function(element){return element.id==id} );
}

View file

@ -0,0 +1,842 @@
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*
*/
// 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
return result.replace(/\s+$/g, "");
// strip trailing
};
String.prototype.lcfirst = function() {
return this.charAt(0).toLowerCase() + this.substr(1);
};
String.prototype.ucfirst = function() {
return this.charAt(0).toUpperCase() + this.substr(1);
};
String.prototype.startsWith = function(str) {
return this.indexOf(str) == 0;
};
// Returns the text in this element
function getText(element) {
var text = "";
var isRecentFirefox = (browserVersion.isFirefox && browserVersion.firefoxVersion >= "1.5");
if (isRecentFirefox || browserVersion.isKonqueror || browserVersion.isSafari || browserVersion.isOpera) {
text = getTextContent(element);
} else if (element.textContent) {
text = element.textContent;
} else if (element.innerText) {
text = element.innerText;
}
text = normalizeNewlines(text);
text = normalizeSpaces(text);
return text.trim();
}
function getTextContent(element, preformatted) {
if (element.nodeType == 3 /*Node.TEXT_NODE*/) {
var text = element.data;
if (!preformatted) {
text = text.replace(/\n|\r|\t/g, " ");
}
return text;
}
if (element.nodeType == 1 /*Node.ELEMENT_NODE*/) {
var childrenPreformatted = preformatted || (element.tagName == "PRE");
var text = "";
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes.item(i);
text += getTextContent(child, childrenPreformatted);
}
// Handle block elements that introduce newlines
// -- From HTML spec:
//<!ENTITY % block
// "P | %heading; | %list; | %preformatted; | DL | DIV | NOSCRIPT |
// BLOCKQUOTE | F:wORM | HR | TABLE | FIELDSET | ADDRESS">
//
// TODO: should potentially introduce multiple newlines to separate blocks
if (element.tagName == "P" || element.tagName == "BR" || element.tagName == "HR" || element.tagName == "DIV") {
text += "\n";
}
return text;
}
return '';
}
/**
* Convert all newlines to \m
*/
function normalizeNewlines(text)
{
return text.replace(/\r\n|\r/g, "\n");
}
/**
* Replace multiple sequential spaces with a single space, and then convert &nbsp; to space.
*/
function normalizeSpaces(text)
{
// IE has already done this conversion, so doing it again will remove multiple nbsp
if (browserVersion.isIE)
{
return text;
}
// Replace multiple spaces with a single space
// TODO - this shouldn't occur inside PRE elements
text = text.replace(/\ +/g, " ");
// Replace &nbsp; with a space
var nbspPattern = new RegExp(String.fromCharCode(160), "g");
if (browserVersion.isSafari) {
return replaceAll(text, String.fromCharCode(160), " ");
} else {
return text.replace(nbspPattern, " ");
}
}
function replaceAll(text, oldText, newText) {
while (text.indexOf(oldText) != -1) {
text = text.replace(oldText, newText);
}
return text;
}
function xmlDecode(text) {
text = text.replace(/&quot;/g, '"');
text = text.replace(/&apos;/g, "'");
text = text.replace(/&lt;/g, "<");
text = text.replace(/&gt;/g, ">");
text = text.replace(/&amp;/g, "&");
return text;
}
// Sets the text in this element
function setText(element, text) {
if (element.textContent != null) {
element.textContent = text;
} else if (element.innerText != null) {
element.innerText = text;
}
}
// Get the value of an <input> element
function getInputValue(inputElement) {
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, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) {
canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
if (element.fireEvent) {
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);
}
}
function getKeyCodeFromKeySequence(keySequence) {
var match = /^\\(\d{1,3})$/.exec(keySequence);
if (match != null) {
return match[1];
}
match = /^.$/.exec(keySequence);
if (match != null) {
return match[0].charCodeAt(0);
}
// this is for backward compatibility with existing tests
// 1 digit ascii codes will break however because they are used for the digit chars
match = /^\d{2,3}$/.exec(keySequence);
if (match != null) {
return match[0];
}
throw new SeleniumError("invalid keySequence");
}
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) {
var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown);
keyEvent.keyCode = keycode;
element.fireEvent('on' + eventType, keyEvent);
}
else {
var evt;
if (window.KeyEvent) {
evt = document.createEvent('KeyEvents');
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);
}
}
function removeLoadListener(element, command) {
LOG.info('Removing loadListenter for ' + element + ', ' + command);
if (window.removeEventListener)
element.removeEventListener("load", command, true);
else if (window.detachEvent)
element.detachEvent("onload", 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", augmentedCommand, true);
else if (window.attachEvent)
element.attachEvent("onload", augmentedCommand);
}
/**
* Override the broken getFunctionName() method from JsUnit
* This file must be loaded _after_ the jsunitCore.js
*/
function getFunctionName(aFunction) {
var regexpResult = aFunction.toString().match(/function (\w*)/);
if (regexpResult && regexpResult[1]) {
return regexpResult[1];
}
return 'anonymous';
}
function getDocumentBase(doc) {
var bases = document.getElementsByTagName("base");
if (bases && bases.length && bases[0].href) {
return bases[0].href;
}
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) {
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');
}
var PatternMatcher = function(pattern) {
this.selectStrategy(pattern);
};
PatternMatcher.prototype = {
selectStrategy: function(pattern) {
this.pattern = pattern;
var strategyName = 'glob';
// by default
if (/^([a-z-]+):(.*)/.test(pattern)) {
var possibleNewStrategyName = RegExp.$1;
var possibleNewPattern = RegExp.$2;
if (PatternMatcher.strategies[possibleNewStrategyName]) {
strategyName = possibleNewStrategyName;
pattern = possibleNewPattern;
}
}
var matchStrategy = PatternMatcher.strategies[strategyName];
if (!matchStrategy) {
throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName);
}
this.strategy = matchStrategy;
this.matcher = new matchStrategy(pattern);
},
matches: function(actual) {
return this.matcher.matches(actual + '');
// Note: appending an empty string avoids a Konqueror bug
}
};
/**
* A "static" convenience method for easy matching
*/
PatternMatcher.matches = function(pattern, actual) {
return new PatternMatcher(pattern).matches(actual);
};
PatternMatcher.strategies = {
/**
* Exact matching, e.g. "exact:***"
*/
exact: function(expected) {
this.expected = expected;
this.matches = function(actual) {
return actual == this.expected;
};
},
/**
* Match by regular expression, e.g. "regexp:^[0-9]+$"
*/
regexp: function(regexpString) {
this.regexp = new RegExp(regexpString);
this.matches = function(actual) {
return this.regexp.test(actual);
};
},
regex: function(regexpString) {
this.regexp = new RegExp(regexpString);
this.matches = function(actual) {
return this.regexp.test(actual);
};
},
/**
* "globContains" (aka "wildmat") patterns, e.g. "glob:one,two,*",
* but don't require a perfect match; instead succeed if actual
* contains something that matches globString.
* Making this distinction is motivated by a bug in IE6 which
* leads to the browser hanging if we implement *TextPresent tests
* by just matching against a regular expression beginning and
* ending with ".*". The globcontains strategy allows us to satisfy
* the functional needs of the *TextPresent ops more efficiently
* and so avoid running into this IE6 freeze.
*/
globContains: function(globString) {
this.regexp = new RegExp(PatternMatcher.regexpFromGlobContains(globString));
this.matches = function(actual) {
return this.regexp.test(actual);
};
},
/**
* "glob" (aka "wildmat") patterns, e.g. "glob:one,two,*"
*/
glob: function(globString) {
this.regexp = new RegExp(PatternMatcher.regexpFromGlob(globString));
this.matches = function(actual) {
return this.regexp.test(actual);
};
}
};
PatternMatcher.convertGlobMetaCharsToRegexpMetaChars = function(glob) {
var re = glob;
re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1");
re = re.replace(/\?/g, "(.|[\r\n])");
re = re.replace(/\*/g, "(.|[\r\n])*");
return re;
};
PatternMatcher.regexpFromGlobContains = function(globContains) {
return PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(globContains);
};
PatternMatcher.regexpFromGlob = function(glob) {
return "^" + PatternMatcher.convertGlobMetaCharsToRegexpMetaChars(glob) + "$";
};
var Assert = {
fail: function(message) {
throw new AssertionFailedError(message);
},
/*
* Assert.equals(comment?, expected, actual)
*/
equals: function() {
var args = new AssertionArguments(arguments);
if (args.expected === args.actual) {
return;
}
Assert.fail(args.comment +
"Expected '" + args.expected +
"' but was '" + args.actual + "'");
},
/*
* Assert.matches(comment?, pattern, actual)
*/
matches: function() {
var args = new AssertionArguments(arguments);
if (PatternMatcher.matches(args.expected, args.actual)) {
return;
}
Assert.fail(args.comment +
"Actual value '" + args.actual +
"' did not match '" + args.expected + "'");
},
/*
* Assert.notMtches(comment?, pattern, actual)
*/
notMatches: function() {
var args = new AssertionArguments(arguments);
if (!PatternMatcher.matches(args.expected, args.actual)) {
return;
}
Assert.fail(args.comment +
"Actual value '" + args.actual +
"' did match '" + args.expected + "'");
}
};
// Preprocess the arguments to allow for an optional comment.
function AssertionArguments(args) {
if (args.length == 2) {
this.comment = "";
this.expected = args[0];
this.actual = args[1];
} else {
this.comment = args[0] + "; ";
this.expected = args[1];
this.actual = args[2];
}
}
function AssertionFailedError(message) {
this.isAssertionFailedError = true;
this.isSeleniumError = true;
this.message = message;
this.failureMessage = message;
}
function SeleniumError(message) {
var error = new Error(message);
error.isSeleniumError = true;
return error;
}
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;
}
elementSetStyle(element, {"backgroundColor" : element.originalColor});
} catch (e) {} // DGF unhighlighting is very dangerous and low priority
}, 200);
}
// for use from vs.2003 debugger
function o2s(obj) {
var s = "";
for (key in obj) {
var line = key + "->" + obj[key];
line.replace("\n", " ");
s += line + "\n";
}
return s;
}
var seenReadyStateWarning = false;
function openSeparateApplicationWindow(url, suppressMozillaWarning) {
// resize the Selenium window itself
window.resizeTo(1200, 500);
window.moveTo(window.screenX, 0);
var appWindow = window.open(url + '?start=true', 'main');
try {
var windowHeight = 500;
if (window.outerHeight) {
windowHeight = window.outerHeight;
} else if (document.documentElement && document.documentElement.offsetHeight) {
windowHeight = document.documentElement.offsetHeight;
}
if (window.screenLeft && !window.screenX) window.screenX = window.screenLeft;
if (window.screenTop && !window.screenY) window.screenY = window.screenTop;
appWindow.resizeTo(1200, screen.availHeight - windowHeight - 60);
appWindow.moveTo(window.screenX, window.screenY + windowHeight + 25);
} catch (e) {
LOG.error("Couldn't resize app window");
LOG.exception(e);
}
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;
}
return appWindow;
}
var URLConfiguration = classCreate();
objectExtend(URLConfiguration.prototype, {
initialize: function() {
},
_isQueryParameterTrue: function (name) {
var parameterValue = this._getQueryParameter(name);
if (parameterValue == null) return false;
if (parameterValue.toLowerCase() == "true") return true;
if (parameterValue.toLowerCase() == "on") return true;
return false;
},
_getQueryParameter: function(searchKey) {
var str = this.queryString
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;
},
_extractArgs: function() {
var str = SeleniumHTARunner.commandLine;
if (str == null || str == "") return new Array();
var matches = str.match(/(?:\"([^\"]+)\"|(?!\"([^\"]+)\")(\S+))/g);
// We either want non quote stuff ([^"]+) surrounded by quotes
// or we want to look-ahead, see that the next character isn't
// a quoted argument, and then grab all the non-space stuff
// this will return for the line: "foo" bar
// the results "\"foo\"" and "bar"
// So, let's unquote the quoted arguments:
var args = new Array;
for (var i = 0; i < matches.length; i++) {
args[i] = matches[i];
args[i] = args[i].replace(/^"(.*)"$/, "$1");
}
return args;
},
isMultiWindowMode:function() {
return this._isQueryParameterTrue('multiWindow');
},
getBaseUrl:function() {
return this._getQueryParameter('baseUrl');
}
});
function safeScrollIntoView(element) {
if (element.scrollIntoView) {
element.scrollIntoView(false);
return;
}
// TODO: work out how to scroll browsers that don't support
// scrollIntoView (like Konqueror)
}

View file

@ -0,0 +1,79 @@
<script language="JavaScript">
if (window["selenium_has_been_loaded_into_this_window"]==null)
{
__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
// assigned a regular frame for selenium?)
//
BrowserBot.prototype.getContentWindow = function() {
if (window["seleniumInSameWindow"] != null) return window;
return window.parent;
};
BrowserBot.prototype.getTargetWindow = function(windowName) {
if (window["seleniumInSameWindow"] != null) return window;
return window.parent;
};
BrowserBot.prototype.getCurrentWindow = function() {
if (window["seleniumInSameWindow"] != null) return window;
return window.parent;
};
LOG.openLogWindow = function(message, className) {
// disable for now
};
BrowserBot.prototype.relayToRC = function(name) {
var object = eval(name);
var s = 'state:' + serializeObject(name, object) + "\n";
sendToRC(s,"state=true");
}
BrowserBot.prototype.relayBotToRC = function(s) {
this.relayToRC("selenium." + s);
}
function selenium_frameRunTest(oldOnLoadRoutine) {
if (oldOnLoadRoutine) {
eval(oldOnLoadRoutine);
}
runSeleniumTest();
}
function seleniumOnLoad() {
injectedSessionId = @SESSION_ID@;
window["selenium_has_been_loaded_into_this_window"] = true;
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";
}
injectedSessionId = @SESSION_ID@;
}
</script>

View file

@ -0,0 +1,7 @@
<script language="JavaScript">
// Ideally I would avoid polluting the namespace by enclosing this snippet with
// curly braces, but I want to make it easy to look at what URL I used for anyone
// who is interested in looking into http://jira.openqa.org/browse/SRC-101:
var _sel_url_ = "http://" + location.host + "/selenium-server/core/scripts/injection.html";
document.write('<iframe name="selenium" width=0 height=0 id="selenium" src="' + _sel_url_ + '"></iframe>');
</script>

View file

@ -0,0 +1,70 @@
/*
This is an experiment in using the Narcissus JavaScript engine
to allow Selenium scripts to be written in plain JavaScript.
The 'jsparse' function will compile each high level block into a Selenium table script.
TODO:
1) Test! (More browsers, more sample scripts)
2) Stepping and walking lower levels of the parse tree
3) Calling Selenium commands directly from JavaScript
4) Do we want comments to appear in the TestRunner?
5) Fix context so variables don't have to be global
For now, variables defined with "var" won't be found
if used later on in a script.
6) Fix formatting
*/
function jsparse() {
var script = document.getElementById('sejs')
var fname = 'javascript script';
parse_result = parse(script.text, fname, 0);
var x2 = new ExecutionContext(GLOBAL_CODE);
ExecutionContext.current = x2;
var new_test_source = '';
var new_line = '';
for (i=0;i<parse_result.$length;i++){
var the_start = parse_result[i].start;
var the_end;
if ( i == (parse_result.$length-1)) {
the_end = parse_result.tokenizer.source.length;
} else {
the_end = parse_result[i+1].start;
}
var script_fragment = parse_result.tokenizer.source.slice(the_start,the_end)
new_line = '<tr><td style="display:none;" class="js">getEval</td>' +
'<td style="display:none;">currentTest.doNextCommand()</td>' +
'<td style="white-space: pre;">' + script_fragment + '</td>' +
'<td></td></tr>\n';
new_test_source += new_line;
//eval(script_fragment);
};
execute(parse_result,x2)
// Create HTML Table
body = document.body
body.innerHTML += "<table class='selenium' id='se-js-table'>"+
"<tbody>" +
"<tr><td>// " + document.title + "</td></tr>" +
new_test_source +
"</tbody" +
"</table>";
//body.innerHTML = "<pre>" + parse_result + "</pre>"
}

View file

@ -0,0 +1,175 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Narcissus JavaScript engine.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* Narcissus - JS implemented in JS.
*
* Well-known constants and lookup tables. Many consts are generated from the
* tokens table via eval to minimize redundancy, so consumers must be compiled
* separately to take advantage of the simple switch-case constant propagation
* done by SpiderMonkey.
*/
// jrh
//module('JS.Defs');
GLOBAL = this;
var tokens = [
// End of source.
"END",
// Operators and punctuators. Some pair-wise order matters, e.g. (+, -)
// and (UNARY_PLUS, UNARY_MINUS).
"\n", ";",
",",
"=",
"?", ":", "CONDITIONAL",
"||",
"&&",
"|",
"^",
"&",
"==", "!=", "===", "!==",
"<", "<=", ">=", ">",
"<<", ">>", ">>>",
"+", "-",
"*", "/", "%",
"!", "~", "UNARY_PLUS", "UNARY_MINUS",
"++", "--",
".",
"[", "]",
"{", "}",
"(", ")",
// Nonterminal tree node type codes.
"SCRIPT", "BLOCK", "LABEL", "FOR_IN", "CALL", "NEW_WITH_ARGS", "INDEX",
"ARRAY_INIT", "OBJECT_INIT", "PROPERTY_INIT", "GETTER", "SETTER",
"GROUP", "LIST",
// Terminals.
"IDENTIFIER", "NUMBER", "STRING", "REGEXP",
// Keywords.
"break",
"case", "catch", "const", "continue",
"debugger", "default", "delete", "do",
"else", "enum",
"false", "finally", "for", "function",
"if", "in", "instanceof",
"new", "null",
"return",
"switch",
"this", "throw", "true", "try", "typeof",
"var", "void",
"while", "with",
// Extensions
"require", "bless", "mixin", "import"
];
// Operator and punctuator mapping from token to tree node type name.
// NB: superstring tokens (e.g., ++) must come before their substring token
// counterparts (+ in the example), so that the opRegExp regular expression
// synthesized from this list makes the longest possible match.
var opTypeNames = {
'\n': "NEWLINE",
';': "SEMICOLON",
',': "COMMA",
'?': "HOOK",
':': "COLON",
'||': "OR",
'&&': "AND",
'|': "BITWISE_OR",
'^': "BITWISE_XOR",
'&': "BITWISE_AND",
'===': "STRICT_EQ",
'==': "EQ",
'=': "ASSIGN",
'!==': "STRICT_NE",
'!=': "NE",
'<<': "LSH",
'<=': "LE",
'<': "LT",
'>>>': "URSH",
'>>': "RSH",
'>=': "GE",
'>': "GT",
'++': "INCREMENT",
'--': "DECREMENT",
'+': "PLUS",
'-': "MINUS",
'*': "MUL",
'/': "DIV",
'%': "MOD",
'!': "NOT",
'~': "BITWISE_NOT",
'.': "DOT",
'[': "LEFT_BRACKET",
']': "RIGHT_BRACKET",
'{': "LEFT_CURLY",
'}': "RIGHT_CURLY",
'(': "LEFT_PAREN",
')': "RIGHT_PAREN"
};
// Hash of keyword identifier to tokens index. NB: we must null __proto__ to
// avoid toString, etc. namespace pollution.
var keywords = {__proto__: null};
// Define const END, etc., based on the token names. Also map name to index.
var consts = " ";
for (var i = 0, j = tokens.length; i < j; i++) {
if (i > 0)
consts += "; ";
var t = tokens[i];
if (/^[a-z]/.test(t)) {
consts += t.toUpperCase();
keywords[t] = i;
} else {
consts += (/^\W/.test(t) ? opTypeNames[t] : t);
}
consts += " = " + i;
tokens[t] = i;
}
eval(consts + ";");
// Map assignment operators to their indexes in the tokens array.
var assignOps = ['|', '^', '&', '<<', '>>', '>>>', '+', '-', '*', '/', '%'];
for (i = 0, j = assignOps.length; i < j; i++) {
t = assignOps[i];
assignOps[t] = tokens[t];
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/*
This is an experiment in creating a "selenese" parser that drastically
cuts down on the line noise associated with writing tests in HTML.
The 'parse' function will accept the follow sample commands.
test-cases:
//comment
command "param"
command "param" // comment
command "param" "param2"
command "param" "param2" // this is a comment
TODO:
1) Deal with multiline parameters
2) Escape quotes properly
3) Determine whether this should/will become the "preferred" syntax
for delivered Selenium self-test scripts
*/
function separse(doc) {
// Get object
script = doc.getElementById('testcase')
// Split into lines
lines = script.text.split('\n');
var command_pattern = / *(\w+) *"([^"]*)" *(?:"([^"]*)"){0,1}(?: *(\/\/ *.+))*/i;
var comment_pattern = /^ *(\/\/ *.+)/
// Regex each line into selenium command and convert into table row.
// eg. "<command> <quote> <quote> <comment>"
var new_test_source = '';
var new_line = '';
for (var x=0; x < lines.length; x++) {
result = lines[x].match(command_pattern);
if (result != null) {
new_line = "<tr><td>" + (result[1] || '&nbsp;') + "</td>" +
"<td>" + (result[2] || '&nbsp;') + "</td>" +
"<td>" + (result[3] || '&nbsp;') + "</td>" +
"<td>" + (result[4] || '&nbsp;') + "</td></tr>\n";
new_test_source += new_line;
}
result = lines[x].match(comment_pattern);
if (result != null) {
new_line = '<tr><td rowspan="1" colspan="4">' +
(result[1] || '&nbsp;') +
'</td></tr>';
new_test_source += new_line;
}
}
// Create HTML Table
body = doc.body
body.innerHTML += "<table class='selenium' id='testtable'>"+
new_test_source +
"</table>";
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,142 @@
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*
*/
// Although it's generally better web development practice not to use
// browser-detection (feature detection is better), the subtle browser
// differences that Selenium has to work around seem to make it
// necessary. Maybe as we learn more about what we need, we can do this in
// a more "feature-centric" rather than "browser-centric" way.
var BrowserVersion = function() {
this.name = navigator.appName;
if (window.opera != null) {
this.browser = BrowserVersion.OPERA;
this.isOpera = true;
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 (this.name == "Microsoft Internet Explorer") {
this.browser = BrowserVersion.IE;
this.isIE = 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;
}
if (navigator.userAgent.indexOf('Safari') != -1) {
this.browser = BrowserVersion.SAFARI;
this.isSafari = true;
this.khtml = true;
return;
}
if (navigator.userAgent.indexOf('Konqueror') != -1) {
this.browser = BrowserVersion.KONQUEROR;
this.isKonqueror = true;
this.khtml = true;
return;
}
if (navigator.userAgent.indexOf('Firefox') != -1) {
this.browser = BrowserVersion.FIREFOX;
this.isFirefox = true;
this.isGecko = true;
var result = /.*Firefox\/([\d\.]+).*/.exec(navigator.userAgent);
if (result) {
this.firefoxVersion = result[1];
}
checkChrome();
return;
}
if (navigator.userAgent.indexOf('Gecko') != -1) {
this.browser = BrowserVersion.MOZILLA;
this.isMozilla = true;
this.isGecko = true;
checkChrome();
return;
}
this.browser = BrowserVersion.UNKNOWN;
}
BrowserVersion.OPERA = "Opera";
BrowserVersion.IE = "IE";
BrowserVersion.KONQUEROR = "Konqueror";
BrowserVersion.SAFARI = "Safari";
BrowserVersion.FIREFOX = "Firefox";
BrowserVersion.MOZILLA = "Mozilla";
BrowserVersion.UNKNOWN = "Unknown";
var browserVersion = new BrowserVersion();

View file

@ -0,0 +1,375 @@
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*/
// 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);
}
}
},
_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);
}
}
},
_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;
this.registerAssert(assertName, assertBlock, true);
// Register the assert with the "verify" prefix, and do not halt on failure.
var verifyName = "verify" + match[1];
this.registerAssert(verifyName, assertBlock, false);
}
}
},
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 = 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 + "'");
}
};
},
_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 = 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 + "'");
}
};
},
_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 = accessBlock(arguments[0], arguments[1]);
} else if (arguments.length == 1) {
accessorResult = accessBlock(arguments[0]);
} else {
accessorResult = accessBlock();
}
if (accessorResult) {
return new PredicateResult(true, "true");
} else {
return new PredicateResult(false, "false");
}
};
},
_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 = predicateBlock(target, value);
result.isTrue = !result.isTrue;
return result;
};
},
createAssertionFromPredicate: function(predicateBlock) {
// Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
return function(target, value) {
var result = predicateBlock(target, value);
if (!result.isTrue) {
Assert.fail(result.message);
}
};
},
_invertPredicateName: function(baseName) {
var matchResult = /^(.*)Present$/.exec(baseName);
if (matchResult != null) {
return matchResult[1] + "NotPresent";
}
return "Not" + baseName;
},
_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 terminationCondition = function () {
try {
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
// not even been created yet.
// TODO: possibly should rethrow some types of exception.
return false;
}
};
return Selenium.decorateFunctionWithTimeout(terminationCondition, this.defaultTimeout);
};
},
_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
this.registerAction("waitForNot" + baseName, waitForNotActionBlock, false, true);
},
_registerStoreCommandForAccessor: function(baseName, accessBlock, requiresTarget) {
var action;
if (requiresTarget) {
action = function(target, varName) {
storedVars[varName] = accessBlock(target);
};
} else {
action = function(varName) {
storedVars[varName] = accessBlock();
};
}
this.registerAction("store" + baseName, action, false, true);
}
});
function PredicateResult(isTrue, message) {
this.isTrue = isTrue;
this.message = message;
}
// NOTE: The 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.
function CommandHandler(type, haltOnFailure) {
this.type = type;
this.haltOnFailure = haltOnFailure;
}
// 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(actionBlock, wait, dontCheckAlerts) {
this.actionBlock = actionBlock;
CommandHandler.call(this, "action", true);
if (wait) {
this.wait = true;
}
// note that dontCheckAlerts could be undefined!!!
this.checkAlerts = (dontCheckAlerts) ? false : true;
}
ActionHandler.prototype = new CommandHandler;
ActionHandler.prototype.execute = function(seleniumApi, command) {
if (this.checkAlerts && (null == /(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
// todo: this conditional logic is ugly
seleniumApi.ensureNoUnhandledPopups();
}
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) {
terminationCondition = seleniumApi.makePageLoadCondition();
}
return new ActionResult(terminationCondition);
};
function ActionResult(terminationCondition) {
this.terminationCondition = terminationCondition;
}
function AccessorHandler(accessBlock) {
this.accessBlock = accessBlock;
CommandHandler.call(this, "accessor", true);
}
AccessorHandler.prototype = new CommandHandler;
AccessorHandler.prototype.execute = function(seleniumApi, command) {
var returnValue = this.accessBlock(command.target, command.value);
return new AccessorResult(returnValue);
};
function AccessorResult(result) {
this.result = result;
}
/**
* Handler for assertions and verifications.
*/
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.assertBlock(command.target, command.value);
} catch (e) {
// If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.
if (!e.isAssertionFailedError) {
throw e;
}
if (this.haltOnFailure) {
var error = new SeleniumError(e.failureMessage);
throw error;
}
result.setFailed(e.failureMessage);
}
return result;
};
function AssertResult() {
this.passed = true;
}
AssertResult.prototype.setFailed = function(message) {
this.passed = null;
this.failed = true;
this.failureMessage = message;
}
function SeleniumCommand(command, target, value, isBreakpoint) {
this.command = command;
this.target = target;
this.value = value;
this.isBreakpoint = isBreakpoint;
}

View file

@ -0,0 +1,177 @@
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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 TestLoop(commandFactory) {
this.commandFactory = commandFactory;
}
TestLoop.prototype = {
start : function() {
selenium.reset();
LOG.debug("currentTest.start()");
this.continueTest();
},
continueTest : function() {
/**
* Select the next command and continue the test.
*/
LOG.debug("currentTest.continueTest() - acquire the next command");
if (! this.aborted) {
this.currentCommand = this.nextCommand();
}
if (! this.requiresCallBack) {
this.continueTestAtCurrentCommand();
} // otherwise, just finish and let the callback invoke continueTestAtCurrentCommand()
},
continueTestAtCurrentCommand : function() {
LOG.debug("currentTest.continueTestAtCurrentCommand()");
if (this.currentCommand) {
// TODO: rename commandStarted to commandSelected, OR roll it into nextCommand
this.commandStarted(this.currentCommand);
this._resumeAfterDelay();
} else {
this._testComplete();
}
},
_resumeAfterDelay : function() {
/**
* Pause, then execute the current command.
*/
// Get the command delay. If a pauseInterval is set, use it once
// and reset it. Otherwise, use the defined command-interval.
var delay = this.pauseInterval || this.getCommandInterval();
this.pauseInterval = undefined;
if (this.currentCommand.isBreakpoint || delay < 0) {
// Pause: enable the "next/continue" button
this.pause();
} else {
window.setTimeout(fnBind(this.resume, this), delay);
}
},
resume: function() {
/**
* Select the next command and continue the test.
*/
LOG.debug("currentTest.resume() - actually execute");
try {
selenium.browserbot.runScheduledPollers();
this._executeCurrentCommand();
this.continueTestWhenConditionIsTrue();
} catch (e) {
if (!this._handleCommandError(e)) {
this._testComplete();
} else {
this.continueTest();
}
}
},
_testComplete : function() {
selenium.ensureNoUnhandledPopups();
this.testComplete();
},
_executeCurrentCommand : function() {
/**
* Execute the current command.
*
* @return a function which will be used to determine when
* execution can continue, or null if we can continue immediately
*/
var command = this.currentCommand;
LOG.info("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");
var handler = this.commandFactory.getCommandHandler(command.command);
if (handler == null) {
throw new SeleniumError("Unknown command: '" + command.command + "'");
}
command.target = selenium.preprocessParameter(command.target);
command.value = selenium.preprocessParameter(command.value);
LOG.debug("Command found, going to execute " + command.command);
this.result = handler.execute(selenium, command);
this.waitForCondition = this.result.terminationCondition;
},
_handleCommandError : function(e) {
if (!e.isSeleniumError) {
LOG.exception(e);
var msg = "Selenium failure. Please report to selenium-dev@openqa.org, with error details from the log window.";
if (e.message) {
msg += " The error message is: " + e.message;
}
return this.commandError(msg);
} else {
LOG.error(e.message);
return this.commandError(e.message);
}
},
continueTestWhenConditionIsTrue: function () {
/**
* Busy wait for waitForCondition() to become true, and then carry
* on with test. Fail the current test if there's a timeout or an
* exception.
*/
//LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
selenium.browserbot.runScheduledPollers();
try {
if (this.waitForCondition == null) {
LOG.debug("null condition; let's continueTest()");
LOG.debug("Command complete");
this.commandComplete(this.result);
this.continueTest();
} else if (this.waitForCondition()) {
LOG.debug("condition satisfied; let's continueTest()");
this.waitForCondition = null;
LOG.debug("Command complete");
this.commandComplete(this.result);
this.continueTest();
} else {
//LOG.debug("waitForCondition was false; keep waiting!");
window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
}
} catch (e) {
this.result = {};
this.result.failed = true;
this.result.failureMessage = extractExceptionMessage(e);
this.commandComplete(this.result);
this.continueTest();
}
},
pause : function() {},
nextCommand : function() {},
commandStarted : function() {},
commandComplete : function() {},
commandError : function() {},
testComplete : function() {},
getCommandInterval : function() {
return 0;
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*/
var Logger = function() {
this.logWindow = null;
}
Logger.prototype = {
pendingMessages: new Array(),
setLogLevelThreshold: function(logLevel) {
this.pendingLogLevelThreshold = logLevel;
this.show();
// NOTE: log messages will be discarded until the log window is
// fully loaded.
},
getLogWindow: function() {
if (this.logWindow && this.logWindow.closed) {
this.logWindow = null;
}
if (this.logWindow && this.pendingLogLevelThreshold && this.logWindow.setThresholdLevel) {
this.logWindow.setThresholdLevel(this.pendingLogLevelThreshold);
// can't just directly log because that action would loop back
// to this code infinitely
var pendingMessage = new LogMessage("info", "Log level programmatically set to " + this.pendingLogLevelThreshold + " (presumably by driven-mode test code)");
this.pendingMessages.push(pendingMessage);
this.pendingLogLevelThreshold = null; // let's only go this way one time
}
return this.logWindow;
},
openLogWindow: function() {
this.logWindow = window.open(
getDocumentBase(document) + "SeleniumLog.html", "SeleniumLog",
"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;
},
show: function() {
if (! this.getLogWindow()) {
this.openLogWindow();
}
setTimeout(function(){LOG.info("Log window displayed");}, 500);
},
logHook: function(className, message) {
},
log: function(className, message) {
var logWindow = this.getLogWindow();
this.logHook(className, message);
if (logWindow) {
if (logWindow.append) {
if (this.pendingMessages.length > 0) {
logWindow.append("info: Appending missed logging messages", "info");
while (this.pendingMessages.length > 0) {
var msg = this.pendingMessages.shift();
logWindow.append(msg.type + ": " + msg.msg, msg.type);
}
logWindow.append("info: Done appending missed logging messages", "info");
}
logWindow.append(className + ": " + message, className);
}
} else {
// uncomment this to turn on background logging
/* 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(className, message));
}
},
close: function(message) {
if (this.logWindow != null) {
try {
this.logWindow.close();
} catch (e) {
// swallow exception
// the window is probably closed if we get an exception here
}
this.logWindow = null;
}
},
debug: function(message) {
this.log("debug", message);
},
info: function(message) {
this.log("info", message);
},
warn: function(message) {
this.log("warn", message);
},
error: function(message) {
this.log("error", message);
},
exception: function(exception) {
this.error("Unexpected Exception: " + extractExceptionMessage(exception));
this.error("Exception details: " + describe(exception, ', '));
}
};
var LOG = new Logger();
var LogMessage = function(type, msg) {
this.type = type;
this.msg = msg;
}

View file

@ -0,0 +1,466 @@
/*
* Copyright 2005 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.
*
*/
passColor = "#cfffcf";
failColor = "#ffcfcf";
errorColor = "#ffffff";
workingColor = "#DEE7EC";
doneColor = "#FFFFCC";
var injectedSessionId;
var cmd1 = document.createElement("div");
var cmd2 = document.createElement("div");
var cmd3 = document.createElement("div");
var cmd4 = document.createElement("div");
var postResult = "START";
var debugMode = false;
var relayToRC = null;
var proxyInjectionMode = false;
var uniqueId = 'sel_' + Math.round(100000 * Math.random());
var RemoteRunnerOptions = classCreate();
objectExtend(RemoteRunnerOptions.prototype, URLConfiguration.prototype);
objectExtend(RemoteRunnerOptions.prototype, {
initialize: function() {
this._acquireQueryString();
},
isDebugMode: function() {
return this._isQueryParameterTrue("debugMode");
},
getContinue: function() {
return this._getQueryParameter("continue");
},
getDriverUrl: function() {
return this._getQueryParameter("driverUrl");
},
getSessionId: function() {
return this._getQueryParameter("sessionId");
},
_acquireQueryString: function () {
if (this.queryString) return;
if (browserVersion.isHTA) {
var args = this._extractArgs();
if (args.length < 2) return null;
this.queryString = args[1];
} else if (proxyInjectionMode) {
this.queryString = selenium.browserbot.getCurrentWindow().location.search.substr(1);
} else {
this.queryString = top.location.search.substr(1);
}
}
});
var runOptions;
function runSeleniumTest() {
runOptions = new RemoteRunnerOptions();
var testAppWindow;
if (runOptions.isMultiWindowMode()) {
testAppWindow = openSeparateApplicationWindow('Blank.html', true);
} else if ($('myiframe') != null) {
var myiframe = $('myiframe');
if (myiframe) {
testAppWindow = myiframe.contentWindow;
}
}
else {
proxyInjectionMode = true;
testAppWindow = window;
}
selenium = Selenium.createForWindow(testAppWindow, proxyInjectionMode);
if (runOptions.getBaseUrl()) {
selenium.browserbot.baseUrl = runOptions.getBaseUrl();
}
if (!debugMode) {
debugMode = runOptions.isDebugMode();
}
if (proxyInjectionMode) {
LOG.log = logToRc;
selenium.browserbot._modifyWindow(testAppWindow);
}
else if (debugMode) {
LOG.logHook = logToRc;
}
window.selenium = selenium;
commandFactory = new CommandHandlerFactory();
commandFactory.registerAll(selenium);
currentTest = new RemoteRunner(commandFactory);
if (document.getElementById("commandList") != null) {
document.getElementById("commandList").appendChild(cmd4);
document.getElementById("commandList").appendChild(cmd3);
document.getElementById("commandList").appendChild(cmd2);
document.getElementById("commandList").appendChild(cmd1);
}
var doContinue = runOptions.getContinue();
if (doContinue != null) postResult = "OK";
currentTest.start();
}
function buildDriverUrl() {
var driverUrl = runOptions.getDriverUrl();
if (driverUrl != null) {
return driverUrl;
}
var s = window.location.href
var slashPairOffset = s.indexOf("//") + "//".length
var pathSlashOffset = s.substring(slashPairOffset).indexOf("/")
return s.substring(0, slashPairOffset + pathSlashOffset) + "/selenium-server/driver/";
}
function logToRc(logLevel, message) {
if (logLevel == null) {
logLevel = "debug";
}
if (debugMode) {
sendToRC("logLevel=" + logLevel + ":" + message.replace(/[\n\r\015]/g, " ") + "\n", "logging=true");
}
}
function isArray(x) {
return ((typeof x) == "object") && (x["length"] != null);
}
function serializeString(name, s) {
return name + "=unescape(\"" + escape(s) + "\");";
}
function serializeObject(name, x)
{
var s = '';
if (isArray(x))
{
s = name + "=new Array(); ";
var len = x["length"];
for (var j = 0; j < len; j++)
{
s += serializeString(name + "[" + j + "]", x[j]);
}
}
else if (typeof x == "string")
{
s = serializeString(name, x);
}
else
{
throw "unrecognized object not encoded: " + name + "(" + x + ")";
}
return s;
}
function relayBotToRC(s) {
}
// seems like no one uses this, but in fact it is called using eval from server-side PI mode code; however,
// because multiple names can map to the same popup, assigning a single name confuses matters sometimes;
// thus, I'm disabling this for now. -Nelson 10/21/06
function setSeleniumWindowName(seleniumWindowName) {
//selenium.browserbot.getCurrentWindow()['seleniumWindowName'] = seleniumWindowName;
}
RemoteRunner = classCreate();
objectExtend(RemoteRunner.prototype, new TestLoop());
objectExtend(RemoteRunner.prototype, {
initialize : function(commandFactory) {
this.commandFactory = commandFactory;
this.requiresCallBack = true;
this.commandNode = null;
this.xmlHttpForCommandsAndResults = null;
},
nextCommand : function() {
var urlParms = "";
if (postResult == "START") {
urlParms += "seleniumStart=true";
}
this.xmlHttpForCommandsAndResults = XmlHttp.create();
sendToRC(postResult, urlParms, fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults);
},
commandStarted : function(command) {
this.commandNode = document.createElement("div");
var innerHTML = command.command + '(';
if (command.target != null && command.target != "") {
innerHTML += command.target;
if (command.value != null && command.value != "") {
innerHTML += ', ' + command.value;
}
}
innerHTML += ")";
if (innerHTML.length >40) {
innerHTML = innerHTML.substring(0,40);
innerHTML += "...";
}
this.commandNode.innerHTML = innerHTML;
this.commandNode.style.backgroundColor = workingColor;
if (document.getElementById("commandList") != null) {
document.getElementById("commandList").removeChild(cmd1);
document.getElementById("commandList").removeChild(cmd2);
document.getElementById("commandList").removeChild(cmd3);
document.getElementById("commandList").removeChild(cmd4);
cmd4 = cmd3;
cmd3 = cmd2;
cmd2 = cmd1;
cmd1 = this.commandNode;
document.getElementById("commandList").appendChild(cmd4);
document.getElementById("commandList").appendChild(cmd3);
document.getElementById("commandList").appendChild(cmd2);
document.getElementById("commandList").appendChild(cmd1);
}
},
commandComplete : function(result) {
if (result.failed) {
if (postResult == "CONTINUATION") {
currentTest.aborted = true;
}
postResult = result.failureMessage;
this.commandNode.title = result.failureMessage;
this.commandNode.style.backgroundColor = failColor;
} else if (result.passed) {
postResult = "OK";
this.commandNode.style.backgroundColor = passColor;
} else {
if (result.result == null) {
postResult = "OK";
} else {
postResult = "OK," + result.result;
}
this.commandNode.style.backgroundColor = doneColor;
}
},
commandError : function(message) {
postResult = "ERROR: " + message;
this.commandNode.style.backgroundColor = errorColor;
this.commandNode.title = message;
},
testComplete : function() {
window.status = "Selenium Tests Complete, for this Test"
// Continue checking for new results
this.continueTest();
postResult = "START";
},
_HandleHttpResponse : function() {
if (this.xmlHttpForCommandsAndResults.readyState == 4) {
if (this.xmlHttpForCommandsAndResults.status == 200) {
if (this.xmlHttpForCommandsAndResults.responseText=="") {
LOG.error("saw blank string xmlHttpForCommandsAndResults.responseText");
return;
}
var command = this._extractCommand(this.xmlHttpForCommandsAndResults);
this.currentCommand = command;
this.continueTestAtCurrentCommand();
} else {
var s = 'xmlHttp returned: ' + this.xmlHttpForCommandsAndResults.status + ": " + this.xmlHttpForCommandsAndResults.statusText;
LOG.error(s);
this.currentCommand = null;
setTimeout(fnBind(this.continueTestAtCurrentCommand, this), 2000);
}
}
},
_extractCommand : function(xmlHttp) {
var command;
try {
var re = new RegExp("^(.*?)\n((.|[\r\n])*)");
if (re.exec(xmlHttp.responseText)) {
command = RegExp.$1;
var rest = RegExp.$2;
rest = rest.trim();
if (rest) {
eval(rest);
}
}
else {
command = xmlHttp.responseText;
}
} catch (e) {
alert('could not get responseText: ' + e.message);
}
if (command.substr(0, '|testComplete'.length) == '|testComplete') {
return null;
}
return this._createCommandFromRequest(command);
},
_delay : function(millis) {
var startMillis = new Date();
while (true) {
milli = new Date();
if (milli - startMillis > millis) {
break;
}
}
},
// Parses a URI query string into a SeleniumCommand object
_createCommandFromRequest : function(commandRequest) {
//decodeURIComponent doesn't strip plus signs
var processed = commandRequest.replace(/\+/g, "%20");
// strip trailing spaces
var processed = processed.replace(/\s+$/, "");
var vars = processed.split("&");
var cmdArgs = new Object();
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
cmdArgs[pair[0]] = pair[1];
}
var cmd = cmdArgs['cmd'];
var arg1 = cmdArgs['1'];
if (null == arg1) arg1 = "";
arg1 = decodeURIComponent(arg1);
var arg2 = cmdArgs['2'];
if (null == arg2) arg2 = "";
arg2 = decodeURIComponent(arg2);
if (cmd == null) {
throw new Error("Bad command request: " + commandRequest);
}
return new SeleniumCommand(cmd, arg1, arg2);
}
})
function sendToRC(dataToBePosted, urlParms, callback, xmlHttpObject, async) {
if (async == null) {
async = true;
}
if (xmlHttpObject == null) {
xmlHttpObject = XmlHttp.create();
}
var url = buildDriverUrl() + "?"
if (urlParms) {
url += urlParms;
}
url += "&localFrameAddress=" + (proxyInjectionMode ? makeAddressToAUTFrame() : "top");
url += getSeleniumWindowNameURLparameters();
url += "&uniqueId=" + uniqueId;
if (callback == null) {
callback = function() {
};
}
url += buildDriverParams() + preventBrowserCaching();
xmlHttpObject.open("POST", url, async);
xmlHttpObject.onreadystatechange = callback;
xmlHttpObject.send(dataToBePosted);
return null;
}
function buildDriverParams() {
var params = "";
var sessionId = runOptions.getSessionId();
if (sessionId == undefined) {
sessionId = injectedSessionId;
}
if (sessionId != undefined) {
params = params + "&sessionId=" + sessionId;
}
return params;
}
function preventBrowserCaching() {
var t = (new Date()).getTime();
return "&counterToMakeURsUniqueAndSoStopPageCachingInTheBrowser=" + t;
}
//
// Return URL parameters pertaining to the name(s?) of the current window
//
// In selenium, the main (i.e., first) window's name is a blank string.
//
// Additional pop-ups are associated with either 1.) the name given by the 2nd parameter to window.open, or 2.) the name of a
// property on the opening window which points at the window.
//
// An example of #2: if window X contains JavaScript as follows:
//
// var windowABC = window.open(...)
//
// Note that the example JavaScript above is equivalent to
//
// window["windowABC"] = window.open(...)
//
function getSeleniumWindowNameURLparameters() {
var w = (proxyInjectionMode ? selenium.browserbot.getCurrentWindow() : window).top;
var s = "&seleniumWindowName=";
if (w.opener == null) {
return s;
}
if (w["seleniumWindowName"] == null) {
s += 'generatedSeleniumWindowName_' + Math.round(100000 * Math.random());
}
else {
s += w["seleniumWindowName"];
}
var windowOpener = w.opener;
for (key in windowOpener) {
var val = null;
try {
val = windowOpener[key];
}
catch(e) {
}
if (val==w) {
s += "&jsWindowNameVar=" + key; // found a js variable in the opener referring to this window
}
}
return s;
}
// construct a JavaScript expression which leads to my frame (i.e., the frame containing the window
// in which this code is operating)
function makeAddressToAUTFrame(w, frameNavigationalJSexpression)
{
if (w == null)
{
w = top;
frameNavigationalJSexpression = "top";
}
if (w == selenium.browserbot.getCurrentWindow())
{
return frameNavigationalJSexpression;
}
for (var j = 0; j < w.frames.length; j++)
{
var t = makeAddressToAUTFrame(w.frames[j], frameNavigationalJSexpression + ".frames[" + j + "]");
if (t != null)
{
return t;
}
}
return null;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
Selenium.version = "0.8.2";
Selenium.revision = "1727";
window.top.document.title += " v" + Selenium.version + " [" + Selenium.revision + "]";

View file

@ -0,0 +1,75 @@
/*
* By default, Selenium looks for a file called "user-extensions.js", and loads and javascript
* code found in that file. This file is a sample of what that file could look like.
*
* user-extensions.js provides a convenient location for adding extensions to Selenium, like
* new actions, checks and locator-strategies.
* By default, this file does not exist. Users can create this file and place their extension code
* in this common location, removing the need to modify the Selenium sources, and hopefully assisting
* with the upgrade process.
*
* You can find contributed extensions at http://wiki.openqa.org/display/SEL/Contributed%20User-Extensions
*/
// The following examples try to give an indication of how Selenium can be extended with javascript.
// All do* methods on the Selenium prototype are added as actions.
// Eg add a typeRepeated action to Selenium, which types the text twice into a text box.
// The typeTwiceAndWait command will be available automatically
Selenium.prototype.doTypeRepeated = function(locator, text) {
// All locator-strategies are automatically handled by "findElement"
var element = this.page().findElement(locator);
// Create the text to type
var valueToType = text + text;
// Replace the element text with the new text
this.page().replaceText(element, valueToType);
};
// All assert* methods on the Selenium prototype are added as checks.
// Eg add a assertValueRepeated check, that makes sure that the element value
// consists of the supplied text repeated.
// The verify version will be available automatically.
Selenium.prototype.assertValueRepeated = function(locator, text) {
// All locator-strategies are automatically handled by "findElement"
var element = this.page().findElement(locator);
// Create the text to verify
var expectedValue = text + text;
// Get the actual element value
var actualValue = element.value;
// Make sure the actual value matches the expected
Assert.matches(expectedValue, actualValue);
};
// All get* methods on the Selenium prototype result in
// store, assert, assertNot, verify, verifyNot, waitFor, and waitForNot commands.
// E.g. add a getTextLength method that returns the length of the text
// of a specified element.
// Will result in support for storeTextLength, assertTextLength, etc.
Selenium.prototype.getTextLength = function(locator) {
return this.getText(locator).length;
};
// All locateElementBy* methods are added as locator-strategies.
// Eg add a "valuerepeated=" locator, that finds the first element with the supplied value, repeated.
// The "inDocument" is a the document you are searching.
PageBot.prototype.locateElementByValueRepeated = function(text, inDocument) {
// Create the text to search for
var expectedValue = text + text;
// Loop through all elements, looking for ones that have a value === our expected value
var allElements = inDocument.getElementsByTagName("*");
for (var i = 0; i < allElements.length; i++) {
var testElement = allElements[i];
if (testElement.value && testElement.value === expectedValue) {
return testElement;
}
}
return null;
};

View file

@ -0,0 +1,153 @@
// This is a third party JavaScript library from
// http://webfx.eae.net/dhtml/xmlextras/xmlextras.html
// i.e. This has not been written by ThoughtWorks.
//<script>
//////////////////
// Helper Stuff //
//////////////////
// used to find the Automation server name
function getDomDocumentPrefix() {
if (getDomDocumentPrefix.prefix)
return getDomDocumentPrefix.prefix;
var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
var o;
for (var i = 0; i < prefixes.length; i++) {
try {
// try to create the objects
o = new ActiveXObject(prefixes[i] + ".DomDocument");
return getDomDocumentPrefix.prefix = prefixes[i];
}
catch (ex) {};
}
throw new Error("Could not find an installed XML parser");
}
function getXmlHttpPrefix() {
if (getXmlHttpPrefix.prefix)
return getXmlHttpPrefix.prefix;
var prefixes = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
var o;
for (var i = 0; i < prefixes.length; i++) {
try {
// try to create the objects
o = new ActiveXObject(prefixes[i] + ".XmlHttp");
return getXmlHttpPrefix.prefix = prefixes[i];
}
catch (ex) {};
}
throw new Error("Could not find an installed XML parser");
}
//////////////////////////
// Start the Real stuff //
//////////////////////////
// XmlHttp factory
function XmlHttp() {}
XmlHttp.create = function () {
try {
if (window.XMLHttpRequest) {
var req = new XMLHttpRequest();
// some versions of Moz do not support the readyState property
// and the onreadystate event so we patch it!
if (req.readyState == null) {
req.readyState = 1;
req.addEventListener("load", function () {
req.readyState = 4;
if (typeof req.onreadystatechange == "function")
req.onreadystatechange();
}, false);
}
return req;
}
if (window.ActiveXObject) {
return new ActiveXObject(getXmlHttpPrefix() + ".XmlHttp");
}
}
catch (ex) {}
// fell through
throw new Error("Your browser does not support XmlHttp objects");
};
// XmlDocument factory
function XmlDocument() {}
XmlDocument.create = function () {
try {
// DOM2
if (document.implementation && document.implementation.createDocument) {
var doc = document.implementation.createDocument("", "", null);
// some versions of Moz do not support the readyState property
// and the onreadystate event so we patch it!
if (doc.readyState == null) {
doc.readyState = 1;
doc.addEventListener("load", function () {
doc.readyState = 4;
if (typeof doc.onreadystatechange == "function")
doc.onreadystatechange();
}, false);
}
return doc;
}
if (window.ActiveXObject)
return new ActiveXObject(getDomDocumentPrefix() + ".DomDocument");
}
catch (ex) {}
throw new Error("Your browser does not support XmlDocument objects");
};
// Create the loadXML method and xml getter for Mozilla
if (window.DOMParser &&
window.XMLSerializer &&
window.Node && Node.prototype && Node.prototype.__defineGetter__) {
// XMLDocument did not extend the Document interface in some versions
// of Mozilla. Extend both!
//XMLDocument.prototype.loadXML =
Document.prototype.loadXML = function (s) {
// parse the string to a new doc
var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
// remove all initial children
while (this.hasChildNodes())
this.removeChild(this.lastChild);
// insert and import nodes
for (var i = 0; i < doc2.childNodes.length; i++) {
this.appendChild(this.importNode(doc2.childNodes[i], true));
}
};
/*
* xml getter
*
* This serializes the DOM tree to an XML String
*
* Usage: var sXml = oNode.xml
*
*/
// XMLDocument did not extend the Document interface in some versions
// of Mozilla. Extend both!
/*
XMLDocument.prototype.__defineGetter__("xml", function () {
return (new XMLSerializer()).serializeToString(this);
});
*/
Document.prototype.__defineGetter__("xml", function () {
return (new XMLSerializer()).serializeToString(this);
});
}