This is a work in progress. Please feel free to ask questions and/or
+provide answers; send email to the Selenium users email address at selenium-users@lists.public.thoughtworks.org.
It is used for functional or system testing web applications. These tests
+are also sometimes called acceptance, customer, or integration tests. Selenium is not meant for unit testing.
Question:
+I was trying to write a simple script that does a google search.
+I have been running into all sorts of problems. Does this work for you?
+Here is my test:
+
+
+
Test Type
+
+
+
+
open
+
http://www.google.com/
+
+
+
+
type
+
q
+
testing tools
+
+
+
click
+
submitButton
+
+
+
+
.
+
Answer:
+The quick answer is that because of cross-site scripting security built into
+JavaScript engines in all browsers, you can't edit the content of a web page
+from another domain. The foreign page will probably load correctly and be visible
+in the test runner window, but Selenium won't be able to query or edit its contents.
+In other words, you can't run selenium on "foo.com" and
+run a test that edits values and clicks buttons against "bar.com". So, in
+its current form, you can't "script" google.com because your script isn't
+currently hosted on google.com. When Selenium and the application you are
+testing is hosted on the same domain, however, you do not run into the
+cross-site scripting security feature/limitation.
Also, if cross-site scripting security didn't exist, be careful about your
+field and button references in your tests. The current version
+of Selenium uses the "id" attribute of the object you are referring to in your
+test. The search field and submit button at google.com have "name" attributes,
+but not not "id" attributes. Therefore, Selenium wouldn't be able to find the objects.
+Future versions of Selenium will be able to search for objects by more than
+just the id attribute, though.
There are a few ways around cross-site scripting to access a remote server.
+You could use a combination of proxying and URL rewriting in Apache to
+trick the browser into the thinking the application and the testing tool
+are coming from the same domain.
+
Another option is to run Selenium as an "HTA" application, or "HTML
+Application" in Internet Explorer. HTA applications run in the security
+context of any trusted application on the client, so there is no cross-site
+scripting limitation. (You can find out more here:
+http://msdn.microsoft.com/workshop/author/hta/overview/htaoverview.asp) The
+equivalent to this "security-free" client on the Mozilla side of the fence
+would be to write a XUL wrapper/extension.
+
Also, please see the answer to the related question: "Why can't I script google.com".
The developers on the Selenium project use Mozilla Composer to
+create plain HTML text files for their tests.
+By default, Mozilla Composer writes very clean HTML without any extra, unnecessary markup.
+
Future versions of Selenium may support RST (ReStructred Text), or wiki-table
+syntax, natively. However, you are free to use another format now,
+as long as you remember to generate the HTML files from your source files,
+either during your build process or dynamically at run-time.
+
+
diff --git a/tests/FunctionalTests/selenium/doc/FAQ.txt b/tests/FunctionalTests/selenium/doc/FAQ.txt
new file mode 100644
index 00000000..ad9894ee
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/FAQ.txt
@@ -0,0 +1,127 @@
+===========================================
+ Selenium Frequently Asked Questions
+===========================================
+
+.. Please note that until there's a Q&A-specific construct available,
+ this FAQ will use section titles for questions. Therefore
+ questions must fit on one line. The title may be a summary of the
+ question, with the full question in the section body.
+
+This is a work in progress. Please feel free to ask questions and/or
+provide answers; send email to the Selenium users email address at `selenium-users@lists.public.thoughtworks.org`__.
+
+.. _let us know:
+__ mailto:selenium-users@lists.public.thoughtworks.org
+
+
+.. contents::
+.. sectnum::
+
+
+
+
+Selenium
+========
+
+What is Selenium used for?
+--------------------------
+
+It is used for functional or system testing web applications. These tests
+are also sometimes called acceptance, customer, or integration tests. Selenium is not meant for unit testing.
+
+
+
+Why can't I script google.com?
+------------------------------
+Question:
+*I was trying to write a simple script that does a google search.
+I have been running into all sorts of problems. Does this work for you?
+Here is my test:* |test|.
+
+.. |test| raw:: html
+
+
+
+
+
Test Type
+
+
+
+
open
+
http://www.google.com/
+
+
+
+
type
+
q
+
testing tools
+
+
+
click
+
submitButton
+
+
+
+
+
+Answer:
+The quick answer is that because of cross-site scripting security built into
+JavaScript engines in all browsers, you can't edit the content of a web page
+from another domain. The foreign page will probably load correctly and be visible
+in the test runner window, but Selenium won't be able to query or edit its contents.
+In other words, you can't run selenium on "foo.com" and
+run a test that edits values and clicks buttons against "bar.com". So, in
+its current form, you can't "script" google.com because your script isn't
+currently hosted on google.com. When Selenium and the application you are
+testing is hosted on the same domain, however, you do not run into the
+cross-site scripting security feature/limitation.
+
+You read more about cross-site scripting here: http://www.devarticles.com/c/a/JavaScript/JavaScript-Security/
+
+Also, if cross-site scripting security didn't exist, be careful about your
+field and button references in your tests. The current version
+of Selenium uses the "id" attribute of the object you are referring to in your
+test. The search field and submit button at google.com have "name" attributes,
+but not not "id" attributes. Therefore, Selenium wouldn't be able to find the objects.
+Future versions of Selenium will be able to search for objects by more than
+just the id attribute, though.
+
+
+How can I run my test against a foreign or remote server and get around cross-site scripting security?
+------------------------------------------------------------------------------------------------------
+
+There are a few ways around cross-site scripting to access a remote server.
+You could use a combination of proxying and URL rewriting in Apache to
+trick the browser into the thinking the application and the testing tool
+are coming from the same domain.
+
+Another option is to run Selenium as an "HTA" application, or "HTML
+Application" in Internet Explorer. HTA applications run in the security
+context of any trusted application on the client, so there is no cross-site
+scripting limitation. (You can find out more here:
+http://msdn.microsoft.com/workshop/author/hta/overview/htaoverview.asp) The
+equivalent to this "security-free" client on the Mozilla side of the fence
+would be to write a XUL wrapper/extension.
+
+Also, please see the answer to the related question: "Why can't I script google.com".
+
+
+How do you create test tables?
+------------------------------
+
+The developers on the Selenium project use Mozilla Composer to
+create plain HTML text files for their tests.
+By default, Mozilla Composer writes very clean HTML without any extra, unnecessary markup.
+
+Future versions of Selenium may support RST (ReStructred Text), or wiki-table
+syntax, natively. However, you are free to use another format now,
+as long as you remember to generate the HTML files from your source files,
+either during your build process or dynamically at run-time.
+
+
+
+:Author:
+ Jason Huggins
+:Created Date: 11/05/2004
+:Modified Date: 11/05/2004
+:Created With: reStructuredText: http://docutils.sourceforge.net/rst.html
\ No newline at end of file
diff --git a/tests/FunctionalTests/selenium/doc/contact.html b/tests/FunctionalTests/selenium/doc/contact.html
new file mode 100644
index 00000000..b109444d
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/contact.html
@@ -0,0 +1,23 @@
+
+
+
+
+ Selenium Contact
+
+
+
+For more information about Selenium, please use Confluence and the standard mailing lists below.
+
+This page details important information for people developing drivers
+for Selenium.
+
Same Origin Policy
+This is a security issue that affects all modern browsers. It is well
+described here,
+but for our purposes, it constrains the way that a
+JavaScript in a browser may interoperate with other open frames and
+windows. In short, the Selenium JavaScript app must come from (or
+appear to) the same origin as the AUT.
+
The Driver
+The driver has a number of duties. These are typically..
+
+
Instantiating the web server with a dynamic app (servlets for
+Java)
+
Launching a browser with a with a URL that makes sense for
+connecting to that server
+
providing an API to such that commands can be routed though the
+dynamic app to the browser
+
+
Local, Remote and URLs
+
+An application may be testable in a remote location by humans, but for
+scripted testing and the need for the driver to dynamically drive the
+browser, it is optimal for the driver to be on the same machine as that
+serving the AUT and the browser testing it.
+
+Thus, an application as deployed ...
+
+
+
+... would appear like so for the purposes of testing ...
+
+
+
+As with the standalone version of Selenium, there are a number of files
+(HTML and JavaScript) that comprise the bulk of the testing framework
+and sit in the browser as testing occurs. It makes most sense to put
+these in a virtual directory on the same server ...
+
+
+
+The dynamic webapp needs to be similarly mounted ...
+
+
+
+As the dynamic is the link between what is happening in the browser and
+the driving process, it we need to somehow have an instance reference
+to it. This is easier in some languages and web servers than
+others. Also full programatic start/stop control over the web
+server is not always possible for some larger web servers
+implementations.
+
Reply/Request Architecture
+Because a browser cannot open a socket and listen on it, we must
+effectively initiate communication thru the driver on the browser side.
+Ignoring the possibilities granted by keep-alive, we simply poll from
+the browser to the server and pick up commands to process inside the
+browser. Results of commands are also passed back to the dynamic
+hander over the same mechanism. These are in fact done in the same HTTP
+request. The results from the previous
+command go back as query string parameters, and the next command is communicated in a
+text/plain document
+
+The previous/next business introduces some complexity on the driver
+side of the design. Namely hidden behind the API, the driver must
+manage queues for the outgoing commands and the (some time later)
+incoming responses. Java, until 1.5, did not have a blocking
+queue to make this easy. Most other languages did.
+
Selenese
+
+
+
+
Selenese is the contrived (and mostly hidden) wire language
+that the
+driver uses to speak to the browser-bot through the dynamic
+handler. It uses HTTP for its transport, and is quite
+simple.
+
+Responses come from the browser to the driver in a query
+string like
+
+ commandResult=OK,
+
+Commands go from the driver to the
+browser-bot in a text/plain document:
+
+ | open | /foo/bar.html | |
+
+This two way communication is of course invisible to the observer.
+
The
+BrowserBot, by understanding Selenese, allows a process other than the
+browser itsself to direct events on the Application Under Test.
+
+The Selenese language is simple enough to be commandable by any
+language that has an API that can handle HTTP requests.
+
+Thus, Selenese allows many different open, free or closed license
+drivers to interoperate with the BrowserBot.
+
+
+
+
Choregraphy
+The driver cleary has some fairly heavy things to do. It is
+important for some robustness to be built into the design. For example
+it may take a long time for the browser to be instantiated on a
+particular platform. It is appropriate for wait timeouts to be
+generous here. Similarly whilst issuing individual commands it is
+important for the driver to wait a sufficient amount of time for a
+command to execute (and its result to come back). For the most
+part on a localhost setup, it will be because the AUT is slow, but it
+could be because some break in the app means that there will be no
+subsequent response. Some timeout regime should be able to
+recover from, mark a test as failed (for reasons of timeout), and start
+again with the next test.
+
+
+
+
diff --git a/tests/FunctionalTests/selenium/doc/driven.html b/tests/FunctionalTests/selenium/doc/driven.html
new file mode 100644
index 00000000..a5f33dff
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/driven.html
@@ -0,0 +1,206 @@
+
+
+
+
+
+ Driven Selenium Reference
+
+
+
+
+
+
Overview
+Driven Selenium is where the browser is under the the control of an
+adjacent process. That process is either a Java, .Net, Ruby or Python
+application and it is typically run in conjunction with a unit testing
+framework like JUnit or NUnit. Also possible, is a console application
+driving a browser interactively.
+
Selenium & Selenese
+The key to this mode of operation is manner in which the browset-bot
+takes instruction from the driver. If it were possible, the
+browser-bot's javascript would open a server socket and await requests
+from the driver. It is against the rules for browser embedded
+javascript, to open ports for incoking requests as it would be a
+general breach of security for the client-OS that the browser is
+running on. What a browser can do is open addition requests to
+the same server that its source came from. See http://www.mozilla.org/projects/security/components/same-origin.html
+for more info.
+
+To overcome the limitations of Javascript in a browser page is the page
+continuously requests pages from the driver (which has conveniently
+opened a web server). The pages which are retrieved from the server are
+in fact plain text and each is an individual instruction from the
+driver for what the browser-bot is to do next. E.g. -
+
+ | open | /foo/bar.html | |
+
+We refer to this architecture are reply/request rather than the more
+ususal request/reply.
+
+The test script is one that would be recognisable to people adept with
+unit test frameworks :
+
+For Java -
+
+ public void testOKClick() {
+ selenium.verifyTitle("First Page");
+ selenium.open("/TestPage.html");
+ selenium.click("OKButton");
+ selenium.verifyTitle("Another Page");
+ }
+
+The difference from normal unit testing is that as part of the startup,
+three major things have to happen:
+
+
The test framework needs to publish a fresh copy of the
+Application Under Test (AUT).
+Selenium prefers to mount its own web server temporarily for the
+purposes of testing.
+
The test framework needs to publish the static Selenium pages
+(refer selenium dir in TestRunner mode above) in an apparent directory
+on the same web server as (1).
+
The test framework needs to open a browser instance and point it
+to Selenium.html served in (2) above.
+
+As each of these isa fairly time consuming operation, it is best that
+all three of those happen in a one time setup mode. As such, and
+even though these leverage a unit testing framework, this is definately
+for acceptance or functional testing.
+
Example Setup
+
+
+
For Java -
+
+ selenium = new DefaultSelenium("c:\foo\bar-web-app\");
+
+The above will instantiate a web server using Jetty, and
+publish it at http://localhost:8080. The Selenium pages will appear to
+be run from http://localhost:8080/selenium-driver. The default browser
+for Windows, Linux or Mac will be instantiated and directed to accept
+test instructions from the driver.
+
+The above would ususally be done in a setup method if under unit test
+control. See http://junit.sourceforge.net/doc/faq/faq.htm#organize_3
+for advice on one time setup for Java.
+
+A more complex case could be -
+
+ selenium = new DefaultSelenium(new
+TomcatCommandProcessor("c:\foo\bar-web-app"), new
+MyOperaBrowserLauncher()),
+
+
+The best way to deply the driven form of Selenium is where an embedded
+web server is used. With the Java version, this could be Jetty or Tomcat.
+
+In advance of a series of selenese instructions being issued to the
+browser, a web server containing the AUT and some static pages for
+Selenium itself will be programmatically started and used to
+communicate selenese instructions to the browser. When the driver
+process is complete the web server will be programmatically stopped.
+
+
[ For release 0.2 - this is the only
+mode that really works. Those below will be fine for 0.3 and above ]
+
+
Adjacent Web Server
+
+By adjacent we mean a process on the same machine as the driver. As
+such it would appear as localhost to browsers.
+
+For the .Net driver embedded is very unlikely as Internet Information
+Server is running in its own process. For the Java driver, this could
+simple be a necessary choice - i.e. the deployment target is WebLogic
+or
+WebSphere which are not too embeddable.
+
+In this scenario we suggest you deploy a small web-app alongside the
+AUT that will liase between the driver process and the browser. Of
+course, there is less fine grained control over the starting and
+stopping of the server and indeed the version of the AUT. If the web
+server supports it, it is best to copy a fresh version of the AUT to
+the underlying directory that the web-app is mapped to. We call the
+small web-app the selenese proxy. It does of course slow things down a
+fraction.
+
+Selenese-proxy
+
+If you can deploy a copy of the selenese proxy to remote web server,
+and configure it to forward requests to your machine, then you can
+essentially script that remote web app. The downside of this is that
+that remote machine can essentially only be driven from the machine
+that is configured to drive it. i.e. it would need to be reconfigured
+to be driven from elsewhere. The upside is that you can to a great
+extent mix and match your technologies to achieve this proxying (a Java
+driver could use a Python selenese-proxy script a web-app).
+
Nearby Web Server
+
+This is where the AUT is running on a nearby testing stack or dedicated
+development box (not the developer's own workstation).
+
+To achieve this the selenese proxy needs to be deployed again, this
+time to that nearby machine. It will need to be reconfigured to
+indicate that selenese traffic is either forwarded to a particular
+machine.
+
+
Remote Web Server
+
+This is where the AUT is running on a remote machine, which you have no
+control over. A good example would be www.google.com. It is
+worth pointing out that this is of more interest to hackers or data
+harvesters than testing professionals, as what self respecting
+development group would prevent you from deploying at least the
+Selenese Proxy webapp to a testing stack.
+
+Funnel
+
+We are writing an application called the funnel that can help us
+overcome the same
+origin issue that is key to Selenium. It essentially makes a
+selenium-driver/ directory appear on a remote web site for the purposes
+of the browser.
+
+
+
+Selenium is a test tool for web applications. Selenium tests run
+directly in a browsers, just as real users do. And they run in
+Internet Explorer, Mozilla and Firefox on Windows, Linux and Macintosh. No
+other test tool covers such a wide array of platforms.
+
+
+
+
Browser compatability testing.
+Test your application to see if it works correctly on different
+browsers and operating systems. The same script can run on any Selenium
+platform.
+
+
System functional testing.
+Create regression tests to verify application functionality and user
+acceptance.
+
+
+Selenium
+uses a unique mechanism which allows it to run on so multiple
+platforms. Installed with your application webserver, Selenium
+automatically deploys it's JavaScript automation engine -- the Browser
+Bot -- to your browser when you point it at the Selenium install point
+on your webserver. Thus, you must have write access to the machine your
+web application server is running on to install Selenium.
+
+
"Considering the simplicity of it, it is
+almost surprising that no one has thought of doing this previously. The
+framework is simple and the code is neat and very maintainable.
+Sometimes it takes a work of genius to find the uncomplicated solution
+to a potentially complicated problem." - Antony Marcano
+
+
+
+
Selenium was
+developed by team
+of programmers and testers at
+ThoughtWorks. It is
+open-source software and can
+be downloaded and used without charge. It is currently under active
+development by our team. Stay tuned for updates and further
+announcements.
+
+
+ThoughtWorks is a leader in Agile development methods for enterprise
+software development. Selenium is designed specifically for the
+acceptance testing requirements of Agile teams. However, teams
+using more traditional development will also find it useful.
+
Supported Browsers and Platforms
+
+
+
+
+
+
+
Internet
+Explorer
+
+
Mozilla
+
+
Firefox
+
+
Safari
+
+
+
+
Windows XP
+
+
6.0
+
+
1.6+, 1.7+
+
+
0.8+, 0.9+, 1.0
+
+
+
+
+
+
Red Hat Linux
+
+
+
+
1.6+, 1.7+
+
+
0.8+, 0.9+, 1.0+
+
+
+
+
+
+
Mac OS X 10.3
+
+
not supported
+
+
1.6+, 1.7+
+
+
0.8+, 0.9+, 1.0+
+
+
1.3+
+
+
+
+
+
+
+
+
How does Selenium Work?
+
+Selenium uses JavaScript and Iframes to embed a test automation
+engine in your browser. This technique should work with any
+JavaScript-enabled browser. Because different browsers handle
+JavaScript somewhat differently, we usually have to tweak the engine to
+support new browsers.
+
Where did Selenium Come From?
+Selenium grew out of a testing framework that was
+developed to acceptance-test the functionality of ThoughtWorks' new
+web-based time & expense reporting application. It was written by
+Jason Huggins, Paul Gross and Jie Tina Wang.
+
Jason
+started demoing the test framework for various colleagues. Many were
+excited about its immediate and intuitive visual feedback, as well as
+its potential to grow as a reusable testing framework for other web
+applications.
+And Selenium was born.
+
+
Having Trouble?
+Check out our Frequently
+Asked Questions page
+for more information.
+
+
diff --git a/tests/FunctionalTests/selenium/doc/images/Adjacent.png b/tests/FunctionalTests/selenium/doc/images/Adjacent.png
new file mode 100644
index 00000000..6da4ad66
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/Adjacent.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/Embedded.png b/tests/FunctionalTests/selenium/doc/images/Embedded.png
new file mode 100644
index 00000000..8e5967d7
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/Embedded.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png b/tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png
new file mode 100644
index 00000000..3cf28aec
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/SmallAdjacent.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png b/tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png
new file mode 100644
index 00000000..399acbf5
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/SmallEmbedded.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/SmallStandalone.png b/tests/FunctionalTests/selenium/doc/images/SmallStandalone.png
new file mode 100644
index 00000000..f04266a0
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/SmallStandalone.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/Standalone.png b/tests/FunctionalTests/selenium/doc/images/Standalone.png
new file mode 100644
index 00000000..f9b670c0
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/Standalone.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/localhostAut.png b/tests/FunctionalTests/selenium/doc/images/localhostAut.png
new file mode 100644
index 00000000..25204654
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/localhostAut.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/localhostDriver.png b/tests/FunctionalTests/selenium/doc/images/localhostDriver.png
new file mode 100644
index 00000000..a904d7d4
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/localhostDriver.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/localhostSelenium.png b/tests/FunctionalTests/selenium/doc/images/localhostSelenium.png
new file mode 100644
index 00000000..af527be5
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/localhostSelenium.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/stockmeister.png b/tests/FunctionalTests/selenium/doc/images/stockmeister.png
new file mode 100644
index 00000000..b07aabc6
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/stockmeister.png differ
diff --git a/tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png b/tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png
new file mode 100644
index 00000000..fa80b414
Binary files /dev/null and b/tests/FunctionalTests/selenium/doc/images/tested-with-selenium.png differ
diff --git a/tests/FunctionalTests/selenium/doc/index.html b/tests/FunctionalTests/selenium/doc/index.html
new file mode 100644
index 00000000..8f7fed80
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+ Selenium
+
+
+
JSRMI (Javascript Remote Method Invocation) is a portable browser-neutral Javascript library that makes it possible to execute
+Javascript functions in a browser from a process external to the browser.
+
JSRMI is not in any way tied to Java's RMI, but provides a similar mechanism. JSRMI is completely decoupled from the core Selenium Javascript API, but it can
+be used to access the Selenium API from outside the browser.
+
All of the
+browser-side JSRMI code resides in the rmi.js script - available in the Selenium
+distribution.
This will modify the text of a text area in the browser. Looks strangely
+familiar to Javascript doesn't it? You can of course call the selenium API too
+if the browser has loaded the main Selenium page (which also includes the rmi.js
+script - at least that is the plan - I hope (Aslak)).
+
How does it work?
+
(You can safely skip this section if you don't care - this is gory details)
+
Browser side
+
The rmi.js script uses the
+
+XMLHttpRequest object (available in all compatible browsers) to communicate
+with the external process. It executes a GET-POST loop which is repeated ad
+infinitum - pulling JSRMI invocations from the external process with GET and
+POSTing the results back. This all happens in a separate thread, thereby having
+minimal impact on the rest of the web page - without causing a page refresh.
+
The rmi.js script will do a HTTP GET to a URL on the same host/port as the rmi.js
+script was loaded from. The content returned from the GET (which must comply
+with the JSRMI protocol) is then translated into
+Javascript and dynamically executed via Javascript's
+eval() function.
+
The result of the function call (typically a Javascript object) is translated
+back into the JSRMI protocol format and POSTed back to the same URL as the GET.
+
External process side
+
The external process typically consists of a library that embeds the
+following functionality:
+
+
A HTTP server (should be light to ensure fast startup)
+
An API that translates local invocations into the JSRMI protocol
+
Two blocking queues:
+
+
Output queue - HTTP GET invocations will take JSRMI protocol strings
+ (representing browser side invocations) from this queue and block until
+ something is available. These strings are returned to the HTTP client (The
+ rmi.js script in the browser). This means a blocking GET for the JSRMI
+ browser side).
+
Input queue - HTTP POST data from the browser side JSRMI will be enqued
+ here. This data represents results of browser side Javascript invocations.
+
+
+
A local invocation should translate the invocation to a JSRMI protocol string
+and put it on the output queue (which jsrmi will GET). It should then wait for
+the result of the browser side invocation to be put back on the input queue via
+a POST from jsrmi. Finally it should translate the return value (another JSRMI
+protocol string) into a native object and return it to the caller.
+
At any given point in time there should only be one single JSRMI protocol
+string in one of the queues - depending on the where the invocation is in its
+lifecycle.
+
Reference objects
+
JSRMI allows objects (such as browser side HTMLDocument, HTMLTextField etc)
+to be transferred back and forth. This is based on a simple mechanism where each
+object is given a unique id and maintained in a pool on each side. this pool is
+used to reference and dereference native objects back and forth from the JSRMI
+protocol strings.
+
Why would I use JSRMI?
+
With Selenium
+
The Selenium browser runtime will load both selenium test scripts and the web
+pages from the web application you're testing into the browser. Modern browsers
+don't allow content to be loaded from different hosts (cross browsing security
+restrictions). A web application being tested will typically be deployed on a
+server, and therefore the selenium test scripts must be loaded from the same web
+server. Depending on who's writing the Selenium scripts and executing them, this
+may or may not be a restriction to its usage.
+
Under some circumstances it is desirable to keep Selenium test scripts on
+your local machine (the same as the one running the browser with the Selenium
+runtime) rather than on a remote web server hosting the web application being
+tested. Some examples are:
+
+
The edit/run cycle of selenium scripts can be cumbersome if the script has
+ to be deployed to a server each time it is modified. Being able to keep the
+ scripts on a different machine (such as the one on your desk) can
+ significantly improve the ease of use and rapid development of tests. JSRMI
+ lets you do just that.
+
Putting in place a deployment routine for selenium script requires
+ technical knowledge of the web application's build process as well as the web
+ server hosting it. Many users of Selenium will not have easy access to this
+ expertise. JSRMI lets these users use Selenium nevertheless.
+
+
It is important to emphasise that hosting the Selenium scripts on a local
+machine would also require that the browser loads the web site being tested from
+the local machine. Aren't we creating new problems by requiring testers to
+install a full web server environment on their machines? Actually no. The JSRMI
+libraries in Ruby and Java (and those who may follow) will soon provide a light
+HTTP proxy server. The local browser will load the remote web site through this
+HTTP proxy. The browser's security restrictions are satisfied since all content
+(Selenium runtime, Selenium scripts and the remote website) is loaded through
+localhost.
+
Scripting of existing web applications
+
Think of all boring web form tasks you could automate with JSRMI....
How do I implement a JSRMI client library for language X?
+
Start by understand the inner workings - look at the ruby implementation and
+study the javascript. (When the JSRMI protocol is better described, studying
+this code should not be necessary).
+
It will be to implement a JSRMI client
+library in a dynamic language than a static language, because dynamic languages
+allow arbitrary messages (method invocations) to be sent to an object. Most
+dynamic languages (Ruby, Python, Groovy, Smalltalk) have a generic mechanism to
+intercept any message and an object message->JSRMI protocol translation logic is
+trivial to implement based on this.
+
Guidelines for static languages such as Java and C#
+
JSRMI clients for static languages such as Java or C# will either have to
+choose a subset of the Javascript functions and objects that you want to access,
+or implement some generic invocation mechanism that allows raw JSRMI protocol
+strings.
+
The former is more easy to use from a user perspective, but will be
+restricted in terms of flexibility. The latter is completely generic, but
+awkward to deal with from a user perspective.
+
The recommendation is to implement a raw interface to the JSRMI protocol and
+have a generic dynamic proxy implementation on top of that. This way the API
+support can easily be extended simply by implementing new interfaces for the
+Javascript counterparts and generate dynamic proxies on the fly as needed.
+
Calling functions/methods in an external process from the browser using JSRMI
+
This is currently not possible.
+
+
\ No newline at end of file
diff --git a/tests/FunctionalTests/selenium/doc/release-notes.html b/tests/FunctionalTests/selenium/doc/release-notes.html
new file mode 100644
index 00000000..f7b7971b
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/release-notes.html
@@ -0,0 +1,97 @@
+
+
+
+ Release Notes
+
+
+
+
+
+Release information for different distributions of Selenium
+
+
Selenium 0.6.0
+
+September 24, 2005
+
+
+
pattern-matching
Support for regular-expression and exact matching. Allow patterns to be used to match alert and confirmation messages.
+
support for Javascript prompts
Capture and verify prompt-messages, set up return values.
Zope product plug-in for Plone Content Management System
+
Many, many bug fixes
+
+
+
+
Selenium 0.2
+
+Jan 20, 2005
+
+
+
Java and Ruby drivers
+
+
Cross browser capability improved
+
+
+
+
+
diff --git a/tests/FunctionalTests/selenium/doc/rst2html.bat b/tests/FunctionalTests/selenium/doc/rst2html.bat
new file mode 100644
index 00000000..8fc9043f
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/rst2html.bat
@@ -0,0 +1,3 @@
+rem - Uses Python docutils-0.3.5
+rem - URL: http://docutils.sourceforge.net
+c:/python23/python.exe c:/python23/Scripts/rst2html.py ./seleniumReference.txt ./seleniumReference.html
\ No newline at end of file
diff --git a/tests/FunctionalTests/selenium/doc/seleniumReference.html b/tests/FunctionalTests/selenium/doc/seleniumReference.html
new file mode 100644
index 00000000..b9b186b9
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/seleniumReference.html
@@ -0,0 +1,1148 @@
+
+
+
+
+
+
+Selenium Reference
+
+
+
+
Selenium Reference
+
+
+
A command is what tells Selenium what to do. Selenium commands come in two 'flavors', Actions and Assertions.
+Each command call is one line in the test table of the form:
+
+
+
+
+
+
+
+
+
command
+
target
+
value
+
+
+
+
+
Actions are commands that generally manipulate the state of the application. They do things like "click this link" and "select that option". If an Action fails, or has an error, the execution of the current test is stopped.
+
Checks verify the state of the application conforms to what is expected. Examples include "make sure the page title is X" and "check that this checkbox is checked". It is possible to tell Selenium to stop the test when an Assertion fails, or to simply record the failure and continue.
+
Element Locators tell Selenium which HTML element a command refers to. Many commands require an Element Locator as the "target" attribute. Examples of Element Locators include "elementId" and "document.forms[0].element". These are described more clearly in the next section.
+
Patterns are used for various reasons, e.g. to specify the expected value of an input field, or identify a select option. Selenium supports various types of pattern, including regular-expressions, all of which are described in more detail below.
Select Option Specifiers provide different ways of specifying options of an HTML Select element (e.g. for selecting a specific option, or for asserting that the selected option satisfies a specification). There are several forms of Select Option Specifier.
+
+
label=labelPattern
+
+
matches options based on their labels, i.e. the visible text.
+
+
label=regexp:^[Oo]ther
+
+
+
+
+
value=valuePattern
+
+
matches options based on their values.
+
+
value=other
+
+
+
+
+
id=id
+
+
matches options based on their ids.
+
+
id=option1
+
+
+
+
+
index=index
+
+
matches an option based on its index (offset from zero).
+
+
index=2
+
+
+
+
+
+
Without a prefix, the default behaviour is to only match on labels.
Various Pattern syntaxes are available for matching string values:
+
+
glob:pattern
+
Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a kind of limited regular-expression syntax typically used in command-line shells. In a glob pattern, "*" represents any sequence of characters, and "?" represents any single character. Glob patterns match against the entire string.
+
regexp:regexp
+
Match a string using a regular-expression. The full power of JavaScript regular-expressions is available.
+
exact:string
+
Match a string exactly, verbatim, without any of that fancy wildcard stuff.
+
+
If no pattern prefix is specified, Selenium assumes that it's a "glob" pattern.
Actions tell Selenium to do something in the application. They generally represent something a user would do.
+
Many Actions can be called with the "AndWait" suffix. This suffix tells Selenium that the action will cause the browser to make a call to the server, and that Selenium should wait for a new page to load.
+The exceptions to this pattern are the "open" and "click" actions, which will both wait for a page to load by default.
+
open( url )
+
+
Opens a URL in the test frame. This accepts both relative and absolute URLs.
+
Note: The URL must be on the same site as Selenium due to security restrictions in the browser (Cross Site Scripting).
Clicks on a link, button, checkbox or radio button.
+If the click action causes a new page to load (like a link usually does), use "clickAndWait".
+
examples:
+
+
+
+
+
+
+
+
+
click
+
aCheckbox
+
+
+
clickAndWait
+
submitButton
+
+
+
clickAndWait
+
anyLink
+
+
+
+
+
+
+
note:
+
Selenium will always automatically click on a popup dialog raised by the alert() or confirm()
+methods. (The exception is those raised during 'onload', which are not yet handled by Selenium).
+You must use [verify|assert]Alert or [verify|assert]Confirmation to tell Selenium that you expect the
+popup dialog. You may use chooseCancelOnNextConfirmation to click 'cancel' on the next confirmation
+dialog instead of clicking 'OK'.
+
+
+
type( inputLocator, value )
+
+
Sets the value of an input field, as though you typed it in.
+
Can also be used to set the value of combo boxes, check boxes, etc. In these cases, value should be the value of the option selected, not the visible text.
+
examples:
+
+
+
+
+
+
+
+
+
type
+
nameField
+
John Smith
+
+
typeAndWait
+
textBoxThatSubmitsOnChange
+
newValue
+
+
+
+
+
+
select( dropDownLocator, optionSpecifier )
+
+
Select an option from a drop-down, based on the optionSpecifier. If more than one option matches the specifier (e.g. due to the use of globs like "f*b*", or due to more than one option having the same label or value), then the first matches is selected.
+
examples:
+
+
+
+
+
+
+
+
+
select
+
dropDown
+
Australian Dollars
+
+
select
+
dropDown
+
index=0
+
+
selectAndWait
+
currencySelector
+
value=AUD
+
+
selectAndWait
+
currencySelector
+
label=Aus*lian D*rs
+
+
+
+
+
+
selectWindow( windowId )
+
+
Selects a popup window. Once a popup window has been selected, all commands go to that window. To select the main window again, use "null" as the target.
+
target: The id of the window to select.
+
value:ignored
+
examples:
+
+
+
+
+
+
+
+
+
selectWindow
+
myPopupWindow
+
+
+
selectWindow
+
null
+
+
+
+
+
+
+
goBack()
+
+
Simulates the user clicking the "back" button on their browser.
+
examples:
+
+
+
+
+
+
+
+
+
goBack
+
+
+
+
+
+
+
+
close()
+
+
Simulates the user clicking the "close" button in the titlebar of a popup window.
+
examples:
+
+
+
+
+
+
+
+
+
close
+
+
+
+
+
+
+
+
pause( milliseconds )
+
+
Pauses the execution of the test script for a specified amount of time. This is useful for debugging a script or pausing to wait for some server side action.
+
examples:
+
+
+
+
+
+
+
+
+
pause
+
5000
+
+
+
pause
+
2000
+
+
+
+
+
+
+
fireEvent( elementLocator, eventName )
+
+
Explicitly simulate an event, to trigger the corresponding "onevent" handler.
+
examples:
+
+
+
+
+
+
+
+
+
fireEvent
+
textField
+
focus
+
+
fireEvent
+
dropDown
+
blur
+
+
+
+
+
+
waitForValue( inputLocator, value )
+
+
Waits for a specified input (e.g. a hidden field) to have a specified value. Will succeed immediately if the input already has the value. This is implemented by polling for the value. Warning: can block indefinitely if the input never has the specified value.
+
example:
+
+
+
+
+
+
+
+
+
waitForValue
+
finishIndication
+
isfinished
+
+
+
+
+
+
store( valueToStore, variableName )
+
+
Stores a value into a variable. The value can be constructed using either variable substitution or javascript evaluation, as detailed in 'Parameter construction and Variables' (below).
+
examples:
+
+
+
+
+
+
+
+
+
store
+
Mr John Smith
+
fullname
+
+
store
+
${title} ${firstname} ${surname}
+
fullname
+
+
store
+
javascript{Math.round(Math.PI * 100) / 100}
+
PI
+
+
+
+
+
+
storeValue( inputLocator, variableName )
+
+
Stores the value of an input field into a variable.
Stores the value of an element attribute into a variable.
+
examples:
+
+
+
+
+
+
+
+
+
storeAttribute
+
input1@class
+
classOfInput1
+
+
verifyAttribute
+
input2@class
+
${classOfInput1}
+
+
+
+
+
+
chooseCancelOnNextConfirmation()
+
+
Instructs Selenium to click Cancel on the next javascript confirmation dialog to be raised. By default, the confirm function will return true, having the same effect as manually clicking OK. After running this command, the next confirmation will behave as if the user had clicked Cancel.
+
examples:
+
+
+
+
+
+
+
+
+
chooseCancelOnNextConfirmation
+
+
+
+
+
+
+
+
answerOnNextPrompt( answerString )
+
+
Instructs Selenium to return the specified answerString in response to the next prompt.
Checks are used to verify the state of the application. They can be used to check the value of a form field, the presense of some text, or the URL of the current page.
+
All Selenium Checks can be used in 2 modes, "assert" and "verify". These behave identically, except that when an "assert" check fails, the test is aborted. When a "verify" check fails, the test will continue execution.
+This allows a single "assert" to ensure that the application is on the correct page, followed by a bunch of "verify" checks to test form field values, labels, etc.
+
assertLocation( relativeLocation )
+
+
examples:
+
+
+
+
+
+
+
+
+
verifyLocation
+
/mypage
+
+
+
assertLocation
+
/mypage
+
+
+
+
+
+
+
assertTitle( titlePattern )
+
+
Verifies the title of the current page.
+
examples:
+
+
+
+
+
+
+
+
+
verifyTitle
+
My Page
+
+
+
assertTitle
+
My Page
+
+
+
+
+
+
+
assertValue( inputLocator, valuePattern )
+
+
Verifies the value of an input field (or anything else with a value parameter). For checkbox/radio elements, the value will be "on" or "off" depending on whether the element is checked or not.
+
examples:
+
+
+
+
+
+
+
+
+
verifyValue
+
nameField
+
John Smith
+
+
assertValue
+
document.forms[2].nameField
+
John Smith
+
+
+
+
+
+
assertSelected( selectLocator, optionSpecifier )
+
+
Verifies that the selected option of a drop-down satisfies the optionSpecifier.
Verifies the labels of all options in a drop-down against a comma-separated list. Commas in an expected option can be escaped as ",".
+
examples:
+
+
+
+
+
+
+
+
+
verifySelectOptions
+
dropdown2
+
John Smith,Dave Bird
+
+
assertSelectOptions
+
document.forms[2].dropDown
+
Smith\, J,Bird\, D
+
+
+
+
+
+
assertText( elementLocator, textPattern )
+
+
Verifies the text of an element. This works for any element that contains text. This command uses either the textContent (Mozilla-like browsers) or the innerText (IE-like browsers) of the element, which is the rendered text shown to the user.
Verifies that the specified text appears somewhere on the rendered page shown to the user.
+
examples:
+
+
+
+
+
+
+
+
+
verifyTextPresent
+
You are now logged in.
+
+
+
assertTextPresent
+
You are now logged in.
+
+
+
+
+
+
+
assertTextNotPresent( text )
+
+Verifies that the specified text does NOT appear anywhere on the rendered page.
+
assertElementPresent( elementLocator )
+
+
Verifies that the specified element is somewhere on the page.
+
examples:
+
+
+
+
+
+
+
+
+
verifyElementPresent
+
submitButton
+
+
+
assertElementPresent
+
//img[@alt='foo']
+
+
+
+
+
+
+
assertElementNotPresent( elementLocator )
+
+
Verifies that the specified element is NOT on the page.
+
examples:
+
+
+
+
+
+
+
+
+
verifyElementNotPresent
+
cancelButton
+
+
+
assertElementNotPresent
+
cancelButton
+
+
+
+
+
+
+
assertTable( cellAddress, valuePattern )
+
+
Verifies the text in a cell of a table. The cellAddress syntax tableName.row.column, where row and column start at 0.
+
examples:
+
+
+
+
+
+
+
+
+
verifyTable
+
myTable.1.6
+
Submitted
+
+
assertTable
+
results.0.2
+
13
+
+
+
+
+
+
assertVisible( elementLocator )
+
+
Verifies that the specified element is both present and visible. An element can be rendered invisible by setting the CSS "visibility" property to "hidden", or the "display" property to "none", either for the element itself or one if its ancestors.
+
examples:
+
+
+
+
+
+
+
+
+
verifyVisible
+
postcode
+
+
+
assertVisible
+
postcode
+
+
+
+
+
+
+
assertNotVisible( elementLocator )
+
+
Verifies that the specified element is NOT visible. Elements that are simply not present are also considered invisible.
+
examples:
+
+
+
+
+
+
+
+
+
verifyNotVisible
+
postcode
+
+
+
assertNotVisible
+
postcode
+
+
+
+
+
+
+
verifyEditable / assertEditable( inputLocator )
+
+
Verifies that the specified element is editable, ie. it's an input element, and hasn't been disabled.
+
examples:
+
+
+
+
+
+
+
+
+
verifyEditable
+
shape
+
+
+
assertEditable
+
colour
+
+
+
+
+
+
+
assertNotEditable( inputLocator )
+
+Verifies that the specified element is NOT editable, ie. it's NOT an input element, or has been disabled.
+
assertAlert( messagePattern )
+
+
Verifies that a javascript alert with the specified message was generated. Alerts must be verified in the same order that they were generated.
+
Verifying an alert has the same effect as manually clicking OK. If an alert is generated but you do not verify it, the next Selenium action will fail.
+
NOTE: under Selenium, javascript alerts will NOT pop up a visible alert dialog.
+
NOTE: Selenium does NOT support javascript alerts that are generated in a page's onload() event handler. In this case a visible dialog WILL be generated and Selenium will hang until you manually click OK.
+
examples:
+
+
+
+
+
+
+
+
+
verifyAlert
+
Invalid Phone Number
+
+
+
assertAlert
+
Invalid Phone Number
+
+
+
+
+
+
+
assertConfirmation( messagePattern )
+
+
Verifies that a javascript confirmation dialog with the specified message was generated. Like alerts, confirmations must be verified in the same order that they were generated.
+
By default, the confirm function will return true, having the same effect as manually clicking OK. This can be changed by prior execution of the chooseCancelOnNextConfirmation command (see above). If an confirmation is generated but you do not verify it, the next Selenium action will fail.
+
NOTE: under Selenium, javascript confirmations will NOT pop up a visible dialog.
+
NOTE: Selenium does NOT support javascript confirmations that are generated in a page's onload() event handler. In this case a visible dialog WILL be generated and Selenium will hang until you manually click OK.
+
examples:
+
+
+
+
+
+
+
+
+
assertConfirmation
+
Remove this user?
+
+
+
verifyConfirmation
+
Are you sure?
+
+
+
+
+
+
+
assertPrompt( messagePattern )
+
+
Verifies that a javascript prompt dialog with the specified message was generated. Like alerts, prompts must be verified in the same order that they were generated.
+
Successful handling of the prompt requires prior execution of the answerOnNextPrompt command (see above). If a prompt is generated but you do not verify it, the next Selenium action will fail.
All Selenium command parameters can be constructed using both simple
+variable substitution as well as full javascript. Both of these
+mechanisms can access previously stored variables, but do so using
+different syntax.
+
Stored Variables
+
The commands store, storeValue and storeText can be used to store a variable
+value for later access. Internally, these variables are stored in a map called "storedVars",
+with values keyed by the variable name. These commands are documented in the command reference.
+
Variable substitution
+
Variable substitution provides a simple way to include a previously stored variable in a
+command parameter. This is a simple mechanism, by which the variable to substitute is indicated
+by ${variableName}. Multiple variables can be substituted, and intermixed with static text.
+
Example:
+
+
+
+
+
+
+
+
+
store
+
Mr
+
title
+
+
storeValue
+
nameField
+
surname
+
+
store
+
${title} ${surname}
+
fullname
+
+
type
+
textElement
+
Full name is: ${fullname}
+
+
+
+
+
Javascript evaluation
+
Javascript evaluation provides the full power of javascript in constructing a command parameter.
+To use this mechanism, the entire parameter value must be prefixed by
+'javascript{' with a trailing '}'. The text inside the braces is evaluated as a javascript expression,
+and can access previously stored variables using the storedVars map detailed above.
+Note that variable substitution cannot be combined with javascript evaluation.
It can be quite simple to extend Selenium, adding your own actions, checks and locator-strategies.
+This is done with javascript by adding methods to the Selenium object prototype, and the PageBot
+object prototype. On startup, Selenium will automatically look through methods on these prototypes,
+using name patterns to recognise which ones are actions, checks and locators.
+
The following examples try to give an indication of how Selenium can be extended with javascript.
+
+
Actions
+
+
All doFoo methods on the Selenium prototype are added as actions. For each action foo there
+is also an action fooAndWait registered. An action method can take up to 2 parameters, which
+will be passed the second and third column values in the test.
+
Example: Add a "typeRepeated" action to Selenium, which types the text twice into a text box.
+
+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);
+};
+
+
+
Checks
+
+
All assertFoo methods on the Selenium prototype are added as checks. For each check foo there
+is an assertFoo and verifyFoo registered. An assert method can take up to 2 parameters, which
+will be passed the second and third column values in the test.
+
Example: Add a valueRepeated check, that makes sure that the element value
+consists of the supplied text repeated. The 2 commands that would be available in tests would be
+assertValueRepeated and verifyValueRepeated.
+
+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
+ this.assertMatches(expectedValue, actualValue);
+};
+
+
+
Locator Strategies
+
+
All locateElementByFoo methods on the PageBot prototype are added as locator-strategies. A locator strategy takes 2 parameters, the first being the locator string (minus the prefix), and the second being the document in which to search.
+
Example: Add a "valuerepeated=" locator, that finds the first element a value attribute equal to the 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;
+};
+
+
+
user-extensions.js
+
+
By default, Selenium looks for a file called "user-extensions.js", and loads the javascript code found in that file. This file provides a convenient location for adding features to Selenium, without needing to modify the core Selenium sources.
+
In the standard distibution, 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.
+
+
+
:
+
+
+
+
diff --git a/tests/FunctionalTests/selenium/doc/seleniumReference.txt b/tests/FunctionalTests/selenium/doc/seleniumReference.txt
new file mode 100644
index 00000000..e7819869
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/seleniumReference.txt
@@ -0,0 +1,771 @@
+=========================
+Selenium Reference
+=========================
+
+--------
+Concepts
+--------
+
+ A **command** is what tells Selenium what to do. Selenium commands come in two 'flavors', **Actions** and **Assertions**.
+ Each command call is one line in the test table of the form:
+
+ ======= ====== =====
+ command target value
+ ======= ====== =====
+
+ **Actions** are commands that generally manipulate the state of the application. They do things like "click this link" and "select that option". If an Action fails, or has an error, the execution of the current test is stopped.
+
+ **Assertions** verify the state of the application conforms to what is expected. Examples include "make sure the page title is X" and "verify that this checkbox is checked". It is possible to tell Selenium to stop the test when an Assertion fails, or to simply record the failure and continue.
+
+ **Element Locators** tell Selenium which HTML element a command refers to. Many commands require an Element Locator as the "target" attribute. Examples of Element Locators include "elementId" and "document.forms[0].element". These are described more clearly in the next section.
+
+ **Patterns** are used for various reasons, e.g. to specify the expected value of an input field, or identify a select option. Selenium supports various types of pattern, including regular-expressions, all of which are described in more detail below.
+
+-----------------
+Element Locators
+-----------------
+
+ Element Locators allow Selenium to identify which HTML element a
+ command refers to. The format of a locator is:
+
+ *locatorType*\ **=**\ *argument*
+
+ We support the following strategies for locating
+ elements:
+
+ **id=**\ *id*
+
+ Select the element with the specified @id attribute.
+
+ **name=**\ *name*
+
+ Select the first element with the specified @name attribute.
+ - username
+ - name=username
+
+ The name may optionally be following by one or more *element-filters*, separated from the name by whitespace. If the *filterType* is not specified, **value** is assumed.
+ - name=flavour value=chocolate
+
+ **identifier=**\ *id*
+
+ Select the element with the specified @id attribute. If no match is found, select the first element whose @name attribute is *id*.
+
+ **dom=**\ *javascriptExpression*
+
+ Find an element using JavaScript traversal of the HTML Document Object Model. DOM locators *must* begin with "document.".
+ - dom=document.forms['myForm'].myDropdown
+ - dom=document.images[56]
+
+ **xpath=**\ *xpathExpression*
+
+ Locate an element using an XPath expression.
+ - xpath=//img[@alt='The image alt text']
+ - xpath=//table[@id='table1']//tr[4]/td[2]
+
+ **link=**\ *textPattern*
+
+ Select the link (anchor) element which contains text matching the specified *pattern*.
+ - link=The link text
+
+
+ If no *locatorType* is specified, Selenium uses:
+
+ * **dom**, for locators starting with "document."
+ * **xpath**, for locators starting with "//"
+ * **identifier**, otherwise
+
+---------------
+Element Filters
+---------------
+
+ Element filters can be used with a locator to refine a list of candidate elements. They are currently used only in the 'name' element-locator.
+
+ Filters look much like locators, ie.
+
+ *filterType*\ **=**\ *argument*
+
+ Supported element-filters are:
+
+ **value=**\ *valuePattern*
+
+ Matches elements based on their values. This is particularly useful for refining a list of similarly-named toggle-buttons.
+
+ **index=**\ *index*
+
+ Selects a single element based on its position in the list offset from zero).
+
+------------------------
+Select Option Specifiers
+------------------------
+
+ Select Option Specifiers provide different ways of specifying options of an HTML Select element (e.g. for selecting a specific option, or for asserting that the selected option satisfies a specification). There are several forms of Select Option Specifier.
+
+ **label=**\ *labelPattern*
+ matches options based on their labels, i.e. the visible text.
+ - label=regexp:^[Oo]ther
+
+ **value=**\ *valuePattern*
+ matches options based on their values.
+ - value=other
+
+ **id=**\ *id*
+ matches options based on their ids.
+ - id=option1
+
+ **index=**\ *index*
+ matches an option based on its index (offset from zero).
+ - index=2
+
+ If no optionSpecifierType prefix is provided, the default behaviour is to match on **label**.
+
+------------------------
+String-match Patterns
+------------------------
+
+ Various Pattern syntaxes are available for matching string values:
+
+ **glob:**\ *pattern*
+ Match a string against a "glob" (aka "wildmat") pattern. "Glob" is a kind of limited regular-expression syntax typically used in command-line shells. In a glob pattern, "*" represents any sequence of characters, and "?" represents any single character. Glob patterns match against the entire string.
+
+ **regexp:**\ *regexp*
+ Match a string using a regular-expression. The full power of JavaScript regular-expressions is available.
+
+ **exact:**\ *string*
+ Match a string exactly, verbatim, without any of that fancy wildcard stuff.
+
+ If no pattern prefix is specified, Selenium assumes that it's a "glob" pattern.
+
+----------------
+Selenium Actions
+----------------
+
+ Actions tell Selenium to do something in the application. They generally represent something a user would do.
+
+ Many **Actions** can be called with the "AndWait" suffix. This suffix tells Selenium that the action will cause the browser to make a call to the server, and that Selenium should wait for a new page to load.
+
+ **open**\ ( *url* )
+
+ Opens a URL in the test frame. This accepts both relative and absolute URLs.
+
+ The "open" command waits for the page to load before proceeding,
+ ie. the "AndWait" suffix is implicit.
+
+ *Note*: The URL must be on the same site as Selenium due to security restrictions in the browser (Cross Site Scripting).
+
+ **examples:**
+
+ ==== ================= =====
+ open /mypage
+ open http://localhost/
+ ==== ================= =====
+
+ **click**\ ( *elementLocator* )
+
+ Clicks on a link, button, checkbox or radio button.
+ If the click action causes a new page to load (like a link usually does), use "clickAndWait".
+
+ **examples:**
+
+ ============ ================== =====
+ click aCheckbox
+ clickAndWait submitButton
+ clickAndWait anyLink
+ ============ ================== =====
+
+ **type**\ ( *inputLocator*, *value* )
+
+ Sets the *value* of an input field, as though you typed it in.
+
+ Can also be used to set the value of combo boxes, check boxes, etc. In these cases, *value* should be the value of the option selected, not the visible text.
+
+ **examples:**
+
+ =========== ========================== ==========
+ type nameField John Smith
+ typeAndWait textBoxThatSubmitsOnChange newValue
+ =========== ========================== ==========
+
+ **select**\ ( *dropDownLocator*, *optionSpecifier* )
+
+ Select an option from a drop-down, based on the *optionSpecifier*. If more than one option matches the specifier (e.g. due to the use of globs like "f*b*", or due to more than one option having the same label or value), then the first matches is selected.
+
+ **examples:**
+
+ ============= ================ ==========
+ select dropDown Australian Dollars
+ select dropDown index=0
+ selectAndWait currencySelector value=AUD
+ selectAndWait currencySelector label=Aus*lian D*rs
+ ============= ================ ==========
+
+ **check**\ ( *toggleButtonLocator* )
+
+ Check a toggle-button (ie. a check-box or radio-button).
+
+ Note: if addressing the toggle-button element by name, you'll need to append an element-filter (e.g. typically by value), since toggle-button groups consist of input-elements with the same name.
+
+ **examples:**
+
+ ============= ======================== ==========
+ check name=flavour value=honey
+ check flavour honey
+ ============= ======================== ==========
+
+ **uncheck**\ ( *toggleButtonLocator* )
+
+ Un-check a toggle-button.
+
+ **selectWindow**\ ( *windowId* )
+
+ Selects a popup window. Once a popup window has been selected, all commands go to that window. To select the main window again, use "null" as the target.
+
+ **target:** The id of the window to select.
+
+ **value:** *ignored*
+
+ **examples:**
+
+ ============ ============= =====
+ selectWindow myPopupWindow
+ selectWindow null
+ ============ ============= =====
+
+ **goBack**\ ()
+
+ Simulates the user clicking the "back" button on their browser.
+
+ **examples:**
+
+ ============= ==== =====
+ goBackAndWait
+ ============= ==== =====
+
+ **close**\ ()
+
+ Simulates the user clicking the "close" button in the titlebar of a popup window.
+
+ **examples:**
+
+ ====== ==== =====
+ close
+ ====== ==== =====
+
+ **pause**\ ( *milliseconds* )
+
+ Pauses the execution of the test script for a specified amount of time. This is useful for debugging a script or pausing to wait for some server side action.
+
+ **examples:**
+
+ ===== ==== =====
+ pause 5000
+ pause 2000
+ ===== ==== =====
+
+ **fireEvent**\ ( *elementLocator*, *eventName* )
+
+ Explicitly simulate an event, to trigger the corresponding "on\ *event*" handler.
+
+ **examples:**
+
+ ========= =========== ========
+ fireEvent textField focus
+ fireEvent dropDown blur
+ ========= =========== ========
+
+ **waitForValue**\ ( *inputLocator*, *value* )
+
+ Waits for a specified input (e.g. a hidden field) to have a specified *value*. Will succeed immediately if the input already has the value. This is implemented by polling for the value. Warning: can block indefinitely if the input never has the specified value.
+
+ **example:**
+
+ ============ ================ ==========
+ waitForValue finishIndication isfinished
+ ============ ================ ==========
+
+ **store**\ ( *valueToStore*, *variableName* )
+
+ Stores a value into a variable. The value can be constructed using either variable substitution or JavaScript evaluation, as detailed in 'Parameter construction and Variables' (below).
+
+ **examples:**
+
+ ========== ============================================ =========
+ store Mr John Smith fullname
+ store ${title} ${firstname} ${surname} fullname
+ store javascript{Math.round(Math.PI * 100) / 100} PI
+ ========== ============================================ =========
+
+ **storeValue**\ ( *inputLocator*, *variableName* )
+
+ Stores the value of an input field into a variable.
+
+ **examples:**
+
+ ========== ========= =========
+ storeValue userName userID
+ type userName ${userID}
+ ========== ========= =========
+
+ **storeText**\ ( *elementLocator*, *variableName* )
+
+ Stores the text of an element into a variable.
+
+ **examples:**
+
+ =========== =========== ====================
+ storeText currentDate expectedStartDate
+ verifyValue startDate ${expectedStartDate}
+ =========== =========== ====================
+
+ **storeAttribute**\ ( *elementLocator*\ @\ *attributeName*, *variableName* )
+
+ Stores the value of an element attribute into a variable.
+
+ **examples:**
+
+ =============== ============== ====================
+ storeAttribute input1@\ class classOfInput1
+ verifyAttribute input2@\ class ${classOfInput1}
+ =============== ============== ====================
+
+ **chooseCancelOnNextConfirmation**\ ()
+
+ By default, Selenium's overridden window.confirm() function will
+ return true, as if the user had manually clicked OK. After running
+ this command, the next call to confirm() will return false, as if
+ the user had clicked Cancel.
+
+ **examples:**
+
+ ============================== ===== =====
+ chooseCancelOnNextConfirmation
+ ============================== ===== =====
+
+ **answerOnNextPrompt**\ ( *answerString* )
+
+ Instructs Selenium to return the specified *answerString* in
+ response to the next call to window.prompt().
+
+ **examples:**
+
+ ========================== ========= =====
+ answerOnNextPrompt Kangaroo
+ ========================== ========= =====
+
+-------------------
+Selenium Assertions
+-------------------
+
+ Assertions are used to verify the state of the application. They can be used to check the value of a form field, the presense of some text, or the URL of the current page.
+
+ All Selenium assertions can be used in 2 modes, "assert" and "verify". These behave identically, except that when an "assert" fails, the test is aborted. When a "verify" fails, the test will continue execution. This allows a single "assert" to ensure that the application is on the correct page, followed by a bunch of "verify" assertions to test form field values, labels, etc.
+
+ A growing number of assertions have a negative version. In most cases, except where indicated, if the positive assertion is of the form **assertXYZ**, then the negative cases will be of the form **assertNotXYZ**.
+
+ **assertLocation**\ ( *relativeLocation* )
+
+ **examples:**
+
+ ============== ======= =====
+ verifyLocation /mypage
+ assertLocation /mypage
+ ============== ======= =====
+
+ **assertTitle**\ ( *titlePattern* )
+
+ Verifies the title of the current page.
+
+ **examples:**
+
+ =========== ======= =====
+ verifyTitle My Page
+ assertTitle My Page
+ =========== ======= =====
+
+ **assertNotTitle**\ ( *titlePattern* )
+
+ Verifies that the title of the current page does not match the specified pattern.
+
+ **assertValue**\ ( *inputLocator*, *valuePattern* )
+
+ Verifies the value of an input field (or anything else with a value parameter). For checkbox/radio elements, the value will be "on" or "off" depending on whether the element is checked or not.
+
+ **examples:**
+
+ =========== =========================== ==========
+ verifyValue nameField John Smith
+ assertValue document.forms[2].nameField John Smith
+ =========== =========================== ==========
+
+ **assertNotValue**\ ( *inputLocator*, *valuePattern* )
+
+ Verifies the value of an input field does not match the specified pattern.
+
+ **assertSelected**\ ( *selectLocator*, *optionSpecifier* )
+
+ Verifies that the selected option of a drop-down satisfies the *optionSpecifier*.
+
+ **examples:**
+
+ ============== =========================== ==========
+ verifySelected dropdown2 John Smith
+ verifySelected dropdown2 value=js*123
+ assertSelected document.forms[2].dropDown label=J* Smith
+ assertSelected document.forms[2].dropDown index=0
+ ============== =========================== ==========
+
+ **assertSelectOptions**\ ( *selectLocator*, *optionLabelList* )
+
+ Verifies the labels of all options in a drop-down against a comma-separated list. Commas in an expected option can be escaped as "\,".
+
+ **examples:**
+
+ =================== =========================== ====================
+ verifySelectOptions dropdown2 John Smith,Dave Bird
+ assertSelectOptions document.forms[2].dropDown Smith\\, J,Bird\\, D
+ =================== =========================== ====================
+
+ **assertChecked**\ ( *toggleButtonLocator* )
+
+ Verifies that the specified toggle-button element is checked.
+
+ **examples:**
+
+ ============= ======================== ==========
+ verifyChecked flavour honey
+ ============= ======================== ==========
+
+ **assertText**\ ( *elementLocator*, *textPattern* )
+
+ Verifies the text of an element. This works for any element that contains text. This command uses either the textContent (Mozilla-like browsers) or the innerText (IE-like browsers) of the element, which is the rendered text shown to the user.
+
+ **examples:**
+
+ ========== ==================== ==========
+ verifyText statusMessage Successful
+ assertText //div[@id='foo']//h1 Successful
+ ========== ==================== ==========
+
+ **assertNotText**\ ( *elementLocator*, *textPattern* )
+
+ Verifies that the text of an element does not match the specified pattern.
+
+ **assertAttribute**\ ( *elementLocator*\ @\ *attributeName*, *valuePattern* )
+
+ Verifies the value of an element attribute.
+
+ **examples:**
+
+ =============== ====================== ==========
+ verifyAttribute txt1@\ class bigAndBold
+ assertAttribute document.images[0]@alt alt-text
+ verifyAttribute //img[@id='foo']/@alt alt-text
+ =============== ====================== ==========
+
+ **assertNotAttribute**\ ( *elementLocator*\ @\ *attributeName*, *valuePattern* )
+
+ Verifies that the value of an element attribute does not match the specified pattern.
+
+ **assertTextPresent**\ ( *text* )
+
+ Verifies that the specified text appears somewhere on the rendered page shown to the user.
+
+ **examples:**
+
+ ================= ====================== =====
+ verifyTextPresent You are now logged in.
+ assertTextPresent You are now logged in.
+ ================= ====================== =====
+
+ **assertTextNotPresent**\ ( *text* )
+
+ Verifies that the specified text does NOT appear anywhere on the rendered page.
+
+ **assertElementPresent**\ ( *elementLocator* )
+
+ Verifies that the specified element is somewhere on the page.
+
+ **examples:**
+
+ ==================== ================= =====
+ verifyElementPresent submitButton
+ assertElementPresent //img[@alt='foo']
+ ==================== ================= =====
+
+ **assertElementNotPresent**\ ( *elementLocator* )
+
+ Verifies that the specified element is NOT on the page.
+
+ **examples:**
+
+ ======================= ============ =====
+ verifyElementNotPresent cancelButton
+ assertElementNotPresent cancelButton
+ ======================= ============ =====
+
+ **assertTable**\( *cellAddress*, *valuePattern* )
+
+ Verifies the text in a cell of a table. The *cellAddress* syntax *tableName.row.column*, where row and column start at 0.
+
+ **examples:**
+
+ =========== =========== =========
+ verifyTable myTable.1.6 Submitted
+ assertTable results.0.2 13
+ =========== =========== =========
+
+ **assertNotTable**\( *cellAddress*, *valuePattern* )
+
+ Verifies that the text in a cell of a table does not match the specified pattern. Note that this will fail if the table cell does not exist.
+
+ **assertVisible**\ ( *elementLocator* )
+
+ Verifies that the specified element is both present *and* visible. An element can be rendered invisible by setting the CSS "visibility" property to "hidden", or the "display" property to "none", either for the element itself or one if its ancestors.
+
+ **examples:**
+
+ ============= ======== =====
+ verifyVisible postcode
+ assertVisible postcode
+ ============= ======== =====
+
+ **assertNotVisible**\ ( *elementLocator* )
+
+ Verifies that the specified element is NOT visible. Elements that are simply not present are also considered invisible.
+
+ **examples:**
+
+ ================ ======== =====
+ verifyNotVisible postcode
+ assertNotVisible postcode
+ ================ ======== =====
+
+ **verifyEditable / assertEditable**\ ( *inputLocator* )
+
+ Verifies that the specified element is editable, ie. it's an input element, and hasn't been disabled.
+
+ **examples:**
+
+ ============== ======== =====
+ verifyEditable shape
+ assertEditable colour
+ ============== ======== =====
+
+ **assertNotEditable**\ ( *inputLocator* )
+
+ Verifies that the specified element is NOT editable, ie. it's NOT an input element, or has been disabled.
+
+ **assertAlert**\ ( *messagePattern* )
+
+ Verifies that a JavaScript alert was generated, with the specified
+ message.
+
+ If an alert is generated but you do not verify it, the next
+ Selenium action will fail. Alerts must be verified in the order
+ that they were generated.
+
+ **examples:**
+
+ ============== ==================== =====
+ verifyAlert Invalid Phone Number
+ assertAlert Invalid Phone Number
+ ============== ==================== =====
+
+ **assertConfirmation**\ ( *messagePattern* )
+
+ Verifies that a JavaScript confirmation dialog was generated, with
+ the specified message.
+
+ By default, the confirm function will return true, having the same
+ effect as manually clicking OK. This can be changed by prior
+ execution of the **chooseCancelOnNextConfirmation** command (see
+ above).
+
+ Like alerts, any unexpected confirmation will cause the test to
+ fail, and confirmations must be verified in the order that they
+ were generated.
+
+ **examples:**
+
+ ================== ==================== =====
+ assertConfirmation Remove this user?
+ verifyConfirmation Are you sure?
+ ================== ==================== =====
+
+ **assertPrompt**\ ( *messagePattern* )
+
+ Verifies that a JavaScript prompt dialog was generated, with the
+ specified message.
+
+ Successful handling of the prompt requires prior execution of the
+ **answerOnNextPrompt** command (see above).
+
+ Like alerts, unexpected prompts will cause the test to fail, and
+ they must be verified in the order that they were generated.
+
+ **examples:**
+
+ ================== ============================= =====
+ answerOnNextPrompt Joe
+ click id=delegate
+ verifyPrompt Delegate to who?
+ ================== ============================= =====
+
+-------------------------------------------
+Handling of alert(), confirm() and prompt()
+-------------------------------------------
+
+ Selenium overrides the default implementations of the JavaScript
+ window.alert(), window.confirm() and window.prompt() functions,
+ enabling tests to simulate the actions of the user when these occur.
+ Under normal condition, no visible JavaScript dialog-box will appear.
+
+ If your application generates alerts, confirmations, or prompts, you
+ *must* use assertAlert, assertConfirmation and assertPrompt (or their
+ "verify" equivalents) to handle them. Any unhandled alerts will result
+ in the test failing.
+
+ *PROVISO:* Selenium is unable to handle alerts, confirmations, or
+ prompts raised during processing of the 'onload' event. In such cases
+ a visible dialog-box WILL appear, and Selenium will hang until you
+ manually handle it. This is an unfortunate restriction, but at this
+ time we have no solution.
+
+------------------------------------
+Parameter construction and Variables
+------------------------------------
+
+ All Selenium command parameters can be constructed using both simple
+ variable substitution as well as full JavaScript. Both of these
+ mechanisms can access previously stored variables, but do so using
+ different syntax.
+
+ **Stored Variables**
+
+ The commands *store*, *storeValue* and *storeText* can be used to store a variable
+ value for later access. Internally, these variables are stored in a map called "storedVars",
+ with values keyed by the variable name. These commands are documented in the command reference.
+
+ **Variable substitution**
+
+ Variable substitution provides a simple way to include a previously stored variable in a
+ command parameter. This is a simple mechanism, by which the variable to substitute is indicated
+ by ${variableName}. Multiple variables can be substituted, and intermixed with static text.
+
+ Example:
+
+ ========== ==================== ==========
+ store Mr title
+ storeValue nameField surname
+ store ${title} ${surname} fullname
+ type textElement Full name is: ${fullname}
+ ========== ==================== ==========
+
+ **JavaScript evaluation**
+
+ JavaScript evaluation provides the full power of JavaScript in constructing a command parameter.
+ To use this mechanism, the *entire* parameter value must be prefixed by
+ 'javascript{' with a trailing '}'. The text inside the braces is evaluated as a JavaScript expression,
+ and can access previously stored variables using the *storedVars* map detailed above.
+ Note that variable substitution cannot be combined with JavaScript evaluation.
+
+ Example:
+
+ ========== ================================================ ==========
+ store javascript{'merchant' + (new Date()).getTime()} merchantId
+ type textElement javascript{storedVars['merchantId'].toUpperCase()}
+ ========== ================================================ ==========
+
+------------------
+Extending Selenium
+------------------
+
+ It can be quite simple to extend Selenium, adding your own actions, assertions and locator-strategies.
+ This is done with JavaScript by adding methods to the Selenium object prototype, and the PageBot
+ object prototype. On startup, Selenium will automatically look through methods on these prototypes,
+ using name patterns to recognise which ones are actions, assertions and locators.
+
+ The following examples try to give an indication of how Selenium can be extended with JavaScript.
+
+**Actions**
+
+ All *doFoo* methods on the Selenium prototype are added as actions. For each action *foo* there
+ is also an action *fooAndWait* registered. An action method can take up to 2 parameters, which
+ will be passed the second and third column values in the test.
+
+ Example: Add a "typeRepeated" action to Selenium, which types the text twice into a text box.
+
+ ::
+
+ 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);
+ };
+
+**Assertions**
+
+ All *assertFoo* methods on the Selenium prototype are added as
+ assertions. For each assertion *foo* there is an *assertFoo* and
+ *verifyFoo* registered. An assert method can take up to 2 parameters,
+ which will be passed the second and third column values in the test.
+
+ Example: Add a *valueRepeated* assertion, that makes sure that the
+ element value consists of the supplied text repeated. The 2 commands
+ that would be available in tests would be *assertValueRepeated* and
+ *verifyValueRepeated*.
+
+ ::
+
+ 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);
+ };
+
+**Locator Strategies**
+
+ All *locateElementByFoo* methods on the PageBot prototype are added as locator-strategies. A locator strategy takes 2 parameters, the first being the locator string (minus the prefix), and the second being the document in which to search.
+
+ Example: Add a "valuerepeated=" locator, that finds the first element a value attribute equal to the 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;
+ };
+
+**user-extensions.js**
+
+ By default, Selenium looks for a file called "user-extensions.js", and loads the JavaScript code found in that file. This file provides a convenient location for adding features to Selenium, without needing to modify the core Selenium sources.
+
+ In the standard distibution, 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.
+
+------------------
+
+:
diff --git a/tests/FunctionalTests/selenium/doc/testRunner.txt b/tests/FunctionalTests/selenium/doc/testRunner.txt
new file mode 100644
index 00000000..56b949d2
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/testRunner.txt
@@ -0,0 +1,99 @@
+=========================
+TestRunner Reference
+=========================
+-----------
+Test Suites
+-----------
+
+ A test suite is represented by an HTML document containing a single-column table. Each entry in the table should be a hyperlink to a test-case document. The first row will be ignored by Selenium, so this can be used for a title, and is typically used to hold a title.
+
+ By default Selenium will attempt to load the test-suite from "tests/TestSuite.html". An alternative test-suite source can be specified by appending a "test" parameter to the TestRunner.html URL, e.g.::
+
+ http://localhost:8000/TestRunner.html?test=AllTests.php
+
+ The "test" URL is interpreted relative to the location of TestRunner.html.
+
+----------
+Test Cases
+----------
+
+ A test-case is represented by an HTML document, containing a table with 3 columns: *command*, *target*, *value*. Not all commands take a value, however. In this case either leave the column blank or use a to make the table look better.
+
+ The first row will be ignored by Selenium, so this can be used for a title or any other information.
+
+ Example:
+
+ ========== ============ ==========
+ Simple Test Table
+ --------------------------------------
+ open /mypage
+ type nameField John Smith
+ click submitButton True
+ verifyText name John Smith
+ ========== ============ ==========
+
+-----------------
+SetUp / TearDown
+-----------------
+
+ There are no setUp and tearDown commands in Selenium, but there is a way to handle these common testing operations. On the site being tested, create URLs for setUp and tearDown. Then, when the test runner opens these URLs, the server can do whatever setUp or tearDown is necessary.
+
+ Example:
+
+ For the T&E project, we wanted the functional tests to run as a dummy user. Therefore, we made a /setUpFT URL that would create a dummy user and write the userID to the page. Then, we can store this value (using the command storeValue) and use it in the script. Finally, we made a /tearDownFT URL which takes the dummy userID as a parameter and deletes the user. Therefore, our tests look like this:
+
+ ========== ============================ ==========
+ Setup and Teardown
+ ------------------------------------------------------
+ open /setUpFT
+ storeValue userid
+ open /login
+ type userID ${userid}
+ click submit
+ open /tearDownFT?userid=${userid}
+ ========== ============================ ==========
+
+
+----------------------
+Continuous Integration
+----------------------
+
+ Selenium can be integrated with an automated build. When the parameter "auto=true" is added to the URL, Selenium will run the entire suite of tests, and then post the results to a handling URL. The default URL is "/postResults", but an alternative handler location can be provided by specifying a "resultsUrl" parameter.
+
+ The fields of the post are:
+
+ ================== ======================================================================================================
+ Parameter Description
+ ================== ======================================================================================================
+ result the word "passed" or "failed" depending on whether the whole suite passed or at least one test failed.
+ totalTime the time in seconds for the whole suite to run
+ numTestPasses tht total number of tests which passed
+ numTestFailures the total number of tests which failed.
+ numCommandPasses the total number of commands which passed.
+ numCommandFailures the total number of commands which failed.
+ numCommandErrors the total number of commands which errored.
+ suite the suite table, including the hidden column of test results
+ testTable.1 the first test table
+ testTable.2 the second test table
+ ... ...
+ testTable.N The Nth test table
+ ================== ======================================================================================================
+
+ Therefore, the steps for continuous integration are:
+ 1. Create a servlet-type application at the url /postResults which can read the parameters above and write them to a file
+ 2. Create a script which can start up a brower and send to to the URL: selenium?auto=true
+ - Generally, this can be done by merely calling the browser with the URL as an argument:
+ firefox.exe http://localhost/selenium?auto=true
+ 3. Make your continuous build:
+ - Call the script from step 2, preferably using more than one browser
+ - Wait for it to finish, possibly by checking for the existence of the file(s) from step 1
+ - Parse these files to determine whether the build passed or failed
+ - Act accordingly (send emails, update a build web page, etc.)
+
+
+------------------
+
+:Authors: Paul Gross, Jason Huggins
+:Created Date: 08/23/2004
+:Modified Date: 28/01/2005
+:Created With: reStructuredText: http://docutils.sourceforge.net/rst.html
diff --git a/tests/FunctionalTests/selenium/doc/testrunner.html b/tests/FunctionalTests/selenium/doc/testrunner.html
new file mode 100644
index 00000000..86cac0cb
--- /dev/null
+++ b/tests/FunctionalTests/selenium/doc/testrunner.html
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+TestRunner Reference
+
+
+
+
A test suite is represented by an HTML document containing a single-column table. Each entry in the table should be a hyperlink to a test-case document. The first row will be ignored by Selenium, so this can be used for a title, and is typically used to hold a title.
+
By default Selenium will attempt to load the test-suite from "tests/TestSuite.html". An alternative test-suite source can be specified by appending a "test" parameter to the TestRunner.html URL, e.g.:
A test-case is represented by an HTML document, containing a table with 3 columns: command, target, value. Not all commands take a value, however. In this case either leave the column blank or use a to make the table look better.
+
The first row will be ignored by Selenium, so this can be used for a title or any other information.
There are no setUp and tearDown commands in Selenium, but there is a way to handle these common testing operations. On the site being tested, create URLs for setUp and tearDown. Then, when the test runner opens these URLs, the server can do whatever setUp or tearDown is necessary.
+
Example:
+
+
For the T&E project, we wanted the functional tests to run as a dummy user. Therefore, we made a /setUpFT URL that would create a dummy user and write the userID to the page. Then, we can store this value (using the command storeValue) and use it in the script. Finally, we made a /tearDownFT URL which takes the dummy userID as a parameter and deletes the user. Therefore, our tests look like this:
Selenium can be integrated with an automated build. When the parameter "auto=true" is added to the URL, Selenium will run the entire suite of tests, and then post the results to a handling URL. The default URL is "/postResults", but an alternative handler location can be provided by specifying a "resultsUrl" parameter.
+
The fields of the post are:
+
+
+
+
+
+
+
+
Parameter
+
Description
+
+
+
+
result
+
the word "passed" or "failed" depending on whether the whole suite passed or at least one test failed.
+
+
totalTime
+
the time in seconds for the whole suite to run
+
+
numTestPasses
+
tht total number of tests which passed
+
+
numTestFailures
+
the total number of tests which failed.
+
+
numCommandPasses
+
the total number of commands which passed.
+
+
numCommandFailures
+
the total number of commands which failed.
+
+
numCommandErrors
+
the total number of commands which errored.
+
+
suite
+
the suite table, including the hidden column of test results
+
+
testTable.1
+
the first test table
+
+
testTable.2
+
the second test table
+
+
...
+
...
+
+
testTable.N
+
The Nth test table
+
+
+
+
+
+
Therefore, the steps for continuous integration are:
+
+
Create a servlet-type application at the url /postResults which can read the parameters above and write them to a file
+
+
+
Create a script which can start up a brower and send to to the URL: selenium?auto=true
+
+
+
Generally, this can be done by merely calling the browser with the URL as an argument:
+Broadly speaking there are two modes of operation for Selenium
+TestRunner and Driven
+
TestRunner
+
+
+The TestRunner mode of operation for Selenium is where its HTML &
+Javascript
+and the test suite are deployed alongside the Application Under Test
+(AUT) on a arbitrary web server. The test suite is coded as tables in a
+HTML page for each test.
+
+See test runner documentation for more
+information.
+
Driven
+
+Driven Selenium is where the browser is under the the control of a
+process on the same machine. That process is either a Java, .Net, Ruby
+or Python
+application and it is typically run in conjunction with a unit testing
+framework like JUnit or NUnit. Also possible, is a console application
+driving a browser interactively.
+
+The test script is one that would be recognisable to people adept with
+unit test frameworks :
+
+ public void testOKClick() {
+ selenium.verifyTitle("First Page");
+ selenium.open("/TestPage.html");
+ selenium.click("OKButton");
+ selenium.verifyTitle("Another Page");
+ }
+
+The difference from normal unit testing is that as part of the startup,
+three major things have to happen:
+
+
The test framework needs to publish a fresh copy of the AUT.
+Selenium prefers to mount its own web server temporarily for the
+purposes of testing.
+
The test framework needs to publish the static Selenium's HTML
+pages and Javascript in an apparent directory
+on the same web server as (1).
+
The test framework needs to open a browser instance and point it
+to Selenium.html served in (2) above.
+
+As each of these is a fairly time consuming operation, it is best that
+all three of those happen in a one-time setup mode. As such, and
+even though these leverage a unit testing framework, this is definately
+for acceptance or functional rather than unit-testing.
+
+Some variations in the accesibility of the the webserver in question
+for testing purposes or its scriptablity mean a more complex setup is
+required:
+
+
+
+See the driven documentation for more
+information.
+
+
This page is generated using JavaScript. If you see this text, your
+ browser doesn't support JavaScript.
+
+
+
diff --git a/tests/FunctionalTests/selenium/html-xpath/carnation.jpg b/tests/FunctionalTests/selenium/html-xpath/carnation.jpg
new file mode 100644
index 00000000..6b1e16b3
Binary files /dev/null and b/tests/FunctionalTests/selenium/html-xpath/carnation.jpg differ
diff --git a/tests/FunctionalTests/selenium/html-xpath/example.html b/tests/FunctionalTests/selenium/html-xpath/example.html
new file mode 100644
index 00000000..3d7c62af
--- /dev/null
+++ b/tests/FunctionalTests/selenium/html-xpath/example.html
@@ -0,0 +1,75 @@
+
+
+ DOM Level 3 XPath Example
+
+
+
+
+ Test Node 1
+
+
+
+
+
+ Test Node 2
+
Test Node 3: Unclosed Paragraph tag
+
+ Test Node 4: Nesting
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js b/tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js
new file mode 100644
index 00000000..4b9e05f6
--- /dev/null
+++ b/tests/FunctionalTests/selenium/html-xpath/html-xpath-patched.js
@@ -0,0 +1,657 @@
+/*
+ html-xpath, an implementation of DOM Level 3 XPath for Internet Explorer 5+
+ Copyright (C) 2004 Dimitri Glazkov
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+*/
+
+/** SELENIUM:PATCH TO ALLOW USE WITH DOCUMENTS FROM OTHER WINDOWS: 2004-11-24
+ TODO resubmit this to http://sf.net/projects/html-xpath */
+function addXPathSupport(document) {
+/** END SELENIUM:PATCH */
+
+var isIe = /MSIE [56789]/.test(navigator.userAgent) && (navigator.platform == "Win32");
+
+// Mozilla has support by default, we don't have an implementation for the rest
+if (isIe)
+{
+ // release number
+ document.DomL3XPathRelease = "0.0.3.0";
+
+ // XPathException
+ // An Error object will be thrown, this is just a handler to instantiate that object
+ var XPathException = new _XPathExceptionHandler();
+ function _XPathExceptionHandler()
+ {
+ this.INVALID_EXPRESSION_ERR = 51;
+ this.TYPE_ERR = 52;
+ this.NOT_IMPLEMENTED_ERR = -1;
+ this.RUNTIME_ERR = -2;
+
+ this.ThrowNotImplemented = function(message)
+ {
+ ThrowError(this.NOT_IMPLEMENTED_ERR, "This functionality is not implemented.", message);
+ }
+
+ this.ThrowInvalidExpression = function(message)
+ {
+ ThrowError(this.INVALID_EXPRESSION_ERR, "Invalid expression", message);
+ }
+
+ this.ThrowType = function(message)
+ {
+ ThrowError(this.TYPE_ERR, "Type error", message);
+ }
+
+ this.Throw = function(message)
+ {
+ ThrowError(this.RUNTIME_ERR, "Run-time error", message);
+ }
+
+ function ThrowError(code, description, message)
+ {
+ var error = new Error(code, "DOM-L3-XPath " + document.DomL3XPathRelease + ": " + description + (message ? ", \"" + message + "\"": ""));
+ error.code = code;
+ error.name = "XPathException";
+ throw error;
+ }
+ }
+
+ // DOMException
+ // An Error object will be thrown, this is just a handler to instantiate that object
+ var DOMException = new _DOMExceptionHandler();
+ function _DOMExceptionHandler()
+ {
+ this.ThrowInvalidState = function(message)
+ {
+ ThrowError(13, "The state of the object is no longer valid", message);
+ }
+
+ function ThrowError(code, description, message)
+ {
+ var error = new Error(code, "DOM : " + description + (message ? ", \"" + message + "\"": ""));
+ error.code = code;
+ error.name = "DOMException";
+ throw error;
+ }
+ }
+
+ // XPathEvaluator
+ // implemented as document object methods
+
+ // XPathExpression createExpression(String expression, XPathNSResolver resolver)
+ document.createExpression = function
+ (
+ expression, // String
+ resolver // XPathNSResolver
+ )
+ {
+ // returns XPathExpression object
+ return new XPathExpression(expression, resolver);
+ }
+
+ // XPathNSResolver createNSResolver(nodeResolver)
+ document.createNSResolver = function
+ (
+ nodeResolver // Node
+ )
+ {
+ // returns XPathNSResolver
+ return new XPathNSResolver(nodeResolver);
+ }
+
+ // XPathResult evaluate(String expresison, Node contextNode, XPathNSResolver resolver, Number type, XPathResult result)
+ document.evaluate = function
+ (
+ expression, // String
+ contextNode, // Node
+ resolver, // XPathNSResolver
+ type, // Number
+ result // XPathResult
+ )
+ // can raise XPathException, DOMException
+ {
+ // return XPathResult
+ return document.createExpression(expression, resolver).evaluate(contextNode, type, result);
+ }
+
+ // XPathExpression
+ function XPathExpression
+ (
+ expression, // String
+ resolver // XPathNSResolver
+ )
+ {
+ this.expressionString = expression;
+ this.resolver = resolver;
+
+ // XPathResult evaluate(Node contextNode, Number type, XPathResult result)
+ this.evaluate = function
+ (
+ contextNode, // Node
+ type, // Number
+ result // XPathResult
+ )
+ // raises XPathException, DOMException
+ {
+ // return XPathResult
+ return (result && result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type));
+ }
+
+ this.toString = function()
+ {
+ return "[XPathExpression]";
+ }
+ }
+
+ // XPathNSResolver
+ function XPathNSResolver(node)
+ {
+ this.node = node;
+
+ // String lookupNamespaceURI(String prefix)
+ this.lookupNamespaceURI = function
+ (
+ prefix // String
+ )
+ {
+ XPathException.ThrowNotImplemented();
+ // return String
+ return null;
+ }
+
+ this.toString = function()
+ {
+ return "[XPathNSResolver]";
+ }
+ }
+
+ // XPathResult
+ XPathResult.ANY_TYPE = 0;
+ XPathResult.NUMBER_TYPE = 1;
+ XPathResult.STRING_TYPE = 2;
+ XPathResult.BOOLEAN_TYPE = 3;
+ XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
+ XPathResult.UNORDERED_SNAPSHOT_TYPE = 6;
+ XPathResult.ORDERED_SNAPSHOT_TYPE = 7;
+ XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
+ XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
+
+ function XPathResult
+ (
+ expression, // XPathExpression
+ contextNode, // Node
+ resolver, // XPathNSResolver
+ type // Number
+ )
+ {
+ this.initialize = function(expression, contextNode, resolver, type)
+ {
+ this._domResult = null;
+ this._expression = expression;
+ this._contextNode = contextNode;
+ this._resolver = resolver;
+ if (type)
+ {
+ this.resultType = type;
+ this._isIterator = (type == XPathResult.UNORDERED_NODE_ITERATOR_TYPE ||
+ type == XPathResult.ORDERED_NODE_ITERATOR_TYPE ||
+ type == XPathResult.ANY_TYPE);
+ this._isSnapshot = (type == XPathResult.UNORDERED_SNAPSHOT_TYPE || type == XPathResult.ORDERED_SNAPSHOT_TYPE);
+ this._isNodeSet = type > XPathResult.BOOLEAN_TYPE;
+ }
+ else
+ {
+ this.resultType = XPathResult.ANY_TYPE;
+ this._isIterator = true;
+ this._isSnapshot = false;
+ this._isNodeSet = true;
+ }
+ return this;
+ }
+
+ this.initialize(expression, contextNode, resolver, type);
+
+ this.getInvalidIteratorState = function()
+ {
+ return documentChangeDetected() || !this._isIterator;
+ }
+
+ this.getSnapshotLength = function()
+ // raises XPathException
+ {
+ if (!this._isSnapshot)
+ {
+ XPathException.ThrowType("Snapshot is not an expected result type");
+ }
+ activateResult(this);
+ // return Number
+ return this._domResult.length;
+ }
+
+ // Node iterateNext()
+ this.iterateNext = function()
+ // raises XPathException, DOMException
+ {
+ if (!this._isIterator)
+ {
+ XPathException.ThrowType("Iterator is not an expected result type");
+ }
+ activateResult(this);
+ if (documentChangeDetected())
+ {
+ DOMException.ThrowInvalidState("iterateNext");
+ }
+ // return Node
+ return getNextNode(this);
+ }
+
+ // Node snapshotItem(Number index)
+ this.snapshotItem = function(index)
+ // raises XPathException
+ {
+ if (!this._isSnapshot)
+ {
+ XPathException.ThrowType("Snapshot is not an expected result type");
+ }
+ // return Node
+ return getItemNode(this, index);
+ }
+
+ this.toString = function()
+ {
+ return "[XPathResult]";
+ }
+
+ // returns string value of the result, if result type is STRING_TYPE
+ // otherwise throws an XPathException
+ this.getStringValue = function()
+ {
+ if (this.resultType != XPathResult.STRING_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return String");
+ }
+ return getNodeText(this);
+ }
+
+ // returns number value of the result, if the result is NUMBER_TYPE
+ // otherwise throws an XPathException
+ this.getNumberValue = function()
+ {
+ if (this.resultType != XPathResult.NUMBER_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return Number");
+ }
+ var number = parseInt(getNodeText(this));
+ if (isNaN(number))
+ {
+ XPathException.ThrowType("The result can not be converted to Number");
+ }
+ return number;
+ }
+
+ // returns boolean value of the result, if the result is BOOLEAN_TYPE
+ // otherwise throws an XPathException
+ this.getBooleanValue = function()
+ {
+ if (this.resultType != XPathResult.BOOLEAN_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return Boolean");
+ }
+
+ var
+ text = getNodeText(this);
+ bool = (text ? text.toLowerCase() : null);
+ if (bool == "false" || bool == "true")
+ {
+ return bool;
+ }
+ XPathException.ThrowType("The result can not be converted to Boolean");
+ }
+
+ // returns single node, if the result is ANY_UNORDERED_NODE_TYPE or FIRST_ORDERED_NODE_TYPE
+ // otherwise throws an XPathException
+ this.getSingleNodeValue = function()
+ {
+ if (this.resultType != XPathResult.ANY_UNORDERED_NODE_TYPE &&
+ this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return single Node value");
+ }
+ return getSingleNode(this);
+ }
+
+ function documentChangeDetected()
+ {
+ return document._XPathMsxmlDocumentHelper.documentChangeDetected();
+ }
+
+ function getNodeText(result)
+ {
+ activateResult(result);
+ return result._textResult;
+// return ((node = getSingleNode(result)) ? (node.nodeType == 1 ? node.innerText : node.nodeValue) : null);
+ }
+
+ function findNode(result, current)
+ {
+ switch(current.nodeType)
+ {
+ case 1: // NODE_ELEMENT
+ var id = current.attributes.getNamedItem("id");
+ if (id)
+ {
+ return document.getElementById(id.value);
+ }
+ XPathException.Throw("unable to locate element in XML tree");
+ case 2: // NODE_ATTRIBUTE
+ var id = current.selectSingleNode("..").attributes.getNamedItem("id");
+ if (id)
+ {
+ var node = document.getElementById(id.text);
+ if (node)
+ {
+ return node.attributes.getNamedItem(current.nodeName);
+ }
+ }
+ XPathException.Throw("unable to locate attribute in XML tree");
+ case 3: // NODE_TEXT
+ var id = current.selectSingleNode("..").attributes.getNamedItem("id");
+ if (id)
+ {
+ var node = document.getElementById(id.value);
+ if (node)
+ {
+ for(child in node.childNodes)
+ {
+ if (child.nodeType == 3 && child.nodeValue == current.nodeValue)
+ {
+ return child;
+ }
+ }
+ }
+ }
+ XPathException.Throw("unable to locate text in XML tree");
+ }
+ XPathException.Throw("unknown node type");
+ }
+
+ function activateResult(result)
+ {
+ if (!result._domResult)
+ {
+ try
+ {
+ var expression = result._expression.expressionString;
+
+ // adjust expression if contextNode is not a document
+ if (result._contextNode != document && expression.indexOf("//") != 0)
+ {
+
+ expression = "//*[@id = '" + result._contextNode.id + "']" +
+ (expression.indexOf("/") == 0 ? "" : "/") + expression;
+ }
+
+ if (result._isNodeSet)
+ {
+ result._domResult = document._XPathMsxmlDocumentHelper.getDom().selectNodes(expression);
+ }
+ else
+ {
+ result._domResult = true;
+ result._textResult = document._XPathMsxmlDocumentHelper.getTextResult(expression);
+ }
+
+ }
+ catch(error)
+ {
+ alert(error.description);
+ XPathException.ThrowInvalidExpression(error.description);
+ }
+ }
+ }
+
+ function getSingleNode(result)
+ {
+ var node = getItemNode(result, 0);
+ result._domResult = null;
+ return node;
+ }
+
+ function getItemNode(result, index)
+ {
+ activateResult(result);
+ var current = result._domResult.item(index);
+ return (current ? findNode(result, current) : null);
+ }
+
+ function getNextNode(result)
+ {
+ var current = result._domResult.nextNode;
+ if (current)
+ {
+ return findNode(result, current);
+ }
+ result._domResult = null;
+ return null;
+ }
+ }
+
+ document.reloadDom = function()
+ {
+ document._XPathMsxmlDocumentHelper.reset();
+ }
+
+ document._XPathMsxmlDocumentHelper = new _XPathMsxmlDocumentHelper();
+ function _XPathMsxmlDocumentHelper()
+ {
+ this.getDom = function()
+ {
+ activateDom(this);
+ return this.dom;
+ }
+
+ this.getXml = function()
+ {
+ activateDom(this);
+ return this.dom.xml;
+ }
+
+ this.getTextResult = function(expression)
+ {
+ expression = expression.replace(//g, ">").replace(/"/g, "\"");
+ var xslText = "" +
+ "" +
+ "";
+ var xsl = new ActiveXObject("Msxml2.DOMDocument");
+ xsl.loadXML(xslText);
+ try
+ {
+ var result = this.getDom().transformNode(xsl);
+ }
+ catch(error)
+ {
+ alert("Error: " + error.description);
+ }
+ return result;
+ }
+
+ this.reset = function()
+ {
+ this.dom = null;
+ }
+
+ function onPropertyChangeEventHandler()
+ {
+ document._propertyChangeDetected = true;
+ }
+
+ this.documentChangeDetected = function()
+ {
+ return (document.ignoreDocumentChanges ? false : this._currentElementCount != document.all.length || document._propertyChangeDetected);
+ }
+
+ function activateDom(helper)
+ {
+ if (!helper.dom)
+ {
+ var dom = new ActiveXObject("Msxml2.DOMDocument");
+/** SELENIUM:PATCH TO ALLOW PROVIDE FULL XPATH SUPPORT */
+ dom.setProperty("SelectionLanguage", "XPath");
+/** END SELENIUM:PATCH */
+ dom.async = false;
+ dom.resolveExternals = false;
+ loadDocument(dom, helper);
+ helper.dom = dom;
+ helper._currentElementCount = document.all.length;
+ document._propertyChangeDetected = false;
+ }
+ else
+ {
+ if (helper.documentChangeDetected())
+ {
+ var dom = helper.dom;
+ dom.load("");
+ loadDocument(dom, helper);
+ helper._currentElementCount = document.all.length;
+ document._propertyChangeDetected = false;
+ }
+ }
+ }
+
+ function loadDocument(dom, helper)
+ {
+ return loadNode(dom, dom, document.body, helper);
+ }
+
+
+/** SELENIUM:PATCH for loadNode() - see SEL-68 */
+ function loadNode(dom, domParentNode, node, helper)
+ {
+ // Bad node scenarios
+ // 1. If the node contains a /, it's broken HTML
+ // 2. If the node doesn't have a name (typically from broken HTML), the node can't be loaded
+ // 3. Node types we can't deal with
+ //
+ // In all scenarios, we just skip the node. We won't be able to
+ // query on these nodes, but they're broken anyway.
+ if (node.nodeName.indexOf("/") > -1
+ || node.nodeName == ""
+ || node.nodeName == "#document"
+ || node.nodeName == "#document-fragment"
+ || node.nodeName == "#cdata-section"
+ || node.nodeName == "#xml-declaration"
+ || node.nodeName == "#whitespace"
+ || node.nodeName == "#significat-whitespace"
+ )
+ {
+ return;
+ }
+
+ // #comment is a , which must be created with createComment()
+ if (node.nodeName == "#comment")
+ {
+ try
+ {
+ domParentNode.appendChild(dom.createComment(node.nodeValue));
+ }
+ catch (ex)
+ {
+ // it's just a comment, we don't care
+ }
+ }
+ else if (node.nodeType == 3)
+ {
+ domParentNode.appendChild(dom.createTextNode(node.nodeValue));
+ }
+ else
+ {
+ var domNode = dom.createElement(node.nodeName.toLowerCase());
+ if (!node.id)
+ {
+ node.id = node.uniqueID;
+ }
+ domParentNode.appendChild(domNode);
+ loadAttributes(dom, domNode, node);
+ var length = node.childNodes.length;
+ for(var i = 0; i < length; i ++ )
+ {
+ loadNode(dom, domNode, node.childNodes[i], helper);
+ }
+ node.attachEvent("onpropertychange", onPropertyChangeEventHandler);
+ }
+ }
+/** END SELENIUM:PATCH */
+
+ function loadAttributes(dom, domParentNode, node)
+ {
+ for (var i = 0; i < node.attributes.length; i ++ )
+ {
+ var attribute = node.attributes[i];
+ var attributeValue = attribute.nodeValue;
+ if (attributeValue && attribute.specified)
+ {
+ var domAttribute = dom.createAttribute(attribute.nodeName);
+ domAttribute.value = attributeValue;
+ domParentNode.setAttributeNode(domAttribute);
+ }
+ }
+ }
+
+ }
+}
+else
+{
+ document.reloadDom = function() {}
+ XPathResult.prototype.getStringValue = function()
+ {
+ return this.stringValue;
+ }
+
+ XPathResult.prototype.getNumberValue = function()
+ {
+ return this.numberValue;
+ }
+
+ XPathResult.prototype.getBooleanValue = function()
+ {
+ return this.booleanValue;
+ }
+
+ XPathResult.prototype.getSingleNodeValue = function()
+ {
+ return this.singleNodeValue;
+ }
+
+ XPathResult.prototype.getInvalidIteratorState = function()
+ {
+ return this.invalidIteratorState;
+ }
+
+ XPathResult.prototype.getSnapshotLength = function()
+ {
+ return this.snapshotLength;
+ }
+
+ XPathResult.prototype.getResultType = function()
+ {
+ return this.resultType;
+ }
+}
+/** SELENIUM:PATCH TO ALLOW USE WITH CONTAINED DOCUMENTS */
+}
+/** END SELENIUM:PATCH */
+
diff --git a/tests/FunctionalTests/selenium/html-xpath/html-xpath.js b/tests/FunctionalTests/selenium/html-xpath/html-xpath.js
new file mode 100644
index 00000000..acad8b93
--- /dev/null
+++ b/tests/FunctionalTests/selenium/html-xpath/html-xpath.js
@@ -0,0 +1,610 @@
+/*
+ html-xpath, an implementation of DOM Level 3 XPath for Internet Explorer 5+
+ Copyright (C) 2004 Dimitri Glazkov
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+
+*/
+
+var isIe = /MSIE [56789]/.test(navigator.userAgent) && (navigator.platform == "Win32");
+
+// Mozilla has support by default, we don't have an implementation for the rest
+if (isIe)
+{
+ // release number
+ document.DomL3XPathRelease = "0.0.3.0";
+
+ // XPathException
+ // An Error object will be thrown, this is just a handler to instantiate that object
+ var XPathException = new _XPathExceptionHandler();
+ function _XPathExceptionHandler()
+ {
+ this.INVALID_EXPRESSION_ERR = 51;
+ this.TYPE_ERR = 52;
+ this.NOT_IMPLEMENTED_ERR = -1;
+ this.RUNTIME_ERR = -2;
+
+ this.ThrowNotImplemented = function(message)
+ {
+ ThrowError(this.NOT_IMPLEMENTED_ERR, "This functionality is not implemented.", message);
+ }
+
+ this.ThrowInvalidExpression = function(message)
+ {
+ ThrowError(this.INVALID_EXPRESSION_ERR, "Invalid expression", message);
+ }
+
+ this.ThrowType = function(message)
+ {
+ ThrowError(this.TYPE_ERR, "Type error", message);
+ }
+
+ this.Throw = function(message)
+ {
+ ThrowError(this.RUNTIME_ERR, "Run-time error", message);
+ }
+
+ function ThrowError(code, description, message)
+ {
+ var error = new Error(code, "DOM-L3-XPath " + document.DomL3XPathRelease + ": " + description + (message ? ", \"" + message + "\"": ""));
+ error.code = code;
+ error.name = "XPathException";
+ throw error;
+ }
+ }
+
+ // DOMException
+ // An Error object will be thrown, this is just a handler to instantiate that object
+ var DOMException = new _DOMExceptionHandler();
+ function _DOMExceptionHandler()
+ {
+ this.ThrowInvalidState = function(message)
+ {
+ ThrowError(13, "The state of the object is no longer valid", message);
+ }
+
+ function ThrowError(code, description, message)
+ {
+ var error = new Error(code, "DOM : " + description + (message ? ", \"" + message + "\"": ""));
+ error.code = code;
+ error.name = "DOMException";
+ throw error;
+ }
+ }
+
+ // XPathEvaluator
+ // implemented as document object methods
+
+ // XPathExpression createExpression(String expression, XPathNSResolver resolver)
+ document.createExpression = function
+ (
+ expression, // String
+ resolver // XPathNSResolver
+ )
+ {
+ // returns XPathExpression object
+ return new XPathExpression(expression, resolver);
+ }
+
+ // XPathNSResolver createNSResolver(nodeResolver)
+ document.createNSResolver = function
+ (
+ nodeResolver // Node
+ )
+ {
+ // returns XPathNSResolver
+ return new XPathNSResolver(nodeResolver);
+ }
+
+ // XPathResult evaluate(String expresison, Node contextNode, XPathNSResolver resolver, Number type, XPathResult result)
+ document.evaluate = function
+ (
+ expression, // String
+ contextNode, // Node
+ resolver, // XPathNSResolver
+ type, // Number
+ result // XPathResult
+ )
+ // can raise XPathException, DOMException
+ {
+ // return XPathResult
+ return document.createExpression(expression, resolver).evaluate(contextNode, type, result);
+ }
+
+ // XPathExpression
+ function XPathExpression
+ (
+ expression, // String
+ resolver // XPathNSResolver
+ )
+ {
+ this.expressionString = expression;
+ this.resolver = resolver;
+
+ // XPathResult evaluate(Node contextNode, Number type, XPathResult result)
+ this.evaluate = function
+ (
+ contextNode, // Node
+ type, // Number
+ result // XPathResult
+ )
+ // raises XPathException, DOMException
+ {
+ // return XPathResult
+ return (result && result.constructor == XPathResult ? result.initialize(this, contextNode, resolver, type) : new XPathResult(this, contextNode, resolver, type));
+ }
+
+ this.toString = function()
+ {
+ return "[XPathExpression]";
+ }
+ }
+
+ // XPathNSResolver
+ function XPathNSResolver(node)
+ {
+ this.node = node;
+
+ // String lookupNamespaceURI(String prefix)
+ this.lookupNamespaceURI = function
+ (
+ prefix // String
+ )
+ {
+ XPathException.ThrowNotImplemented();
+ // return String
+ return null;
+ }
+
+ this.toString = function()
+ {
+ return "[XPathNSResolver]";
+ }
+ }
+
+ // XPathResult
+ XPathResult.ANY_TYPE = 0;
+ XPathResult.NUMBER_TYPE = 1;
+ XPathResult.STRING_TYPE = 2;
+ XPathResult.BOOLEAN_TYPE = 3;
+ XPathResult.UNORDERED_NODE_ITERATOR_TYPE = 4;
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE = 5;
+ XPathResult.UNORDERED_SNAPSHOT_TYPE = 6;
+ XPathResult.ORDERED_SNAPSHOT_TYPE = 7;
+ XPathResult.ANY_UNORDERED_NODE_TYPE = 8;
+ XPathResult.FIRST_ORDERED_NODE_TYPE = 9;
+
+ function XPathResult
+ (
+ expression, // XPathExpression
+ contextNode, // Node
+ resolver, // XPathNSResolver
+ type // Number
+ )
+ {
+ this.initialize = function(expression, contextNode, resolver, type)
+ {
+ this._domResult = null;
+ this._expression = expression;
+ this._contextNode = contextNode;
+ this._resolver = resolver;
+ if (type)
+ {
+ this.resultType = type;
+ this._isIterator = (type == XPathResult.UNORDERED_NODE_ITERATOR_TYPE ||
+ type == XPathResult.ORDERED_NODE_ITERATOR_TYPE ||
+ type == XPathResult.ANY_TYPE);
+ this._isSnapshot = (type == XPathResult.UNORDERED_SNAPSHOT_TYPE || type == XPathResult.ORDERED_SNAPSHOT_TYPE);
+ this._isNodeSet = type > XPathResult.BOOLEAN_TYPE;
+ }
+ else
+ {
+ this.resultType = XPathResult.ANY_TYPE;
+ this._isIterator = true;
+ this._isSnapshot = false;
+ this._isNodeSet = true;
+ }
+ return this;
+ }
+
+ this.initialize(expression, contextNode, resolver, type);
+
+ this.getInvalidIteratorState = function()
+ {
+ return documentChangeDetected() || !this._isIterator;
+ }
+
+ this.getSnapshotLength = function()
+ // raises XPathException
+ {
+ if (!this._isSnapshot)
+ {
+ XPathException.ThrowType("Snapshot is not an expected result type");
+ }
+ activateResult(this);
+ // return Number
+ return this._domResult.length;
+ }
+
+ // Node iterateNext()
+ this.iterateNext = function()
+ // raises XPathException, DOMException
+ {
+ if (!this._isIterator)
+ {
+ XPathException.ThrowType("Iterator is not an expected result type");
+ }
+ activateResult(this);
+ if (documentChangeDetected())
+ {
+ DOMException.ThrowInvalidState("iterateNext");
+ }
+ // return Node
+ return getNextNode(this);
+ }
+
+ // Node snapshotItem(Number index)
+ this.snapshotItem = function(index)
+ // raises XPathException
+ {
+ if (!this._isSnapshot)
+ {
+ XPathException.ThrowType("Snapshot is not an expected result type");
+ }
+ // return Node
+ return getItemNode(this, index);
+ }
+
+ this.toString = function()
+ {
+ return "[XPathResult]";
+ }
+
+ // returns string value of the result, if result type is STRING_TYPE
+ // otherwise throws an XPathException
+ this.getStringValue = function()
+ {
+ if (this.resultType != XPathResult.STRING_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return String");
+ }
+ return getNodeText(this);
+ }
+
+ // returns number value of the result, if the result is NUMBER_TYPE
+ // otherwise throws an XPathException
+ this.getNumberValue = function()
+ {
+ if (this.resultType != XPathResult.NUMBER_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return Number");
+ }
+ var number = parseInt(getNodeText(this));
+ if (isNaN(number))
+ {
+ XPathException.ThrowType("The result can not be converted to Number");
+ }
+ return number;
+ }
+
+ // returns boolean value of the result, if the result is BOOLEAN_TYPE
+ // otherwise throws an XPathException
+ this.getBooleanValue = function()
+ {
+ if (this.resultType != XPathResult.BOOLEAN_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return Boolean");
+ }
+
+ var
+ text = getNodeText(this);
+ bool = (text ? text.toLowerCase() : null);
+ if (bool == "false" || bool == "true")
+ {
+ return bool;
+ }
+ XPathException.ThrowType("The result can not be converted to Boolean");
+ }
+
+ // returns single node, if the result is ANY_UNORDERED_NODE_TYPE or FIRST_ORDERED_NODE_TYPE
+ // otherwise throws an XPathException
+ this.getSingleNodeValue = function()
+ {
+ if (this.resultType != XPathResult.ANY_UNORDERED_NODE_TYPE &&
+ this.resultType != XPathResult.FIRST_ORDERED_NODE_TYPE)
+ {
+ XPathException.ThrowType("The expression can not be converted to return single Node value");
+ }
+ return getSingleNode(this);
+ }
+
+ function documentChangeDetected()
+ {
+ return document._XPathMsxmlDocumentHelper.documentChangeDetected();
+ }
+
+ function getNodeText(result)
+ {
+ activateResult(result);
+ return result._textResult;
+// return ((node = getSingleNode(result)) ? (node.nodeType == 1 ? node.innerText : node.nodeValue) : null);
+ }
+
+ function findNode(result, current)
+ {
+ switch(current.nodeType)
+ {
+ case 1: // NODE_ELEMENT
+ var id = current.attributes.getNamedItem("id");
+ if (id)
+ {
+ return document.getElementById(id.value);
+ }
+ XPathException.Throw("unable to locate element in XML tree");
+ case 2: // NODE_ATTRIBUTE
+ var id = current.selectSingleNode("..").attributes.getNamedItem("id");
+ if (id)
+ {
+ var node = document.getElementById(id.text);
+ if (node)
+ {
+ return node.attributes.getNamedItem(current.nodeName);
+ }
+ }
+ XPathException.Throw("unable to locate attribute in XML tree");
+ case 3: // NODE_TEXT
+ var id = current.selectSingleNode("..").attributes.getNamedItem("id");
+ if (id)
+ {
+ var node = document.getElementById(id.value);
+ if (node)
+ {
+ for(child in node.childNodes)
+ {
+ if (child.nodeType == 3 && child.nodeValue == current.nodeValue)
+ {
+ return child;
+ }
+ }
+ }
+ }
+ XPathException.Throw("unable to locate text in XML tree");
+ }
+ XPathException.Throw("unknown node type");
+ }
+
+ function activateResult(result)
+ {
+ if (!result._domResult)
+ {
+ try
+ {
+ var expression = result._expression.expressionString;
+
+ // adjust expression if contextNode is not a document
+ if (result._contextNode != document && expression.indexOf("//") != 0)
+ {
+
+ expression = "//*[@id = '" + result._contextNode.id + "']" +
+ (expression.indexOf("/") == 0 ? "" : "/") + expression;
+ }
+
+ if (result._isNodeSet)
+ {
+ result._domResult = document._XPathMsxmlDocumentHelper.getDom().selectNodes(expression);
+ }
+ else
+ {
+ result._domResult = true;
+ result._textResult = document._XPathMsxmlDocumentHelper.getTextResult(expression);
+ }
+
+ }
+ catch(error)
+ {
+ alert(error.description);
+ XPathException.ThrowInvalidExpression(error.description);
+ }
+ }
+ }
+
+ function getSingleNode(result)
+ {
+ var node = getItemNode(result, 0);
+ result._domResult = null;
+ return node;
+ }
+
+ function getItemNode(result, index)
+ {
+ activateResult(result);
+ var current = result._domResult.item(index);
+ return (current ? findNode(result, current) : null);
+ }
+
+ function getNextNode(result)
+ {
+ var current = result._domResult.nextNode;
+ if (current)
+ {
+ return findNode(result, current);
+ }
+ result._domResult = null;
+ return null;
+ }
+ }
+
+ document.reloadDom = function()
+ {
+ document._XPathMsxmlDocumentHelper.reset();
+ }
+
+ document._XPathMsxmlDocumentHelper = new _XPathMsxmlDocumentHelper();
+ function _XPathMsxmlDocumentHelper()
+ {
+ this.getDom = function()
+ {
+ activateDom(this);
+ return this.dom;
+ }
+
+ this.getXml = function()
+ {
+ activateDom(this);
+ return this.dom.xml;
+ }
+
+ this.getTextResult = function(expression)
+ {
+ expression = expression.replace(//g, ">").replace(/"/g, "\"");
+ var xslText = "" +
+ "" +
+ "";
+ var xsl = new ActiveXObject("Msxml2.DOMDocument");
+ xsl.loadXML(xslText);
+ try
+ {
+ var result = this.getDom().transformNode(xsl);
+ }
+ catch(error)
+ {
+ alert("Error: " + error.description);
+ }
+ return result;
+ }
+
+ this.reset = function()
+ {
+ this.dom = null;
+ }
+
+ function onPropertyChangeEventHandler()
+ {
+ document._propertyChangeDetected = true;
+ }
+
+ this.documentChangeDetected = function()
+ {
+ return (document.ignoreDocumentChanges ? false : this._currentElementCount != document.all.length || document._propertyChangeDetected);
+ }
+
+ function activateDom(helper)
+ {
+ if (!helper.dom)
+ {
+ var dom = new ActiveXObject("Msxml2.DOMDocument");
+ dom.async = false;
+ dom.resolveExternals = false;
+ loadDocument(dom, helper);
+ helper.dom = dom;
+ helper._currentElementCount = document.all.length;
+ document._propertyChangeDetected = false;
+ }
+ else
+ {
+ if (helper.documentChangeDetected())
+ {
+ var dom = helper.dom;
+ dom.load("");
+ loadDocument(dom, helper);
+ helper._currentElementCount = document.all.length;
+ document._propertyChangeDetected = false;
+ }
+ }
+ }
+
+ function loadDocument(dom, helper)
+ {
+ return loadNode(dom, dom, document.body, helper);
+ }
+
+ function loadNode(dom, domParentNode, node, helper)
+ {
+ if (node.nodeType == 3)
+ {
+ domParentNode.appendChild(dom.createTextNode(node.nodeValue));
+ }
+ else
+ {
+ var domNode = dom.createElement(node.nodeName.toLowerCase());
+ if (!node.id)
+ {
+ node.id = node.uniqueID;
+ }
+ domParentNode.appendChild(domNode);
+ loadAttributes(dom, domNode, node);
+ var length = node.childNodes.length;
+ for(var i = 0; i < length; i ++ )
+ {
+ loadNode(dom, domNode, node.childNodes[i], helper);
+ }
+ node.attachEvent("onpropertychange", onPropertyChangeEventHandler);
+ }
+ }
+
+ function loadAttributes(dom, domParentNode, node)
+ {
+ for (var i = 0; i < node.attributes.length; i ++ )
+ {
+ var attribute = node.attributes[i];
+ var attributeValue = attribute.nodeValue;
+ if (attributeValue && attribute.specified)
+ {
+ var domAttribute = dom.createAttribute(attribute.nodeName);
+ domAttribute.value = attributeValue;
+ domParentNode.setAttributeNode(domAttribute);
+ }
+ }
+ }
+
+ }
+}
+else
+{
+ document.reloadDom = function() {}
+ XPathResult.prototype.getStringValue = function()
+ {
+ return this.stringValue;
+ }
+
+ XPathResult.prototype.getNumberValue = function()
+ {
+ return this.numberValue;
+ }
+
+ XPathResult.prototype.getBooleanValue = function()
+ {
+ return this.booleanValue;
+ }
+
+ XPathResult.prototype.getSingleNodeValue = function()
+ {
+ return this.singleNodeValue;
+ }
+
+ XPathResult.prototype.getInvalidIteratorState = function()
+ {
+ return this.invalidIteratorState;
+ }
+
+ XPathResult.prototype.getSnapshotLength = function()
+ {
+ return this.snapshotLength;
+ }
+
+ XPathResult.prototype.getResultType = function()
+ {
+ return this.resultType;
+ }
+}
\ No newline at end of file
diff --git a/tests/FunctionalTests/selenium/html-xpath/license.txt b/tests/FunctionalTests/selenium/html-xpath/license.txt
new file mode 100644
index 00000000..b1e3f5a2
--- /dev/null
+++ b/tests/FunctionalTests/selenium/html-xpath/license.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/tests/FunctionalTests/selenium/html-xpath/rainbow.jpg b/tests/FunctionalTests/selenium/html-xpath/rainbow.jpg
new file mode 100644
index 00000000..757995b6
Binary files /dev/null and b/tests/FunctionalTests/selenium/html-xpath/rainbow.jpg differ
diff --git a/tests/FunctionalTests/selenium/htmlutils.js b/tests/FunctionalTests/selenium/htmlutils.js
new file mode 100644
index 00000000..73922b13
--- /dev/null
+++ b/tests/FunctionalTests/selenium/htmlutils.js
@@ -0,0 +1,283 @@
+/*
+ * 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 some HTML utility functions that
+// make it possible to handle elements in a way that is
+// compatible with both IE-like and Mozilla-like browsers
+
+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) {
+ text = "";
+
+ if(element.textContent) {
+ text = element.textContent;
+ } else if(element.innerText) {
+ text = element.innerText;
+ }
+ // Replace with a space
+ // TODO - should this be in the match() code instead?
+ text = text.replace(/\240/g, " ");
+ return text.trim();
+}
+
+// Sets the text in this element
+function setText(element, text) {
+ if(element.textContent) {
+ element.textContent = text;
+ } else if(element.innerText) {
+ element.innerText = text;
+ }
+}
+
+// Get the value of an element
+function getInputValue(inputElement) {
+ if (inputElement.type.toUpperCase() == 'CHECKBOX' ||
+ inputElement.type.toUpperCase() == 'RADIO')
+ {
+ return (inputElement.checked ? 'on' : 'off');
+ }
+ return inputElement.value;
+}
+
+/* Fire an event in a browser-compatible manner */
+function triggerEvent(element, eventType, canBubble) {
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+ if (element.fireEvent) {
+ element.fireEvent('on' + eventType);
+ }
+ else {
+ var evt = document.createEvent('HTMLEvents');
+ evt.initEvent(eventType, canBubble, true);
+ element.dispatchEvent(evt);
+ }
+}
+
+/* Fire a mouse event in a browser-compatible manner */
+function triggerMouseEvent(element, eventType, canBubble) {
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
+ if (element.fireEvent) {
+ element.fireEvent('on' + eventType);
+ }
+ else {
+ var evt = document.createEvent('MouseEvents');
+ evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+ element.dispatchEvent(evt);
+ }
+}
+
+function removeLoadListener(element, command) {
+ if (window.removeEventListener)
+ element.removeEventListener("load", command, true);
+ else if (window.detachEvent)
+ element.detachEvent("onload", command);
+}
+
+function addLoadListener(element, command) {
+ if (window.addEventListener)
+ element.addEventListener("load",command, true);
+ else if (window.attachEvent)
+ element.attachEvent("onload",command);
+}
+
+function addUnloadListener(element, command) {
+ if (window.addEventListener)
+ element.addEventListener("unload",command, true);
+ else if (window.attachEvent)
+ element.attachEvent("onunload",command);
+}
+
+/**
+ * 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 describe(object, delimiter) {
+ var props = new Array();
+ for (var prop in object) {
+ props.push(prop + " -> " + object[prop]);
+ }
+ return props.join(delimiter || '\n');
+}
+
+PatternMatcher = function(pattern) {
+ this.selectStrategy(pattern);
+};
+PatternMatcher.prototype = {
+
+ selectStrategy: function(pattern) {
+ this.pattern = pattern;
+ var strategyName = 'glob'; // by default
+ if (/^([a-z-]+):(.*)/.test(pattern)) {
+ strategyName = RegExp.$1;
+ pattern = RegExp.$2;
+ }
+ var matchStrategy = PatternMatcher.strategies[strategyName];
+ if (!matchStrategy) {
+ throw new SeleniumError("cannot find PatternMatcher.strategies." + strategyName);
+ }
+ 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);
+ };
+ },
+
+ /**
+ * "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.regexpFromGlob = function(glob) {
+ var re = glob;
+ re = re.replace(/([.^$+(){}\[\]\\|])/g, "\\$1");
+ re = re.replace(/\?/g, "(.|[\r\n])");
+ re = re.replace(/\*/g, "(.|[\r\n])*");
+ return "^" + re + "$";
+};
+
+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.failureMessage = message;
+}
+
+function SeleniumError(message) {
+ var error = new Error(message);
+ error.isSeleniumError = true;
+ return error;
+};
diff --git a/tests/FunctionalTests/selenium/index.html b/tests/FunctionalTests/selenium/index.html
new file mode 100644
index 00000000..eaaaa308
--- /dev/null
+++ b/tests/FunctionalTests/selenium/index.html
@@ -0,0 +1,60 @@
+
+
+Selenium starting points
+
+
+
+
+
+
Selenium
+
+
+Acceptance tests: These test-suites demonstrate/exercise the
+functionality of Selenium.
+
+
+
+
diff --git a/tests/FunctionalTests/selenium/install-readme.txt b/tests/FunctionalTests/selenium/install-readme.txt
new file mode 100644
index 00000000..0946f4c4
--- /dev/null
+++ b/tests/FunctionalTests/selenium/install-readme.txt
@@ -0,0 +1,9 @@
+Copy the "selenium" folder to a web accessible directory in the same web server as the application you want to test.
+In Apache, this would mean a subdirectory of "htdocs".
+
+Because of javascript security settings standard in most browsers, Selenium needs to be available on the same host and port as your application.
+
+Once deployed to the server, to run Selenium's self-tests, check out:
+http://:/selenium/TestRunner.html
+
+Read the website for more details. (http://selenium.thoughtworks.com)
\ No newline at end of file
diff --git a/tests/FunctionalTests/selenium/jsmock/mock-tests.html b/tests/FunctionalTests/selenium/jsmock/mock-tests.html
new file mode 100644
index 00000000..f0cc6758
--- /dev/null
+++ b/tests/FunctionalTests/selenium/jsmock/mock-tests.html
@@ -0,0 +1,205 @@
+
+
+
+
+
+
+ JsMock Tests
+
+
+
+
+
+
+
+
JsMock Tests
+
+
This page contains tests for JsMock. To see them, take a look at the source. To run them, load this file via JsUnit's testRunner.html
+
+
diff --git a/tests/FunctionalTests/selenium/jsmock/mock.js b/tests/FunctionalTests/selenium/jsmock/mock.js
new file mode 100644
index 00000000..8c0b4b9f
--- /dev/null
+++ b/tests/FunctionalTests/selenium/jsmock/mock.js
@@ -0,0 +1,124 @@
+/*
+ * 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 simple mock library for Javascript
+//
+// Original code by Aslak Hellesoy and Ji Wang
+
+Mock = function() {
+ this.expectedInvocations = {};
+ this.expectedArgs = {};
+ this.returnValues = {};
+ this.attrs = [];
+ this.expectedProperties = {};
+
+ this.expects = function() {
+ functionName = arguments[0];
+ this.expectedArgs[functionName] = [];
+ for(i = 1; i < arguments.length; i++) {
+ this.expectedArgs[functionName][i-1] = arguments[i];
+ }
+ javascriptCode = "this." + functionName + " = function() {\n" +
+ " // mark this function as \"executed\"\n" +
+ " this.expectedInvocations[\"" + functionName + "\"] = true;\n" +
+ " assertEquals(\"" + functionName + ": Wrong number of arguments.\", " + this.expectedArgs[functionName].length + ", arguments.length);\n" +
+ " for(i = 0; i < arguments.length; i++) {\n" +
+ " assertEquals(this.expectedArgs[\"" + functionName + "\"][i], arguments[i]);\n" +
+ " };\n" +
+ " var returnValue = this.returnValues[\"" + functionName + "\"];\n" +
+ " if (returnValue && returnValue.isMockError) { throw returnValue };\n" +
+ " return returnValue;\n" +
+ "}";
+ eval(javascriptCode);
+ // initially mark this function as "not yet executed"
+ this.expectedInvocations[functionName] = false;
+ this.attrs[this.attrs.length] = "dummy";
+ return new Returner(this, functionName);
+ };
+
+ this.expectsProperty = function() {
+ var propertyName = arguments[0];
+ if(arguments.length == 2) {
+ expectedPropertyValue = arguments[1];
+ this.expectedProperties[propertyName] = expectedPropertyValue;
+ this.attrs[this.attrs.length] = "dummy";
+ } else {
+ return new PropertySetter(this, propertyName);
+ }
+ };
+
+ this.verify = function() {
+ // loop over all expected invocations and see if they were called
+ for(var functionName in this.expectedInvocations) {
+ var wasCalled = this.expectedInvocations[functionName];
+ if(!wasCalled) {
+ fail("Expected function not called:" + functionName);
+ }
+ }
+ var currentAttrs = [];
+ var currentAttrCount = 0;
+
+ // verify that all expected properties are set
+// for(var attr in this) {
+// currentAttrs[currentAttrCount] = attr;
+// currentAttrCount++;
+// }
+// if(this.attrs.length < currentAttrCount) {
+// unexpectedAttr = currentAttrs[this.attrs.length]
+// fail("Unexpected property was set: " + unexpectedAttr + "=" + eval("this." + unexpectedAttr))
+// }
+
+ // verify that all expected properties are set with the right value
+// for(var attr in this.expectedProperties) {
+// if(this.expectedProperties[attr] != eval("this." + attr)) {
+// fail("Expected property was not set: " + attr + "=" + this.expectedProperties[attr])
+// }
+// }
+ };
+
+ var attrCount = 0;
+ for(var attr in this) {
+ this.attrs[attrCount] = attr;
+ attrCount++;
+ }
+};
+
+Returner = function(mock, functionName) {
+ this.mock = mock;
+ this.functionName = functionName;
+};
+
+Returner.prototype.returns = function(returnValue) {
+ this.mock.returnValues[this.functionName] = returnValue;
+};
+
+Returner.prototype.andThrows = function(message) {
+ var error = new Error(message);
+ error.isMockError = true;
+ this.mock.returnValues[this.functionName] = error;
+};
+
+PropertySetter = function(mock, propertyName) {
+ this.mock = mock;
+ this.propertyName = propertyName;
+};
+
+PropertySetter.prototype.returns = function(returnValue) {
+ var ref = new Object();
+ ref.value = returnValue;
+ eval("this.mock." + this.propertyName + "=ref.value");
+};
diff --git a/tests/FunctionalTests/selenium/php/TestRunner.php b/tests/FunctionalTests/selenium/php/TestRunner.php
new file mode 100644
index 00000000..fb3cefb3
--- /dev/null
+++ b/tests/FunctionalTests/selenium/php/TestRunner.php
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+Prado Functional Test Runner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+