From 9e52d1ddac7fd4e54c0744a00aae6786081267af Mon Sep 17 00:00:00 2001 From: wei <> Date: Sun, 25 Mar 2007 23:19:26 +0000 Subject: Update Selenium to 0.8.2 --- .gitattributes | 2 + tests/test_tools/selenium/core/SeleniumLog.html | 14 +- .../selenium/core/TestRunner-splash.html | 1 + .../selenium/core/scripts/find_matching_child.js | 16 +- .../test_tools/selenium/core/scripts/htmlutils.js | 411 +++-- .../selenium/core/scripts/injection.html | 19 +- .../selenium/core/scripts/selenium-api.js | 1454 ++++++++++------- .../selenium/core/scripts/selenium-browserbot.js | 1634 +++++++++++++------- .../core/scripts/selenium-browserdetect.js | 48 +- .../core/scripts/selenium-commandhandlers.js | 375 ++--- .../core/scripts/selenium-executionloop.js | 63 +- .../selenium/core/scripts/selenium-logging.js | 30 +- .../selenium/core/scripts/selenium-remoterunner.js | 466 ++++++ .../selenium/core/scripts/selenium-testrunner.js | 516 ++++--- .../selenium/core/scripts/selenium-version.js | 4 +- tests/test_tools/selenium/core/selenium-test.css | 43 + tests/test_tools/selenium/core/selenium.css | 44 +- tests/test_tools/selenium/php/TestRunner.php | 3 +- tests/test_tools/selenium/php/selenium.php | 2 +- tests/test_tools/selenium/prado-functional-test.js | 74 +- 20 files changed, 3400 insertions(+), 1819 deletions(-) create mode 100644 tests/test_tools/selenium/core/scripts/selenium-remoterunner.js create mode 100644 tests/test_tools/selenium/core/selenium-test.css diff --git a/.gitattributes b/.gitattributes index 0b3cd201..2c48926a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1797,6 +1797,8 @@ tests/test_tools/selenium/core/scripts/narcissus-defs.js -text tests/test_tools/selenium/core/scripts/narcissus-exec.js -text tests/test_tools/selenium/core/scripts/narcissus-parse.js -text tests/test_tools/selenium/core/scripts/se2html.js -text +tests/test_tools/selenium/core/scripts/selenium-remoterunner.js -text +tests/test_tools/selenium/core/selenium-test.css -text tests/test_tools/selenium/php/TestSuiteHeader.php -text tests/test_tools/selenium/reference.html -text tests/test_tools/simpletest/collector.php -text diff --git a/tests/test_tools/selenium/core/SeleniumLog.html b/tests/test_tools/selenium/core/SeleniumLog.html index dfa0080a..dffa184f 100644 --- a/tests/test_tools/selenium/core/SeleniumLog.html +++ b/tests/test_tools/selenium/core/SeleniumLog.html @@ -2,6 +2,7 @@ Selenium Log Console + @@ -15,8 +16,6 @@ var logLevels = { error: 3 }; -var logLevelThreshold = null; - function getThresholdLevel() { var buttons = document.getElementById('logLevelChooser').level; for (var i = 0; i < buttons.length; i++) { @@ -27,10 +26,9 @@ function getThresholdLevel() { } function setThresholdLevel(logLevel) { - logLevelThreshold = logLevel; var buttons = document.getElementById('logLevelChooser').level; for (var i = 0; i < buttons.length; i++) { - if (buttons[i].value==logLevel) { + if (buttons[i].value==logLevel) { buttons[i].checked = true; } else { @@ -40,9 +38,7 @@ function setThresholdLevel(logLevel) { } function append(message, logLevel) { - if (logLevelThreshold==null) { - logLevelThreshold = getThresholdLevel(); - } + var logLevelThreshold = getThresholdLevel(); if (logLevels[logLevel] < logLevels[logLevelThreshold]) { return; } @@ -64,9 +60,9 @@ function append(message, logLevel) { value="error" /> - -

Selenium Log Console

diff --git a/tests/test_tools/selenium/core/TestRunner-splash.html b/tests/test_tools/selenium/core/TestRunner-splash.html index 205bb8ef..1c32dd79 100644 --- a/tests/test_tools/selenium/core/TestRunner-splash.html +++ b/tests/test_tools/selenium/core/TestRunner-splash.html @@ -15,6 +15,7 @@ Copyright 2005 ThoughtWorks, Inc --> + diff --git a/tests/test_tools/selenium/core/scripts/find_matching_child.js b/tests/test_tools/selenium/core/scripts/find_matching_child.js index 197d1032..fbf35b75 100644 --- a/tests/test_tools/selenium/core/scripts/find_matching_child.js +++ b/tests/test_tools/selenium/core/scripts/find_matching_child.js @@ -15,8 +15,8 @@ * */ -Element.findMatchingChildren = function(element, selector) { - var matches = $A([]); +elementFindMatchingChildren = function(element, selector) { + var matches = []; var childCount = element.childNodes.length; for (var i=0; i element function getInputValue(inputElement) { - if (inputElement.type.toUpperCase() == 'CHECKBOX' || - inputElement.type.toUpperCase() == 'RADIO') - { - return (inputElement.checked ? 'on' : 'off'); + if (inputElement.type) { + if (inputElement.type.toUpperCase() == 'CHECKBOX' || + inputElement.type.toUpperCase() == 'RADIO') + { + return (inputElement.checked ? 'on' : 'off'); + } + } + if (inputElement.value == null) { + throw new SeleniumError("This element has no value; is it really a form field?"); } return inputElement.value; } /* Fire an event in a browser-compatible manner */ -function triggerEvent(element, eventType, canBubble) { +function triggerEvent(element, eventType, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { canBubble = (typeof(canBubble) == undefined) ? true : canBubble; if (element.fireEvent) { - element.fireEvent('on' + eventType); + var evt = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown); + element.fireEvent('on' + eventType, evt); } else { var evt = document.createEvent('HTMLEvents'); + + try { + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + } catch (e) { + // On Firefox 1.0, you can only set these during initMouseEvent or initKeyEvent + // we'll have to ignore them here + LOG.exception(e); + } + evt.initEvent(eventType, canBubble, true); element.dispatchEvent(evt); } @@ -179,14 +284,23 @@ function getKeyCodeFromKeySequence(keySequence) { if (match != null) { return match[0]; } - throw SeleniumError("invalid keySequence"); + throw new SeleniumError("invalid keySequence"); } -function triggerKeyEvent(element, eventType, keySequence, canBubble) { +function createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { + var evt = element.ownerDocument.createEventObject(); + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + return evt; +} + +function triggerKeyEvent(element, eventType, keySequence, canBubble, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown) { var keycode = getKeyCodeFromKeySequence(keySequence); canBubble = (typeof(canBubble) == undefined) ? true : canBubble; if (element.fireEvent) { - keyEvent = element.ownerDocument.createEventObject(); + var keyEvent = createEventObject(element, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown); keyEvent.keyCode = keycode; element.fireEvent('on' + eventType, keyEvent); } @@ -194,82 +308,24 @@ function triggerKeyEvent(element, eventType, keySequence, canBubble) { var evt; if (window.KeyEvent) { evt = document.createEvent('KeyEvents'); - evt.initKeyEvent(eventType, true, true, window, false, false, false, false, keycode, keycode); + evt.initKeyEvent(eventType, true, true, window, controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown, keycode, keycode); } else { evt = document.createEvent('UIEvents'); + + evt.shiftKey = shiftKeyDown; + evt.metaKey = metaKeyDown; + evt.altKey = altKeyDown; + evt.ctrlKey = controlKeyDown; + evt.initUIEvent(eventType, true, true, window, 1); evt.keyCode = keycode; + evt.which = keycode; } element.dispatchEvent(evt); } } -/* Fire a mouse event in a browser-compatible manner */ -function triggerMouseEvent(element, eventType, canBubble, clientX, clientY) { - clientX = clientX ? clientX : 0; - clientY = clientY ? clientY : 0; - - // TODO: set these attributes -- they don't seem to be needed by the initial test cases, but that could change... - var screenX = 0; - var screenY = 0; - - canBubble = (typeof(canBubble) == undefined) ? true : canBubble; - if (element.fireEvent) { - LOG.error("element has fireEvent"); - if (!screenX && !screenY && !clientX && !clientY) { - element.fireEvent('on' + eventType); - } - else { - var ieEvent = element.ownerDocument.createEventObject(); - ieEvent.detail = 0; - ieEvent.screenX = screenX; - ieEvent.screenY = screenY; - ieEvent.clientX = clientX; - ieEvent.clientY = clientY; - ieEvent.ctrlKey = false; - ieEvent.altKey = false; - ieEvent.shiftKey = false; - ieEvent.metaKey = false; - ieEvent.button = 1; - ieEvent.relatedTarget = null; - - // when we go this route, window.event is never set to contain the event we have just created. - // ideally we could just slide it in as follows in the try-block below, but this normally - // doesn't work. This is why I try to avoid this code path, which is only required if we need to - // set attributes on the event (e.g., clientX). - try { - window.event = ieEvent; - } - catch(e) { - // getting an "Object does not support this action or property" error. Save the event away - // for future reference. - // TODO: is there a way to update window.event? - - // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere: - selenium.browserbot.getCurrentWindow().selenium_event = ieEvent; - } - element.fireEvent('on' + eventType, ieEvent); - } - } - else { - LOG.error("element doesn't have fireEvent"); - var evt = document.createEvent('MouseEvents'); - if (evt.initMouseEvent) - { - LOG.error("element has initMouseEvent"); - //Safari - evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY, false, false, false, false, 0, null) - } - else { - LOG.error("element doesen't has initMouseEvent"); - // TODO we should be initialising other mouse-event related attributes here - evt.initEvent(eventType, canBubble, true); - } - element.dispatchEvent(evt); - } -} - function removeLoadListener(element, command) { LOG.info('Removing loadListenter for ' + element + ', ' + command); if (window.removeEventListener) @@ -280,10 +336,13 @@ function removeLoadListener(element, command) { function addLoadListener(element, command) { LOG.info('Adding loadListenter for ' + element + ', ' + command); + var augmentedCommand = function() { + command.call(this, element); + } if (window.addEventListener && !browserVersion.isOpera) - element.addEventListener("load", command, true); + element.addEventListener("load", augmentedCommand, true); else if (window.attachEvent) - element.attachEvent("onload", command); + element.attachEvent("onload", augmentedCommand); } /** @@ -306,10 +365,160 @@ function getDocumentBase(doc) { return ""; } +function getTagName(element) { + var tagName; + if (element && element.tagName && element.tagName.toLowerCase) { + tagName = element.tagName.toLowerCase(); + } + return tagName; +} + +function absolutify(url, baseUrl) { + /** returns a relative url in its absolute form, given by baseUrl. + * + * This function is a little odd, because it can take baseUrls that + * aren't necessarily directories. It uses the same rules as the HTML + * <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 absolute + return url; + } + + var loc; + try { + loc = parseUrl(baseUrl); + } catch (e) { + // is it an absolute windows file path? let's play the hero in that case + if (/^\w:\\/.test(baseUrl)) { + baseUrl = "file:///" + baseUrl.replace(/\\/g, "/"); + loc = parseUrl(baseUrl); + } else { + throw new SeleniumError("baseUrl wasn't absolute: " + baseUrl); + } + } + loc.search = null; + loc.hash = null; + + // if url begins with /, then that's the whole pathname + if (/^\//.test(url)) { + loc.pathname = url; + var result = reassembleLocation(loc); + return result; + } + + // if pathname is null, then we'll just append "/" + the url + if (!loc.pathname) { + loc.pathname = "/" + url; + var result = reassembleLocation(loc); + return result; + } + + // if pathname ends with /, just append url + if (/\/$/.test(loc.pathname)) { + loc.pathname += url; + var result = reassembleLocation(loc); + return result; + } + + // if we're here, then the baseUrl has a pathname, but it doesn't end with / + // in that case, we replace everything after the final / with the relative url + loc.pathname = loc.pathname.replace(/[^\/\\]+$/, url); + var result = reassembleLocation(loc); + return result; + +} + +var URL_REGEX = /^((\w+):\/\/)(([^:]+):?([^@]+)?@)?([^\/\?:]*):?(\d+)?(\/?[^\?#]+)?\??([^#]+)?#?(.+)?/; + +function parseUrl(url) { + var fields = ['url', null, 'protocol', null, 'username', 'password', 'host', 'port', 'pathname', 'search', 'hash']; + var result = URL_REGEX.exec(url); + if (!result) { + throw new SeleniumError("Invalid URL: " + url); + } + var loc = new Object(); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + if (field == null) { + continue; + } + loc[field] = result[i]; + } + return loc; +} + +function reassembleLocation(loc) { + if (!loc.protocol) { + throw new Error("Not a valid location object: " + o2s(loc)); + } + var protocol = loc.protocol; + protocol = protocol.replace(/:$/, ""); + var url = protocol + "://"; + if (loc.username) { + url += loc.username; + if (loc.password) { + url += ":" + loc.password; + } + url += "@"; + } + if (loc.host) { + url += loc.host; + } + + if (loc.port) { + url += ":" + loc.port; + } + + if (loc.pathname) { + url += loc.pathname; + } + + if (loc.search) { + url += "?" + loc.search; + } + if (loc.hash) { + var hash = loc.hash; + hash = loc.hash.replace(/^#/, ""); + url += "#" + hash; + } + return url; +} + +function canonicalize(url) { + var tempLink = window.document.createElement("link"); + tempLink.href = url; // this will canonicalize the href + return tempLink.href; +} + +function extractExceptionMessage(ex) { + if (ex == null) return "null exception"; + if (ex.message != null) return ex.message; + if (ex.toString && ex.toString() != null) return ex.toString(); +} + + function describe(object, delimiter) { var props = new Array(); for (var prop in object) { - props.push(prop + " -> " + object[prop]); + try { + props.push(prop + " -> " + object[prop]); + } catch (e) { + props.push(prop + " -> [htmlutils: ack! couldn't read this property! (Permission Denied?)]"); + } } return props.join(delimiter || '\n'); } @@ -502,28 +711,27 @@ function SeleniumError(message) { return error; } -var Effect = new Object(); - -Object.extend(Effect, { - highlight : function(element) { - var highLightColor = "yellow"; - if (element.originalColor == undefined) { // avoid picking up highlight - element.originalColor = Element.getStyle(element, "background-color"); - } - Element.setStyle(element, {"background-color" : highLightColor}); - window.setTimeout(function() { +function highlight(element) { + var highLightColor = "yellow"; + if (element.originalColor == undefined) { // avoid picking up highlight + element.originalColor = elementGetStyle(element, "background-color"); + } + elementSetStyle(element, {"backgroundColor" : highLightColor}); + window.setTimeout(function() { + try { //if element is orphan, probably page of it has already gone, so ignore if (!element.parentNode) { return; } - Element.setStyle(element, {"background-color" : element.originalColor}); - }, 200); - } -}); + elementSetStyle(element, {"backgroundColor" : element.originalColor}); + } catch (e) {} // DGF unhighlighting is very dangerous and low priority + }, 200); +} + // for use from vs.2003 debugger -function objToString(obj) { +function o2s(obj) { var s = ""; for (key in obj) { var line = key + "->" + obj[key]; @@ -535,7 +743,7 @@ function objToString(obj) { var seenReadyStateWarning = false; -function openSeparateApplicationWindow(url) { +function openSeparateApplicationWindow(url, suppressMozillaWarning) { // resize the Selenium window itself window.resizeTo(1200, 500); window.moveTo(window.screenX, 0); @@ -560,7 +768,7 @@ function openSeparateApplicationWindow(url) { } - if (window.document.readyState == null && !seenReadyStateWarning) { + if (!suppressMozillaWarning && window.document.readyState == null && !seenReadyStateWarning) { alert("Beware! Mozilla bug 300992 means that we can't always reliably detect when a new page has loaded. Install the Selenium IDE extension or the readyState extension available from selenium.openqa.org to make page load detection more reliable."); seenReadyStateWarning = true; } @@ -568,8 +776,8 @@ function openSeparateApplicationWindow(url) { return appWindow; } -var URLConfiguration = Class.create(); -Object.extend(URLConfiguration.prototype, { +var URLConfiguration = classCreate(); +objectExtend(URLConfiguration.prototype, { initialize: function() { }, _isQueryParameterTrue: function (name) { @@ -615,6 +823,11 @@ Object.extend(URLConfiguration.prototype, { isMultiWindowMode:function() { return this._isQueryParameterTrue('multiWindow'); + }, + + getBaseUrl:function() { + return this._getQueryParameter('baseUrl'); + } }); diff --git a/tests/test_tools/selenium/core/scripts/injection.html b/tests/test_tools/selenium/core/scripts/injection.html index d41fbe69..a75c7211 100644 --- a/tests/test_tools/selenium/core/scripts/injection.html +++ b/tests/test_tools/selenium/core/scripts/injection.html @@ -1,21 +1,22 @@ + - @@ -49,6 +49,7 @@ to work-around a bug in IE on Win2K whereby the HTA application doesn't function diff --git a/tests/test_tools/selenium/php/selenium.php b/tests/test_tools/selenium/php/selenium.php index c9d403f1..474c0d6d 100644 --- a/tests/test_tools/selenium/php/selenium.php +++ b/tests/test_tools/selenium/php/selenium.php @@ -142,7 +142,7 @@ class SeleneseInterpreter $command = array($func, $ID, $value); if(is_int(strpos(strtolower($func), 'visible'))) - $this->addCommand(array('pause','500',''),$trace); + $this->addCommand(array('pause','250',''),$trace); return $this->addCommand($command, $trace); } diff --git a/tests/test_tools/selenium/prado-functional-test.js b/tests/test_tools/selenium/prado-functional-test.js index 4cda378c..9d4446f0 100644 --- a/tests/test_tools/selenium/prado-functional-test.js +++ b/tests/test_tools/selenium/prado-functional-test.js @@ -1,19 +1,45 @@ -Object.extend(HtmlTestRunner.prototype, { - loadSuiteFrame: function() { - if (selenium == null) { - selenium = Selenium.createForWindow(this._getApplicationWindow()); - this._registerCommandHandlers(); - } - this.controlPanel.setHighlightOption(); - //var testSuiteName = this.controlPanel.getTestSuiteName(); - var testSuiteName = document.location+'?testSuites'; - if (testSuiteName) { - suiteFrame.load(testSuiteName, this._onloadTestSuite.bind(this)); - } + +objectExtend(HtmlTestRunnerControlPanel.prototype, { + getTestSuiteName: function() { + return document.location+'?testSuites'; //this._getQueryParameter("test"); } }); -Object.extend(HtmlTestRunnerControlPanel.prototype, { +SeleniumFrame.prototype._setLocation = function(location) { + /* var isChrome = browserVersion.isChrome || false; + var isHTA = browserVersion.isHTA || false; + // DGF TODO multiWindow + location += "?thisIsChrome=" + isChrome + "&thisIsHTA=" + isHTA;*/ + if (browserVersion.isSafari) { + // safari doesn't reload the page when the location equals to current location. + // hence, set the location to blank so that the page will reload automatically. + this.frame.src = "about:blank"; + this.frame.src = location; + } else { + this.frame.contentWindow.location.replace(location); + } + }; + +SeleniumFrame.prototype._attachStylesheet = function() +{ + var base_url = script_base_url; + var d = this.getDocument(); + var head = d.getElementsByTagName('head').item(0); + var styleLink = d.createElement("link"); + styleLink.rel = "stylesheet"; + styleLink.type = "text/css"; + styleLink.href = base_url + "core/selenium-test.css"; + head.appendChild(styleLink); +}; + +HtmlTestFrame.prototype._setLocation = SeleniumFrame.prototype._setLocation; +HtmlTestSuiteFrame.prototype._setLocation = SeleniumFrame.prototype._setLocation; + +HtmlTestFrame.prototype._attachStylesheet = SeleniumFrame.prototype._attachStylesheet; +HtmlTestSuiteFrame.prototype._attachStylesheet = SeleniumFrame.prototype._attachStylesheet; + + +objectExtend(HtmlTestRunnerControlPanel.prototype, { _parseQueryParameter: function() { var tempRunInterval = this._getQueryParameter("runInterval"); if (tempRunInterval) { @@ -37,7 +63,7 @@ Selenium.prototype.getAttribute = function(target) { */ Selenium.prototype.isVisible = function(locator) { var element; - element = this.page().findElement(locator); + element = this.page().findElement(locator); if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) var visibility = element.style["visibility"]; @@ -64,13 +90,13 @@ Selenium.prototype._isDisplayed = function(element) { return true; }; -Selenium.prototype.assertEmptySelection = function(selectLocator, optionLocator) +Selenium.prototype.assertEmptySelection = function(selectLocator, optionLocator) { /** * Verifies that the selected option of a drop-down satisfies the optionSpecifier. - * + * *

See the select command for more information about option locators.

- * + * * @param selectLocator an element locator identifying a drop-down menu * @param optionLocator an option locator, typically just an option label (e.g. "John Smith") */ @@ -78,9 +104,9 @@ Selenium.prototype.assertEmptySelection = function(selectLocator, optionLocator) var locator = this.optionLocatorFactory.fromLocatorString(optionLocator); return element.selectedIndex == -1; } - -Object.extend(HtmlTestSuite.prototype, { + +objectExtend(HtmlTestSuite.prototype, { _onTestSuiteComplete: function() { this.markDone(); var result = new TestResult(this.failed, this.getTestTable()); @@ -89,7 +115,7 @@ Object.extend(HtmlTestSuite.prototype, { }); - + // Post the results to a servlet, CGI-script, etc. The URL of the // results-handler defaults to "/postResults", but an alternative location @@ -208,10 +234,10 @@ function parse_resultCell(resultCell,rowNum,form) function get_color_status(element) { - var color = element.getAttribute("bgcolor"); - if(color == FeedbackColors.passColor) return "passed"; - if(color == FeedbackColors.failColor) return "failed"; - if(color == FeedbackColors.doneColor) return "done"; + var color = element.className + if(color == 'status_passed') return "passed"; + if(color == 'status_failed') return "failed"; + if(color == 'status_done') return "done"; return ""; } -- cgit v1.2.3