Blame | Letzte Änderung | Log anzeigen | RSS feed
/** 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 leadingreturn 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 elementfunction 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 blocksif (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 to space.*/function normalizeSpaces(text){// IE has already done this conversion, so doing it again will remove multiple nbspif (browserVersion.isIE){return text;}// Replace multiple spaces with a single space// TODO - this shouldn't occur inside PRE elementstext = text.replace(/\ +/g, " ");// Replace with a spacevar 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(/"/g, '"');text = text.replace(/'/g, "'");text = text.replace(/</g, "<");text = text.replace(/>/g, ">");text = text.replace(/&/g, "&");return text;}// Sets the text in this elementfunction setText(element, text) {if (element.textContent != null) {element.textContent = text;} else if (element.innerText != null) {element.innerText = text;}}// Get the value of an <input> elementfunction 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 hereLOG.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 charsmatch = /^\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* <base> 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 absolutereturn url;}var loc;try {loc = parseUrl(baseUrl);} catch (e) {// is it an absolute windows file path? let's play the hero in that caseif (/^\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 pathnameif (/^\//.test(url)) {loc.pathname = url;var result = reassembleLocation(loc);return result;}// if pathname is null, then we'll just append "/" + the urlif (!loc.pathname) {loc.pathname = "/" + url;var result = reassembleLocation(loc);return result;}// if pathname ends with /, just append urlif (/\/$/.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 urlloc.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 hrefreturn 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 defaultif (/^([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 highlightelement.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 ignoreif (!element.parentNode) {return;}elementSetStyle(element, {"backgroundColor" : element.originalColor});} catch (e) {} // DGF unhighlighting is very dangerous and low priority}, 200);}// for use from vs.2003 debuggerfunction 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 itselfwindow.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.queryStringif (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)}